mirror of
https://github.com/golang/go
synced 2024-09-24 19:20:13 -06:00
cmd/go: don't attempt to downgrade to incompatible versions
When we downgrade a module (using 'go get m@none' or similar), we exclude versions of other modules that depend on it. We'll try previous versions (in the "versions" list returned by the proxy or in codeRepo.Versions for vcs) until we find a version that doesn't require an excluded module version. If older versions of a module are broken for some reason, mvs.Downgrade currently panics. With this change, we ignore versions with errors during downgrade. A frequent cause of this is incompatible v2+ versions. These are common if a repository tagged v2.0.0 before migrating to modules, then tagged v2.0.1 with a go.mod file later. v2.0.0 is incorrectly considered part of the v2 module. Fixes #31942 Change-Id: Icaa75c5c93f73f18a400c22f18a8cc603aa4011a Reviewed-on: https://go-review.googlesource.com/c/go/+/177337 Run-TryBot: Jay Conrod <jayconrod@google.com> TryBot-Result: Gobot Gobot <gobot@golang.org> Reviewed-by: Bryan C. Mills <bcmills@google.com>
This commit is contained in:
parent
703fb665d6
commit
06642d8e77
@ -13,7 +13,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
|
|
||||||
"cmd/go/internal/base"
|
|
||||||
"cmd/go/internal/module"
|
"cmd/go/internal/module"
|
||||||
"cmd/go/internal/par"
|
"cmd/go/internal/par"
|
||||||
)
|
)
|
||||||
@ -118,7 +117,7 @@ func BuildList(target module.Version, reqs Reqs) ([]module.Version, error) {
|
|||||||
return buildList(target, reqs, nil)
|
return buildList(target, reqs, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) module.Version) ([]module.Version, error) {
|
func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) (module.Version, error)) ([]module.Version, error) {
|
||||||
// Explore work graph in parallel in case reqs.Required
|
// Explore work graph in parallel in case reqs.Required
|
||||||
// does high-latency network operations.
|
// does high-latency network operations.
|
||||||
type modGraphNode struct {
|
type modGraphNode struct {
|
||||||
@ -133,6 +132,10 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) mo
|
|||||||
min = map[string]string{} // maps module path to minimum required version
|
min = map[string]string{} // maps module path to minimum required version
|
||||||
haveErr int32
|
haveErr int32
|
||||||
)
|
)
|
||||||
|
setErr := func(n *modGraphNode, err error) {
|
||||||
|
n.err = err
|
||||||
|
atomic.StoreInt32(&haveErr, 1)
|
||||||
|
}
|
||||||
|
|
||||||
var work par.Work
|
var work par.Work
|
||||||
work.Add(target)
|
work.Add(target)
|
||||||
@ -149,8 +152,7 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) mo
|
|||||||
|
|
||||||
required, err := reqs.Required(m)
|
required, err := reqs.Required(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
node.err = err
|
setErr(node, err)
|
||||||
atomic.StoreInt32(&haveErr, 1)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
node.required = required
|
node.required = required
|
||||||
@ -159,9 +161,9 @@ func buildList(target module.Version, reqs Reqs, upgrade func(module.Version) mo
|
|||||||
}
|
}
|
||||||
|
|
||||||
if upgrade != nil {
|
if upgrade != nil {
|
||||||
u := upgrade(m)
|
u, err := upgrade(m)
|
||||||
if u.Path == "" {
|
if err != nil {
|
||||||
base.Errorf("Upgrade(%v) returned zero module", m)
|
setErr(node, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if u != m {
|
if u != m {
|
||||||
@ -332,17 +334,12 @@ func Req(target module.Version, list []module.Version, base []string, reqs Reqs)
|
|||||||
// UpgradeAll returns a build list for the target module
|
// UpgradeAll returns a build list for the target module
|
||||||
// in which every module is upgraded to its latest version.
|
// in which every module is upgraded to its latest version.
|
||||||
func UpgradeAll(target module.Version, reqs Reqs) ([]module.Version, error) {
|
func UpgradeAll(target module.Version, reqs Reqs) ([]module.Version, error) {
|
||||||
return buildList(target, reqs, func(m module.Version) module.Version {
|
return buildList(target, reqs, func(m module.Version) (module.Version, error) {
|
||||||
if m.Path == target.Path {
|
if m.Path == target.Path {
|
||||||
return target
|
return target, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
latest, err := reqs.Upgrade(m)
|
return reqs.Upgrade(m)
|
||||||
if err != nil {
|
|
||||||
panic(err) // TODO
|
|
||||||
}
|
|
||||||
m.Version = latest.Version
|
|
||||||
return m
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -351,7 +348,7 @@ func UpgradeAll(target module.Version, reqs Reqs) ([]module.Version, error) {
|
|||||||
func Upgrade(target module.Version, reqs Reqs, upgrade ...module.Version) ([]module.Version, error) {
|
func Upgrade(target module.Version, reqs Reqs, upgrade ...module.Version) ([]module.Version, error) {
|
||||||
list, err := reqs.Required(target)
|
list, err := reqs.Required(target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // TODO
|
return nil, err
|
||||||
}
|
}
|
||||||
// TODO: Maybe if an error is given,
|
// TODO: Maybe if an error is given,
|
||||||
// rerun with BuildList(upgrade[0], reqs) etc
|
// rerun with BuildList(upgrade[0], reqs) etc
|
||||||
@ -370,7 +367,7 @@ func Upgrade(target module.Version, reqs Reqs, upgrade ...module.Version) ([]mod
|
|||||||
func Downgrade(target module.Version, reqs Reqs, downgrade ...module.Version) ([]module.Version, error) {
|
func Downgrade(target module.Version, reqs Reqs, downgrade ...module.Version) ([]module.Version, error) {
|
||||||
list, err := reqs.Required(target)
|
list, err := reqs.Required(target)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // TODO
|
return nil, err
|
||||||
}
|
}
|
||||||
max := make(map[string]string)
|
max := make(map[string]string)
|
||||||
for _, r := range list {
|
for _, r := range list {
|
||||||
@ -409,7 +406,17 @@ func Downgrade(target module.Version, reqs Reqs, downgrade ...module.Version) ([
|
|||||||
}
|
}
|
||||||
list, err := reqs.Required(m)
|
list, err := reqs.Required(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err) // TODO
|
// If we can't load the requirements, we couldn't load the go.mod file.
|
||||||
|
// There are a number of reasons this can happen, but this usually
|
||||||
|
// means an older version of the module had a missing or invalid
|
||||||
|
// go.mod file. For example, if example.com/mod released v2.0.0 before
|
||||||
|
// migrating to modules (v2.0.0+incompatible), then added a valid go.mod
|
||||||
|
// in v2.0.1, downgrading from v2.0.1 would cause this error.
|
||||||
|
//
|
||||||
|
// TODO(golang.org/issue/31730, golang.org/issue/30134): if the error
|
||||||
|
// is transient (we couldn't download go.mod), return the error from
|
||||||
|
// Downgrade. Currently, we can't tell what kind of error it is.
|
||||||
|
exclude(m)
|
||||||
}
|
}
|
||||||
for _, r := range list {
|
for _, r := range list {
|
||||||
add(r)
|
add(r)
|
||||||
@ -429,7 +436,12 @@ List:
|
|||||||
for excluded[r] {
|
for excluded[r] {
|
||||||
p, err := reqs.Previous(r)
|
p, err := reqs.Previous(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err // TODO
|
// This is likely a transient error reaching the repository,
|
||||||
|
// rather than a permanent error with the retrieved version.
|
||||||
|
//
|
||||||
|
// TODO(golang.org/issue/31730, golang.org/issue/30134):
|
||||||
|
// decode what to do based on the actual error.
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
// If the target version is a pseudo-version, it may not be
|
// If the target version is a pseudo-version, it may not be
|
||||||
// included when iterating over prior versions using reqs.Previous.
|
// included when iterating over prior versions using reqs.Previous.
|
||||||
|
9
src/cmd/go/testdata/mod/example.com_downgrade_v2.0.0.txt
vendored
Normal file
9
src/cmd/go/testdata/mod/example.com_downgrade_v2.0.0.txt
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
example.com/downgrade v2.0.0
|
||||||
|
written by hand
|
||||||
|
|
||||||
|
-- .mod --
|
||||||
|
module example.com/downgrade
|
||||||
|
|
||||||
|
require rsc.io/quote v1.5.2
|
||||||
|
-- .info --
|
||||||
|
{"Version":"v2.0.0"}
|
13
src/cmd/go/testdata/mod/example.com_downgrade_v2_v2.0.1.txt
vendored
Normal file
13
src/cmd/go/testdata/mod/example.com_downgrade_v2_v2.0.1.txt
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
example.com/downgrade/v2 v2.0.1
|
||||||
|
written by hand
|
||||||
|
|
||||||
|
-- .mod --
|
||||||
|
module example.com/downgrade/v2
|
||||||
|
|
||||||
|
require rsc.io/quote v1.5.2
|
||||||
|
-- .info --
|
||||||
|
{"Version":"v2.0.1"}
|
||||||
|
-- go.mod --
|
||||||
|
module example.com/downgrade/v2
|
||||||
|
|
||||||
|
require rsc.io/quote v1.5.2
|
14
src/cmd/go/testdata/mod/example.com_latemigrate_v2_v2.0.0.txt
vendored
Normal file
14
src/cmd/go/testdata/mod/example.com_latemigrate_v2_v2.0.0.txt
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
example.com/latemigrate/v2 v2.0.0
|
||||||
|
written by hand
|
||||||
|
|
||||||
|
This repository migrated to modules in v2.0.1 after v2.0.0 was already tagged.
|
||||||
|
All versions require rsc.io/quote so we can test downgrades.
|
||||||
|
|
||||||
|
v2.0.0 is technically part of example.com/latemigrate as v2.0.0+incompatible.
|
||||||
|
Proxies may serve it as part of the version list for example.com/latemigrate/v2.
|
||||||
|
'go get' must be able to ignore these versions.
|
||||||
|
|
||||||
|
-- .mod --
|
||||||
|
module example.com/latemigrate
|
||||||
|
-- .info --
|
||||||
|
{"Version":"v2.0.0"}
|
20
src/cmd/go/testdata/mod/example.com_latemigrate_v2_v2.0.1.txt
vendored
Normal file
20
src/cmd/go/testdata/mod/example.com_latemigrate_v2_v2.0.1.txt
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
example.com/latemigrate/v2 v2.0.1
|
||||||
|
written by hand
|
||||||
|
|
||||||
|
This repository migrated to modules in v2.0.1 after v2.0.0 was already tagged.
|
||||||
|
All versions require rsc.io/quote so we can test downgrades.
|
||||||
|
|
||||||
|
v2.0.1 belongs to example.com/latemigrate/v2.
|
||||||
|
|
||||||
|
-- .mod --
|
||||||
|
module example.com/latemigrate/v2
|
||||||
|
|
||||||
|
require rsc.io/quote v1.3.0
|
||||||
|
-- .info --
|
||||||
|
{"Version":"v2.0.1"}
|
||||||
|
-- go.mod --
|
||||||
|
module example.com/latemigrate/v2
|
||||||
|
|
||||||
|
require rsc.io/quote v1.3.0
|
||||||
|
-- late.go --
|
||||||
|
package late
|
17
src/cmd/go/testdata/script/mod_get_downgrade.txt
vendored
17
src/cmd/go/testdata/script/mod_get_downgrade.txt
vendored
@ -2,6 +2,7 @@ env GO111MODULE=on
|
|||||||
[short] skip
|
[short] skip
|
||||||
|
|
||||||
# downgrade sampler should downgrade quote
|
# downgrade sampler should downgrade quote
|
||||||
|
cp go.mod.orig go.mod
|
||||||
go get rsc.io/sampler@v1.0.0
|
go get rsc.io/sampler@v1.0.0
|
||||||
go list -m all
|
go list -m all
|
||||||
stdout 'rsc.io/quote v1.4.0'
|
stdout 'rsc.io/quote v1.4.0'
|
||||||
@ -31,9 +32,21 @@ stdout 'rsc.io/quote v1.4.0'
|
|||||||
stdout 'rsc.io/sampler v1.0.0'
|
stdout 'rsc.io/sampler v1.0.0'
|
||||||
! stdout golang.org/x/text
|
! stdout golang.org/x/text
|
||||||
|
|
||||||
-- go.mod --
|
# downgrading away quote should also downgrade away latemigrate/v2,
|
||||||
|
# since there are no older versions. v2.0.0 is incompatible.
|
||||||
|
cp go.mod.orig go.mod
|
||||||
|
go list -m -versions example.com/latemigrate/v2
|
||||||
|
stdout v2.0.0 # proxy may serve incompatible versions
|
||||||
|
go get rsc.io/quote@none
|
||||||
|
go list -m all
|
||||||
|
! stdout 'example.com/latemigrate/v2'
|
||||||
|
|
||||||
|
-- go.mod.orig --
|
||||||
module x
|
module x
|
||||||
require rsc.io/quote v1.5.1
|
require (
|
||||||
|
rsc.io/quote v1.5.1
|
||||||
|
example.com/latemigrate/v2 v2.0.1
|
||||||
|
)
|
||||||
-- go.mod.empty --
|
-- go.mod.empty --
|
||||||
module x
|
module x
|
||||||
-- x.go --
|
-- x.go --
|
||||||
|
Loading…
Reference in New Issue
Block a user