diff --git a/src/cmd/linuxkit/pkglib/build.go b/src/cmd/linuxkit/pkglib/build.go index e2a1d50e4..a52a92997 100644 --- a/src/cmd/linuxkit/pkglib/build.go +++ b/src/cmd/linuxkit/pkglib/build.go @@ -70,8 +70,8 @@ func (p Pkg) Build(bos ...BuildOpt) error { return fmt.Errorf("Unknown arch %q", arch) } - if bo.release == "" { - r, err := gitCommitTag("HEAD") + if p.git != nil && bo.release == "" { + r, err := p.git.commitTag("HEAD") if err != nil { return err } @@ -97,11 +97,11 @@ func (p Pkg) Build(bos ...BuildOpt) error { var args []string - if p.gitRepo != "" { + if p.git != nil && p.gitRepo != "" { args = append(args, "--label", "org.opencontainers.image.source="+p.gitRepo) } - if !p.dirty { - commit, err := gitCommitHash("HEAD") + if p.git != nil && !p.dirty { + commit, err := p.git.commitHash("HEAD") if err != nil { return err } diff --git a/src/cmd/linuxkit/pkglib/git.go b/src/cmd/linuxkit/pkglib/git.go index ce2f48ce7..41a8c990d 100644 --- a/src/cmd/linuxkit/pkglib/git.go +++ b/src/cmd/linuxkit/pkglib/git.go @@ -4,6 +4,7 @@ package pkglib import ( "fmt" + "io" "os" "os/exec" "regexp" @@ -19,9 +20,32 @@ func init() { treeHashRe = regexp.MustCompile("^[0-7]{6} [^ ]+ ([0-9a-f]{40})\t.+\n$") } -func gitCommandStdout(args ...string) (string, error) { - cmd := exec.Command("git", args...) - cmd.Stderr = os.Stderr +type git struct { + dir string +} + +// Returns git==nil and no error if the path is not within a git repository +func newGit(dir string) (*git, error) { + g := &git{dir} + + // Check if dir really is within a git directory + ok, err := g.isWorkTree(dir) + if err != nil { + return nil, err + } + if !ok { + return nil, nil + } + return g, nil +} + +func (g git) mkCmd(args ...string) *exec.Cmd { + return exec.Command("git", append([]string{"-C", g.dir}, args...)...) +} + +func (g git) commandStdout(stderr io.Writer, args ...string) (string, error) { + cmd := g.mkCmd(args...) + cmd.Stderr = stderr if debugGitCommands { fmt.Fprintf(os.Stderr, "+ %v\n", cmd.Args) @@ -33,8 +57,8 @@ func gitCommandStdout(args ...string) (string, error) { return string(out), nil } -func gitCommand(args ...string) error { - cmd := exec.Command("git", args...) +func (g git) command(args ...string) error { + cmd := g.mkCmd(args...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if debugGitCommands { @@ -43,8 +67,27 @@ func gitCommand(args ...string) error { return cmd.Run() } -func gitTreeHash(pkg, commit string) (string, error) { - out, err := gitCommandStdout("ls-tree", "--full-tree", commit, "--", pkg) +func (g git) isWorkTree(pkg string) (bool, error) { + tf, err := g.commandStdout(nil, "rev-parse", "--is-inside-work-tree") + if err != nil { + // If we executed git ok but it errored then that's because this isn't a git repo + if _, ok := err.(*exec.ExitError); ok { + return false, nil + } + return false, err + } + + tf = strings.TrimSpace(tf) + + if tf == "true" { + return true, nil + } + + return false, fmt.Errorf("unexpected output from git rev-parse --is-inside-work-tree: %s", tf) +} + +func (g git) treeHash(pkg, commit string) (string, error) { + out, err := g.commandStdout(os.Stderr, "ls-tree", "--full-tree", commit, "--", pkg) if err != nil { return "", err } @@ -57,8 +100,8 @@ func gitTreeHash(pkg, commit string) (string, error) { return matches[1], nil } -func gitCommitHash(commit string) (string, error) { - out, err := gitCommandStdout("rev-parse", commit) +func (g git) commitHash(commit string) (string, error) { + out, err := g.commandStdout(os.Stderr, "rev-parse", commit) if err != nil { return "", err } @@ -66,8 +109,8 @@ func gitCommitHash(commit string) (string, error) { return strings.TrimSpace(out), nil } -func gitCommitTag(commit string) (string, error) { - out, err := gitCommandStdout("tag", "-l", "--points-at", commit) +func (g git) commitTag(commit string) (string, error) { + out, err := g.commandStdout(os.Stderr, "tag", "-l", "--points-at", commit) if err != nil { return "", err } @@ -75,7 +118,7 @@ func gitCommitTag(commit string) (string, error) { return strings.TrimSpace(out), nil } -func gitIsDirty(pkg, commit string) (bool, error) { +func (g git) isDirty(pkg, commit string) (bool, error) { // If it isn't HEAD it can't be dirty if commit != "HEAD" { return false, nil @@ -86,11 +129,11 @@ func gitIsDirty(pkg, commit string) (bool, error) { // because `git diff-index` only uses the `lstat` result and // not the actual file contents. Running `git update-index // --refresh` updates the cache. - if err := gitCommand("update-index", "-q", "--refresh"); err != nil { + if err := g.command("update-index", "-q", "--refresh"); err != nil { return false, err } - err := gitCommand("diff-index", "--quiet", commit, "--", pkg) + err := g.command("diff-index", "--quiet", commit, "--", pkg) if err == nil { return false, nil } diff --git a/src/cmd/linuxkit/pkglib/pkglib.go b/src/cmd/linuxkit/pkglib/pkglib.go index a5578d7f9..5e7ac0a8f 100644 --- a/src/cmd/linuxkit/pkglib/pkglib.go +++ b/src/cmd/linuxkit/pkglib/pkglib.go @@ -36,6 +36,7 @@ type Pkg struct { hash string dirty bool commitHash string + git *git } // NewFromCLI creates a Pkg from a set of CLI arguments. Calls fs.Parse() @@ -138,20 +139,27 @@ func NewFromCLI(fs *flag.FlagSet, args ...string) (Pkg, error) { } }) - gitDirty, err := gitIsDirty(hashPath, hashCommit) + git, err := newGit(pkgPath) if err != nil { return Pkg{}, err } - dirty = dirty || gitDirty - - if hash == "" { - if hash, err = gitTreeHash(hashPath, hashCommit); err != nil { + if git != nil { + gitDirty, err := git.isDirty(hashPath, hashCommit) + if err != nil { return Pkg{}, err } - if dirty { - hash += "-dirty" + dirty = dirty || gitDirty + + if hash == "" { + if hash, err = git.treeHash(hashPath, hashCommit); err != nil { + return Pkg{}, err + } + + if dirty { + hash += "-dirty" + } } } @@ -167,6 +175,7 @@ func NewFromCLI(fs *flag.FlagSet, args ...string) (Pkg, error) { cache: !pi.DisableCache, dirty: dirty, pkgPath: pkgPath, + git: git, }, nil } @@ -189,7 +198,11 @@ func (p Pkg) ReleaseTag(release string) (string, error) { // Tag returns the tag to use for the package func (p Pkg) Tag() string { - return p.org + "/" + p.image + ":" + p.hash + t := p.hash + if t == "" { + t = "latest" + } + return p.org + "/" + p.image + ":" + t } // TrustEnabled returns true if trust is enabled