1
0
mirror of https://github.com/golang/go synced 2024-11-26 17:36:56 -07:00

cmd/go: add a '-e' flag to 'mod tidy' and 'mod vendor'

This flag, like the -e flag to 'go list', instructs the command to
make a best effort to continue in spite of errors for specific packages.

Fixes #26603

Change-Id: I5ee2f50c71870ae8ef3f9b3e5b045474adcca525
Reviewed-on: https://go-review.googlesource.com/c/go/+/255960
Trust: Bryan C. Mills <bcmills@google.com>
Trust: Jay Conrod <jayconrod@google.com>
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Michael Matloob <matloob@golang.org>
Reviewed-by: Jay Conrod <jayconrod@google.com>
This commit is contained in:
Bryan C. Mills 2020-09-18 10:23:58 -04:00
parent 4e1d812afc
commit 3aa09489ab
9 changed files with 150 additions and 21 deletions

View File

@ -66,6 +66,13 @@ Do not send CLs removing the interior tags from such phrases.
TODO: write and link to tutorial or blog post TODO: write and link to tutorial or blog post
</p> </p>
<p><!-- golang.org/issue/26603 -->
The <code>go</code> <code>mod</code> <code>vendor</code>
and <code>go</code> <code>mod</code> <code>tidy</code> subcommands now accept
the <code>-e</code> flag, which instructs them to proceed despite errors in
resolving missing packages.
</p>
<h4 id="go-test"><code>go</code> <code>test</code></h4> <h4 id="go-test"><code>go</code> <code>test</code></h4>
<p><!-- golang.org/issue/29062 --> <p><!-- golang.org/issue/29062 -->

View File

@ -1231,7 +1231,7 @@
// //
// Usage: // Usage:
// //
// go mod tidy [-v] // go mod tidy [-e] [-v]
// //
// Tidy makes sure go.mod matches the source code in the module. // Tidy makes sure go.mod matches the source code in the module.
// It adds any missing modules necessary to build the current module's // It adds any missing modules necessary to build the current module's
@ -1242,12 +1242,15 @@
// The -v flag causes tidy to print information about removed modules // The -v flag causes tidy to print information about removed modules
// to standard error. // to standard error.
// //
// The -e flag causes tidy to attempt to proceed despite errors
// encountered while loading packages.
//
// //
// Make vendored copy of dependencies // Make vendored copy of dependencies
// //
// Usage: // Usage:
// //
// go mod vendor [-v] // go mod vendor [-e] [-v]
// //
// Vendor resets the main module's vendor directory to include all packages // Vendor resets the main module's vendor directory to include all packages
// needed to build and test all the main module's packages. // needed to build and test all the main module's packages.
@ -1256,6 +1259,9 @@
// The -v flag causes vendor to print the names of vendored // The -v flag causes vendor to print the names of vendored
// modules and packages to standard error. // modules and packages to standard error.
// //
// The -e flag causes vendor to attempt to proceed despite errors
// encountered while loading packages.
//
// //
// Verify dependencies have expected content // Verify dependencies have expected content
// //

View File

@ -2164,7 +2164,7 @@ func PackagesAndErrors(ctx context.Context, patterns []string) []*Package {
loadOpts := modload.PackageOpts{ loadOpts := modload.PackageOpts{
ResolveMissingImports: true, ResolveMissingImports: true,
LoadTests: ModResolveTests, LoadTests: ModResolveTests,
AllowErrors: true, SilenceErrors: true,
} }
matches, _ = modload.LoadPackages(ctx, loadOpts, patterns...) matches, _ = modload.LoadPackages(ctx, loadOpts, patterns...)
} else { } else {

View File

@ -15,7 +15,7 @@ import (
) )
var cmdTidy = &base.Command{ var cmdTidy = &base.Command{
UsageLine: "go mod tidy [-v]", UsageLine: "go mod tidy [-e] [-v]",
Short: "add missing and remove unused modules", Short: "add missing and remove unused modules",
Long: ` Long: `
Tidy makes sure go.mod matches the source code in the module. Tidy makes sure go.mod matches the source code in the module.
@ -26,12 +26,18 @@ to go.sum and removes any unnecessary ones.
The -v flag causes tidy to print information about removed modules The -v flag causes tidy to print information about removed modules
to standard error. to standard error.
The -e flag causes tidy to attempt to proceed despite errors
encountered while loading packages.
`, `,
Run: runTidy,
} }
var tidyE bool // if true, report errors but proceed anyway.
func init() { func init() {
cmdTidy.Run = runTidy // break init cycle
cmdTidy.Flag.BoolVar(&cfg.BuildV, "v", false, "") cmdTidy.Flag.BoolVar(&cfg.BuildV, "v", false, "")
cmdTidy.Flag.BoolVar(&tidyE, "e", false, "")
base.AddModCommonFlags(&cmdTidy.Flag) base.AddModCommonFlags(&cmdTidy.Flag)
} }
@ -57,8 +63,9 @@ func runTidy(ctx context.Context, cmd *base.Command, args []string) {
Tags: imports.AnyTags(), Tags: imports.AnyTags(),
ResolveMissingImports: true, ResolveMissingImports: true,
LoadTests: true, LoadTests: true,
AllowErrors: false, // TODO(#26603): Make this a flag. AllowErrors: tidyE,
}, "all") }, "all")
modload.TidyBuildList() modload.TidyBuildList()
modload.TrimGoSum() modload.TrimGoSum()
modload.WriteGoMod() modload.WriteGoMod()

