Skip to content

Commit 0bb9ced

Browse files
committed
Add ArchGitChecksum template command in bashbrew cat
This also finally adds `bashbrew context` as an explicit subcommand so that issues with this code are easier to test/debug (so we can generate the actual tarball and compare it to previous versions of it, versions generated by `git archive`, etc). As-is, this currently generates verbatim identical checksums to https://github.com/docker-library/meta-scripts/blob/0cde8de57dfe411ed5578feffe1b10f811e11dc2/sources.sh#L90-L96 (by design). We'll wait to do any cache bust there until we implement `Dockerfile`/context filtering: ```console $ bashbrew cat varnish:stable --format '{{ .TagEntry.GitCommit }} {{ .TagEntry.Directory }}' 0c295b528f28a98650fb2580eab6d34b30b165c4 stable/debian $ git -C "$BASHBREW_CACHE/git" archive 0c295b528f28a98650fb2580eab6d34b30b165c4:stable/debian/ | ./tar-scrubber | sha256sum 3aef5ac859b23d65dfe5e9f2a47750e9a32852222829cfba762a870c1473fad6 $ bashbrew cat --format '{{ .ArchGitChecksum arch .TagEntry }}' varnish:stable 3aef5ac859b23d65dfe5e9f2a47750e9a32852222829cfba762a870c1473fad6 ``` (Choosing `varnish:stable` there because it currently has [some 100% valid dangling symlinks](https://github.com/varnish/docker-varnish/tree/6b1c6ffedcfececac71e46a85122c1adaef25868/stable/debian/scripts) that tripped up my code beautifully 💕) From a performance perspective (which was the original reason for looking into / implementing this), running the `meta-scripts/sources.sh` script against `--all` vs this, my local system gets ~18.5m vs ~4.5m (faster being this new pure-Go implementation).
1 parent 4e0ea8d commit 0bb9ced

File tree

10 files changed

+601
-103
lines changed

10 files changed

+601
-103
lines changed

cmd/bashbrew/git.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,20 @@ func getGitCommit(commit string) (string, error) {
9696
return h.String(), nil
9797
}
9898

