Skip to content

Commit 1ab9f68

Browse files
author
Bryan C. Mills
committed
cmd/go/internal/modfetch: handle codeRoot == path for paths with major-version suffixes
Fixes #30647 Change-Id: Icbcfdb3907bc003ac17a8c7df17ecb41daf82eb4 Reviewed-on: https://go-review.googlesource.com/c/go/+/166117 Run-TryBot: Bryan C. Mills <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Jay Conrod <[email protected]>
1 parent 514c559 commit 1ab9f68

File tree

2 files changed

+81
-28
lines changed

2 files changed

+81
-28
lines changed

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

+72-28
Original file line numberDiff line numberDiff line change
@@ -23,55 +23,99 @@ import (
2323
type codeRepo struct {
2424
modPath string
2525

26-
code codehost.Repo
26+
// code is the repository containing this module.
27+
code codehost.Repo
28+
// codeRoot is the import path at the root of code.
2729
codeRoot string
28-
codeDir string
30+
// codeDir is the directory (relative to root) at which we expect to find the module.
31+
// If pathMajor is non-empty and codeRoot is not the full modPath,
32+
// then we look in both codeDir and codeDir+modPath
33+
codeDir string
2934

30-
path string
31-
pathPrefix string
32-
pathMajor string
35+
// pathMajor is the suffix of modPath that indicates its major version,
36+
// or the empty string if modPath is at major version 0 or 1.
37+
//
38+
// pathMajor is typically of the form "/vN", but possibly ".vN", or
39+
// ".vN-unstable" for modules resolved using gopkg.in.
40+
pathMajor string
41+
// pathPrefix is the prefix of modPath that excludes pathMajor.
42+
// It is used only for logging.
43+
pathPrefix string
44+
45+
// pseudoMajor is the major version prefix to use when generating
46+
// pseudo-versions for this module, derived from the module path.
47+
//
48+
// TODO(golang.org/issue/29262): We can't distinguish v0 from v1 using the
49+
// path alone: we have to compute it by examining the tags at a particular
50+
// revision.
3351
pseudoMajor string
3452
}
3553

36-
func newCodeRepo(code codehost.Repo, root, path string) (Repo, error) {
37-
if !hasPathPrefix(path, root) {
38-
return nil, fmt.Errorf("mismatched repo: found %s for %s", root, path)
54+
// newCodeRepo returns a Repo that reads the source code for the module with the
55+
// given path, from the repo stored in code, with the root of the repo
56+
// containing the path given by codeRoot.
57+
func newCodeRepo(code codehost.Repo, codeRoot, path string) (Repo, error) {
58+
if !hasPathPrefix(path, codeRoot) {
59+
return nil, fmt.Errorf("mismatched repo: found %s for %s", codeRoot, path)
3960
}
4061
pathPrefix, pathMajor, ok := module.SplitPathVersion(path)
4162
if !ok {
4263
return nil, fmt.Errorf("invalid module path %q", path)
4364
}
65+
if codeRoot == path {
66+
pathPrefix = path
67+
}
4468
pseudoMajor := "v0"
4569
if pathMajor != "" {
4670
pseudoMajor = pathMajor[1:]
4771
}
4872

73+
// Compute codeDir = bar, the subdirectory within the repo
74+
// corresponding to the module root.
75+
//
4976
// At this point we might have:
50-
// codeRoot = github.com/rsc/foo
5177
// path = github.com/rsc/foo/bar/v2
78+
// codeRoot = github.com/rsc/foo
5279
// pathPrefix = github.com/rsc/foo/bar
5380
// pathMajor = /v2
5481
// pseudoMajor = v2
5582
//
56-
// Compute codeDir = bar, the subdirectory within the repo
57-
// corresponding to the module root.
58-
codeDir := strings.Trim(strings.TrimPrefix(pathPrefix, root), "/")
59-
if strings.HasPrefix(path, "gopkg.in/") {
60-
// But gopkg.in is a special legacy case, in which pathPrefix does not start with codeRoot.
61-
// For example we might have:
62-
// codeRoot = gopkg.in/yaml.v2
63-
// pathPrefix = gopkg.in/yaml
64-
// pathMajor = .v2
65-
// pseudoMajor = v2
66-
// codeDir = pathPrefix (because codeRoot is not a prefix of pathPrefix)
67-
// Clear codeDir - the module root is the repo root for gopkg.in repos.
68-
codeDir = ""
83+
// which gives
84+
// codeDir = bar
85+
//
86+
// We know that pathPrefix is a prefix of path, and codeRoot is a prefix of
87+
// path, but codeRoot may or may not be a prefix of pathPrefix, because
88+
// codeRoot may be the entire path (in which case codeDir should be empty).
89+
// That occurs in two situations.
90+
//
91+
// One is when a go-import meta tag resolves the complete module path,
92+
// including the pathMajor suffix:
93+
// path = nanomsg.org/go/mangos/v2
94+
// codeRoot = nanomsg.org/go/mangos/v2
95+
// pathPrefix = nanomsg.org/go/mangos
96+
// pathMajor = /v2
97+
// pseudoMajor = v2
98+
//
99+
// The other is similar: for gopkg.in only, the major version is encoded
100+
// with a dot rather than a slash, and thus can't be in a subdirectory.
101+
// path = gopkg.in/yaml.v2
102+
// codeRoot = gopkg.in/yaml.v2
103+
// pathPrefix = gopkg.in/yaml
104+
// pathMajor = .v2
105+
// pseudoMajor = v2
106+
//
107+
codeDir := ""
108+
if codeRoot != path {
109+
if !hasPathPrefix(pathPrefix, codeRoot) {
110+
return nil, fmt.Errorf("repository rooted at %s cannot contain module %s", codeRoot, path)
111+
}
112+
codeDir = strings.Trim(pathPrefix[len(codeRoot):], "/")
69113
}
70114

71115
r := &codeRepo{
72116
modPath: path,
73117
code: code,
74-
codeRoot: root,
118+
codeRoot: codeRoot,
75119
codeDir: codeDir,
76120
pathPrefix: pathPrefix,
77121
pathMajor: pathMajor,
@@ -149,9 +193,6 @@ func (r *codeRepo) Stat(rev string) (*RevInfo, error) {
149193
return r.Latest()
150194
}
151195
codeRev := r.revToRev(rev)
152-
if semver.IsValid(codeRev) && r.codeDir != "" {
153-
codeRev = r.codeDir + "/" + codeRev
154-
}
155196
info, err := r.code.Stat(codeRev)
156197
if err != nil {
157198
return nil, err
@@ -290,14 +331,17 @@ func (r *codeRepo) findDir(version string) (rev, dir string, gomod []byte, err e
290331
found1 := err1 == nil && isMajor(mpath1, r.pathMajor)
291332

292333
var file2 string
293-
if r.pathMajor != "" && !strings.HasPrefix(r.pathMajor, ".") {
334+
if r.pathMajor != "" && r.codeRoot != r.modPath && !strings.HasPrefix(r.pathMajor, ".") {
294335
// Suppose pathMajor is "/v2".
295336
// Either go.mod should claim v2 and v2/go.mod should not exist,
296337
// or v2/go.mod should exist and claim v2. Not both.
297338
// Note that we don't check the full path, just the major suffix,
298339
// because of replacement modules. This might be a fork of
299340
// the real module, found at a different path, usable only in
300341
// a replace directive.
342+
//
343+
// TODO(bcmills): This doesn't seem right. Investigate futher.
344+
// (Notably: why can't we replace foo/v2 with fork-of-foo/v3?)
301345
dir2 := path.Join(r.codeDir, r.pathMajor[1:])
302346
file2 = path.Join(dir2, "go.mod")
303347
gomod2, err2 := r.code.ReadFile(rev, file2, codehost.MaxGoMod)
@@ -418,7 +462,7 @@ func (r *codeRepo) Zip(dst io.Writer, version string) error {
418462
}
419463
defer dl.Close()
420464
if actualDir != "" && !hasPathPrefix(dir, actualDir) {
421-
return fmt.Errorf("internal error: downloading %v %v: dir=%q but actualDir=%q", r.path, rev, dir, actualDir)
465+
return fmt.Errorf("internal error: downloading %v %v: dir=%q but actualDir=%q", r.modPath, rev, dir, actualDir)
422466
}
423467
subdir := strings.Trim(strings.TrimPrefix(dir, actualDir), "/")
424468

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

+9
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,15 @@ var codeRepoTests = []struct {
323323
time: time.Date(2017, 5, 31, 16, 3, 50, 0, time.UTC),
324324
gomod: "module gopkg.in/natefinch/lumberjack.v2\n",
325325
},
326+
{
327+
path: "nanomsg.org/go/mangos/v2",
328+
rev: "v2.0.2",
329+
version: "v2.0.2",
330+
name: "63f66a65137b9a648ac9f7bf0160b4a4d17d7999",
331+
short: "63f66a65137b",
332+
time: time.Date(2018, 12, 1, 15, 7, 40, 0, time.UTC),
333+
gomod: "module nanomsg.org/go/mangos/v2\n\nrequire (\n\tgithub.1485827954.workers.dev/Microsoft/go-winio v0.4.11\n\tgithub.1485827954.workers.dev/droundy/goopt v0.0.0-20170604162106-0b8effe182da\n\tgithub.1485827954.workers.dev/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect\n\tgithub.1485827954.workers.dev/gorilla/websocket v1.4.0\n\tgithub.1485827954.workers.dev/jtolds/gls v4.2.1+incompatible // indirect\n\tgithub.1485827954.workers.dev/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d // indirect\n\tgithub.1485827954.workers.dev/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c\n\tgolang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 // indirect\n)\n",
334+
},
326335
}
327336

328337
func TestCodeRepo(t *testing.T) {

0 commit comments

Comments
 (0)