]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/go: stamp VCS commit time into binaries
authorMark Pulford <mark@kyne.com.au>
Sat, 16 Oct 2021 06:53:31 +0000 (17:53 +1100)
committerBryan C. Mills <bcmills@google.com>
Tue, 26 Oct 2021 17:10:45 +0000 (17:10 +0000)
Only Git and Mercurial are supported for now.

This CL also:
- Skips tagging "revision" and "committime" for empty repositories.
- Stores the full Mercurial changeset ID instead of the short form.

Fixes #37475

Change-Id: I62ab7a986d1ddb2a0e7166a6404b5aa80c2ee387
Reviewed-on: https://go-review.googlesource.com/c/go/+/356251
Reviewed-by: Bryan C. Mills <bcmills@google.com>
Trust: Bryan C. Mills <bcmills@google.com>
Trust: Michael Matloob <matloob@golang.org>
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Go Bot <gobot@golang.org>

src/cmd/go/internal/load/pkg.go
src/cmd/go/internal/vcs/vcs.go
src/cmd/go/testdata/script/version_buildvcs_git.txt
src/cmd/go/testdata/script/version_buildvcs_hg.txt

index a5be48a49bc2e009d4f8c704415de532663431b2..dfe7849516a48b26f12e205476285bfef9ba7eb3 100644 (file)
@@ -25,6 +25,7 @@ import (
        "sort"
        "strconv"
        "strings"
+       "time"
        "unicode"
        "unicode/utf8"
 
@@ -2364,10 +2365,14 @@ func (p *Package) setBuildInfo() {
                        setVCSError(err)
                        return
                }
-               info.Settings = append(info.Settings, []debug.BuildSetting{
-                       {Key: vcsCmd.Cmd + "revision", Value: st.Revision},
-                       {Key: vcsCmd.Cmd + "uncommitted", Value: strconv.FormatBool(st.Uncommitted)},
-               }...)
+               if st.Revision != "" {
+                       appendSetting(vcsCmd.Cmd+"revision", st.Revision)
+               }
+               if !st.CommitTime.IsZero() {
+                       stamp := st.CommitTime.UTC().Format(time.RFC3339Nano)
+                       appendSetting(vcsCmd.Cmd+"committime", stamp)
+               }
+               appendSetting(vcsCmd.Cmd+"uncommitted", strconv.FormatBool(st.Uncommitted))
        }
 
        text, err := info.MarshalText()
index d1272b66e91dfcb3523971263ec1eb938ae79509..941bd57147e244ecd3e84c31f28f057cc1a1b528 100644 (file)
@@ -18,8 +18,10 @@ import (
        "os"
        "path/filepath"
        "regexp"
+       "strconv"
        "strings"
        "sync"
+       "time"
 
        "cmd/go/internal/base"
        "cmd/go/internal/cfg"
@@ -54,8 +56,9 @@ type Cmd struct {
 
 // Status is the current state of a local repository.
 type Status struct {
-       Revision    string
-       Uncommitted bool
+       Revision    string    // Optional.
+       CommitTime  time.Time // Optional.
+       Uncommitted bool      // Required.
 }
 
 var defaultSecureScheme = map[string]bool{
@@ -159,24 +162,52 @@ func hgRemoteRepo(vcsHg *Cmd, rootDir string) (remoteRepo string, err error) {
 }
 
 func hgStatus(vcsHg *Cmd, rootDir string) (Status, error) {
-       out, err := vcsHg.runOutputVerboseOnly(rootDir, "identify -i")
+       // Output changeset ID and seconds since epoch.
+       out, err := vcsHg.runOutputVerboseOnly(rootDir, `log -l1 -T {node}:{date(date,"%s")}`)
        if err != nil {
                return Status{}, err
        }
-       rev := strings.TrimSpace(string(out))
-       uncommitted := strings.HasSuffix(rev, "+")
-       if uncommitted {
-               // "+" means a tracked file is edited.
-               rev = rev[:len(rev)-len("+")]
-       } else {
-               // Also look for untracked files.
-               out, err = vcsHg.runOutputVerboseOnly(rootDir, "status -u")
+
+       // Successful execution without output indicates an empty repo (no commits).
+       var rev string
+       var commitTime time.Time
+       if len(out) > 0 {
+               rev, commitTime, err = parseRevTime(out)
                if err != nil {
                        return Status{}, err
                }
-               uncommitted = len(out) > 0
        }
-       return Status{Revision: rev, Uncommitted: uncommitted}, nil
+
+       // Also look for untracked files.
+       out, err = vcsHg.runOutputVerboseOnly(rootDir, "status")
+       if err != nil {
+               return Status{}, err
+       }
+       uncommitted := len(out) > 0
+
+       return Status{
+               Revision:    rev,
+               CommitTime:  commitTime,
+               Uncommitted: uncommitted,
+       }, nil
+}
+
+// parseRevTime parses commit details in "revision:seconds" format.
+func parseRevTime(out []byte) (string, time.Time, error) {
+       buf := string(bytes.TrimSpace(out))
+
+       i := strings.IndexByte(buf, ':')
+       if i < 1 {
+               return "", time.Time{}, errors.New("unrecognized VCS tool output")
+       }
+       rev := buf[:i]
+
+       secs, err := strconv.ParseInt(string(buf[i+1:]), 10, 64)
+       if err != nil {
+               return "", time.Time{}, fmt.Errorf("unrecognized VCS tool output: %v", err)
+       }
+
+       return rev, time.Unix(secs, 0), nil
 }
 
 // vcsGit describes how to use Git.
@@ -263,18 +294,33 @@ func gitRemoteRepo(vcsGit *Cmd, rootDir string) (remoteRepo string, err error) {
        return "", errParse
 }
 
-func gitStatus(cmd *Cmd, repoDir string) (Status, error) {
-       out, err := cmd.runOutputVerboseOnly(repoDir, "rev-parse HEAD")
+func gitStatus(vcsGit *Cmd, rootDir string) (Status, error) {
+       out, err := vcsGit.runOutputVerboseOnly(rootDir, "status --porcelain")
        if err != nil {
                return Status{}, err
        }
-       rev := string(bytes.TrimSpace(out))
-       out, err = cmd.runOutputVerboseOnly(repoDir, "status --porcelain")
-       if err != nil {
+       uncommitted := len(out) > 0
+
+       // "git status" works for empty repositories, but "git show" does not.
+       // Assume there are no commits in the repo when "git show" fails with
+       // uncommitted files and skip tagging revision / committime.
+       var rev string
+       var commitTime time.Time
+       out, err = vcsGit.runOutputVerboseOnly(rootDir, "show -s --format=%H:%ct")
+       if err != nil && !uncommitted {
                return Status{}, err
+       } else if err == nil {
+               rev, commitTime, err = parseRevTime(out)
+               if err != nil {
+                       return Status{}, err
+               }
        }
-       uncommitted := len(out) != 0
-       return Status{Revision: rev, Uncommitted: uncommitted}, nil
+
+       return Status{
+               Revision:    rev,
+               CommitTime:  commitTime,
+               Uncommitted: uncommitted,
+       }, nil
 }
 
 // vcsBzr describes how to use Bazaar.
index 78ce2e835ec9c1e647f5d1dfe43c65df2caf02a9..3d56c6d8b465a87310ada4fe54d8e00fb010576a 100644 (file)
@@ -28,16 +28,25 @@ cd ..
 env PATH=$oldpath
 rm .git
 
-# If there is a repository in a parent directory, there should be VCS info.
+# If there is an empty repository in a parent directory, only "uncommitted" is tagged.
 exec git init
 exec git config user.email gopher@golang.org
 exec git config user.name 'J.R. Gopher'
+cd a
+go install
+go version -m $GOBIN/a$GOEXE
+! stdout gitrevision
+! stdout gitcommittime
+stdout '^\tbuild\tgituncommitted\ttrue$'
+rm $GOBIN/a$GOEXE
+
+# Revision and commit time are tagged for repositories with commits.
 exec git add -A
 exec git commit -m 'initial commit'
-cd a
 go install
 go version -m $GOBIN/a$GOEXE
 stdout '^\tbuild\tgitrevision\t'
+stdout '^\tbuild\tgitcommittime\t'
 stdout '^\tbuild\tgituncommitted\tfalse$'
 rm $GOBIN/a$GOEXE
 
index 9dcb8dd950d3b30c8b982b5594888c1b1bf4d1d0..df4938742d6da48dd0ddff88b1776eb8a0a0a984 100644 (file)
@@ -29,14 +29,24 @@ cd ..
 env PATH=$oldpath
 rm .hg
 
-# If there is a repository in a parent directory, there should be VCS info.
+# If there is an empty repository in a parent directory, only "uncommitted" is tagged.
 exec hg init
+cd a
+go install
+go version -m $GOBIN/a$GOEXE
+! stdout hgrevision
+! stdout hgcommittime
+stdout '^\tbuild\thguncommitted\ttrue$'
+cd ..
+
+# Revision and commit time are tagged for repositories with commits.
 exec hg add a README
 exec hg commit -m 'initial commit'
 cd a
 go install
 go version -m $GOBIN/a$GOEXE
 stdout '^\tbuild\thgrevision\t'
+stdout '^\tbuild\thgcommittime\t'
 stdout '^\tbuild\thguncommitted\tfalse$'
 rm $GOBIN/a$GOEXE