From 6a201d61fff91ab21c1ace1d5ba8e6cf498b6f4b Mon Sep 17 00:00:00 2001 From: Tianon Gravi Date: Tue, 9 Jan 2024 12:46:46 -0800 Subject: [PATCH] Add better `ModTime` in `gitfs` and `ArchGitTime` helper (for `SOURCE_DATE_EPOCH`) --- cmd/bashbrew/git.go | 16 ++++++++++++++++ pkg/gitfs/fs.go | 13 ++++++++++++- pkg/gitfs/tarscrub_test.go | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 60 insertions(+), 1 deletion(-) diff --git a/cmd/bashbrew/git.go b/cmd/bashbrew/git.go index 986486ac..9a424d83 100644 --- a/cmd/bashbrew/git.go +++ b/cmd/bashbrew/git.go @@ -11,6 +11,7 @@ import ( "path/filepath" "regexp" "strings" + "time" "github.com/urfave/cli" @@ -110,6 +111,21 @@ func (r Repo) archGitFS(arch string, entry *manifest.Manifest2822Entry) (fs.FS, return fs.Sub(gitFS, entry.ArchDirectory(arch)) } +// returns the timestamp of the ArchGitCommit -- useful for SOURCE_DATE_EPOCH +func (r Repo) ArchGitTime(arch string, entry *manifest.Manifest2822Entry) (time.Time, error) { + f, err := r.archGitFS(arch, entry) + if err != nil { + return time.Time{}, err + } + + fi, err := fs.Stat(f, ".") + if err != nil { + return time.Time{}, err + } + + return fi.ModTime(), nil +} + func gitCommitFS(commit string) (fs.FS, error) { if err := ensureGitInit(); err != nil { return nil, err diff --git a/pkg/gitfs/fs.go b/pkg/gitfs/fs.go index d9e3f36a..040705d4 100644 --- a/pkg/gitfs/fs.go +++ b/pkg/gitfs/fs.go @@ -17,6 +17,14 @@ import ( // https://github.com/go-git/go-git/issues/296 +func CommitTime(commit *goGitPlumbingObject.Commit) time.Time { + if commit.Committer.When.After(commit.Author.When) { + return commit.Committer.When + } else { + return commit.Author.When + } +} + func CommitHash(repo *goGit.Repository, commit string) (fs.FS, error) { gitCommit, err := repo.CommitObject(goGitPlumbing.NewHash(commit)) if err != nil { @@ -31,6 +39,7 @@ func CommitHash(repo *goGit.Repository, commit string) (fs.FS, error) { storer: repo.Storer, tree: tree, name: ".", + Mod: CommitTime(gitCommit), }, }, nil } @@ -49,6 +58,8 @@ type gitFS struct { tree *goGitPlumbingObject.Tree entry *goGitPlumbingObject.TreeEntry // might be nil ("." at the top-level of the repo) + Mod time.Time + // cached values name string // full path from the repository root size int64 // Tree.Size value for non-directories (more efficient than opening/reading the blob) @@ -343,7 +354,7 @@ func (f gitFS) Mode() fs.FileMode { // https://pkg.go.dev/io/fs#FileInfo: modification time func (f gitFS) ModTime() time.Time { - return time.Time{} // TODO maybe pass down whichever is more recent of commit.Author.When vs commit.Committer.When ? + return f.Mod } // https://pkg.go.dev/io/fs#FileInfo: abbreviation for Mode().IsDir() diff --git a/pkg/gitfs/tarscrub_test.go b/pkg/gitfs/tarscrub_test.go index 7de592e1..6a1e9fe1 100644 --- a/pkg/gitfs/tarscrub_test.go +++ b/pkg/gitfs/tarscrub_test.go @@ -42,3 +42,35 @@ func ExampleGitVarnish() { fmt.Printf("%x\n", h.Sum(nil)) // Output: 3aef5ac859b23d65dfe5e9f2a47750e9a32852222829cfba762a870c1473fad6 } + +// this example is nice because it has a different committer vs author timestamp +// https://github.com/tianon/docker-bash/commit/eb7e541caccc813d297e77cf4068f89553256673 +// https://github.com/docker-library/official-images/blob/8718b8afb62ff1a001d99bb4f77d95fe352ba187/library/bash +func ExampleGitBash() { + repo, err := git.Clone(memory.NewStorage(), nil, &git.CloneOptions{ + URL: "https://github.com/tianon/docker-bash.git", + SingleBranch: true, + }) + if err != nil { + panic(err) + } + + commit, err := gitfs.CommitHash(repo, "eb7e541caccc813d297e77cf4068f89553256673") + if err != nil { + panic(err) + } + + f, err := fs.Sub(commit, "5.2") + if err != nil { + panic(err) + } + + h := sha256.New() + + if err := tarscrub.WriteTar(f, h); err != nil { + panic(err) + } + + fmt.Printf("%x\n", h.Sum(nil)) + // Output: 011c8eda9906b94916a14e7969cfcff3974f1fbf2ff7b8e5c1867ff491dc01d3 +}