Skip to content

Commit 24a6ca0

Browse files
author
Bryan C. Mills
committed
cmd/go/internal/modfetch: always check for a go.mod file when fetching from version control
If the module path declared in the go.mod file does not match the path we are trying to resolve, a build using that module is doomed to fail. Since we know that the module path does not match in the underlying repo, we also know that the requested module does not exist at the requested version. Therefore, we should reject that version in Stat with a “not exist” error — sooner rather than later — so that modload.Query will continue to check other candidate paths (for example, with a major-version suffix added or removed). Fixes #33099 Change-Id: I43c980f78ed75fa6ace90f237cc3aad46c22d83a Reviewed-on: https://go-review.googlesource.com/c/go/+/186237 Run-TryBot: Bryan C. Mills <bcmills@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Jay Conrod <jayconrod@google.com>
1 parent 20e4540 commit 24a6ca0

File tree

4 files changed

+100
-46
lines changed

4 files changed

+100
-46
lines changed

src/cmd/go/internal/modfetch/coderepo.go

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ type codeRepo struct {
3131
codeRoot string
3232
// codeDir is the directory (relative to root) at which we expect to find the module.
3333
// If pathMajor is non-empty and codeRoot is not the full modPath,
34-
// then we look in both codeDir and codeDir+modPath
34+
// then we look in both codeDir and codeDir/pathMajor[1:].
3535
codeDir string
3636

3737
// pathMajor is the suffix of modPath that indicates its major version,
@@ -248,20 +248,25 @@ func (r *codeRepo) convert(info *codehost.RevInfo, statVers string) (*RevInfo, e
248248
// exist as required by info2.Version and the module path represented by r.
249249
checkGoMod := func() (*RevInfo, error) {
250250
// If r.codeDir is non-empty, then the go.mod file must exist: the module
251-
// author, not the module consumer, gets to decide how to carve up the repo
251+
// authornot the module consumer, gets to decide how to carve up the repo
252252
// into modules.
253-
if r.codeDir != "" {
254-
_, _, _, err := r.findDir(info2.Version)
255-
if err != nil {
256-
// TODO: It would be nice to return an error like "not a module".
257-
// Right now we return "missing go.mod", which is a little confusing.
258-
return nil, &module.ModuleError{
259-
Path: r.modPath,
260-
Err: &module.InvalidVersionError{
261-
Version: info2.Version,
262-
Err: notExistError(err.Error()),
263-
},
264-
}
253+
//
254+
// Conversely, if the go.mod file exists, the module author — not the module
255+
// consumer — gets to determine the module's path
256+
//
257+
// r.findDir verifies both of these conditions. Execute it now so that
258+
// r.Stat will correctly return a notExistError if the go.mod location or
259+
// declared module path doesn't match.
260+
_, _, _, err := r.findDir(info2.Version)
261+
if err != nil {
262+
// TODO: It would be nice to return an error like "not a module".
263+
// Right now we return "missing go.mod", which is a little confusing.
264+
return nil, &module.ModuleError{
265+
Path: r.modPath,
266+
Err: &module.InvalidVersionError{
267+
Version: info2.Version,
268+
Err: notExistError(err.Error()),
269+
},
265270
}
266271
}
267272

@@ -571,6 +576,10 @@ func (r *codeRepo) versionToRev(version string) (rev string, err error) {
571576
return r.revToRev(version), nil
572577
}
573578

579+
// findDir locates the directory within the repo containing the module.
580+
//
581+
// If r.pathMajor is non-empty, this can be either r.codeDir or — if a go.mod
582+
// file exists — r.codeDir/r.pathMajor[1:].
574583
func (r *codeRepo) findDir(version string) (rev, dir string, gomod []byte, err error) {
575584
rev, err = r.versionToRev(version)
576585
if err != nil {

src/cmd/go/internal/modfetch/coderepo_test.go

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ var codeRepoTests = []codeRepoTest{
105105
name: "45f53230a74ad275c7127e117ac46914c8126160",
106106
short: "45f53230a74a",
107107
time: time.Date(2018, 7, 19, 1, 21, 27, 0, time.UTC),
108-
ziperr: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0",
108+
err: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0",
109109
},
110110
{
111111
vcs: "git",
@@ -136,15 +136,14 @@ var codeRepoTests = []codeRepoTest{
136136
},
137137
},
138138
{
139-
vcs: "git",
140-
path: "github.com/rsc/vgotest1/v2",
141-
rev: "45f53230a",
142-
version: "v2.0.0",
143-
name: "45f53230a74ad275c7127e117ac46914c8126160",
144-
short: "45f53230a74a",
145-
time: time.Date(2018, 7, 19, 1, 21, 27, 0, time.UTC),
146-
gomoderr: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0",
147-
ziperr: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0",
139+
vcs: "git",
140+
path: "github.com/rsc/vgotest1/v2",
141+
rev: "45f53230a",
142+
version: "v2.0.0",
143+
name: "45f53230a74ad275c7127e117ac46914c8126160",
144+
short: "45f53230a74a",
145+
time: time.Date(2018, 7, 19, 1, 21, 27, 0, time.UTC),
146+
err: "missing github.com/rsc/vgotest1/go.mod and .../v2/go.mod at revision v2.0.0",
148147
},
149148
{
150149
vcs: "git",
@@ -154,7 +153,7 @@ var codeRepoTests = []codeRepoTest{
154153
name: "80d85c5d4d17598a0e9055e7c175a32b415d6128",
155154
short: "80d85c5d4d17",
156155
time: time.Date(2018, 2, 19, 23, 10, 6, 0, time.UTC),
157-
ziperr: "missing github.com/rsc/vgotest1/go.mod and .../v54321/go.mod at revision 80d85c5d4d17",
156+
err: "missing github.com/rsc/vgotest1/go.mod and .../v54321/go.mod at revision 80d85c5d4d17",
158157
},
159158
{
160159
vcs: "git",
@@ -210,24 +209,24 @@ var codeRepoTests = []codeRepoTest{
210209
gomod: "module \"github.com/rsc/vgotest1/v2\" // root go.mod\n",
211210
},
212211
{
213-
vcs: "git",
214-
path: "github.com/rsc/vgotest1/v2",
215-
rev: "v2.0.3",
216-
version: "v2.0.3",
217-
name: "f18795870fb14388a21ef3ebc1d75911c8694f31",
218-
short: "f18795870fb1",
219-
time: time.Date(2018, 2, 19, 23, 16, 4, 0, time.UTC),
220-
gomoderr: "github.com/rsc/vgotest1/v2/go.mod has non-.../v2 module path \"github.com/rsc/vgotest\" at revision v2.0.3",
212+
vcs: "git",
213+
path: "github.com/rsc/vgotest1/v2",
214+
rev: "v2.0.3",
215+
version: "v2.0.3",
216+
name: "f18795870fb14388a21ef3ebc1d75911c8694f31",
217+
short: "f18795870fb1",
218+
time: time.Date(2018, 2, 19, 23, 16, 4, 0, time.UTC),
219+
err: "github.com/rsc/vgotest1/v2/go.mod has non-.../v2 module path \"github.com/rsc/vgotest\" at revision v2.0.3",
221220
},
222221
{
223-
vcs: "git",
224-
path: "github.com/rsc/vgotest1/v2",
225-
rev: "v2.0.4",
226-
version: "v2.0.4",
227-
name: "1f863feb76bc7029b78b21c5375644838962f88d",
228-
short: "1f863feb76bc",
229-
time: time.Date(2018, 2, 20, 0, 3, 38, 0, time.UTC),
230-
gomoderr: "github.com/rsc/vgotest1/go.mod and .../v2/go.mod both have .../v2 module paths at revision v2.0.4",
222+
vcs: "git",
223+
path: "github.com/rsc/vgotest1/v2",
224+
rev: "v2.0.4",
225+
version: "v2.0.4",
226+
name: "1f863feb76bc7029b78b21c5375644838962f88d",
227+
short: "1f863feb76bc",
228+
time: time.Date(2018, 2, 20, 0, 3, 38, 0, time.UTC),
229+
err: "github.com/rsc/vgotest1/go.mod and .../v2/go.mod both have .../v2 module paths at revision v2.0.4",
231230
},
232231
{
233232
vcs: "git",
@@ -504,6 +503,7 @@ func TestCodeRepo(t *testing.T) {
504503
tt.name = remap(tt.name, m)
505504
tt.short = remap(tt.short, m)
506505
tt.rev = remap(tt.rev, m)
506+
tt.err = remap(tt.err, m)
507507
tt.gomoderr = remap(tt.gomoderr, m)
508508
tt.ziperr = remap(tt.ziperr, m)
509509
t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.rev, f(tt))
@@ -631,15 +631,30 @@ var latestTests = []struct {
631631
err: "no commits",
632632
},
633633
{
634-
vcs: "git",
635-
path: "github.com/rsc/vgotest1",
636-
version: "v0.0.0-20180219223237-a08abb797a67",
634+
vcs: "git",
635+
path: "github.com/rsc/vgotest1",
636+
err: `github.com/rsc/vgotest1@v0.0.0-20180219223237-a08abb797a67: invalid version: go.mod has post-v0 module path "github.com/vgotest1/v2" at revision a08abb797a67`,
637+
},
638+
{
639+
vcs: "git",
640+
path: "github.com/rsc/vgotest1/v2",
641+
err: `github.com/rsc/vgotest1/v2@v2.0.0-20180219223237-a08abb797a67: invalid version: github.com/rsc/vgotest1/go.mod and .../v2/go.mod both have .../v2 module paths at revision a08abb797a67`,
637642
},
638643
{
639644
vcs: "git",
640645
path: "github.com/rsc/vgotest1/subdir",
641646
err: "github.com/rsc/vgotest1/subdir@v0.0.0-20180219223237-a08abb797a67: invalid version: missing github.com/rsc/vgotest1/subdir/go.mod at revision a08abb797a67",
642647
},
648+
{
649+
vcs: "git",
650+
path: "vcs-test.golang.org/git/commit-after-tag.git",
651+
version: "v1.0.1-0.20190715211727-b325d8217783",
652+
},
653+
{
654+
vcs: "git",
655+
path: "vcs-test.golang.org/git/no-tags.git",
656+
version: "v0.0.0-20190715212047-e706ba1d9f6d",
657+
},
643658
{
644659
vcs: "mod",
645660
path: "swtch.com/testmod",

src/cmd/go/internal/modload/query_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,8 +161,11 @@ var queryTests = []struct {
161161
{path: queryRepoV2, query: "v2.6.0-pre1", vers: "v2.6.0-pre1"},
162162
{path: queryRepoV2, query: "latest", vers: "v2.5.5"},
163163

164-
{path: queryRepoV3, query: "e0cf3de987e6", vers: "v3.0.0-20180704024501-e0cf3de987e6"},
165-
{path: queryRepoV3, query: "latest", vers: "v3.0.0-20180704024501-e0cf3de987e6"},
164+
// e0cf3de987e6 is the latest commit on the master branch, and it's actually
165+
// v1.19.10-pre1, not anything resembling v3: attempting to query it as such
166+
// should fail.
167+
{path: queryRepoV3, query: "e0cf3de987e6", err: `vcs-test.golang.org/git/querytest.git/v3@v3.0.0-20180704024501-e0cf3de987e6: invalid version: go.mod has non-.../v3 module path "vcs-test.golang.org/git/querytest.git" (and .../v3/go.mod does not exist) at revision e0cf3de987e6`},
168+
{path: queryRepoV3, query: "latest", err: `no matching versions for query "latest"`},
166169

167170
{path: emptyRepo, query: "latest", vers: "v0.0.0-20180704023549-7bb914627242"},
168171
{path: emptyRepo, query: ">v0.0.0", err: `no matching versions for query ">v0.0.0"`},
@@ -182,7 +185,10 @@ func TestQuery(t *testing.T) {
182185
ok, _ := path.Match(allow, m.Version)
183186
return ok
184187
}
188+
tt := tt
185189
t.Run(strings.ReplaceAll(tt.path, "/", "_")+"/"+tt.query+"/"+tt.current+"/"+allow, func(t *testing.T) {
190+
t.Parallel()
191+
186192
info, err := Query(tt.path, tt.query, tt.current, allowed)
187193
if tt.err != "" {
188194
if err == nil {
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
env GO111MODULE=on
2+
env GOPROXY=direct
3+
env GOSUMDB=off
4+
5+
[!net] skip
6+
[!exec:git] skip
7+
8+
# golang.org/issue/33099: if an import path ends in a major-version suffix,
9+
# ensure that 'direct' mode can resolve the package to the module.
10+
# For a while, (*modfetch.codeRepo).Stat was not checking for a go.mod file,
11+
# which would produce a hard error at the subsequent call to GoMod.
12+
13+
go list all
14+
15+
-- go.mod --
16+
module example.com
17+
go 1.13
18+
19+
-- main.go --
20+
package main
21+
22+
import _ "vcs-test.golang.org/git/v3pkg.git/v3"
23+
24+
func main() {}

0 commit comments

Comments
 (0)