cmd/go: skip git sha256 tests if git < 2.29

Fix test building on older Ubuntu LTS releases (that are still
supported). Git SHA256 support was only included in 2.29, which came out
in 2021. Check the output of `git version` and skip these tests if the
version is older than that introduction.

Thanks to @ianlancetaylor for flagging this.

Updates: #73704
Change-Id: I9d413a63fa43f34f94c274bba7f7b883c80433b6
Reviewed-on: https://go-review.googlesource.com/c/go/+/698835
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Michael Matloob <matloob@golang.org>
Reviewed-by: Michael Matloob <matloob@google.com>
Auto-Submit: Michael Matloob <matloob@google.com>
Reviewed-by: Ian Alexander <jitsu@google.com>
This commit is contained in:
David Finkel 2025-08-24 15:15:06 -04:00 committed by Gopher Robot
parent 385dc33250
commit 388c41c412
9 changed files with 112 additions and 1 deletions

View file

@ -16,14 +16,18 @@ import (
"io/fs" "io/fs"
"log" "log"
"os" "os"
"os/exec"
"path" "path"
"path/filepath" "path/filepath"
"reflect" "reflect"
"regexp"
"runtime" "runtime"
"strings" "strings"
"sync" "sync"
"testing" "testing"
"time" "time"
"golang.org/x/mod/semver"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
@ -192,9 +196,29 @@ func testRepo(ctx context.Context, t *testing.T, remote string) (Repo, error) {
return NewRepo(ctx, vcsName, remote, false) return NewRepo(ctx, vcsName, remote, false)
} }
var gitVersLineExtract = regexp.MustCompile(`git version\s+([\d.]+)`)
func gitVersion(t testing.TB) string {
gitOut, runErr := exec.Command("git", "version").CombinedOutput()
if runErr != nil {
t.Logf("failed to execute git version: %s", runErr)
return "v0"
}
matches := gitVersLineExtract.FindSubmatch(gitOut)
if len(matches) < 2 {
t.Logf("git version extraction regexp did not match version line: %q", gitOut)
return "v0"
}
return "v" + string(matches[1])
}
const minGitSHA256Vers = "v2.29"
func TestTags(t *testing.T) { func TestTags(t *testing.T) {
t.Parallel() t.Parallel()
gitVers := gitVersion(t)
type tagsTest struct { type tagsTest struct {
repo string repo string
prefix string prefix string
@ -204,6 +228,9 @@ func TestTags(t *testing.T) {
runTest := func(tt tagsTest) func(*testing.T) { runTest := func(tt tagsTest) func(*testing.T) {
return func(t *testing.T) { return func(t *testing.T) {
t.Parallel() t.Parallel()
if tt.repo == gitsha256repo && semver.Compare(gitVers, minGitSHA256Vers) < 0 {
t.Skipf("git version is too old (%+v); skipping git sha256 test", gitVers)
}
ctx := testContext(t) ctx := testContext(t)
r, err := testRepo(ctx, t, tt.repo) r, err := testRepo(ctx, t, tt.repo)
@ -288,6 +315,8 @@ func TestTags(t *testing.T) {
func TestLatest(t *testing.T) { func TestLatest(t *testing.T) {
t.Parallel() t.Parallel()
gitVers := gitVersion(t)
type latestTest struct { type latestTest struct {
repo string repo string
info *RevInfo info *RevInfo
@ -297,6 +326,10 @@ func TestLatest(t *testing.T) {
t.Parallel() t.Parallel()
ctx := testContext(t) ctx := testContext(t)
if tt.repo == gitsha256repo && semver.Compare(gitVers, minGitSHA256Vers) < 0 {
t.Skipf("git version is too old (%+v); skipping git sha256 test", gitVers)
}
r, err := testRepo(ctx, t, tt.repo) r, err := testRepo(ctx, t, tt.repo)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -375,6 +408,8 @@ func TestLatest(t *testing.T) {
func TestReadFile(t *testing.T) { func TestReadFile(t *testing.T) {
t.Parallel() t.Parallel()
gitVers := gitVersion(t)
type readFileTest struct { type readFileTest struct {
repo string repo string
rev string rev string
@ -387,6 +422,10 @@ func TestReadFile(t *testing.T) {
t.Parallel() t.Parallel()
ctx := testContext(t) ctx := testContext(t)
if tt.repo == gitsha256repo && semver.Compare(gitVers, minGitSHA256Vers) < 0 {
t.Skipf("git version is too old (%+v); skipping git sha256 test", gitVers)
}
r, err := testRepo(ctx, t, tt.repo) r, err := testRepo(ctx, t, tt.repo)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -468,6 +507,8 @@ type zipFile struct {
func TestReadZip(t *testing.T) { func TestReadZip(t *testing.T) {
t.Parallel() t.Parallel()
gitVers := gitVersion(t)
type readZipTest struct { type readZipTest struct {
repo string repo string
rev string rev string
@ -480,6 +521,10 @@ func TestReadZip(t *testing.T) {
t.Parallel() t.Parallel()
ctx := testContext(t) ctx := testContext(t)
if tt.repo == gitsha256repo && semver.Compare(gitVers, minGitSHA256Vers) < 0 {
t.Skipf("git version is too old (%+v); skipping git sha256 test", gitVers)
}
r, err := testRepo(ctx, t, tt.repo) r, err := testRepo(ctx, t, tt.repo)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
@ -753,6 +798,8 @@ var hgmap = map[string]string{
func TestStat(t *testing.T) { func TestStat(t *testing.T) {
t.Parallel() t.Parallel()
gitVers := gitVersion(t)
type statTest struct { type statTest struct {
repo string repo string
rev string rev string
@ -764,6 +811,10 @@ func TestStat(t *testing.T) {
t.Parallel() t.Parallel()
ctx := testContext(t) ctx := testContext(t)
if tt.repo == gitsha256repo && semver.Compare(gitVers, minGitSHA256Vers) < 0 {
t.Skipf("git version is too old (%+v); skipping git sha256 test", gitVers)
}
r, err := testRepo(ctx, t, tt.repo) r, err := testRepo(ctx, t, tt.repo)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

View file

@ -18,12 +18,14 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"regexp"
"runtime" "runtime"
"strconv" "strconv"
"strings" "strings"
"time" "time"
"golang.org/x/mod/module" "golang.org/x/mod/module"
"golang.org/x/mod/semver"
"golang.org/x/mod/zip" "golang.org/x/mod/zip"
) )
@ -42,6 +44,7 @@ func newScriptEngine() *script.Engine {
return script.OnceCondition(summary, func() (bool, error) { return f(), nil }) return script.OnceCondition(summary, func() (bool, error) { return f(), nil })
} }
add("bzr", lazyBool("the 'bzr' executable exists and provides the standard CLI", hasWorkingBzr)) add("bzr", lazyBool("the 'bzr' executable exists and provides the standard CLI", hasWorkingBzr))
add("git-min-vers", script.PrefixCondition("<suffix> indicates a minimum git version", hasAtLeastGitVersion))
interrupt := func(cmd *exec.Cmd) error { return cmd.Process.Signal(os.Interrupt) } interrupt := func(cmd *exec.Cmd) error { return cmd.Process.Signal(os.Interrupt) }
gracePeriod := 30 * time.Second // arbitrary gracePeriod := 30 * time.Second // arbitrary
@ -394,3 +397,25 @@ func hasWorkingBzr() bool {
err = exec.Command(bzr, "help").Run() err = exec.Command(bzr, "help").Run()
return err == nil return err == nil
} }
var gitVersLineExtract = regexp.MustCompile(`git version\s+([\d.]+)`)
func gitVersion() (string, error) {
gitOut, runErr := exec.Command("git", "version").CombinedOutput()
if runErr != nil {
return "v0", fmt.Errorf("failed to execute git version: %w", runErr)
}
matches := gitVersLineExtract.FindSubmatch(gitOut)
if len(matches) < 2 {
return "v0", fmt.Errorf("git version extraction regexp did not match version line: %q", gitOut)
}
return "v" + string(matches[1]), nil
}
func hasAtLeastGitVersion(s *script.State, minVers string) (bool, error) {
gitVers, gitVersErr := gitVersion()
if gitVersErr != nil {
return false, gitVersErr
}
return semver.Compare(minVers, gitVers) <= 0, nil
}

View file

@ -14,10 +14,13 @@ import (
"os" "os"
"os/exec" "os/exec"
"path/filepath" "path/filepath"
"regexp"
"runtime" "runtime"
"runtime/debug" "runtime/debug"
"sync" "sync"
"testing" "testing"
"golang.org/x/mod/semver"
) )
func scriptConditions(t *testing.T) map[string]script.Cond { func scriptConditions(t *testing.T) map[string]script.Cond {
@ -41,6 +44,7 @@ func scriptConditions(t *testing.T) map[string]script.Cond {
add("case-sensitive", script.OnceCondition("$WORK filesystem is case-sensitive", isCaseSensitive)) add("case-sensitive", script.OnceCondition("$WORK filesystem is case-sensitive", isCaseSensitive))
add("cc", script.PrefixCondition("go env CC = <suffix> (ignoring the go/env file)", ccIs)) add("cc", script.PrefixCondition("go env CC = <suffix> (ignoring the go/env file)", ccIs))
add("git", lazyBool("the 'git' executable exists and provides the standard CLI", hasWorkingGit)) add("git", lazyBool("the 'git' executable exists and provides the standard CLI", hasWorkingGit))
add("git-min-vers", script.PrefixCondition("<suffix> indicates a minimum git version", hasAtLeastGitVersion))
add("net", script.PrefixCondition("can connect to external network host <suffix>", hasNet)) add("net", script.PrefixCondition("can connect to external network host <suffix>", hasNet))
add("trimpath", script.OnceCondition("test binary was built with -trimpath", isTrimpath)) add("trimpath", script.OnceCondition("test binary was built with -trimpath", isTrimpath))
@ -153,6 +157,28 @@ func hasWorkingGit() bool {
return err == nil return err == nil
} }
var gitVersLineExtract = regexp.MustCompile(`git version\s+([\d.]+)`)
func gitVersion() (string, error) {
gitOut, runErr := exec.Command("git", "version").CombinedOutput()
if runErr != nil {
return "v0", fmt.Errorf("failed to execute git version: %w", runErr)
}
matches := gitVersLineExtract.FindSubmatch(gitOut)
if len(matches) < 2 {
return "v0", fmt.Errorf("git version extraction regexp did not match version line: %q", gitOut)
}
return "v" + string(matches[1]), nil
}
func hasAtLeastGitVersion(s *script.State, minVers string) (bool, error) {
gitVers, gitVersErr := gitVersion()
if gitVersErr != nil {
return false, gitVersErr
}
return semver.Compare(minVers, gitVers) <= 0, nil
}
func hasWorkingBzr() bool { func hasWorkingBzr() bool {
bzr, err := exec.LookPath("bzr") bzr, err := exec.LookPath("bzr")
if err != nil { if err != nil {

View file

@ -399,6 +399,8 @@ The available conditions are:
GOOS/GOARCH supports -fuzz with instrumentation GOOS/GOARCH supports -fuzz with instrumentation
[git] [git]
the 'git' executable exists and provides the standard CLI the 'git' executable exists and provides the standard CLI
[git-min-vers:*]
<suffix> indicates a minimum git version
[go-builder] [go-builder]
GO_BUILDER_NAME is non-empty GO_BUILDER_NAME is non-empty
[link] [link]

View file

@ -1,5 +1,6 @@
[short] skip [short] skip
[!git] skip [!git] skip
[!git-min-vers:v2.29] skip
env GOPRIVATE=vcs-test.golang.org env GOPRIVATE=vcs-test.golang.org

View file

@ -1,5 +1,6 @@
[short] skip [short] skip
[!git] skip [!git] skip
[!git-min-vers:v2.29] skip
env GOPRIVATE=vcs-test.golang.org env GOPRIVATE=vcs-test.golang.org

View file

@ -1,5 +1,6 @@
[short] skip [short] skip
[!git] skip [!git] skip
[!git-min-vers:v2.29] skip
# This is a git sha256-mode copy of mod_download_git_bareRepository # This is a git sha256-mode copy of mod_download_git_bareRepository

View file

@ -1,3 +1,5 @@
[!git-min-vers:v2.29] skip
handle git handle git
# This is a sha256 version of gitrepo1.txt (which uses sha1 hashes) # This is a sha256 version of gitrepo1.txt (which uses sha1 hashes)

View file

@ -55,6 +55,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"maps"
"slices"
"sort" "sort"
"strings" "strings"
"time" "time"
@ -518,7 +520,7 @@ func (e *Engine) conditionsActive(s *State, conds []condition) (bool, error) {
if ok { if ok {
impl = e.Conds[prefix] impl = e.Conds[prefix]
if impl == nil { if impl == nil {
return false, fmt.Errorf("unknown condition prefix %q", prefix) return false, fmt.Errorf("unknown condition prefix %q; known: %v", prefix, slices.Collect(maps.Keys(e.Conds)))
} }
if !impl.Usage().Prefix { if !impl.Usage().Prefix {
return false, fmt.Errorf("condition %q cannot be used with a suffix", prefix) return false, fmt.Errorf("condition %q cannot be used with a suffix", prefix)