Skip to content

Commit 835e36f

Browse files
samthanawallagopherbot
authored andcommitted
cmd/go: add subdirectory support to go-import meta tag
This CL adds ability to specify a subdirectory in the go-import meta tag. A go-import meta tag now will support: <meta name="go-import" content="root-path vcs repo-url subdir"> Fixes: #34055 Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest,gotip-windows-amd64-longtest Change-Id: Iedac520f97e0646254cc1bd2f97d5a9a5236829b Reviewed-on: https://go-review.googlesource.com/c/go/+/625577 Reviewed-by: Michael Matloob <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Auto-Submit: Sam Thanawalla <[email protected]>
1 parent 2c35900 commit 835e36f

14 files changed

+275
-29
lines changed

src/cmd/go/alldocs.go

+20-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/go/internal/help/helpdoc.go

+20-2
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,11 @@ The meta tag has the form:
241241
242242
<meta name="go-import" content="import-prefix vcs repo-root">
243243
244+
Starting in Go 1.25, an optional subdirectory will be recognized by the
245+
go command:
246+
247+
<meta name="go-import" content="import-prefix vcs repo-root subdir">
248+
244249
The import-prefix is the import path corresponding to the repository
245250
root. It must be a prefix or an exact match of the package being
246251
fetched with "go get". If it's not an exact match, another http
@@ -255,6 +260,12 @@ The vcs is one of "bzr", "fossil", "git", "hg", "svn".
255260
The repo-root is the root of the version control system
256261
containing a scheme and not containing a .vcs qualifier.
257262
263+
The subdir specifies the directory within the repo-root where the
264+
Go module's root (including its go.mod file) is located. It allows
265+
you to organize your repository with the Go module code in a subdirectory
266+
rather than directly at the repository's root.
267+
If set, all vcs tags must be prefixed with "subdir". i.e. "subdir/v1.2.3"
268+
258269
For example,
259270
260271
import "example.org/pkg/foo"
@@ -269,8 +280,15 @@ If that page contains the meta tag
269280
<meta name="go-import" content="example.org git https://code.org/r/p/exproj">
270281
271282
the go tool will verify that https://example.org/?go-get=1 contains the
272-
same meta tag and then git clone https://code.org/r/p/exproj into
273-
GOPATH/src/example.org.
283+
same meta tag and then download the code from the Git repository at https://code.org/r/p/exproj
284+
285+
If that page contains the meta tag
286+
287+
<meta name="go-import" content="example.org git https://code.org/r/p/exproj foo/subdir">
288+
289+
the go tool will verify that https://example.org/?go-get=1 contains the same meta
290+
tag and then download the code from the "foo/subdir" subdirectory within the Git repository
291+
at https://code.org/r/p/exproj
274292
275293
Downloaded packages are stored in the module cache.
276294
See https://golang.org/ref/mod#module-cache.

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

+18-3
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,11 @@ type codeRepo struct {
5959
}
6060

6161
// newCodeRepo returns a Repo that reads the source code for the module with the
62-
// given path, from the repo stored in code, with the root of the repo
63-
// containing the path given by codeRoot.
64-
func newCodeRepo(code codehost.Repo, codeRoot, path string) (Repo, error) {
62+
// given path, from the repo stored in code.
63+
// codeRoot gives the import path corresponding to the root of the repository,
64+
// and subdir gives the subdirectory within the repo containing the module.
65+
// If subdir is empty, the module is at the root of the repo.
66+
func newCodeRepo(code codehost.Repo, codeRoot, subdir, path string) (Repo, error) {
6567
if !hasPathPrefix(path, codeRoot) {
6668
return nil, fmt.Errorf("mismatched repo: found %s for %s", codeRoot, path)
6769
}
@@ -108,13 +110,26 @@ func newCodeRepo(code codehost.Repo, codeRoot, path string) (Repo, error) {
108110
// pathMajor = .v2
109111
// pseudoMajor = v2
110112
//
113+
// Starting in 1.25, subdir may be passed in by the go-import meta tag.
114+
// So it may be the case that:
115+
// path = github.com/rsc/foo/v2
116+
// codeRoot = github.com/rsc/foo
117+
// subdir = bar/subdir
118+
// pathPrefix = github.com/rsc/foo
119+
// pathMajor = /v2
120+
// pseudoMajor = v2
121+
// which means that codeDir = bar/subdir
122+
111123
codeDir := ""
112124
if codeRoot != path {
113125
if !hasPathPrefix(pathPrefix, codeRoot) {
114126
return nil, fmt.Errorf("repository rooted at %s cannot contain module %s", codeRoot, path)
115127
}
116128
codeDir = strings.Trim(pathPrefix[len(codeRoot):], "/")
117129
}
130+
if subdir != "" {
131+
codeDir = filepath.ToSlash(filepath.Join(codeDir, subdir))
132+
}
118133

119134
r := &codeRepo{
120135
modPath: path,

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

+11-1
Original file line numberDiff line numberDiff line change
@@ -884,6 +884,16 @@ var latestTests = []struct {
884884
path: "swtch.com/testmod",
885885
version: "v1.1.1",
886886
},
887+
{
888+
vcs: "git",
889+
path: "vcs-test.golang.org/go/gitreposubdir",
890+
version: "v1.2.3",
891+
},
892+
{
893+
vcs: "git",
894+
path: "vcs-test.golang.org/go/gitreposubdirv2/v2",
895+
version: "v2.0.0",
896+
},
887897
}
888898

889899
func TestLatest(t *testing.T) {
@@ -950,7 +960,7 @@ func TestNonCanonicalSemver(t *testing.T) {
950960
},
951961
}
952962

953-
cr, err := newCodeRepo(ch, root, root)
963+
cr, err := newCodeRepo(ch, root, "", root)
954964
if err != nil {
955965
t.Fatal(err)
956966
}

src/cmd/go/internal/modfetch/repo.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -240,7 +240,7 @@ func LookupLocal(ctx context.Context, codeRoot string, path string, dir string)
240240
if err != nil {
241241
return nil, err
242242
}
243-
r, err := newCodeRepo(code, codeRoot, path)
243+
r, err := newCodeRepo(code, codeRoot, "", path)
244244
if err == nil && traceRepo {
245245
r = newLoggingRepo(r)
246246
}
@@ -319,7 +319,7 @@ func lookupDirect(ctx context.Context, path string) (Repo, error) {
319319
if err != nil {
320320
return nil, err
321321
}
322-
return newCodeRepo(code, rr.Root, path)
322+
return newCodeRepo(code, rr.Root, rr.SubDir, path)
323323
}
324324

325325
func lookupCodeRepo(ctx context.Context, rr *vcs.RepoRoot, local bool) (codehost.Repo, error) {

src/cmd/go/internal/vcs/discovery.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -54,12 +54,17 @@ func parseMetaGoImports(r io.Reader, mod ModuleMode) ([]metaImport, error) {
5454
if attrValue(e.Attr, "name") != "go-import" {
5555
continue
5656
}
57-
if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 {
58-
imports = append(imports, metaImport{
57+
if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 || len(f) == 4 {
58+
mi := metaImport{
5959
Prefix: f[0],
6060
VCS: f[1],
6161
RepoRoot: f[2],
62-
})
62+
}
63+
// An optional subdirectory may be provided.
64+
if len(f) == 4 {
65+
mi.SubDir = f[3]
66+
}
67+
imports = append(imports, mi)
6368
}
6469
}
6570

src/cmd/go/internal/vcs/discovery_test.go

+23-13
Original file line numberDiff line numberDiff line change
@@ -18,82 +18,92 @@ var parseMetaGoImportsTests = []struct {
1818
{
1919
`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
2020
IgnoreMod,
21-
[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
21+
[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar", ""}},
2222
},
2323
{
2424
`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
2525
<meta name="go-import" content="baz/quux git http://github.com/rsc/baz/quux">`,
2626
IgnoreMod,
2727
[]metaImport{
28-
{"foo/bar", "git", "https://github.com/rsc/foo/bar"},
29-
{"baz/quux", "git", "http://github.com/rsc/baz/quux"},
28+
{"foo/bar", "git", "https://github.com/rsc/foo/bar", ""},
29+
{"baz/quux", "git", "http://github.com/rsc/baz/quux", ""},
3030
},
3131
},
3232
{
3333
`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
3434
<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">`,
3535
IgnoreMod,
3636
[]metaImport{
37-
{"foo/bar", "git", "https://github.com/rsc/foo/bar"},
37+
{"foo/bar", "git", "https://github.com/rsc/foo/bar", ""},
3838
},
3939
},
4040
{
4141
`<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">
4242
<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
4343
IgnoreMod,
4444
[]metaImport{
45-
{"foo/bar", "git", "https://github.com/rsc/foo/bar"},
45+
{"foo/bar", "git", "https://github.com/rsc/foo/bar", ""},
4646
},
4747
},
4848
{
4949
`<meta name="go-import" content="foo/bar mod http://github.com/rsc/baz/quux">
5050
<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
5151
PreferMod,
5252
[]metaImport{
53-
{"foo/bar", "mod", "http://github.com/rsc/baz/quux"},
53+
{"foo/bar", "mod", "http://github.com/rsc/baz/quux", ""},
5454
},
5555
},
5656
{
5757
`<head>
5858
<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
5959
</head>`,
6060
IgnoreMod,
61-
[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
61+
[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar", ""}},
6262
},
6363
{
6464
`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">
6565
<body>`,
6666
IgnoreMod,
67-
[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
67+
[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar", ""}},
6868
},
6969
{
7070
`<!doctype html><meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar">`,
7171
IgnoreMod,
72-
[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar"}},
72+
[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar", ""}},
7373
},
7474
{
7575
// XML doesn't like <div style=position:relative>.
7676
`<!doctype html><title>Page Not Found</title><meta name=go-import content="chitin.io/chitin git https://github.com/chitin-io/chitin"><div style=position:relative>DRAFT</div>`,
7777
IgnoreMod,
78-
[]metaImport{{"chitin.io/chitin", "git", "https://github.com/chitin-io/chitin"}},
78+
[]metaImport{{"chitin.io/chitin", "git", "https://github.com/chitin-io/chitin", ""}},
7979
},
8080
{
8181
`<meta name="go-import" content="myitcv.io git https://github.com/myitcv/x">
8282
<meta name="go-import" content="myitcv.io/blah2 mod https://raw.githubusercontent.com/myitcv/pubx/master">
8383
`,
8484
IgnoreMod,
85-
[]metaImport{{"myitcv.io", "git", "https://github.com/myitcv/x"}},
85+
[]metaImport{{"myitcv.io", "git", "https://github.com/myitcv/x", ""}},
8686
},
8787
{
8888
`<meta name="go-import" content="myitcv.io git https://github.com/myitcv/x">
8989
<meta name="go-import" content="myitcv.io/blah2 mod https://raw.githubusercontent.com/myitcv/pubx/master">
9090
`,
9191
PreferMod,
9292
[]metaImport{
93-
{"myitcv.io/blah2", "mod", "https://raw.githubusercontent.com/myitcv/pubx/master"},
94-
{"myitcv.io", "git", "https://github.com/myitcv/x"},
93+
{"myitcv.io/blah2", "mod", "https://raw.githubusercontent.com/myitcv/pubx/master", ""},
94+
{"myitcv.io", "git", "https://github.com/myitcv/x", ""},
9595
},
9696
},
97+
{
98+
`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar subdir">`,
99+
IgnoreMod,
100+
[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar", "subdir"}},
101+
},
102+
{
103+
`<meta name="go-import" content="foo/bar git https://github.com/rsc/foo/bar subdir/path">`,
104+
IgnoreMod,
105+
[]metaImport{{"foo/bar", "git", "https://github.com/rsc/foo/bar", "subdir/path"}},
106+
},
97107
}
98108

99109
func TestParseMetaGoImports(t *testing.T) {

src/cmd/go/internal/vcs/vcs.go

+5-3
Original file line numberDiff line numberDiff line change
@@ -1056,7 +1056,8 @@ func checkGOVCS(vcs *Cmd, root string) error {
10561056
// RepoRoot describes the repository root for a tree of source code.
10571057
type RepoRoot struct {
10581058
Repo string // repository URL, including scheme
1059-
Root string // import path corresponding to root of repo
1059+
Root string // import path corresponding to the SubDir
1060+
SubDir string // subdirectory within the repo (empty for root)
10601061
IsCustom bool // defined by served <meta> tags (as opposed to hard-coded pattern)
10611062
VCS *Cmd
10621063
}
@@ -1368,6 +1369,7 @@ func repoRootForImportDynamic(importPath string, mod ModuleMode, security web.Se
13681369
rr := &RepoRoot{
13691370
Repo: repoURL,
13701371
Root: mmi.Prefix,
1372+
SubDir: mmi.SubDir,
13711373
IsCustom: true,
13721374
VCS: vcs,
13731375
}
@@ -1457,9 +1459,9 @@ type fetchResult struct {
14571459
}
14581460

14591461
// metaImport represents the parsed <meta name="go-import"
1460-
// content="prefix vcs reporoot" /> tags from HTML files.
1462+
// content="prefix vcs reporoot subdir" /> tags from HTML files.
14611463
type metaImport struct {
1462-
Prefix, VCS, RepoRoot string
1464+
Prefix, VCS, RepoRoot, SubDir string
14631465
}
14641466

14651467
// An ImportMismatchError is returned where metaImport/s are present

src/cmd/go/internal/vcs/vcs_test.go

+22
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,28 @@ func TestMatchGoImport(t *testing.T) {
425425
path: "myitcv.io/other",
426426
mi: metaImport{Prefix: "myitcv.io", VCS: "git", RepoRoot: "https://github.com/myitcv/x"},
427427
},
428+
{
429+
imports: []metaImport{
430+
{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target", SubDir: "subdir"},
431+
},
432+
path: "example.com/user/foo",
433+
mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target", SubDir: "subdir"},
434+
},
435+
{
436+
imports: []metaImport{
437+
{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target", SubDir: "foo/subdir"},
438+
},
439+
path: "example.com/user/foo",
440+
mi: metaImport{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target", SubDir: "foo/subdir"},
441+
},
442+
{
443+
imports: []metaImport{
444+
{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target", SubDir: "subdir"},
445+
{Prefix: "example.com/user/foo", VCS: "git", RepoRoot: "https://example.com/repo/target", SubDir: ""},
446+
},
447+
path: "example.com/user/foo",
448+
err: errors.New("multiple meta tags match import path"),
449+
},
428450
}
429451

430452
for _, test := range tests {

0 commit comments

Comments
 (0)