1
0
mirror of https://github.com/golang/go synced 2024-11-18 00:54:45 -07:00

cmd/go: stamp VCS commit time into binaries

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>
This commit is contained in:
Mark Pulford 2021-10-16 17:53:31 +11:00 committed by Bryan C. Mills
parent 1b2362bb83
commit 76cef81bcf
4 changed files with 98 additions and 28 deletions

View File

@ -25,6 +25,7 @@ import (
"sort" "sort"
"strconv" "strconv"
"strings" "strings"
"time"
"unicode" "unicode"
"unicode/utf8" "unicode/utf8"
@ -2364,10 +2365,14 @@ func (p *Package) setBuildInfo() {
setVCSError(err) setVCSError(err)
return return
} }
info.Settings = append(info.Settings, []debug.BuildSetting{ if st.Revision != "" {
{Key: vcsCmd.Cmd + "revision", Value: st.Revision}, appendSetting(vcsCmd.Cmd+"revision", st.Revision)
{Key: vcsCmd.Cmd + "uncommitted", Value: strconv.FormatBool(st.Uncommitted)}, }
}...) 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() text, err := info.MarshalText()

View File

@ -18,8 +18,10 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"regexp" "regexp"
"strconv"
"strings" "strings"
"sync" "sync"
"time"
"cmd/go/internal/base" "cmd/go/internal/base"
"cmd/go/internal/cfg" "cmd/go/internal/cfg"
@ -54,8 +56,9 @@ type Cmd struct {
// Status is the current state of a local repository. // Status is the current state of a local repository.
type Status struct { type Status struct {
Revision string Revision string // Optional.
Uncommitted bool CommitTime time.Time // Optional.
Uncommitted bool // Required.
} }
var defaultSecureScheme = map[string]bool{ 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) { 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 { if err != nil {
return Status{}, err return Status{}, err
} }
rev := strings.TrimSpace(string(out))
uncommitted := strings.HasSuffix(rev, "+") // Successful execution without output indicates an empty repo (no commits).
if uncommitted { var rev string
// "+" means a tracked file is edited. var commitTime time.Time
rev = rev[:len(rev)-len("+")] if len(out) > 0 {
} else { rev, commitTime, err = parseRevTime(out)
// Also look for untracked files.
out, err = vcsHg.runOutputVerboseOnly(rootDir, "status -u")
if err != nil { if err != nil {
return Status{}, err 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. // vcsGit describes how to use Git.
@ -263,18 +294,33 @@ func gitRemoteRepo(vcsGit *Cmd, rootDir string) (remoteRepo string, err error) {
return "", errParse return "", errParse
} }
func gitStatus(cmd *Cmd, repoDir string) (Status, error) { func gitStatus(vcsGit *Cmd, rootDir string) (Status, error) {
out, err := cmd.runOutputVerboseOnly(repoDir, "rev-parse HEAD") out, err := vcsGit.runOutputVerboseOnly(rootDir, "status --porcelain")
if err != nil { if err != nil {
return Status{}, err return Status{}, err
} }
rev := string(bytes.TrimSpace(out)) uncommitted := len(out) > 0
out, err = cmd.runOutputVerboseOnly(repoDir, "status --porcelain")
if err != nil { // "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 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. // vcsBzr describes how to use Bazaar.

View File

@ -28,16 +28,25 @@ cd ..
env PATH=$oldpath env PATH=$oldpath
rm .git 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 init
exec git config user.email gopher@golang.org exec git config user.email gopher@golang.org
exec git config user.name 'J.R. Gopher' exec git config user.name 'J.R. Gopher'
exec git add -A
exec git commit -m 'initial commit'
cd a cd a
go install go install
go version -m $GOBIN/a$GOEXE 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'
go install
go version -m $GOBIN/a$GOEXE
stdout '^\tbuild\tgitrevision\t' stdout '^\tbuild\tgitrevision\t'
stdout '^\tbuild\tgitcommittime\t'
stdout '^\tbuild\tgituncommitted\tfalse$' stdout '^\tbuild\tgituncommitted\tfalse$'
rm $GOBIN/a$GOEXE rm $GOBIN/a$GOEXE

View File

@ -29,14 +29,24 @@ cd ..
env PATH=$oldpath env PATH=$oldpath
rm .hg 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 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 add a README
exec hg commit -m 'initial commit' exec hg commit -m 'initial commit'
cd a cd a
go install go install
go version -m $GOBIN/a$GOEXE go version -m $GOBIN/a$GOEXE
stdout '^\tbuild\thgrevision\t' stdout '^\tbuild\thgrevision\t'
stdout '^\tbuild\thgcommittime\t'
stdout '^\tbuild\thguncommitted\tfalse$' stdout '^\tbuild\thguncommitted\tfalse$'
rm $GOBIN/a$GOEXE rm $GOBIN/a$GOEXE