View File

@ -25,7 +25,7 @@ import (
) )
var cmdVendor = &base.Command{ var cmdVendor = &base.Command{
UsageLine: "go mod vendor [-v]", UsageLine: "go mod vendor [-e] [-v]",
Short: "make vendored copy of dependencies", Short: "make vendored copy of dependencies",
Long: ` Long: `
Vendor resets the main module's vendor directory to include all packages Vendor resets the main module's vendor directory to include all packages
@ -34,12 +34,18 @@ It does not include test code for vendored packages.
The -v flag causes vendor to print the names of vendored The -v flag causes vendor to print the names of vendored
modules and packages to standard error. modules and packages to standard error.
The -e flag causes vendor to attempt to proceed despite errors
encountered while loading packages.
`, `,
Run: runVendor, Run: runVendor,
} }
var vendorE bool // if true, report errors but proceed anyway
func init() { func init() {
cmdVendor.Flag.BoolVar(&cfg.BuildV, "v", false, "") cmdVendor.Flag.BoolVar(&cfg.BuildV, "v", false, "")
cmdVendor.Flag.BoolVar(&vendorE, "e", false, "")
base.AddModCommonFlags(&cmdVendor.Flag) base.AddModCommonFlags(&cmdVendor.Flag)
} }
@ -54,6 +60,7 @@ func runVendor(ctx context.Context, cmd *base.Command, args []string) {
Tags: imports.AnyTags(), Tags: imports.AnyTags(),
ResolveMissingImports: true, ResolveMissingImports: true,
UseVendorAll: true, UseVendorAll: true,
AllowErrors: vendorE,
} }
_, pkgs := modload.LoadPackages(ctx, loadOpts, "all") _, pkgs := modload.LoadPackages(ctx, loadOpts, "all")

View File

@ -66,10 +66,10 @@ func runWhy(ctx context.Context, cmd *base.Command, args []string) {
modload.RootMode = modload.NeedRoot modload.RootMode = modload.NeedRoot
loadOpts := modload.PackageOpts{ loadOpts := modload.PackageOpts{
Tags: imports.AnyTags(), Tags: imports.AnyTags(),
LoadTests: !*whyVendor, LoadTests: !*whyVendor,
AllowErrors: true, SilenceErrors: true,
UseVendorAll: *whyVendor, UseVendorAll: *whyVendor,
} }
if *whyM { if *whyM {

View File

@ -406,7 +406,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
Tags: imports.AnyTags(), Tags: imports.AnyTags(),
ResolveMissingImports: true, // dubious; see https://golang.org/issue/32567 ResolveMissingImports: true, // dubious; see https://golang.org/issue/32567
LoadTests: *getT, LoadTests: *getT,
AllowErrors: true, // Errors may be fixed by subsequent upgrades or downgrades. SilenceErrors: true, // Errors may be fixed by subsequent upgrades or downgrades.
SilenceUnmatchedWarnings: true, // We will warn after iterating below. SilenceUnmatchedWarnings: true, // We will warn after iterating below.
} }
matches, _ = modload.LoadPackages(ctx, loadOpts, pkgPatterns...) matches, _ = modload.LoadPackages(ctx, loadOpts, pkgPatterns...)

View File

@ -151,11 +151,14 @@ type PackageOpts struct {
// declare 'go 1.16' or higher. // declare 'go 1.16' or higher.
UseVendorAll bool UseVendorAll bool
// AllowErrors indicates that LoadPackages should not log errors in resolving // AllowErrors indicates that LoadPackages should not terminate the process if
// patterns or imports, and should not terminate the process if such an error // an error occurs.
// occurs.
AllowErrors bool AllowErrors bool
// SilenceErrors indicates that LoadPackages should not print errors
// that occur while loading packages. SilenceErrors implies AllowErrors.
SilenceErrors bool
// SilenceUnmatchedWarnings suppresses the warnings normally emitted for // SilenceUnmatchedWarnings suppresses the warnings normally emitted for
// patterns that did not match any packages. // patterns that did not match any packages.
SilenceUnmatchedWarnings bool SilenceUnmatchedWarnings bool
@ -263,23 +266,31 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma
// One last pass to finalize wildcards. // One last pass to finalize wildcards.
updateMatches(loaded) updateMatches(loaded)
checkMultiplePaths()
WriteGoMod()
// Report errors, if any.
checkMultiplePaths()
for _, pkg := range loaded.pkgs { for _, pkg := range loaded.pkgs {
if pkg.err != nil && !opts.AllowErrors { if pkg.err != nil && !opts.SilenceErrors {
base.Errorf("%s: %v", pkg.stackText(), pkg.err) if opts.AllowErrors {
fmt.Fprintf(os.Stderr, "%s: %v\n", pkg.stackText(), pkg.err)
} else {
base.Errorf("%s: %v", pkg.stackText(), pkg.err)
}
} }
if !pkg.isTest() { if !pkg.isTest() {
loadedPackages = append(loadedPackages, pkg.path) loadedPackages = append(loadedPackages, pkg.path)
} }
} }
if !opts.AllowErrors { if !opts.SilenceErrors {
// Also list errors in matching patterns (such as directory permission // Also list errors in matching patterns (such as directory permission
// errors for wildcard patterns). // errors for wildcard patterns).
for _, match := range matches { for _, match := range matches {
for _, err := range match.Errs { for _, err := range match.Errs {
base.Errorf("%v", err) if opts.AllowErrors {
fmt.Fprintf(os.Stderr, "%v\n", err)
} else {
base.Errorf("%v", err)
}
} }
} }
} }
@ -289,6 +300,8 @@ func LoadPackages(ctx context.Context, opts PackageOpts, patterns ...string) (ma
search.WarnUnmatched(matches) search.WarnUnmatched(matches)
} }
// Success! Update go.mod (if needed) and return the results.
WriteGoMod()
sort.Strings(loadedPackages) sort.Strings(loadedPackages)
return matches, loadedPackages return matches, loadedPackages
} }

