aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/cmd/go/internal/modfetch/coderepo.go216
-rw-r--r--src/cmd/go/internal/modfetch/coderepo_test.go301
-rw-r--r--src/cmd/go/testdata/script/mod_invalid_version.txt10
3 files changed, 277 insertions, 250 deletions
diff --git a/src/cmd/go/internal/modfetch/coderepo.go b/src/cmd/go/internal/modfetch/coderepo.go
index 2dcbb99b18..db7496faf6 100644
--- a/src/cmd/go/internal/modfetch/coderepo.go
+++ b/src/cmd/go/internal/modfetch/coderepo.go
@@ -298,16 +298,13 @@ func (r *codeRepo) Latest() (*RevInfo, error) {
// If statVers is a valid module version, it is used for the Version field.
// Otherwise, the Version is derived from the passed-in info and recent tags.
func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, error) {
- info2 := &RevInfo{
- Name: info.Name,
- Short: info.Short,
- Time: info.Time,
- }
-
// If this is a plain tag (no dir/ prefix)
// and the module path is unversioned,
// and if the underlying file tree has no go.mod,
// then allow using the tag with a +incompatible suffix.
+ //
+ // (If the version is +incompatible, then the go.mod file must not exist:
+ // +incompatible is not an ongoing opt-out from semantic import versioning.)
var canUseIncompatible func() bool
canUseIncompatible = func() bool {
var ok bool
@@ -321,19 +318,12 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
return ok
}
- invalidf := func(format string, args ...interface{}) error {
- return &module.ModuleError{
- Path: r.modPath,
- Err: &module.InvalidVersionError{
- Version: info2.Version,
- Err: fmt.Errorf(format, args...),
- },
- }
- }
-
- // checkGoMod verifies that the go.mod file for the module exists or does not
- // exist as required by info2.Version and the module path represented by r.
- checkGoMod := func() (*RevInfo, error) {
+ // checkCanonical verifies that the canonical version v is compatible with the
+ // module path represented by r, adding a "+incompatible" suffix if needed.
+ //
+ // If statVers is also canonical, checkCanonical also verifies that v is
+ // either statVers or statVers with the added "+incompatible" suffix.
+ checkCanonical := func(v string) (*RevInfo, error) {
// If r.codeDir is non-empty, then the go.mod file must exist: the module
// author — not the module consumer, — gets to decide how to carve up the repo
// into modules.
@@ -344,73 +334,91 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
// r.findDir verifies both of these conditions. Execute it now so that
// r.Stat will correctly return a notExistError if the go.mod location or
// declared module path doesn't match.
- _, _, _, err := r.findDir(info2.Version)
+ _, _, _, err := r.findDir(v)
if err != nil {
// TODO: It would be nice to return an error like "not a module".
// Right now we return "missing go.mod", which is a little confusing.
return nil, &module.ModuleError{
Path: r.modPath,
Err: &module.InvalidVersionError{
- Version: info2.Version,
+ Version: v,
Err: notExistError{err: err},
},
}
}
- // If the version is +incompatible, then the go.mod file must not exist:
- // +incompatible is not an ongoing opt-out from semantic import versioning.
- if strings.HasSuffix(info2.Version, "+incompatible") {
- if !canUseIncompatible() {
+ invalidf := func(format string, args ...interface{}) error {
+ return &module.ModuleError{
+ Path: r.modPath,
+ Err: &module.InvalidVersionError{
+ Version: v,
+ Err: fmt.Errorf(format, args...),
+ },
+ }
+ }
+
+ // Add the +incompatible suffix if needed or requested explicitly, and
+ // verify that its presence or absence is appropriate for this version
+ // (which depends on whether it has an explicit go.mod file).
+
+ if v == strings.TrimSuffix(statVers, "+incompatible") {
+ v = statVers
+ }
+ base := strings.TrimSuffix(v, "+incompatible")
+ var errIncompatible error
+ if !module.MatchPathMajor(base, r.pathMajor) {
+ if canUseIncompatible() {
+ v = base + "+incompatible"
+ } else {
if r.pathMajor != "" {
- return nil, invalidf("+incompatible suffix not allowed: module path includes a major version suffix, so major version must match")
+ errIncompatible = invalidf("module path includes a major version suffix, so major version must match")
} else {
- return nil, invalidf("+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required")
+ errIncompatible = invalidf("module contains a go.mod file, so module path must match major version (%q)", path.Join(r.pathPrefix, semver.Major(v)))
}
}
+ } else if strings.HasSuffix(v, "+incompatible") {
+ errIncompatible = invalidf("+incompatible suffix not allowed: major version %s is compatible", semver.Major(v))
+ }
- if err := module.CheckPathMajor(strings.TrimSuffix(info2.Version, "+incompatible"), r.pathMajor); err == nil {
- return nil, invalidf("+incompatible suffix not allowed: major version %s is compatible", semver.Major(info2.Version))
+ if statVers != "" && statVers == module.CanonicalVersion(statVers) {
+ // Since the caller-requested version is canonical, it would be very
+ // confusing to resolve it to anything but itself, possibly with a
+ // "+incompatible" suffix. Error out explicitly.
+ if statBase := strings.TrimSuffix(statVers, "+incompatible"); statBase != base {
+ return nil, &module.ModuleError{
+ Path: r.modPath,
+ Err: &module.InvalidVersionError{
+ Version: statVers,
+ Err: fmt.Errorf("resolves to version %v (%s is not a tag)", v, statBase),
+ },
+ }
}
}
- return info2, nil
+ if errIncompatible != nil {
+ return nil, errIncompatible
+ }
+
+ return &RevInfo{
+ Name: info.Name,
+ Short: info.Short,
+ Time: info.Time,
+ Version: v,
+ }, nil
}
// Determine version.
- //
- // If statVers is canonical, then the original call was repo.Stat(statVers).
- // Since the version is canonical, we must not resolve it to anything but
- // itself, possibly with a '+incompatible' annotation: we do not need to do
- // the work required to look for an arbitrary pseudo-version.
- if statVers != "" && statVers == module.CanonicalVersion(statVers) {
- info2.Version = statVers
-
- if IsPseudoVersion(info2.Version) {
- if err := r.validatePseudoVersion(info, info2.Version); err != nil {
- return nil, err
- }
- return checkGoMod()
- }
- if err := module.CheckPathMajor(info2.Version, r.pathMajor); err != nil {
- if canUseIncompatible() {
- info2.Version += "+incompatible"
- return checkGoMod()
- } else {
- if vErr, ok := err.(*module.InvalidVersionError); ok {
- // We're going to describe why the version is invalid in more detail,
- // so strip out the existing “invalid version” wrapper.
- err = vErr.Err
- }
- return nil, invalidf("module contains a go.mod file, so major version must be compatible: %v", err)
- }
+ if IsPseudoVersion(statVers) {
+ if err := r.validatePseudoVersion(info, statVers); err != nil {
+ return nil, err
}
-
- return checkGoMod()
+ return checkCanonical(statVers)
}
- // statVers is empty or non-canonical, so we need to resolve it to a canonical
- // version or pseudo-version.
+ // statVers is not a pseudo-version, so we need to either resolve it to a
+ // canonical version or verify that it is already a canonical tag
+ // (not a branch).
// Derive or verify a version from a code repo tag.
// Tag must have a prefix matching codeDir.
@@ -441,71 +449,62 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
if v == "" || !strings.HasPrefix(trimmed, v) {
return "", false // Invalid or incomplete version (just vX or vX.Y).
}
- if isRetracted(v) {
- return "", false
- }
if v == trimmed {
tagIsCanonical = true
}
-
- if err := module.CheckPathMajor(v, r.pathMajor); err != nil {
- if canUseIncompatible() {
- return v + "+incompatible", tagIsCanonical
- }
- return "", false
- }
-
return v, tagIsCanonical
}
// If the VCS gave us a valid version, use that.
if v, tagIsCanonical := tagToVersion(info.Version); tagIsCanonical {
- info2.Version = v
- return checkGoMod()
+ if info, err := checkCanonical(v); err == nil {
+ return info, err
+ }
}
// Look through the tags on the revision for either a usable canonical version
// or an appropriate base for a pseudo-version.
- var pseudoBase string
+ var (
+ highestCanonical string
+ pseudoBase string
+ )
for _, pathTag := range info.Tags {
v, tagIsCanonical := tagToVersion(pathTag)
- if tagIsCanonical {
- if statVers != "" && semver.Compare(v, statVers) == 0 {
- // The user requested a non-canonical version, but the tag for the
- // canonical equivalent refers to the same revision. Use it.
- info2.Version = v
- return checkGoMod()
+ if statVers != "" && semver.Compare(v, statVers) == 0 {
+ // The tag is equivalent to the version requested by the user.
+ if tagIsCanonical {
+ // This tag is the canonical form of the requested version,
+ // not some other form with extra build metadata.
+ // Use this tag so that the resolved version will match exactly.
+ // (If it isn't actually allowed, we'll error out in checkCanonical.)
+ return checkCanonical(v)
} else {
- // Save the highest canonical tag for the revision. If we don't find a
- // better match, we'll use it as the canonical version.
+ // The user explicitly requested something equivalent to this tag. We
+ // can't use the version from the tag directly: since the tag is not
+ // canonical, it could be ambiguous. For example, tags v0.0.1+a and
+ // v0.0.1+b might both exist and refer to different revisions.
//
- // NOTE: Do not replace this with semver.Max. Despite the name,
- // semver.Max *also* canonicalizes its arguments, which uses
- // semver.Canonical instead of module.CanonicalVersion and thereby
- // strips our "+incompatible" suffix.
- if semver.Compare(info2.Version, v) < 0 {
- info2.Version = v
- }
+ // The tag is otherwise valid for the module, so we can at least use it as
+ // the base of an unambiguous pseudo-version.
+ //
+ // If multiple tags match, tagToVersion will canonicalize them to the same
+ // base version.
+ pseudoBase = v
+ }
+ }
+ // Save the highest non-retracted canonical tag for the revision.
+ // If we don't find a better match, we'll use it as the canonical version.
+ if tagIsCanonical && semver.Compare(highestCanonical, v) < 0 && !isRetracted(v) {
+ if module.MatchPathMajor(v, r.pathMajor) || canUseIncompatible() {
+ highestCanonical = v
}
- } else if v != "" && semver.Compare(v, statVers) == 0 {
- // The user explicitly requested something equivalent to this tag. We
- // can't use the version from the tag directly: since the tag is not
- // canonical, it could be ambiguous. For example, tags v0.0.1+a and
- // v0.0.1+b might both exist and refer to different revisions.
- //
- // The tag is otherwise valid for the module, so we can at least use it as
- // the base of an unambiguous pseudo-version.
- //
- // If multiple tags match, tagToVersion will canonicalize them to the same
- // base version.
- pseudoBase = v
}
}
- // If we found any canonical tag for the revision, return it.
+ // If we found a valid canonical tag for the revision, return it.
// Even if we found a good pseudo-version base, a canonical version is better.
- if info2.Version != "" {
- return checkGoMod()
+ if highestCanonical != "" {
+ return checkCanonical(highestCanonical)
}
// Find the highest tagged version in the revision's history, subject to
@@ -528,11 +527,10 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
tag, _ = r.code.RecentTag(info.Name, tagPrefix, allowedMajor("v0"))
}
}
- pseudoBase, _ = tagToVersion(tag) // empty if the tag is invalid
+ pseudoBase, _ = tagToVersion(tag)
}
- info2.Version = PseudoVersion(r.pseudoMajor, pseudoBase, info.Time, info.Short)
- return checkGoMod()
+ return checkCanonical(PseudoVersion(r.pseudoMajor, pseudoBase, info.Time, info.Short))
}
// validatePseudoVersion checks that version has a major version compatible with
@@ -556,10 +554,6 @@ func (r *codeRepo) validatePseudoVersion(info *codehost.RevInfo, version string)
}
}()
- if err := module.CheckPathMajor(version, r.pathMajor); err != nil {
- return err
- }
-
rev, err := PseudoVersionRev(version)
if err != nil {
return err
diff --git a/src/cmd/go/internal/modfetch/coderepo_test.go b/src/cmd/go/internal/modfetch/coderepo_test.go
index 02e399f352..d98ea87da2 100644
--- a/src/cmd/go/internal/modfetch/coderepo_test.go
+++ b/src/cmd/go/internal/modfetch/coderepo_test.go
@@ -418,171 +418,204 @@ var codeRepoTests = []codeRepoTest{
zipSum: "h1:JItBZ+gwA5WvtZEGEbuDL4lUttGtLrs53lmdurq3bOg=",
zipFileHash: "9ea9ae1673cffcc44b7fdd3cc89953d68c102449b46c982dbf085e4f2e394da5",
},
+ {
+ // Git branch with a semver name, +incompatible version, and no go.mod file.
+ vcs: "git",
+ path: "vcs-test.golang.org/go/mod/gitrepo1",
+ rev: "v2.3.4+incompatible",
+ err: `resolves to version v2.0.1+incompatible (v2.3.4 is not a tag)`,
+ },
+ {
+ // Git branch with a semver name, matching go.mod file, and compatible version.
+ vcs: "git",
+ path: "vcs-test.golang.org/git/semver-branch.git",
+ rev: "v1.0.0",
+ err: `resolves to version v0.1.1-0.20220202191944-09c4d8f6938c (v1.0.0 is not a tag)`,
+ },
+ {
+ // Git branch with a semver name, matching go.mod file, and disallowed +incompatible version.
+ // The version/tag mismatch takes precedence over the +incompatible mismatched.
+ vcs: "git",
+ path: "vcs-test.golang.org/git/semver-branch.git",
+ rev: "v2.0.0+incompatible",
+ err: `resolves to version v0.1.0 (v2.0.0 is not a tag)`,
+ },
+ {
+ // Git branch with a semver name, matching go.mod file, and mismatched version.
+ // The version/tag mismatch takes precedence over the +incompatible mismatched.
+ vcs: "git",
+ path: "vcs-test.golang.org/git/semver-branch.git",
+ rev: "v2.0.0",
+ err: `resolves to version v0.1.0 (v2.0.0 is not a tag)`,
+ },
+ {
+ // v3.0.0-devel is the same as tag v4.0.0-beta.1, but v4.0.0-beta.1 would
+ // not be allowed because it is incompatible and a go.mod file exists.
+ // The error message should refer to a valid pseudo-version, not the
+ // unusable semver tag.
+ vcs: "git",
+ path: "vcs-test.golang.org/git/semver-branch.git",
+ rev: "v3.0.0-devel",
+ err: `resolves to version v0.1.1-0.20220203155313-d59622f6e4d7 (v3.0.0-devel is not a tag)`,
+ },
}
func TestCodeRepo(t *testing.T) {
testenv.MustHaveExternalNetwork(t)
+ tmpdir := t.TempDir()
- tmpdir, err := os.MkdirTemp("", "modfetch-test-")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(tmpdir)
+ for _, tt := range codeRepoTests {
+ f := func(tt codeRepoTest) func(t *testing.T) {
+ return func(t *testing.T) {
+ t.Parallel()
+ if tt.vcs != "mod" {
+ testenv.MustHaveExecPath(t, tt.vcs)
+ }
- t.Run("parallel", func(t *testing.T) {
- for _, tt := range codeRepoTests {
- f := func(tt codeRepoTest) func(t *testing.T) {
- return func(t *testing.T) {
- t.Parallel()
- if tt.vcs != "mod" {
- testenv.MustHaveExecPath(t, tt.vcs)
- }
+ repo := Lookup("direct", tt.path)
- repo := Lookup("direct", tt.path)
+ if tt.mpath == "" {
+ tt.mpath = tt.path
+ }
+ if mpath := repo.ModulePath(); mpath != tt.mpath {
+ t.Errorf("repo.ModulePath() = %q, want %q", mpath, tt.mpath)
+ }
- if tt.mpath == "" {
- tt.mpath = tt.path
- }
- if mpath := repo.ModulePath(); mpath != tt.mpath {
- t.Errorf("repo.ModulePath() = %q, want %q", mpath, tt.mpath)
+ info, err := repo.Stat(tt.rev)
+ if err != nil {
+ if tt.err != "" {
+ if !strings.Contains(err.Error(), tt.err) {
+ t.Fatalf("repoStat(%q): %v, wanted %q", tt.rev, err, tt.err)
+ }
+ return
}
+ t.Fatalf("repo.Stat(%q): %v", tt.rev, err)
+ }
+ if tt.err != "" {
+ t.Errorf("repo.Stat(%q): success, wanted error", tt.rev)
+ }
+ if info.Version != tt.version {
+ t.Errorf("info.Version = %q, want %q", info.Version, tt.version)
+ }
+ if info.Name != tt.name {
+ t.Errorf("info.Name = %q, want %q", info.Name, tt.name)
+ }
+ if info.Short != tt.short {
+ t.Errorf("info.Short = %q, want %q", info.Short, tt.short)
+ }
+ if !info.Time.Equal(tt.time) {
+ t.Errorf("info.Time = %v, want %v", info.Time, tt.time)
+ }
- info, err := repo.Stat(tt.rev)
- if err != nil {
- if tt.err != "" {
- if !strings.Contains(err.Error(), tt.err) {
- t.Fatalf("repoStat(%q): %v, wanted %q", tt.rev, err, tt.err)
- }
- return
+ if tt.gomod != "" || tt.gomodErr != "" {
+ data, err := repo.GoMod(tt.version)
+ if err != nil && tt.gomodErr == "" {
+ t.Errorf("repo.GoMod(%q): %v", tt.version, err)
+ } else if err != nil && tt.gomodErr != "" {
+ if err.Error() != tt.gomodErr {
+ t.Errorf("repo.GoMod(%q): %v, want %q", tt.version, err, tt.gomodErr)
}
- t.Fatalf("repo.Stat(%q): %v", tt.rev, err)
- }
- if tt.err != "" {
- t.Errorf("repo.Stat(%q): success, wanted error", tt.rev)
- }
- if info.Version != tt.version {
- t.Errorf("info.Version = %q, want %q", info.Version, tt.version)
+ } else if tt.gomodErr != "" {
+ t.Errorf("repo.GoMod(%q) = %q, want error %q", tt.version, data, tt.gomodErr)
+ } else if string(data) != tt.gomod {
+ t.Errorf("repo.GoMod(%q) = %q, want %q", tt.version, data, tt.gomod)
}
- if info.Name != tt.name {
- t.Errorf("info.Name = %q, want %q", info.Name, tt.name)
- }
- if info.Short != tt.short {
- t.Errorf("info.Short = %q, want %q", info.Short, tt.short)
+ }
+
+ needHash := !testing.Short() && (tt.zipFileHash != "" || tt.zipSum != "")
+ if tt.zip != nil || tt.zipErr != "" || needHash {
+ f, err := os.CreateTemp(tmpdir, tt.version+".zip.")
+ if err != nil {
+ t.Fatalf("os.CreateTemp: %v", err)
}
- if !info.Time.Equal(tt.time) {
- t.Errorf("info.Time = %v, want %v", info.Time, tt.time)
+ zipfile := f.Name()
+ defer func() {
+ f.Close()
+ os.Remove(zipfile)
+ }()
+
+ var w io.Writer
+ var h hash.Hash
+ if needHash {
+ h = sha256.New()
+ w = io.MultiWriter(f, h)
+ } else {
+ w = f
}
-
- if tt.gomod != "" || tt.gomodErr != "" {
- data, err := repo.GoMod(tt.version)
- if err != nil && tt.gomodErr == "" {
- t.Errorf("repo.GoMod(%q): %v", tt.version, err)
- } else if err != nil && tt.gomodErr != "" {
- if err.Error() != tt.gomodErr {
- t.Errorf("repo.GoMod(%q): %v, want %q", tt.version, err, tt.gomodErr)
+ err = repo.Zip(w, tt.version)
+ f.Close()
+ if err != nil {
+ if tt.zipErr != "" {
+ if err.Error() == tt.zipErr {
+ return
}
- } else if tt.gomodErr != "" {
- t.Errorf("repo.GoMod(%q) = %q, want error %q", tt.version, data, tt.gomodErr)
- } else if string(data) != tt.gomod {
- t.Errorf("repo.GoMod(%q) = %q, want %q", tt.version, data, tt.gomod)
+ t.Fatalf("repo.Zip(%q): %v, want error %q", tt.version, err, tt.zipErr)
}
+ t.Fatalf("repo.Zip(%q): %v", tt.version, err)
+ }
+ if tt.zipErr != "" {
+ t.Errorf("repo.Zip(%q): success, want error %q", tt.version, tt.zipErr)
}
- needHash := !testing.Short() && (tt.zipFileHash != "" || tt.zipSum != "")
- if tt.zip != nil || tt.zipErr != "" || needHash {
- f, err := os.CreateTemp(tmpdir, tt.version+".zip.")
+ if tt.zip != nil {
+ prefix := tt.path + "@" + tt.version + "/"
+ z, err := zip.OpenReader(zipfile)
if err != nil {
- t.Fatalf("os.CreateTemp: %v", err)
+ t.Fatalf("open zip %s: %v", zipfile, err)
}
- zipfile := f.Name()
- defer func() {
- f.Close()
- os.Remove(zipfile)
- }()
-
- var w io.Writer
- var h hash.Hash
- if needHash {
- h = sha256.New()
- w = io.MultiWriter(f, h)
- } else {
- w = f
- }
- err = repo.Zip(w, tt.version)
- f.Close()
- if err != nil {
- if tt.zipErr != "" {
- if err.Error() == tt.zipErr {
- return
- }
- t.Fatalf("repo.Zip(%q): %v, want error %q", tt.version, err, tt.zipErr)
+ var names []string
+ for _, file := range z.File {
+ if !strings.HasPrefix(file.Name, prefix) {
+ t.Errorf("zip entry %v does not start with prefix %v", file.Name, prefix)
+ continue
}
- t.Fatalf("repo.Zip(%q): %v", tt.version, err)
- }
- if tt.zipErr != "" {
- t.Errorf("repo.Zip(%q): success, want error %q", tt.version, tt.zipErr)
+ names = append(names, file.Name[len(prefix):])
}
-
- if tt.zip != nil {
- prefix := tt.path + "@" + tt.version + "/"
- z, err := zip.OpenReader(zipfile)
- if err != nil {
- t.Fatalf("open zip %s: %v", zipfile, err)
- }
- var names []string
- for _, file := range z.File {
- if !strings.HasPrefix(file.Name, prefix) {
- t.Errorf("zip entry %v does not start with prefix %v", file.Name, prefix)
- continue
- }
- names = append(names, file.Name[len(prefix):])
- }
- z.Close()
- if !reflect.DeepEqual(names, tt.zip) {
- t.Fatalf("zip = %v\nwant %v\n", names, tt.zip)
- }
+ z.Close()
+ if !reflect.DeepEqual(names, tt.zip) {
+ t.Fatalf("zip = %v\nwant %v\n", names, tt.zip)
}
+ }
- if needHash {
- sum, err := dirhash.HashZip(zipfile, dirhash.Hash1)
- if err != nil {
- t.Errorf("repo.Zip(%q): %v", tt.version, err)
- } else if sum != tt.zipSum {
- t.Errorf("repo.Zip(%q): got file with sum %q, want %q", tt.version, sum, tt.zipSum)
- } else if zipFileHash := hex.EncodeToString(h.Sum(nil)); zipFileHash != tt.zipFileHash {
- t.Errorf("repo.Zip(%q): got file with hash %q, want %q (but content has correct sum)", tt.version, zipFileHash, tt.zipFileHash)
- }
+ if needHash {
+ sum, err := dirhash.HashZip(zipfile, dirhash.Hash1)
+ if err != nil {
+ t.Errorf("repo.Zip(%q): %v", tt.version, err)
+ } else if sum != tt.zipSum {
+ t.Errorf("repo.Zip(%q): got file with sum %q, want %q", tt.version, sum, tt.zipSum)
+ } else if zipFileHash := hex.EncodeToString(h.Sum(nil)); zipFileHash != tt.zipFileHash {
+ t.Errorf("repo.Zip(%q): got file with hash %q, want %q (but content has correct sum)", tt.version, zipFileHash, tt.zipFileHash)
}
}
}
}
- t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.rev, f(tt))
- if strings.HasPrefix(tt.path, vgotest1git) {
- for vcs, alt := range altVgotests {
- altTest := tt
- altTest.vcs = vcs
- altTest.path = alt + strings.TrimPrefix(altTest.path, vgotest1git)
- if strings.HasPrefix(altTest.mpath, vgotest1git) {
- altTest.mpath = alt + strings.TrimPrefix(altTest.mpath, vgotest1git)
- }
- var m map[string]string
- if alt == vgotest1hg {
- m = hgmap
- }
- altTest.version = remap(altTest.version, m)
- altTest.name = remap(altTest.name, m)
- altTest.short = remap(altTest.short, m)
- altTest.rev = remap(altTest.rev, m)
- altTest.err = remap(altTest.err, m)
- altTest.gomodErr = remap(altTest.gomodErr, m)
- altTest.zipErr = remap(altTest.zipErr, m)
- altTest.zipSum = ""
- altTest.zipFileHash = ""
- t.Run(strings.ReplaceAll(altTest.path, "/", "_")+"/"+altTest.rev, f(altTest))
+ }
+ t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.rev, f(tt))
+ if strings.HasPrefix(tt.path, vgotest1git) {
+ for vcs, alt := range altVgotests {
+ altTest := tt
+ altTest.vcs = vcs
+ altTest.path = alt + strings.TrimPrefix(altTest.path, vgotest1git)
+ if strings.HasPrefix(altTest.mpath, vgotest1git) {
+ altTest.mpath = alt + strings.TrimPrefix(altTest.mpath, vgotest1git)
+ }
+ var m map[string]string
+ if alt == vgotest1hg {
+ m = hgmap
}
+ altTest.version = remap(altTest.version, m)
+ altTest.name = remap(altTest.name, m)
+ altTest.short = remap(altTest.short, m)
+ altTest.rev = remap(altTest.rev, m)
+ altTest.err = remap(altTest.err, m)
+ altTest.gomodErr = remap(altTest.gomodErr, m)
+ altTest.zipErr = remap(altTest.zipErr, m)
+ altTest.zipSum = ""
+ altTest.zipFileHash = ""
+ t.Run(strings.ReplaceAll(altTest.path, "/", "_")+"/"+altTest.rev, f(altTest))
}
}
- })
+ }
}
var hgmap = map[string]string{
diff --git a/src/cmd/go/testdata/script/mod_invalid_version.txt b/src/cmd/go/testdata/script/mod_invalid_version.txt
index 43b9564356..361d99373f 100644
--- a/src/cmd/go/testdata/script/mod_invalid_version.txt
+++ b/src/cmd/go/testdata/script/mod_invalid_version.txt
@@ -194,10 +194,10 @@ cp go.mod.orig go.mod
go mod edit -require github.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d+incompatible
cd outside
! go list -m github.com/pierrec/lz4
-stderr 'go: example.com@v0.0.0 requires\n\tgithub.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required'
+stderr '^go: example.com@v0.0.0 requires\n\tgithub.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$'
cd ..
! go list -m github.com/pierrec/lz4
-stderr 'github.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required'
+stderr '^go: github.com/pierrec/lz4@v2.0.9-0.20190209155647-9a39efadad3d\+incompatible: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$'
# A +incompatible pseudo-version is valid for a revision of the module
# that lacks a go.mod file.
@@ -222,7 +222,7 @@ stdout 'github.com/pierrec/lz4 v2.0.5\+incompatible'
# not resolve to a pseudo-version with a different major version.
cp go.mod.orig go.mod
! go get -d github.com/pierrec/lz4@v2.0.8
-stderr 'go get: github.com/pierrec/lz4@v2.0.8: invalid version: module contains a go.mod file, so major version must be compatible: should be v0 or v1, not v2'
+stderr 'go get: github.com/pierrec/lz4@v2.0.8: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$'
# An invalid +incompatible suffix for a canonical version should error out,
# not resolve to a pseudo-version.
@@ -233,10 +233,10 @@ cp go.mod.orig go.mod
go mod edit -require github.com/pierrec/lz4@v2.0.8+incompatible
cd outside
! go list -m github.com/pierrec/lz4
-stderr 'github.com/pierrec/lz4@v2.0.8\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required'
+stderr '^go list -m: github.com/pierrec/lz4@v2.0.8\+incompatible: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$'
cd ..
! go list -m github.com/pierrec/lz4
-stderr 'github.com/pierrec/lz4@v2.0.8\+incompatible: invalid version: \+incompatible suffix not allowed: module contains a go.mod file, so semantic import versioning is required'
+stderr '^go list -m: github.com/pierrec/lz4@v2.0.8\+incompatible: invalid version: module contains a go.mod file, so module path must match major version \("github.com/pierrec/lz4/v2"\)$'
-- go.mod.orig --
module example.com