From 7cd10c1149e51a9d2f0868babaf66b8091b9c0b9 Mon Sep 17 00:00:00 2001 From: Jay Conrod Date: Tue, 27 Jul 2021 10:22:35 -0700 Subject: cmd/go: use .mod instead of .zip to determine if version has go.mod file When checking for updates, the go command checks whether the highest compatible version has a go.mod file in order to determine whether +incompatible versions may be considered "latest". Previously, to perform this check, the go command would download the content of the module (the .zip file) to see whether a go.mod file was present at the root. This is slower than necessary, and it caused 'go list -m -u' to try to save the sum for the .zip file in go.sum in some cases. With this change, the go command only downloads the .mod file and checks whether it appears to be a fake file generated for a version that didn't have a go.mod file. This is faster and requires less verification. Fake files only have a "module" directive. It's possible to commit a file that passes this test, but it would be difficult to do accidentally: Go 1.12 and later at least add a "go" directive. A false positive here would cause version queries to have slightly different results but would not affect builds. Fixes #47377 Change-Id: Ie5ffd0b45e39bd0921328a60af99a9f6e5ab6346 Reviewed-on: https://go-review.googlesource.com/c/go/+/337850 Trust: Jay Conrod Run-TryBot: Jay Conrod TryBot-Result: Go Bot Reviewed-by: Michael Matloob --- src/cmd/go/internal/modload/query.go | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) (limited to 'src/cmd/go/internal/modload/query.go') diff --git a/src/cmd/go/internal/modload/query.go b/src/cmd/go/internal/modload/query.go index dda9004a9f..e737ca90fc 100644 --- a/src/cmd/go/internal/modload/query.go +++ b/src/cmd/go/internal/modload/query.go @@ -5,13 +5,13 @@ package modload import ( + "bytes" "context" "errors" "fmt" "io/fs" "os" pathpkg "path" - "path/filepath" "sort" "strings" "sync" @@ -931,14 +931,32 @@ func moduleHasRootPackage(ctx context.Context, m module.Version) (bool, error) { return ok, err } -func versionHasGoMod(ctx context.Context, m module.Version) (bool, error) { - needSum := false - root, _, err := fetch(ctx, m, needSum) +// versionHasGoMod returns whether a version has a go.mod file. +// +// versionHasGoMod fetches the go.mod file (possibly a fake) and true if it +// contains anything other than a module directive with the same path. When a +// module does not have a real go.mod file, the go command acts as if it had one +// that only contained a module directive. Normal go.mod files created after +// 1.12 at least have a go directive. +// +// This function is a heuristic, since it's possible to commit a file that would +// pass this test. However, we only need a heurstic for determining whether +// +incompatible versions may be "latest", which is what this function is used +// for. +// +// This heuristic is useful for two reasons: first, when using a proxy, +// this lets us fetch from the .mod endpoint which is much faster than the .zip +// endpoint. The .mod file is used anyway, even if the .zip file contains a +// go.mod with different content. Second, if we don't fetch the .zip, then +// we don't need to verify it in go.sum. This makes 'go list -m -u' faster +// and simpler. +func versionHasGoMod(_ context.Context, m module.Version) (bool, error) { + _, data, err := rawGoModData(m) if err != nil { return false, err } - fi, err := os.Stat(filepath.Join(root, "go.mod")) - return err == nil && !fi.IsDir(), nil + isFake := bytes.Equal(data, modfetch.LegacyGoMod(m.Path)) + return !isFake, nil } // A versionRepo is a subset of modfetch.Repo that can report information about -- cgit v1.2.3-54-g00ecf