89
src/cmd/go/testdata/script/mod_e.txt vendored Normal file
View File

@ -0,0 +1,89 @@
cp go.mod go.mod.orig
# If a dependency cannot be resolved, 'go mod tidy' fails with an error message
# explaining the problem and does not update the go.mod file.
# TODO(bcmills): Ideally, with less redundancy than these error messages!
! go mod tidy
stderr '^example.com/untidy imports\n\texample.net/directnotfound: cannot find module providing package example.net/directnotfound: module example.net/directnotfound: reading http://.*: 404 Not Found$'
stderr '^example.com/untidy imports\n\texample.net/m imports\n\texample.net/indirectnotfound: cannot find module providing package example.net/indirectnotfound: module example.net/indirectnotfound: reading http://.*: 404 Not Found$'
stderr '^example.com/untidy tested by\n\texample.com/untidy.test imports\n\texample.net/directtestnotfound: cannot find module providing package example.net/directtestnotfound: module example.net/directtestnotfound: reading http://.*: 404 Not Found$'
stderr '^example.com/untidy imports\n\texample.net/m tested by\n\texample.net/m.test imports\n\texample.net/indirecttestnotfound: cannot find module providing package example.net/indirecttestnotfound: module example.net/indirecttestnotfound: reading http://.*: 404 Not Found$'
cmp go.mod.orig go.mod
# If a dependency cannot be resolved, 'go mod vendor' fails with an error message
# explaining the problem, does not update the go.mod file, and does not create
# the vendor directory.
! go mod vendor
stderr '^example.com/untidy imports\n\texample.net/directnotfound: cannot find module providing package example.net/directnotfound: module example.net/directnotfound: reading http://.*: 404 Not Found$'
stderr '^example.com/untidy imports\n\texample.net/m imports\n\texample.net/indirectnotfound: cannot find module providing package example.net/indirectnotfound: module example.net/indirectnotfound: reading http://.*: 404 Not Found$'
stderr '^example.com/untidy tested by\n\texample.com/untidy.test imports\n\texample.net/directtestnotfound: cannot find module providing package example.net/directtestnotfound: module example.net/directtestnotfound: reading http://.*: 404 Not Found$'
! stderr 'indirecttestnotfound' # Vendor prunes test dependencies.
cmp go.mod.orig go.mod
! exists vendor
# 'go mod tidy' still logs the errors, but succeeds and updates go.mod.
go mod tidy -e
stderr -count=4 'cannot find module providing package'
cmp go.mod.final go.mod
# 'go mod vendor -e' still logs the errors, but succeeds and updates go.mod.
cp go.mod.orig go.mod
go mod vendor -e
stderr -count=3 'cannot find module providing package'
cmp go.mod.final go.mod
exists vendor/modules.txt
exists vendor/example.net/m/m.go
-- go.mod --
module example.com/untidy
go 1.16
replace example.net/m v0.1.0 => ./m
-- go.mod.final --
module example.com/untidy
go 1.16
replace example.net/m v0.1.0 => ./m
require example.net/m v0.1.0
-- untidy.go --
package untidy
import (
_ "example.net/m"
_ "example.net/directnotfound"
)
-- untidy_test.go --
package untidy_test
import _ "example.net/directtestnotfound"
-- m/go.mod --
module example.net/m
go 1.16
-- m/m.go --
package m
import _ "example.net/indirectnotfound"
-- m/m_test.go --
package m_test
import _ "example.net/indirecttestnotfound"