mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00:00
cmd/go: support module deprecation
A module is deprecated if its author adds a comment containing a paragraph starting with "Deprecated:" to its go.mod file. The comment must appear immediately before the "module" directive or as a suffix on the same line. The deprecation message runs from just after "Deprecated:" to the end of the paragraph. This is implemented in CL 301089. 'go list -m -u' loads deprecation messages from the latest version of each module, not considering retractions (i.e., deprecations and retractions are loaded from the same version). By default, deprecated modules are printed with a "(deprecated)" suffix. The full deprecation message is available in the -f and -json output. 'go get' prints deprecation warnings for modules named on the command line. It also prints warnings for modules needed to build packages named on the command line if those modules are direct dependencies of the main module. For #40357 Change-Id: Id81fb2b24710681b025becd6cd74f746f4378e78 Reviewed-on: https://go-review.googlesource.com/c/go/+/306334 Trust: Jay Conrod <jayconrod@google.com> Reviewed-by: Bryan C. Mills <bcmills@google.com> Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
parent
952187af12
commit
814c5ff138
20 changed files with 550 additions and 55 deletions
|
|
@ -1135,7 +1135,7 @@
|
|||
//
|
||||
// type Module struct {
|
||||
// Path string
|
||||
// Version string
|
||||
// Deprecated string
|
||||
// }
|
||||
//
|
||||
// type GoMod struct {
|
||||
|
|
|
|||
|
|
@ -348,7 +348,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
|
|||
if *listM {
|
||||
*listFmt = "{{.String}}"
|
||||
if *listVersions {
|
||||
*listFmt = `{{.Path}}{{range .Versions}} {{.}}{{end}}`
|
||||
*listFmt = `{{.Path}}{{range .Versions}} {{.}}{{end}}{{if .Deprecated}} (deprecated){{end}}`
|
||||
}
|
||||
} else {
|
||||
*listFmt = "{{.ImportPath}}"
|
||||
|
|
@ -453,7 +453,7 @@ func runList(ctx context.Context, cmd *base.Command, args []string) {
|
|||
|
||||
var mode modload.ListMode
|
||||
if *listU {
|
||||
mode |= modload.ListU | modload.ListRetracted
|
||||
mode |= modload.ListU | modload.ListRetracted | modload.ListDeprecated
|
||||
}
|
||||
if *listRetracted {
|
||||
mode |= modload.ListRetracted
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ writing it back to go.mod. The JSON output corresponds to these Go types:
|
|||
|
||||
type Module struct {
|
||||
Path string
|
||||
Version string
|
||||
Deprecated string
|
||||
}
|
||||
|
||||
type GoMod struct {
|
||||
|
|
@ -450,7 +450,7 @@ func flagDropRetract(arg string) {
|
|||
|
||||
// fileJSON is the -json output data structure.
|
||||
type fileJSON struct {
|
||||
Module module.Version
|
||||
Module editModuleJSON
|
||||
Go string `json:",omitempty"`
|
||||
Require []requireJSON
|
||||
Exclude []module.Version
|
||||
|
|
@ -458,6 +458,11 @@ type fileJSON struct {
|
|||
Retract []retractJSON
|
||||
}
|
||||
|
||||
type editModuleJSON struct {
|
||||
Path string
|
||||
Deprecated string `json:",omitempty"`
|
||||
}
|
||||
|
||||
type requireJSON struct {
|
||||
Path string
|
||||
Version string `json:",omitempty"`
|
||||
|
|
@ -479,7 +484,10 @@ type retractJSON struct {
|
|||
func editPrintJSON(modFile *modfile.File) {
|
||||
var f fileJSON
|
||||
if modFile.Module != nil {
|
||||
f.Module = modFile.Module.Mod
|
||||
f.Module = editModuleJSON{
|
||||
Path: modFile.Module.Mod.Path,
|
||||
Deprecated: modFile.Module.Deprecated,
|
||||
}
|
||||
}
|
||||
if modFile.Go != nil {
|
||||
f.Go = modFile.Go.Version
|
||||
|
|
|
|||
|
|
@ -354,7 +354,7 @@ func runGet(ctx context.Context, cmd *base.Command, args []string) {
|
|||
pkgPatterns = append(pkgPatterns, q.pattern)
|
||||
}
|
||||
}
|
||||
r.checkPackagesAndRetractions(ctx, pkgPatterns)
|
||||
r.checkPackageProblems(ctx, pkgPatterns)
|
||||
|
||||
// We've already downloaded modules (and identified direct and indirect
|
||||
// dependencies) by loading packages in findAndUpgradeImports.
|
||||
|
|
@ -1463,25 +1463,31 @@ func (r *resolver) chooseArbitrarily(cs pathSet) (isPackage bool, m module.Versi
|
|||
return false, cs.mod
|
||||
}
|
||||
|
||||
// checkPackagesAndRetractions reloads packages for the given patterns and
|
||||
// reports missing and ambiguous package errors. It also reports loads and
|
||||
// reports retractions for resolved modules and modules needed to build
|
||||
// named packages.
|
||||
// checkPackageProblems reloads packages for the given patterns and reports
|
||||
// missing and ambiguous package errors. It also reports retractions and
|
||||
// deprecations for resolved modules and modules needed to build named packages.
|
||||
//
|
||||
// We skip missing-package errors earlier in the process, since we want to
|
||||
// resolve pathSets ourselves, but at that point, we don't have enough context
|
||||
// to log the package-import chains leading to each error.
|
||||
func (r *resolver) checkPackagesAndRetractions(ctx context.Context, pkgPatterns []string) {
|
||||
func (r *resolver) checkPackageProblems(ctx context.Context, pkgPatterns []string) {
|
||||
defer base.ExitIfErrors()
|
||||
|
||||
// Build a list of modules to load retractions for. Start with versions
|
||||
// selected based on command line queries.
|
||||
//
|
||||
// This is a subset of the build list. If the main module has a lot of
|
||||
// dependencies, loading retractions for the entire build list would be slow.
|
||||
relevantMods := make(map[module.Version]struct{})
|
||||
// Gather information about modules we might want to load retractions and
|
||||
// deprecations for. Loading this metadata requires at least one version
|
||||
// lookup per module, and we don't want to load information that's neither
|
||||
// relevant nor actionable.
|
||||
type modFlags int
|
||||
const (
|
||||
resolved modFlags = 1 << iota // version resolved by 'go get'
|
||||
named // explicitly named on command line or provides a named package
|
||||
hasPkg // needed to build named packages
|
||||
direct // provides a direct dependency of the main module
|
||||
)
|
||||
relevantMods := make(map[module.Version]modFlags)
|
||||
for path, reason := range r.resolvedVersion {
|
||||
relevantMods[module.Version{Path: path, Version: reason.version}] = struct{}{}
|
||||
m := module.Version{Path: path, Version: reason.version}
|
||||
relevantMods[m] |= resolved
|
||||
}
|
||||
|
||||
// Reload packages, reporting errors for missing and ambiguous imports.
|
||||
|
|
@ -1518,44 +1524,89 @@ func (r *resolver) checkPackagesAndRetractions(ctx context.Context, pkgPatterns
|
|||
base.SetExitStatus(1)
|
||||
if ambiguousErr := (*modload.AmbiguousImportError)(nil); errors.As(err, &ambiguousErr) {
|
||||
for _, m := range ambiguousErr.Modules {
|
||||
relevantMods[m] = struct{}{}
|
||||
relevantMods[m] |= hasPkg
|
||||
}
|
||||
}
|
||||
}
|
||||
if m := modload.PackageModule(pkg); m.Path != "" {
|
||||
relevantMods[m] = struct{}{}
|
||||
relevantMods[m] |= hasPkg
|
||||
}
|
||||
}
|
||||
for _, match := range matches {
|
||||
for _, pkg := range match.Pkgs {
|
||||
m := modload.PackageModule(pkg)
|
||||
relevantMods[m] |= named
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load and report retractions.
|
||||
type retraction struct {
|
||||
m module.Version
|
||||
err error
|
||||
}
|
||||
retractions := make([]retraction, 0, len(relevantMods))
|
||||
reqs := modload.LoadModFile(ctx)
|
||||
for m := range relevantMods {
|
||||
retractions = append(retractions, retraction{m: m})
|
||||
if reqs.IsDirect(m.Path) {
|
||||
relevantMods[m] |= direct
|
||||
}
|
||||
sort.Slice(retractions, func(i, j int) bool {
|
||||
return retractions[i].m.Path < retractions[j].m.Path
|
||||
})
|
||||
for i := 0; i < len(retractions); i++ {
|
||||
}
|
||||
|
||||
// Load retractions for modules mentioned on the command line and modules
|
||||
// needed to build named packages. We care about retractions of indirect
|
||||
// dependencies, since we might be able to upgrade away from them.
|
||||
type modMessage struct {
|
||||
m module.Version
|
||||
message string
|
||||
}
|
||||
retractions := make([]modMessage, 0, len(relevantMods))
|
||||
for m, flags := range relevantMods {
|
||||
if flags&(resolved|named|hasPkg) != 0 {
|
||||
retractions = append(retractions, modMessage{m: m})
|
||||
}
|
||||
}
|
||||
sort.Slice(retractions, func(i, j int) bool { return retractions[i].m.Path < retractions[j].m.Path })
|
||||
for i := range retractions {
|
||||
i := i
|
||||
r.work.Add(func() {
|
||||
err := modload.CheckRetractions(ctx, retractions[i].m)
|
||||
if retractErr := (*modload.ModuleRetractedError)(nil); errors.As(err, &retractErr) {
|
||||
retractions[i].err = err
|
||||
retractions[i].message = err.Error()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Load deprecations for modules mentioned on the command line. Only load
|
||||
// deprecations for indirect dependencies if they're also direct dependencies
|
||||
// of the main module. Deprecations of purely indirect dependencies are
|
||||
// not actionable.
|
||||
deprecations := make([]modMessage, 0, len(relevantMods))
|
||||
for m, flags := range relevantMods {
|
||||
if flags&(resolved|named) != 0 || flags&(hasPkg|direct) == hasPkg|direct {
|
||||
deprecations = append(deprecations, modMessage{m: m})
|
||||
}
|
||||
}
|
||||
sort.Slice(deprecations, func(i, j int) bool { return deprecations[i].m.Path < deprecations[j].m.Path })
|
||||
for i := range deprecations {
|
||||
i := i
|
||||
r.work.Add(func() {
|
||||
deprecation, err := modload.CheckDeprecation(ctx, deprecations[i].m)
|
||||
if err != nil || deprecation == "" {
|
||||
return
|
||||
}
|
||||
deprecations[i].message = modload.ShortMessage(deprecation, "")
|
||||
})
|
||||
}
|
||||
|
||||
<-r.work.Idle()
|
||||
|
||||
// Report deprecations, then retractions.
|
||||
for _, mm := range deprecations {
|
||||
if mm.message != "" {
|
||||
fmt.Fprintf(os.Stderr, "go: warning: module %s is deprecated: %s\n", mm.m.Path, mm.message)
|
||||
}
|
||||
}
|
||||
var retractPath string
|
||||
for _, r := range retractions {
|
||||
if r.err != nil {
|
||||
fmt.Fprintf(os.Stderr, "go: warning: %v\n", r.err)
|
||||
for _, mm := range retractions {
|
||||
if mm.message != "" {
|
||||
fmt.Fprintf(os.Stderr, "go: warning: %v\n", mm.message)
|
||||
if retractPath == "" {
|
||||
retractPath = r.m.Path
|
||||
retractPath = mm.m.Path
|
||||
} else {
|
||||
retractPath = "<module>"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ type ModulePublic struct {
|
|||
GoMod string `json:",omitempty"` // path to go.mod file describing module, if any
|
||||
GoVersion string `json:",omitempty"` // go version used in module
|
||||
Retracted []string `json:",omitempty"` // retraction information, if any (with -retracted or -u)
|
||||
Deprecated string `json:",omitempty"` // deprecation message, if any (with -u)
|
||||
Error *ModuleError `json:",omitempty"` // error loading module
|
||||
}
|
||||
|
||||
|
|
@ -45,6 +46,9 @@ func (m *ModulePublic) String() string {
|
|||
s += " [" + versionString(m.Update) + "]"
|
||||
}
|
||||
}
|
||||
if m.Deprecated != "" {
|
||||
s += " (deprecated)"
|
||||
}
|
||||
if m.Replace != nil {
|
||||
s += " => " + m.Replace.Path
|
||||
if m.Replace.Version != "" {
|
||||
|
|
@ -53,6 +57,9 @@ func (m *ModulePublic) String() string {
|
|||
s += " [" + versionString(m.Replace.Update) + "]"
|
||||
}
|
||||
}
|
||||
if m.Replace.Deprecated != "" {
|
||||
s += " (deprecated)"
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,8 +112,8 @@ func addUpdate(ctx context.Context, m *modinfo.ModulePublic) {
|
|||
info, err := Query(ctx, m.Path, "upgrade", m.Version, CheckAllowed)
|
||||
var noVersionErr *NoMatchingVersionError
|
||||
if errors.Is(err, fs.ErrNotExist) || errors.As(err, &noVersionErr) {
|
||||
// Ignore "not found" and "no matching version" errors. This usually means
|
||||
// the user is offline or the proxy doesn't have a matching version.
|
||||
// Ignore "not found" and "no matching version" errors.
|
||||
// This means the proxy has no matching version or no versions at all.
|
||||
//
|
||||
// We should report other errors though. An attacker that controls the
|
||||
// network shouldn't be able to hide versions by interfering with
|
||||
|
|
@ -163,9 +163,8 @@ func addRetraction(ctx context.Context, m *modinfo.ModulePublic) {
|
|||
var noVersionErr *NoMatchingVersionError
|
||||
var retractErr *ModuleRetractedError
|
||||
if err == nil || errors.Is(err, fs.ErrNotExist) || errors.As(err, &noVersionErr) {
|
||||
// Ignore "not found" and "no matching version" errors. This usually means
|
||||
// the user is offline or the proxy doesn't have a go.mod file that could
|
||||
// contain retractions.
|
||||
// Ignore "not found" and "no matching version" errors.
|
||||
// This means the proxy has no matching version or no versions at all.
|
||||
//
|
||||
// We should report other errors though. An attacker that controls the
|
||||
// network shouldn't be able to hide versions by interfering with
|
||||
|
|
@ -184,6 +183,31 @@ func addRetraction(ctx context.Context, m *modinfo.ModulePublic) {
|
|||
}
|
||||
}
|
||||
|
||||
// addDeprecation fills in m.Deprecated if the module was deprecated by its
|
||||
// author. m.Error is set if there's an error loading deprecation information.
|
||||
func addDeprecation(ctx context.Context, m *modinfo.ModulePublic) {
|
||||
deprecation, err := CheckDeprecation(ctx, module.Version{Path: m.Path, Version: m.Version})
|
||||
var noVersionErr *NoMatchingVersionError
|
||||
if errors.Is(err, fs.ErrNotExist) || errors.As(err, &noVersionErr) {
|
||||
// Ignore "not found" and "no matching version" errors.
|
||||
// This means the proxy has no matching version or no versions at all.
|
||||
//
|
||||
// We should report other errors though. An attacker that controls the
|
||||
// network shouldn't be able to hide versions by interfering with
|
||||
// the HTTPS connection. An attacker that controls the proxy may still
|
||||
// hide versions, since the "list" and "latest" endpoints are not
|
||||
// authenticated.
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
if m.Error == nil {
|
||||
m.Error = &modinfo.ModuleError{Err: err.Error()}
|
||||
}
|
||||
return
|
||||
}
|
||||
m.Deprecated = deprecation
|
||||
}
|
||||
|
||||
// moduleInfo returns information about module m, loaded from the requirements
|
||||
// in rs (which may be nil to indicate that m was not loaded from a requirement
|
||||
// graph).
|
||||
|
|
|
|||
|
|
@ -191,6 +191,12 @@ func (rs *Requirements) Graph(ctx context.Context) (*ModuleGraph, error) {
|
|||
return cached.mg, cached.err
|
||||
}
|
||||
|
||||
// IsDirect returns whether the given module provides a package directly
|
||||
// imported by a package or test in the main module.
|
||||
func (rs *Requirements) IsDirect(path string) bool {
|
||||
return rs.direct[path]
|
||||
}
|
||||
|
||||
// A ModuleGraph represents the complete graph of module dependencies
|
||||
// of a main module.
|
||||
//
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ type ListMode int
|
|||
const (
|
||||
ListU ListMode = 1 << iota
|
||||
ListRetracted
|
||||
ListDeprecated
|
||||
ListVersions
|
||||
ListRetractedVersions
|
||||
)
|
||||
|
|
@ -52,6 +53,9 @@ func ListModules(ctx context.Context, args []string, mode ListMode) ([]*modinfo.
|
|||
if mode&ListRetracted != 0 {
|
||||
addRetraction(ctx, m)
|
||||
}
|
||||
if mode&ListDeprecated != 0 {
|
||||
addDeprecation(ctx, m)
|
||||
}
|
||||
<-sem
|
||||
}()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -232,6 +232,42 @@ func ShortMessage(message, emptyDefault string) string {
|
|||
return message
|
||||
}
|
||||
|
||||
// CheckDeprecation returns a deprecation message from the go.mod file of the
|
||||
// latest version of the given module. Deprecation messages are comments
|
||||
// before or on the same line as the module directives that start with
|
||||
// "Deprecated:" and run until the end of the paragraph.
|
||||
//
|
||||
// CheckDeprecation returns an error if the message can't be loaded.
|
||||
// CheckDeprecation returns "", nil if there is no deprecation message.
|
||||
func CheckDeprecation(ctx context.Context, m module.Version) (deprecation string, err error) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
err = fmt.Errorf("loading deprecation for %s: %w", m.Path, err)
|
||||
}
|
||||
}()
|
||||
|
||||
if m.Version == "" {
|
||||
// Main module, standard library, or file replacement module.
|
||||
// Don't look up deprecation.
|
||||
return "", nil
|
||||
}
|
||||
if repl := Replacement(module.Version{Path: m.Path}); repl.Path != "" {
|
||||
// All versions of the module were replaced.
|
||||
// We'll look up deprecation separately for the replacement.
|
||||
return "", nil
|
||||
}
|
||||
|
||||
latest, err := queryLatestVersionIgnoringRetractions(ctx, m.Path)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
summary, err := rawGoModSummary(latest)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return summary.deprecated, nil
|
||||
}
|
||||
|
||||
// Replacement returns the replacement for mod, if any, from go.mod.
|
||||
// If there is no replacement for mod, Replacement returns
|
||||
// a module.Version with Path == "".
|
||||
|
|
@ -419,6 +455,7 @@ type modFileSummary struct {
|
|||
goVersionV string // GoVersion with "v" prefix
|
||||
require []module.Version
|
||||
retract []retraction
|
||||
deprecated string
|
||||
}
|
||||
|
||||
// A retraction consists of a retracted version interval and rationale.
|
||||
|
|
@ -597,6 +634,7 @@ func rawGoModSummary(m module.Version) (*modFileSummary, error) {
|
|||
|
||||
if f.Module != nil {
|
||||
summary.module = f.Module.Mod
|
||||
summary.deprecated = f.Module.Deprecated
|
||||
}
|
||||
if f.Go != nil && f.Go.Version != "" {
|
||||
rawGoVersion.LoadOrStore(m, f.Go.Version)
|
||||
|
|
|
|||
12
src/cmd/go/testdata/mod/example.com_deprecated_a_v1.0.0.txt
vendored
Normal file
12
src/cmd/go/testdata/mod/example.com_deprecated_a_v1.0.0.txt
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
-- .info --
|
||||
{"Version":"v1.0.0"}
|
||||
-- .mod --
|
||||
module example.com/deprecated/a
|
||||
|
||||
go 1.17
|
||||
-- go.mod --
|
||||
module example.com/deprecated/a
|
||||
|
||||
go 1.17
|
||||
-- a.go --
|
||||
package a
|
||||
14
src/cmd/go/testdata/mod/example.com_deprecated_a_v1.9.0.txt
vendored
Normal file
14
src/cmd/go/testdata/mod/example.com_deprecated_a_v1.9.0.txt
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
-- .info --
|
||||
{"Version":"v1.9.0"}
|
||||
-- .mod --
|
||||
// Deprecated: in example.com/deprecated/a@v1.9.0
|
||||
module example.com/deprecated/a
|
||||
|
||||
go 1.17
|
||||
-- go.mod --
|
||||
// Deprecated: in example.com/deprecated/a@v1.9.0
|
||||
module example.com/deprecated/a
|
||||
|
||||
go 1.17
|
||||
-- a.go --
|
||||
package a
|
||||
12
src/cmd/go/testdata/mod/example.com_deprecated_b_v1.0.0.txt
vendored
Normal file
12
src/cmd/go/testdata/mod/example.com_deprecated_b_v1.0.0.txt
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
-- .info --
|
||||
{"Version":"v1.0.0"}
|
||||
-- .mod --
|
||||
module example.com/deprecated/b
|
||||
|
||||
go 1.17
|
||||
-- go.mod --
|
||||
module example.com/deprecated/b
|
||||
|
||||
go 1.17
|
||||
-- b.go --
|
||||
package b
|
||||
14
src/cmd/go/testdata/mod/example.com_deprecated_b_v1.9.0.txt
vendored
Normal file
14
src/cmd/go/testdata/mod/example.com_deprecated_b_v1.9.0.txt
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
-- .info --
|
||||
{"Version":"v1.9.0"}
|
||||
-- .mod --
|
||||
// Deprecated: in example.com/deprecated/b@v1.9.0
|
||||
module example.com/deprecated/b
|
||||
|
||||
go 1.17
|
||||
-- go.mod --
|
||||
// Deprecated: in example.com/deprecated/b@v1.9.0
|
||||
module example.com/deprecated/b
|
||||
|
||||
go 1.17
|
||||
-- b.go --
|
||||
package b
|
||||
14
src/cmd/go/testdata/mod/example.com_undeprecated_v1.0.0.txt
vendored
Normal file
14
src/cmd/go/testdata/mod/example.com_undeprecated_v1.0.0.txt
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
-- .info --
|
||||
{"Version":"v1.0.0"}
|
||||
-- .mod --
|
||||
// Deprecated: in v1.0.0
|
||||
module example.com/undeprecated
|
||||
|
||||
go 1.17
|
||||
-- go.mod --
|
||||
// Deprecated: in v1.0.0
|
||||
module example.com/undeprecated
|
||||
|
||||
go 1.17
|
||||
-- undeprecated.go --
|
||||
package undeprecated
|
||||
14
src/cmd/go/testdata/mod/example.com_undeprecated_v1.0.1.txt
vendored
Normal file
14
src/cmd/go/testdata/mod/example.com_undeprecated_v1.0.1.txt
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
-- .info --
|
||||
{"Version":"v1.0.1"}
|
||||
-- .mod --
|
||||
// no longer deprecated
|
||||
module example.com/undeprecated
|
||||
|
||||
go 1.17
|
||||
-- go.mod --
|
||||
// no longer deprecated
|
||||
module example.com/undeprecated
|
||||
|
||||
go 1.17
|
||||
-- undeprecated.go --
|
||||
package undeprecated
|
||||
73
src/cmd/go/testdata/script/mod_deprecate_message.txt
vendored
Normal file
73
src/cmd/go/testdata/script/mod_deprecate_message.txt
vendored
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
# When there is a short single-line message, 'go get' should print it all.
|
||||
go get -d short
|
||||
stderr '^go: warning: module short is deprecated: short$'
|
||||
go list -m -u -f '{{.Deprecated}}' short
|
||||
stdout '^short$'
|
||||
|
||||
# When there is a multi-line message, 'go get' should print the first line.
|
||||
go get -d multiline
|
||||
stderr '^go: warning: module multiline is deprecated: first line$'
|
||||
! stderr 'second line'
|
||||
go list -m -u -f '{{.Deprecated}}' multiline
|
||||
stdout '^first line\nsecond line.$'
|
||||
|
||||
# When there is a long message, 'go get' should print a placeholder.
|
||||
go get -d long
|
||||
stderr '^go: warning: module long is deprecated: \(message omitted: too long\)$'
|
||||
go list -m -u -f '{{.Deprecated}}' long
|
||||
stdout '^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa$'
|
||||
|
||||
# When a message contains unprintable chracters, 'go get' should say that
|
||||
# without printing the message.
|
||||
go get -d unprintable
|
||||
stderr '^go: warning: module unprintable is deprecated: \(message omitted: contains non-printable characters\)$'
|
||||
go list -m -u -f '{{.Deprecated}}' unprintable
|
||||
stdout '^message contains ASCII BEL\x07$'
|
||||
|
||||
-- go.mod --
|
||||
module use
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
short v0.0.0
|
||||
multiline v0.0.0
|
||||
long v0.0.0
|
||||
unprintable v0.0.0
|
||||
)
|
||||
|
||||
replace (
|
||||
short v0.0.0 => ./short
|
||||
multiline v0.0.0 => ./multiline
|
||||
long v0.0.0 => ./long
|
||||
unprintable v0.0.0 => ./unprintable
|
||||
)
|
||||
-- short/go.mod --
|
||||
// Deprecated: short
|
||||
module short
|
||||
|
||||
go 1.16
|
||||
-- short/short.go --
|
||||
package short
|
||||
-- multiline/go.mod --
|
||||
// Deprecated: first line
|
||||
// second line.
|
||||
module multiline
|
||||
|
||||
go 1.16
|
||||
-- multiline/multiline.go --
|
||||
package multiline
|
||||
-- long/go.mod --
|
||||
// Deprecated: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
module long
|
||||
|
||||
go 1.16
|
||||
-- long/long.go --
|
||||
package long
|
||||
-- unprintable/go.mod --
|
||||
// Deprecated: message contains ASCII BEL
|
||||
module unprintable
|
||||
|
||||
go 1.16
|
||||
-- unprintable/unprintable.go --
|
||||
package unprintable
|
||||
18
src/cmd/go/testdata/script/mod_edit.txt
vendored
18
src/cmd/go/testdata/script/mod_edit.txt
vendored
|
|
@ -46,6 +46,10 @@ cmpenv stdout $WORK/go.mod.json
|
|||
go mod edit -json $WORK/go.mod.retractrationale
|
||||
cmp stdout $WORK/go.mod.retractrationale.json
|
||||
|
||||
# go mod edit -json (deprecation)
|
||||
go mod edit -json $WORK/go.mod.deprecation
|
||||
cmp stdout $WORK/go.mod.deprecation.json
|
||||
|
||||
# go mod edit -json (empty mod file)
|
||||
go mod edit -json $WORK/go.mod.empty
|
||||
cmp stdout $WORK/go.mod.empty.json
|
||||
|
|
@ -290,6 +294,20 @@ retract (
|
|||
}
|
||||
]
|
||||
}
|
||||
-- $WORK/go.mod.deprecation --
|
||||
// Deprecated: and the new one is not ready yet
|
||||
module m
|
||||
-- $WORK/go.mod.deprecation.json --
|
||||
{
|
||||
"Module": {
|
||||
"Path": "m",
|
||||
"Deprecated": "and the new one is not ready yet"
|
||||
},
|
||||
"Require": null,
|
||||
"Exclude": null,
|
||||
"Replace": null,
|
||||
"Retract": null
|
||||
}
|
||||
-- $WORK/go.mod.empty --
|
||||
-- $WORK/go.mod.empty.json --
|
||||
{
|
||||
|
|
|
|||
66
src/cmd/go/testdata/script/mod_get_deprecated.txt
vendored
Normal file
66
src/cmd/go/testdata/script/mod_get_deprecated.txt
vendored
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# 'go get pkg' should not show a deprecation message for an unrelated module.
|
||||
go get -d ./use/nothing
|
||||
! stderr 'module.*is deprecated'
|
||||
|
||||
# 'go get pkg' should show a deprecation message for the module providing pkg.
|
||||
go get -d example.com/deprecated/a
|
||||
stderr '^go: warning: module example.com/deprecated/a is deprecated: in example.com/deprecated/a@v1.9.0$'
|
||||
go get -d example.com/deprecated/a@v1.0.0
|
||||
stderr '^go: warning: module example.com/deprecated/a is deprecated: in example.com/deprecated/a@v1.9.0$'
|
||||
|
||||
# 'go get pkg' should show a deprecation message for a module providing
|
||||
# packages directly imported by pkg.
|
||||
go get -d ./use/a
|
||||
stderr '^go: warning: module example.com/deprecated/a is deprecated: in example.com/deprecated/a@v1.9.0$'
|
||||
|
||||
# 'go get pkg' may show a deprecation message for an indirectly required module
|
||||
# if it provides a package named on the command line.
|
||||
go get -d ./use/b
|
||||
! stderr 'module.*is deprecated'
|
||||
go get -d local/use
|
||||
! stderr 'module.*is deprecated'
|
||||
go get -d example.com/deprecated/b
|
||||
stderr '^go: warning: module example.com/deprecated/b is deprecated: in example.com/deprecated/b@v1.9.0$'
|
||||
|
||||
# 'go get pkg' does not show a deprecation message for a module providing a
|
||||
# directly imported package if the module is no longer deprecated in its
|
||||
# latest version, even if the module is deprecated in its current version.
|
||||
go get -d ./use/undeprecated
|
||||
! stderr 'module.*is deprecated'
|
||||
|
||||
-- go.mod --
|
||||
module m
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
example.com/deprecated/a v1.0.0
|
||||
example.com/undeprecated v1.0.0
|
||||
local v0.0.0
|
||||
)
|
||||
|
||||
replace local v0.0.0 => ./local
|
||||
-- use/nothing/nothing.go --
|
||||
package nothing
|
||||
-- use/a/a.go --
|
||||
package a
|
||||
|
||||
import _ "example.com/deprecated/a"
|
||||
-- use/b/b.go --
|
||||
package b
|
||||
|
||||
import _ "local/use"
|
||||
-- use/undeprecated/undeprecated.go --
|
||||
package undeprecated
|
||||
|
||||
import _ "example.com/undeprecated"
|
||||
-- local/go.mod --
|
||||
module local
|
||||
|
||||
go 1.17
|
||||
|
||||
require example.com/deprecated/b v1.0.0
|
||||
-- local/use/use.go --
|
||||
package use
|
||||
|
||||
import _ "example.com/deprecated/b"
|
||||
52
src/cmd/go/testdata/script/mod_list_deprecated.txt
vendored
Normal file
52
src/cmd/go/testdata/script/mod_list_deprecated.txt
vendored
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
# 'go list pkg' does not show deprecation.
|
||||
go list example.com/deprecated/a
|
||||
stdout '^example.com/deprecated/a$'
|
||||
|
||||
# 'go list -m' does not show deprecation.
|
||||
go list -m example.com/deprecated/a
|
||||
stdout '^example.com/deprecated/a v1.9.0$'
|
||||
|
||||
# 'go list -m -versions' does not show deprecation.
|
||||
go list -m -versions example.com/deprecated/a
|
||||
stdout '^example.com/deprecated/a v1.0.0 v1.9.0$'
|
||||
|
||||
# 'go list -m -u' shows deprecation.
|
||||
go list -m -u example.com/deprecated/a
|
||||
stdout '^example.com/deprecated/a v1.9.0 \(deprecated\)$'
|
||||
|
||||
# 'go list -m -u -f' exposes the deprecation message.
|
||||
go list -m -u -f {{.Deprecated}} example.com/deprecated/a
|
||||
stdout '^in example.com/deprecated/a@v1.9.0$'
|
||||
|
||||
# This works even if we use an old version that does not have the deprecation
|
||||
# message in its go.mod file.
|
||||
go get -d example.com/deprecated/a@v1.0.0
|
||||
! grep Deprecated: $WORK/gopath/pkg/mod/cache/download/example.com/deprecated/a/@v/v1.0.0.mod
|
||||
go list -m -u -f {{.Deprecated}} example.com/deprecated/a
|
||||
stdout '^in example.com/deprecated/a@v1.9.0$'
|
||||
|
||||
# 'go list -m -u' does not show deprecation for the main module.
|
||||
go list -m -u
|
||||
! stdout deprecated
|
||||
go list -m -u -f '{{if not .Deprecated}}ok{{end}}'
|
||||
stdout ok
|
||||
|
||||
# 'go list -m -u' does not show a deprecation message for a module that is not
|
||||
# deprecated at the latest version, even if it is deprecated at the current
|
||||
# version.
|
||||
go list -m -u example.com/undeprecated
|
||||
stdout '^example.com/undeprecated v1.0.0 \[v1.0.1\]$'
|
||||
-- go.mod --
|
||||
// Deprecated: main module is deprecated, too!
|
||||
module example.com/use
|
||||
|
||||
go 1.17
|
||||
|
||||
require (
|
||||
example.com/deprecated/a v1.9.0
|
||||
example.com/undeprecated v1.0.0
|
||||
)
|
||||
-- go.sum --
|
||||
example.com/deprecated/a v1.9.0 h1:pRyvBIZheJpQVVnNW4Fdg8QuoqDgtkCreqZZbASV3BE=
|
||||
example.com/deprecated/a v1.9.0/go.mod h1:Z1uUVshSY9kh6l/2hZ8oA9SBviX2yfaeEpcLDz6AZwY=
|
||||
example.com/undeprecated v1.0.0/go.mod h1:1qiRbdA9VzJXDqlG26Y41O5Z7YyO+jAD9do8XCZQ+Gg=
|
||||
68
src/cmd/go/testdata/script/mod_list_deprecated_replace.txt
vendored
Normal file
68
src/cmd/go/testdata/script/mod_list_deprecated_replace.txt
vendored
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
# When all versions are replaced, we should not look up a deprecation message.
|
||||
# We will still look up a deprecation message for the replacement.
|
||||
cp go.mod.allreplaced go.mod
|
||||
go list -m -u -f '{{.Path}}@{{.Version}} <{{.Deprecated}}>{{with .Replace}} => {{.Path}}@{{.Version}} <{{.Deprecated}}>{{end}}' all
|
||||
stdout '^example.com/deprecated/a@v1.0.0 <> => example.com/deprecated/b@v1.0.0 <in example.com/deprecated/b@v1.9.0>$'
|
||||
|
||||
# When one version is replaced, we should see a deprecation message.
|
||||
cp go.mod.onereplaced go.mod
|
||||
go list -m -u -f '{{.Path}}@{{.Version}} <{{.Deprecated}}>{{with .Replace}} => {{.Path}}@{{.Version}} <{{.Deprecated}}>{{end}}' all
|
||||
stdout '^example.com/deprecated/a@v1.0.0 <in example.com/deprecated/a@v1.9.0> => example.com/deprecated/b@v1.0.0 <in example.com/deprecated/b@v1.9.0>$'
|
||||
|
||||
# If the replacement is a directory, we won't look that up.
|
||||
cp go.mod.dirreplacement go.mod
|
||||
go list -m -u -f '{{.Path}}@{{.Version}} <{{.Deprecated}}>{{with .Replace}} => {{.Path}}@{{.Version}} <{{.Deprecated}}>{{end}}' all
|
||||
stdout '^example.com/deprecated/a@v1.0.0 <> => ./a@ <>$'
|
||||
|
||||
# If the latest version of the replacement is replaced, we'll use the content
|
||||
# from that replacement.
|
||||
cp go.mod.latestreplaced go.mod
|
||||
go list -m -u -f '{{.Path}}@{{.Version}} <{{.Deprecated}}>{{with .Replace}} => {{.Path}}@{{.Version}} <{{.Deprecated}}>{{end}}' all
|
||||
stdout '^example.com/deprecated/a@v1.0.0 <> => example.com/deprecated/b@v1.0.0 <in ./b>$'
|
||||
|
||||
-- go.mod.allreplaced --
|
||||
module m
|
||||
|
||||
go 1.17
|
||||
|
||||
require example.com/deprecated/a v1.0.0
|
||||
|
||||
replace example.com/deprecated/a => example.com/deprecated/b v1.0.0
|
||||
-- go.mod.onereplaced --
|
||||
module m
|
||||
|
||||
go 1.17
|
||||
|
||||
require example.com/deprecated/a v1.0.0
|
||||
|
||||
replace example.com/deprecated/a v1.0.0 => example.com/deprecated/b v1.0.0
|
||||
-- go.mod.dirreplacement --
|
||||
module m
|
||||
|
||||
go 1.17
|
||||
|
||||
require example.com/deprecated/a v1.0.0
|
||||
|
||||
replace example.com/deprecated/a => ./a
|
||||
-- go.mod.latestreplaced --
|
||||
module m
|
||||
|
||||
go 1.17
|
||||
|
||||
require example.com/deprecated/a v1.0.0
|
||||
|
||||
replace (
|
||||
example.com/deprecated/a => example.com/deprecated/b v1.0.0
|
||||
example.com/deprecated/b v1.9.0 => ./b
|
||||
)
|
||||
-- go.sum --
|
||||
example.com/deprecated/b v1.0.0/go.mod h1:b19J9ywRGviY7Nq4aJ1WBJ+A7qUlEY9ihp22yI4/F6M=
|
||||
-- a/go.mod --
|
||||
module example.com/deprecated/a
|
||||
|
||||
go 1.17
|
||||
-- b/go.mod --
|
||||
// Deprecated: in ./b
|
||||
module example.com/deprecated/b
|
||||
|
||||
go 1.17
|
||||
Loading…
Add table
Add a link
Reference in a new issue