99+
func (r Repo) archGitFS(arch string, entry *manifest.Manifest2822Entry) (fs.FS, error) {
100+
commit, err := r.fetchGitRepo(arch, entry)
101+
if err != nil {
102+
return nil, fmt.Errorf("failed fetching %q: %w", r.EntryIdentifier(entry), err)
103+
}
104+
105+
gitFS, err := gitCommitFS(commit)
106+
if err != nil {
107+
return nil, err
108+
}
109+
110+
return fs.Sub(gitFS, entry.ArchDirectory(arch))
111+
}
112+
99113
func gitCommitFS(commit string) (fs.FS, error) {
100114
if err := ensureGitInit(); err != nil {
101115
return nil, err

cmd/bashbrew/main.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ package main
33
import (
44
"fmt"
55
"os"
6+
"path"
67
"path/filepath"
78
"strings"
89

910
"github.com/sirupsen/logrus" // this is used by containerd libraries, so we need to set the default log level for it
1011
"github.com/urfave/cli"
12+
xTerm "golang.org/x/term"
1113

1214
"github.com/docker-library/bashbrew/architecture"
1315
"github.com/docker-library/bashbrew/manifest"
@@ -421,6 +423,61 @@ func main() {
421423

422424
Category: "plumbing",
423425
},
426+
{
427+
Name: "context",
428+
Usage: "(eventually Dockerfile-filtered) git archive",
429+
Flags: []cli.Flag{
430+
cli.BoolFlag{
431+
Name: "sha256",
432+
Usage: `print sha256 instead of raw tar`,
433+
},
434+
// TODO "unfiltered" or something for not applying Dockerfile filtering (once that's implemented)
435+
},
436+
Before: subcommandBeforeFactory("context"),
437+
Action: func(c *cli.Context) error {
438+
repos, err := repos(false, c.Args()...)
439+
if err != nil {
440+
return err
441+
}
442+
if len(repos) != 1 {
443+
return fmt.Errorf("'context' expects to act on exactly one architecture of one entry of one repo (got %d repos)", len(repos))
444+
}
445+
446+
r, err := fetch(repos[0])
447+
if err != nil {
448+
return err
449+
}
450+
451+
// TODO technically something like "hello-world:latest" *could* be relaxed a little if it resolves via architecture to one and only one entry 🤔 (but that's a little hard to implement with the existing internal data structures -- see TODO at the top of "sort.go")
452+
453+
if r.TagEntry == nil {
454+
return fmt.Errorf("'context' expects to act on exactly one architecture of one entry of one repo (no specific entry of %q selected)", r.RepoName)
455+
}
456+
if len(r.TagEntries) != 1 {
457+
return fmt.Errorf("'context' expects to act on exactly one architecture of one entry of one repo (got %d entires)", len(r.TagEntries))
458+
}
459+
460+
if !r.TagEntry.HasArchitecture(arch) {
461+
return fmt.Errorf("%q does not include architecture %q", path.Join(namespace, r.RepoName)+":"+r.TagEntry.Tags[0], arch)
462+
}
463+
464+
if c.Bool("sha256") {
465+
sum, err := r.ArchGitChecksum(arch, r.TagEntry)
466+
if err != nil {
467+
return err
468+
}
469+
fmt.Println(sum)
470+
return nil
471+
} else {
472+
if xTerm.IsTerminal(int(os.Stdout.Fd())) {
473+
return fmt.Errorf("cowardly refusing to output a tar to a terminal")
474+
}
475+
return r.archContextTar(arch, r.TagEntry, os.Stdout)
476+
}
477+
},
478+
479+
Category: "plumbing",
480+
},
424481
{
425482
Name: "remote",
426483
Usage: "query registries for bashbrew-related data",

cmd/bashbrew/oci-builder.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ func importOCIBlob(ctx context.Context, cs content.Store, fs iofs.FS, descriptor
8989

9090
// this is "docker build" but for "Builder: oci-import"
9191
func ociImportBuild(tags []string, commit, dir, file string) (*imagespec.Descriptor, error) {
92+
// TODO use r.archGitFS (we have no r or arch or entry here 😅)
9293
fs, err := gitCommitFS(commit)
9394
if err != nil {
9495
return nil, err

cmd/bashbrew/sort.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"pault.ag/go/topsort"
66
)
77

8+
// TODO unify archFilter and applyConstraints handling by pre-filtering the full list of Repo objects such that all that remains are things we should process (thus removing all "if" statements throughout the various loops); re-doing the Architectures and Entries lists to only include ones we should process, etc
9+
810
func sortRepos(repos []string, applyConstraints bool) ([]string, error) {
911
rs := []*Repo{}
1012
rsMap := map[*Repo]string{}
@@ -103,10 +105,10 @@ func sortRepoObjects(rs []*Repo, applyConstraints bool) ([]*Repo, error) {
103105
continue
104106
}
105107
/*
106-
// TODO need archFilter here :(
107-
if archFilter && !entry.HasArchitecture(arch) {
108-
continue
109-
}
108+
// TODO need archFilter here :(
109+
if archFilter && !entry.HasArchitecture(arch) {
110+
continue
111+
}
110112
*/
111113

112114
entryArches := []string{arch}

cmd/bashbrew/tar.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package main
2+
3+
import (
4+
"crypto/sha256"
5+
"fmt"
6+
"io"
7+
8+
"github.com/docker-library/bashbrew/manifest"
9+
"github.com/docker-library/bashbrew/pkg/tarscrub"
10+
)
11+
12+
func (r Repo) archContextTar(arch string, entry *manifest.Manifest2822Entry, w io.Writer) error {
13+
f, err := r.archGitFS(arch, entry)
14+
if err != nil {
15+
return err
16+
}
17+
18+
return tarscrub.WriteTar(f, w)
19+
}
20+
21+
func (r Repo) ArchGitChecksum(arch string, entry *manifest.Manifest2822Entry) (string, error) {
22+
h := sha256.New()
23+
err := r.archContextTar(arch, entry, h)
24+
if err != nil {
25+
return "", err
26+
}
27+
return fmt.Sprintf("%x", h.Sum(nil)), nil
28+
}

0 commit comments

Comments
 (0)