Commit 1dc0110b authored by Jay Conrod's avatar Jay Conrod

cmd/go: improve 'go mod download' and 'go list -m' error messages

modload.ListModules now wraps errors as module.ModuleError as
appropriate. The resulting errors always include the module path and
will include the version, if known.

'go mod download' no longer ignores errors reported by ListModules.
Previously, it started requesting module info, go.mod, and zip. Those
requests would fail, overwriting the original failure. They were
usually less descriptive.

'go mod download' with a module not in the build list (and no version
query) is now an error. Previously, this was silently ignored.

Fixes #30743

Change-Id: Icee8c1c6c5240de135a8b6ba42d6bbcdb757cdac
Reviewed-on: https://go-review.googlesource.com/c/go/+/189323
Run-TryBot: Jay Conrod <jayconrod@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarBryan C. Mills <bcmills@google.com>
parent c5178ef6
...@@ -390,7 +390,7 @@ func runList(cmd *base.Command, args []string) { ...@@ -390,7 +390,7 @@ func runList(cmd *base.Command, args []string) {
if !*listE { if !*listE {
for _, m := range mods { for _, m := range mods {
if m.Error != nil { if m.Error != nil {
base.Errorf("go list -m %s: %v", m.Path, m.Error.Err) base.Errorf("go list -m: %v", m.Error.Err)
} }
} }
base.ExitIfErrors() base.ExitIfErrors()
......
...@@ -89,7 +89,8 @@ func runDownload(cmd *base.Command, args []string) { ...@@ -89,7 +89,8 @@ func runDownload(cmd *base.Command, args []string) {
if info.Replace != nil { if info.Replace != nil {
info = info.Replace info = info.Replace
} }
if info.Version == "" { if info.Version == "" && info.Error == nil {
// main module
continue continue
} }
m := &moduleJSON{ m := &moduleJSON{
...@@ -97,6 +98,10 @@ func runDownload(cmd *base.Command, args []string) { ...@@ -97,6 +98,10 @@ func runDownload(cmd *base.Command, args []string) {
Version: info.Version, Version: info.Version,
} }
mods = append(mods, m) mods = append(mods, m)
if info.Error != nil {
m.Error = info.Error.Err
continue
}
work.Add(m) work.Add(m)
} }
...@@ -110,15 +115,20 @@ func runDownload(cmd *base.Command, args []string) { ...@@ -110,15 +115,20 @@ func runDownload(cmd *base.Command, args []string) {
// downloading the modules. // downloading the modules.
var latestArgs []string var latestArgs []string
for _, m := range mods { for _, m := range mods {
if m.Error != "" {
continue
}
latestArgs = append(latestArgs, m.Path+"@latest") latestArgs = append(latestArgs, m.Path+"@latest")
} }
if len(latestArgs) > 0 {
for _, info := range modload.ListModules(latestArgs, listU, listVersions) { for _, info := range modload.ListModules(latestArgs, listU, listVersions) {
if info.Version != "" { if info.Version != "" {
latest[info.Path] = info.Version latest[info.Path] = info.Version
} }
} }
} }
}
work.Do(10, func(item interface{}) { work.Do(10, func(item interface{}) {
m := item.(*moduleJSON) m := item.(*moduleJSON)
...@@ -169,7 +179,7 @@ func runDownload(cmd *base.Command, args []string) { ...@@ -169,7 +179,7 @@ func runDownload(cmd *base.Command, args []string) {
} else { } else {
for _, m := range mods { for _, m := range mods {
if m.Error != "" { if m.Error != "" {
base.Errorf("%s@%s: %s\n", m.Path, m.Version, m.Error) base.Errorf("%s", m.Error)
} }
} }
base.ExitIfErrors() base.ExitIfErrors()
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
package modload package modload
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"strings" "strings"
...@@ -70,9 +71,7 @@ func listModules(args []string, listVersions bool) []*modinfo.ModulePublic { ...@@ -70,9 +71,7 @@ func listModules(args []string, listVersions bool) []*modinfo.ModulePublic {
mods = append(mods, &modinfo.ModulePublic{ mods = append(mods, &modinfo.ModulePublic{
Path: path, Path: path,
Version: vers, Version: vers,
Error: &modinfo.ModuleError{ Error: modinfoError(path, vers, err),
Err: err.Error(),
},
}) })
continue continue
} }
...@@ -117,18 +116,14 @@ func listModules(args []string, listVersions bool) []*modinfo.ModulePublic { ...@@ -117,18 +116,14 @@ func listModules(args []string, listVersions bool) []*modinfo.ModulePublic {
} else { } else {
mods = append(mods, &modinfo.ModulePublic{ mods = append(mods, &modinfo.ModulePublic{
Path: arg, Path: arg,
Error: &modinfo.ModuleError{ Error: modinfoError(arg, "", err),
Err: err.Error(),
},
}) })
} }
continue continue
} }
mods = append(mods, &modinfo.ModulePublic{ mods = append(mods, &modinfo.ModulePublic{
Path: arg, Path: arg,
Error: &modinfo.ModuleError{ Error: modinfoError(arg, "", errors.New("not a known dependency")),
Err: fmt.Sprintf("module %q is not a known dependency", arg),
},
}) })
} else { } else {
fmt.Fprintf(os.Stderr, "warning: pattern %q matched no module dependencies\n", arg) fmt.Fprintf(os.Stderr, "warning: pattern %q matched no module dependencies\n", arg)
...@@ -138,3 +133,21 @@ func listModules(args []string, listVersions bool) []*modinfo.ModulePublic { ...@@ -138,3 +133,21 @@ func listModules(args []string, listVersions bool) []*modinfo.ModulePublic {
return mods return mods
} }
// modinfoError wraps an error to create an error message in
// modinfo.ModuleError with minimal redundancy.
func modinfoError(path, vers string, err error) *modinfo.ModuleError {
var nerr *NoMatchingVersionError
var merr *module.ModuleError
if errors.As(err, &nerr) {
// NoMatchingVersionError contains the query, so we don't mention the
// query again in ModuleError.
err = &module.ModuleError{Path: path, Err: err}
} else if !errors.As(err, &merr) {
// If the error does not contain path and version, wrap it in a
// module.ModuleError.
err = &module.ModuleError{Path: path, Version: vers, Err: err}
}
return &modinfo.ModuleError{Err: err.Error()}
}
...@@ -85,6 +85,16 @@ exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.3-pre1.zip ...@@ -85,6 +85,16 @@ exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.3-pre1.zip
go mod download -json rsc.io/quote@v1.5.1 go mod download -json rsc.io/quote@v1.5.1
exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.1.zip exists $GOPATH/pkg/mod/cache/download/rsc.io/quote/@v/v1.5.1.zip
# download reports errors encountered when locating modules
! go mod download bad/path
stderr '^module bad/path: not a known dependency$'
! go mod download bad/path@latest
stderr '^bad/path@latest: malformed module path "bad/path": missing dot in first path element$'
! go mod download rsc.io/quote@v1.999.999
stderr '^rsc.io/quote@v1.999.999: reading .*/v1.999.999.info: 404 Not Found$'
! go mod download -json bad/path
stdout '^\t"Error": "module bad/path: not a known dependency"'
# allow go mod download without go.mod # allow go mod download without go.mod
env GO111MODULE=auto env GO111MODULE=auto
rm go.mod rm go.mod
......
...@@ -34,12 +34,12 @@ go list rsc.io/quote/buggy ...@@ -34,12 +34,12 @@ go list rsc.io/quote/buggy
# rsc.io/quote/buggy should not be listable as a module # rsc.io/quote/buggy should not be listable as a module
go list -m -e -f '{{.Error.Err}}' nonexist rsc.io/quote/buggy go list -m -e -f '{{.Error.Err}}' nonexist rsc.io/quote/buggy
stdout '^module "nonexist" is not a known dependency' stdout '^module nonexist: not a known dependency$'
stdout '^module "rsc.io/quote/buggy" is not a known dependency' stdout '^module rsc.io/quote/buggy: not a known dependency$'
! go list -m nonexist rsc.io/quote/buggy ! go list -m nonexist rsc.io/quote/buggy
stderr '^go list -m nonexist: module "nonexist" is not a known dependency' stderr '^go list -m: module nonexist: not a known dependency'
stderr '^go list -m rsc.io/quote/buggy: module "rsc.io/quote/buggy" is not a known dependency' stderr '^go list -m: module rsc.io/quote/buggy: not a known dependency'
# Module loader does not interfere with list -e (golang.org/issue/24149). # Module loader does not interfere with list -e (golang.org/issue/24149).
go list -e -f '{{.Error.Err}}' database go list -e -f '{{.Error.Err}}' database
......
...@@ -22,7 +22,7 @@ go list -m rsc.io/quote@<v1.5.4 ...@@ -22,7 +22,7 @@ go list -m rsc.io/quote@<v1.5.4
stdout 'rsc.io/quote v1.5.2$' stdout 'rsc.io/quote v1.5.2$'
! go list -m rsc.io/quote@>v1.5.3 ! go list -m rsc.io/quote@>v1.5.3
stderr 'go list -m rsc.io/quote: no matching versions for query ">v1.5.3"' stderr 'go list -m: module rsc.io/quote: no matching versions for query ">v1.5.3"'
go list -m -e -f '{{.Error.Err}}' rsc.io/quote@>v1.5.3 go list -m -e -f '{{.Error.Err}}' rsc.io/quote@>v1.5.3
stdout 'no matching versions for query ">v1.5.3"' stdout 'no matching versions for query ">v1.5.3"'
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment