Skip to content

Commit b8eda2b

Browse files
authored
Merge pull request #55 from infosiftr/parent-children-rewrite
Rewrite "bashbrew children" and "bashbrew parents"
2 parents 8281cbe + f54c8e3 commit b8eda2b

File tree

10 files changed

+408
-973
lines changed

10 files changed

+408
-973
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ jobs:
1616
runs-on: ubuntu-latest
1717
steps:
1818
- uses: actions/checkout@v2
19+
- uses: actions/setup-go@v3
20+
with:
21+
go-version: '>=1.18'
1922
- name: Build
2023
run: |
2124
./bashbrew.sh --version > /dev/null

cmd/bashbrew/cmd-children.go

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package main
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"path"
7+
"strings"
8+
9+
"github.com/urfave/cli"
10+
)
11+
12+
func cmdChildren(c *cli.Context) error {
13+
// we don't need this until later, but we want to bail early if we don't have it (before we've done a lot of work creating the graph)
14+
args := c.Args()
15+
if len(args) < 1 {
16+
return fmt.Errorf(`need at least one argument`)
17+
}
18+
19+
allRepos, err := repos(true)
20+
if err != nil {
21+
return cli.NewMultiError(fmt.Errorf(`failed gathering ALL repos list`), err)
22+
}
23+
24+
applyConstraints := c.Bool("apply-constraints")
25+
archFilter := c.Bool("arch-filter")
26+
27+
// build up a list of canonical tag mappings and canonical tag architectures
28+
canonical := map[string]string{}
29+
arches := dedupeSliceMap[string, string]{}
30+
for _, repo := range allRepos {
31+
r, err := fetch(repo)
32+
if err != nil {
33+
return cli.NewMultiError(fmt.Errorf(`failed fetching repo %q`, repo), err)
34+
}
35+
36+
for _, entry := range r.Entries() {
37+
if applyConstraints && r.SkipConstraints(entry) {
38+
continue
39+
}
40+
if archFilter && !entry.HasArchitecture(arch) {
41+
continue
42+
}
43+
44+
tags := r.Tags(namespace, false, entry)
45+
for _, tag := range tags {
46+
canonical[tag] = tags[0]
47+
}
48+
49+
entryArches := []string{arch}
50+
if !applyConstraints && !archFilter {
51+
entryArches = entry.Architectures
52+
}
53+
for _, entryArch := range entryArches {
54+
arches.add(tags[0], entryArch)
55+
}
56+
}
57+
}
58+
59+
// now build up a map of FROM -> canonical tag references and a "repo -> tags" lookup (including things like "alpine:3.11" that are no longer supported)
60+
// for non-canonical/unsupported tags, auto-create/supplement their "arches" list from the thing that's FROM them (so we can filter properly later and make sure "bashbrew children mcr.microsoft.com/windows/servercore" doesn't list non-Windows images that happen to be "FROM xyz-shared-tag" that includes Windows)
61+
children := dedupeSliceMap[string, string]{}
62+
repoTags := dedupeSliceMap[string, string]{}
63+
for _, repo := range allRepos {
64+
r, err := fetch(repo)
65+
if err != nil {
66+
return cli.NewMultiError(fmt.Errorf(`failed fetching repo %q`, repo), err)
67+
}
68+
69+
nsRepo := path.Join(namespace, r.RepoName)
70+
71+
for _, entry := range r.Entries() {
72+
if applyConstraints && r.SkipConstraints(entry) {
73+
continue
74+
}
75+
if archFilter && !entry.HasArchitecture(arch) {
76+
continue
77+
}
78+
79+
entryArches := []string{arch}
80+
if !applyConstraints && !archFilter {
81+
entryArches = entry.Architectures
82+
}
83+
84+
tag := nsRepo + ":" + entry.Tags[0]
85+
repoTags.add(nsRepo, tag)
86+
87+
for _, entryArch := range entryArches {
88+
froms, err := r.ArchDockerFroms(entryArch, entry)
89+
if err != nil {
90+
return cli.NewMultiError(fmt.Errorf(`failed fetching/scraping FROM for %q (tags %q, arch %q)`, r.RepoName, entry.TagsString(), entryArch), err)
91+
}
92+
93+
for _, from := range froms {
94+
if canon, ok := canonical[from]; ok {
95+
from = canon
96+
} else {
97+
// must be unsupported, let's make sure our current implied supported architecture value for it is recorded!
98+
arches.add(from, entryArch)
99+
}
100+
children.add(from, tag)
101+
if fromRepo, _, ok := strings.Cut(from, ":"); ok {
102+
// make sure things like old "alpine" tags that are no longer supported still come up with "bashbrew children alpine"
103+
repoTags.add(fromRepo, from)
104+
}
105+
}
106+
}
107+
}
108+
}
109+
110+
uniq := c.Bool("uniq")
111+
depth := c.Int("depth")
112+
113+
// used in conjunction with "uniq" to make sure we print a given tag once and only once when enabled
114+
seen := map[string]struct{}{}
115+
116+
for _, arg := range args {
117+
var tags []string
118+
if children.has(arg) {
119+
// if the string has children, let's walk them verbatim
120+
tags = []string{arg}
121+
} else if tag, ok := canonical[arg]; ok {
122+
// if the string has a "canonical" tag (meaning is a supported tag), let's use it verbatim (whether it has children or not)
123+
tags = []string{tag}
124+
} else if nsArg := path.Join(namespace, arg); repoTags.has(nsArg) {
125+
// otherwise, let's do a couple lookups based on the provided argument being a repository like "alpine"
126+
tags = repoTags.slice(nsArg)
127+
} else if repoTags.has(arg) {
128+
tags = repoTags.slice(arg)
129+
}
130+
if len(tags) < 1 {
131+
return fmt.Errorf(`failed to resolve argument as repo or tag %q`, arg)
132+
}
133+
134+
for _, tag := range tags {
135+
supportedArches := arches.slice(tag) // this will already be filtered in terms of archFilter / applyConstraints and is pre-implied by the above code for non-supported images like Windows base images (used to filter the children to only those that have intersection to avoid "bashbrew from .../windows/servercore" from listing non-Windows images, for example)
136+
if debugFlag {
137+
fmt.Fprintf(os.Stderr, "DEBUG: relevant architectures of %q: %s\n", tag, strings.Join(supportedArches, ", "))
138+
}
139+
if depth == -1 {
140+
// special value to let "bashbrew children mcr.microsoft.com/windows/servercore" print the list of FROM values in use for a repo
141+
fmt.Println(tag)
142+
continue
143+
}
144+
lookup := []string{tag}
145+
for d := depth; len(lookup) > 0 && (depth == 0 || d > 0); d-- {
146+
nextLookup := []string{}
147+
for _, tag := range lookup {
148+
kids := children.slice(tag)
149+
for _, kid := range kids {
150+
supported := false
151+
for _, arch := range arches.slice(kid) {
152+
if sliceHas[string](supportedArches, arch) {
153+
supported = true
154+
break
155+
}
156+
}
157+
if !supported {
158+
continue
159+
}
160+
nextLookup = append(nextLookup, kid)
161+
if uniq {
162+
if _, ok := seen[kid]; ok {
163+
continue
164+
}
165+
seen[kid] = struct{}{}
166+
}
167+
fmt.Println(kid)
168+
}
169+
}
170+
lookup = nextLookup
171+
}
172+
}
173+
}
174+
175+
return nil
176+
}

cmd/bashbrew/cmd-deps.go

Lines changed: 0 additions & 177 deletions
This file was deleted.

0 commit comments

Comments
 (0)