cmd/gorelease: diagnose load errors with replace or exclude
When package loading fails for a local release module, report when its
go.mod contains replace or exclude directives. Those directives only
apply within the main module and may explain the package errors.
Fixes golang/go#37559
diff --git a/cmd/gorelease/gorelease.go b/cmd/gorelease/gorelease.go
index e155323..ff684eb 100644
--- a/cmd/gorelease/gorelease.go
+++ b/cmd/gorelease/gorelease.go
@@ -413,6 +413,16 @@
m.diagnostics = append(m.diagnostics, prepareDiagnostics...)
m.diagnostics = append(m.diagnostics, loadDiagnostics...)
+ if packagesHaveErrors(m.pkgs) {
+ // m.goModFile was parsed with ParseLax, which ignores replace and
+ // exclude directives.
+ modFile, err := modfile.Parse(m.goModPath, m.goModData, nil)
+ if err == nil {
+ if d := replaceExcludeDiagnostic(modFile); d != "" {
+ m.diagnostics = append(m.diagnostics, d)
+ }
+ }
+ }
highestVersion, err := findSelectedVersion(ctx, tmpLoadDir, m.modPath)
if err != nil {
@@ -1321,9 +1331,9 @@
// from being loaded.
func loadPackages(ctx context.Context, modPath, modRoot, loadDir string, goModData, goSumData []byte, pkgPaths []string) (pkgs []*packages.Package, diagnostics []string, err error) {
// Load packages.
- // TODO(jayconrod): if there are errors loading packages in the release
- // version, try loading in the release directory. Errors there would imply
- // that packages don't load without replace / exclude directives.
+ // If packages fail to load, loadLocalModule may append a diagnostic
+ // explaining that replace and exclude directives in the main module's
+ // go.mod file are not applied here.
cfg := &packages.Config{
Mode: packages.NeedName | packages.NeedTypes | packages.NeedImports | packages.NeedDeps,
Dir: loadDir,
@@ -1354,6 +1364,34 @@
return pkgs, diagnostics, nil
}
+func packagesHaveErrors(pkgs []*packages.Package) bool {
+ for _, pkg := range pkgs {
+ if len(pkg.Errors) > 0 {
+ return true
+ }
+ }
+ return false
+}
+
+func replaceExcludeDiagnostic(f *modfile.File) string {
+ if f == nil {
+ return ""
+ }
+ hasReplace := len(f.Replace) > 0
+ hasExclude := len(f.Exclude) > 0
+ if !hasReplace && !hasExclude {
+ return ""
+ }
+ switch {
+ case hasReplace && hasExclude:
+ return "go.mod: this module contains replace and exclude directives. These directives only apply within the main module and may cause the errors above."
+ case hasReplace:
+ return "go.mod: this module contains replace directives. These directives only apply within the main module and may cause the errors above."
+ default:
+ return "go.mod: this module contains exclude directives. These directives only apply within the main module and may cause the errors above."
+ }
+}
+
type packagePair struct {
base, release *packages.Package
}
diff --git a/cmd/gorelease/testdata/mod/example.com_dep_v1.0.0.txt b/cmd/gorelease/testdata/mod/example.com_dep_v1.0.0.txt
new file mode 100644
index 0000000..e3cb5da
--- /dev/null
+++ b/cmd/gorelease/testdata/mod/example.com_dep_v1.0.0.txt
@@ -0,0 +1,10 @@
+Module example.com/dep v1.0.0 is broken: package dep does not export Fixed.
+
+-- go.mod --
+module example.com/dep
+
+go 1.12
+-- dep.go --
+package dep
+
+const Broken = 1
diff --git a/cmd/gorelease/testdata/mod/example.com_dep_v1.0.1.txt b/cmd/gorelease/testdata/mod/example.com_dep_v1.0.1.txt
new file mode 100644
index 0000000..af9380e
--- /dev/null
+++ b/cmd/gorelease/testdata/mod/example.com_dep_v1.0.1.txt
@@ -0,0 +1,10 @@
+Module example.com/dep v1.0.1 exports Fixed.
+
+-- go.mod --
+module example.com/dep
+
+go 1.12
+-- dep.go --
+package dep
+
+const Fixed = 1
diff --git a/cmd/gorelease/testdata/replace_exclude/exclude_load_error.test b/cmd/gorelease/testdata/replace_exclude/exclude_load_error.test
new file mode 100644
index 0000000..d2296bf
--- /dev/null
+++ b/cmd/gorelease/testdata/replace_exclude/exclude_load_error.test
@@ -0,0 +1,34 @@
+base=none
+release=v0.0.1
+success=false
+proxyVersions=example.com/d...@v1.0.0,example.com/d...@v1.0.1
+-- want --
+# example.com/replaceexclude/exclude
+## errors in release version:
+exclude.go:5:15: undefined: dep.Fixed
+
+# diagnostics
+go.mod: this module contains exclude directives. These directives only apply within the main module and may cause the errors above.
+
+# summary
+v0.0.1 is not a valid semantic version for this release.
+Errors were found in one or more packages.
+-- go.mod --
+module example.com/replaceexclude/exclude
+
+go 1.12
+
+require example.com/dep v1.0.0
+
+exclude example.com/dep v1.0.0
+-- go.sum --
+example.com/dep v1.0.0 h1:xsd8Ex2nR4lIFBJelcK7XXBZ7uHtMNiojwKRBpz3kJc=
+example.com/dep v1.0.0/go.mod h1:CvfpVsXlEpvIlWpb1Ed74iO4zsMolnHdaXcIZt5jBrw=
+example.com/dep v1.0.1 h1:qP3X/Eyoz9bTQYI6mY9skqEZOzXrYLn+p29WdO7cNmY=
+example.com/dep v1.0.1/go.mod h1:CvfpVsXlEpvIlWpb1Ed74iO4zsMolnHdaXcIZt5jBrw=
+-- exclude.go --
+package exclude
+
+import "example.com/dep"
+
+const X = dep.Fixed
diff --git a/cmd/gorelease/testdata/replace_exclude/load_error_no_directives.test b/cmd/gorelease/testdata/replace_exclude/load_error_no_directives.test
new file mode 100644
index 0000000..9928a2c
--- /dev/null
+++ b/cmd/gorelease/testdata/replace_exclude/load_error_no_directives.test
@@ -0,0 +1,27 @@
+base=none
+release=v0.0.1
+success=false
+proxyVersions=example.com/d...@v1.0.0
+-- want --
+# example.com/replaceexclude/nodep
+## errors in release version:
+nodep.go:5:15: undefined: dep.Fixed
+
+# summary
+v0.0.1 is not a valid semantic version for this release.
+Errors were found in one or more packages.
+-- go.mod --
+module example.com/replaceexclude/nodep
+
+go 1.12
+
+require example.com/dep v1.0.0
+-- go.sum --
+example.com/dep v1.0.0 h1:xsd8Ex2nR4lIFBJelcK7XXBZ7uHtMNiojwKRBpz3kJc=
+example.com/dep v1.0.0/go.mod h1:CvfpVsXlEpvIlWpb1Ed74iO4zsMolnHdaXcIZt5jBrw=
+-- nodep.go --
+package nodep
+
+import "example.com/dep"
+
+const X = dep.Fixed
diff --git a/cmd/gorelease/testdata/replace_exclude/replace_load_error.test b/cmd/gorelease/testdata/replace_exclude/replace_load_error.test
new file mode 100644
index 0000000..86faf5a
--- /dev/null
+++ b/cmd/gorelease/testdata/replace_exclude/replace_load_error.test
@@ -0,0 +1,40 @@
+base=none
+release=v0.0.1
+success=false
+proxyVersions=example.com/d...@v1.0.0
+-- want --
+# example.com/replaceexclude/replace
+## errors in release version:
+replace.go:5:15: undefined: dep.Fixed
+
+# diagnostics
+go.mod: this module contains replace directives. These directives only apply within the main module and may cause the errors above.
+
+# summary
+v0.0.1 is not a valid semantic version for this release.
+Errors were found in one or more packages.
+-- go.mod --
+module example.com/replaceexclude/replace
+
+go 1.12
+
+require example.com/dep v1.0.0
+
+replace example.com/dep => ./fixed
+-- go.sum --
+example.com/dep v1.0.0 h1:xsd8Ex2nR4lIFBJelcK7XXBZ7uHtMNiojwKRBpz3kJc=
+example.com/dep v1.0.0/go.mod h1:CvfpVsXlEpvIlWpb1Ed74iO4zsMolnHdaXcIZt5jBrw=
+-- replace.go --
+package replace
+
+import "example.com/dep"
+
+const X = dep.Fixed
+-- fixed/go.mod --
+module example.com/dep
+
+go 1.12
+-- fixed/dep.go --
+package dep
+
+const Fixed = 1
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Congratulations on opening your first change. Thank you for your contribution!
Next steps:
A maintainer will review your change and provide feedback. See
https://go.dev/doc/contribute#review for more info and tips to get your
patch through code review.
Most changes in the Go project go through a few rounds of revision. This can be
surprising to people new to the project. The careful, iterative review process
is our way of helping mentor contributors and ensuring that their contributions
have a lasting impact.
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
@Jonathan Amsterdam @Alan Donovan Friendly ping: PTAL when you have a moment. Thanks!
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
func replaceExcludeDiagnostic(f *modfile.File) string {constructReplace...
otherwise it sounds like you're replacing something
case hasReplace && hasExclude:
return "go.mod: this module contains replace and exclude directives. These directives only apply within the main module and may cause the errors above."
case hasReplace:
return "go.mod: this module contains replace directives. These directives only apply within the main module and may cause the errors above."
default:var dirs string
switch {
case x & y:
dirs = "x and y"
case x:
case y:
default:
handle the neither case here, more symmetric
return ""
...
}return fmt.Sprintf("...%s...")
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
Thanks for the review! I’ve addressed both comments in patch set 4 and verified that all tests pass with go test ./.... PTAL.
func replaceExcludeDiagnostic(f *modfile.File) string {constructReplace...
otherwise it sounds like you're replacing something
Done
case hasReplace && hasExclude:
return "go.mod: this module contains replace and exclude directives. These directives only apply within the main module and may cause the errors above."
case hasReplace:
return "go.mod: this module contains replace directives. These directives only apply within the main module and may cause the errors above."
default:var dirs string
switch {
case x & y:
dirs = "x and y"
case x:
case y:
default:
handle the neither case here, more symmetric
return ""
...
}return fmt.Sprintf("...%s...")
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |
@Jonathan Amsterdam @Alan Donovan Friendly ping: PTAL when you have a moment. Thanks!
Acknowledged
| Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. |