Skip to content

Commit 53f628e

Browse files
author
Jay Conrod
committed
cmd/go/internal/modload: query correct "latest" version through proxy
This fixes a regression introduced in CL 180337. When we query a module at "latest" that has no tagged versions, we tried to use "" as the version because we used info.Name instead of info.Version. This only happened when using a proxy: in direct mode, info.Name is set to the underlying VCS revision, which is fine. Also: serve "/mod/path/@latest" through our test proxy. Previously, we served a 404, which made this bug hard to detect. Fixes #32636 Change-Id: I5c60975656297f862cad66675170e819685ebd39 Reviewed-on: https://go-review.googlesource.com/c/go/+/182697 Run-TryBot: Jay Conrod <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Bryan C. Mills <[email protected]>
1 parent 263db9b commit 53f628e

File tree

4 files changed

+81
-5
lines changed

4 files changed

+81
-5
lines changed

src/cmd/go/internal/modload/query.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ func queryProxy(proxy, path, query, current string, allowed func(module.Version)
241241
// Special case for "latest": if no tags match, use latest commit in repo,
242242
// provided it is not excluded.
243243
if latest, err := repo.Latest(); err == nil && allowed(module.Version{Path: path, Version: latest.Version}) {
244-
return lookup(latest.Name)
244+
return lookup(latest.Version)
245245
}
246246
}
247247

src/cmd/go/proxy_test.go

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,57 @@ func proxyHandler(w http.ResponseWriter, r *http.Request) {
182182
return
183183
}
184184

185+
// Module proxy request: /mod/path/@latest
186+
// Rewrite to /mod/path/@v/<latest>.info where <latest> is the semantically
187+
// latest version, including pseudo-versions.
188+
if i := strings.LastIndex(path, "/@latest"); i >= 0 {
189+
enc := path[:i]
190+
modPath, err := module.DecodePath(enc)
191+
if err != nil {
192+
if !quiet {
193+
fmt.Fprintf(os.Stderr, "go proxy_test: %v\n", err)
194+
}
195+
http.NotFound(w, r)
196+
return
197+
}
198+
199+
// Imitate what "latest" does in direct mode and what proxy.golang.org does.
200+
// Use the latest released version.
201+
// If there is no released version, use the latest prereleased version.
202+
// Otherwise, use the latest pseudoversion.
203+
var latestRelease, latestPrerelease, latestPseudo string
204+
for _, m := range modList {
205+
if m.Path != modPath {
206+
continue
207+
}
208+
if modfetch.IsPseudoVersion(m.Version) && (latestPseudo == "" || semver.Compare(latestPseudo, m.Version) > 0) {
209+
latestPseudo = m.Version
210+
} else if semver.Prerelease(m.Version) != "" && (latestPrerelease == "" || semver.Compare(latestPrerelease, m.Version) > 0) {
211+
latestPrerelease = m.Version
212+
} else if latestRelease == "" || semver.Compare(latestRelease, m.Version) > 0 {
213+
latestRelease = m.Version
214+
}
215+
}
216+
var latest string
217+
if latestRelease != "" {
218+
latest = latestRelease
219+
} else if latestPrerelease != "" {
220+
latest = latestPrerelease
221+
} else if latestPseudo != "" {
222+
latest = latestPseudo
223+
} else {
224+
http.NotFound(w, r)
225+
return
226+
}
227+
228+
encVers, err := module.EncodeVersion(latest)
229+
if err != nil {
230+
http.Error(w, err.Error(), http.StatusInternalServerError)
231+
return
232+
}
233+
path = fmt.Sprintf("%s/@v/%s.info", enc, encVers)
234+
}
235+
185236
// Module proxy request: /mod/path/@v/version[.suffix]
186237
i := strings.Index(path, "/@v/")
187238
if i < 0 {
@@ -198,16 +249,22 @@ func proxyHandler(w http.ResponseWriter, r *http.Request) {
198249
return
199250
}
200251
if file == "list" {
201-
n := 0
252+
// list returns a list of versions, not including pseudo-versions.
253+
// If the module has no tagged versions, we should serve an empty 200.
254+
// If the module doesn't exist, we should serve 404 or 410.
255+
found := false
202256
for _, m := range modList {
203-
if m.Path == path && !modfetch.IsPseudoVersion(m.Version) {
257+
if m.Path != path {
258+
continue
259+
}
260+
found = true
261+
if !modfetch.IsPseudoVersion(m.Version) {
204262
if err := module.Check(m.Path, m.Version); err == nil {
205263
fmt.Fprintf(w, "%s\n", m.Version)
206-
n++
207264
}
208265
}
209266
}
210-
if n == 0 {
267+
if !found {
211268
http.NotFound(w, r)
212269
}
213270
return
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Written by hand.
2+
The "latest" version of a module without any tags.
3+
4+
-- .mod --
5+
module example.com/notags
6+
-- .info --
7+
{"Version":"v0.0.0-20190507143103-cc8cbe209b64","Time":"2019-05-07T07:31:03-07:00"}
8+
-- notags.go --
9+
package notags
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# Check that we can build a module with no tagged versions by querying
2+
# "@latest" through a proxy.
3+
# Verifies golang.org/issue/32636
4+
5+
env GO111MODULE=on
6+
7+
go mod init m
8+
go list example.com/notags
9+
go list -m all
10+
stdout '^example.com/notags v0.0.0-20190507143103-cc8cbe209b64$'

0 commit comments

Comments
 (0)