cmd/go: don't write own toolchain line when updating go line

The Go command had a behavior of writing its own toolchain name when
updating the go line in a go.mod (for example when a user runs go get
go@version). This behavior was often undesirable and the toolchain line
was often removed by users before checking in go.mod files (including in
the x/ repos). It also led to user confusion.

This change removes that behavior. A toolchain line will not be added if
one wasn't present before. The toolchain line can still be removed
though: the toolchain line must be at least the go version, so if the go
version is increased above the toolchain version, the toolchain version
will be bumped up to that go version. Then the toolchain line will then
be dropped because go <version> implies toolchain <version>.

Making this change slightly hurts reproducability because future go
commands run on the go.mod file may be run with a different toolchain
than the one that used it, but that doesn't seem to be worth the
confusion the behavior resulted in.

We expect this change will not have negative consequences, but it could
be possible, and we would like to hear from any users that depended on
the previous behavior in case we need to roll it back before the
release.

Fixes #65847

Change-Id: Id795b7f762e4f90ba0fa8c7935d03f32dfc8590e
Reviewed-on: https://go-review.googlesource.com/c/go/+/656835
Reviewed-by: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Michael Matloob 2025-03-10 13:32:23 -04:00
parent 485480faaa
commit a68bf75d34
11 changed files with 52 additions and 74 deletions

View File

@ -745,34 +745,25 @@ func UpdateWorkGoVersion(wf *modfile.WorkFile, goVers string) (changed bool) {
wf.AddGoStmt(goVers)
// We wrote a new go line. For reproducibility,
// if the toolchain running right now is newer than the new toolchain line,
// update the toolchain line to record the newer toolchain.
// The user never sets the toolchain explicitly in a 'go work' command,
// so this is only happening as a result of a go or toolchain line found
// in a module.
// If the toolchain running right now is a dev toolchain (like "go1.21")
// writing 'toolchain go1.21' will not be useful, since that's not an actual
// toolchain you can download and run. In that case fall back to at least
// checking that the toolchain is new enough for the Go version.
toolchain := "go" + old
if wf.Toolchain != nil {
toolchain = wf.Toolchain.Name
}
if gover.IsLang(gover.Local()) {
toolchain = gover.ToolchainMax(toolchain, "go"+goVers)
} else {
toolchain = gover.ToolchainMax(toolchain, "go"+gover.Local())
if wf.Toolchain == nil {
return true
}
// Drop the toolchain line if it is implied by the go line
// Drop the toolchain line if it is implied by the go line,
// if its version is older than the version in the go line,
// or if it is asking for a toolchain older than Go 1.21,
// which will not understand the toolchain line.
if toolchain == "go"+goVers || gover.Compare(gover.FromToolchain(toolchain), gover.GoStrictVersion) < 0 {
// Previously, a toolchain line set to the local toolchain
// version was added so that future operations on the go file
// would use the same toolchain logic for reproducibility.
// This behavior seemed to cause user confusion without much
// benefit so it was removed. See #65847.
toolchain := wf.Toolchain.Name
toolVers := gover.FromToolchain(toolchain)
if toolchain == "go"+goVers || gover.Compare(toolVers, goVers) < 0 || gover.Compare(toolVers, gover.GoStrictVersion) < 0 {
wf.DropToolchainStmt()
} else {
wf.AddToolchainStmt(toolchain)
}
return true
}
@ -1833,22 +1824,7 @@ func UpdateGoModFromReqs(ctx context.Context, opts WriteOpts) (before, after []b
toolchain = "go" + goVersion
}
// For reproducibility, if we are writing a new go line,
// and we're not explicitly modifying the toolchain line with 'go get toolchain@something',
// and the go version is one that supports switching toolchains,
// and the toolchain running right now is newer than the current toolchain line,
// then update the toolchain line to record the newer toolchain.
//
// TODO(#57001): This condition feels too complicated. Can we simplify it?
// TODO(#57001): Add more tests for toolchain lines.
toolVers := gover.FromToolchain(toolchain)
if wroteGo && !opts.DropToolchain && !opts.ExplicitToolchain &&
gover.Compare(goVersion, gover.GoStrictVersion) >= 0 &&
(gover.Compare(gover.Local(), toolVers) > 0 && !gover.IsLang(gover.Local())) {
toolchain = "go" + gover.Local()
toolVers = gover.FromToolchain(toolchain)
}
if opts.DropToolchain || toolchain == "go"+goVersion || (gover.Compare(toolVers, gover.GoStrictVersion) < 0 && !opts.ExplicitToolchain) {
// go get toolchain@none or toolchain matches go line or isn't valid; drop it.
// TODO(#57001): 'go get' should reject explicit toolchains below GoStrictVersion.

View File

@ -21,7 +21,7 @@ stderr '^go: rsc.io/future@v1.0.0: module rsc.io/future@v1.0.0 requires go >= 1.
go get .
stderr '^go: module rsc.io/future@v1.0.0 requires go >= 1.999; switching to go1.999testmod$'
stderr '^go: upgraded go 1.21 => 1.999$'
stderr '^go: added toolchain go1.999testmod$'
! stderr '^go: added toolchain'
# Now, the various 'go mod' subcommands should succeed.

View File

@ -102,6 +102,4 @@ module example
go 1.23
toolchain go1.23.9
require rsc.io/needall v0.0.1

View File

@ -9,7 +9,7 @@ stderr '^go: rsc.io/needall@v0.0.1 requires go >= 1.23; switching to go1.23.9$'
! stderr '\(running'
stderr '^go: added rsc.io/needall v0.0.1'
grep 'go 1.23' go.mod
grep 'toolchain go1.23.9' go.mod
! grep toolchain go.mod
# GOTOOLCHAIN=min+auto should run the newer toolchain
env GOTOOLCHAIN=go1.21+auto
@ -19,7 +19,7 @@ stderr '^go: rsc.io/needall@v0.0.1 requires go >= 1.23; switching to go1.23.9$'
! stderr '\(running'
stderr '^go: added rsc.io/needall v0.0.1'
grep 'go 1.23' go.mod
grep 'toolchain go1.23.9' go.mod
! grep toolchain go.mod
# GOTOOLCHAIN=go1.21 should NOT run the newer toolchain
env GOTOOLCHAIN=go1.21
@ -67,7 +67,7 @@ cp go.mod.new go.mod
go get go@1.22rc1
stderr '^go: updating go.mod requires go >= 1.22rc1; switching to go1.22.9$'
stderr '^go: upgraded go 1.1 => 1.22rc1$'
stderr '^go: added toolchain go1.22.9$'
! stderr '^go: added toolchain$'
# go get go@1.22.1 should use 1.22.1 exactly, not a later release.
env GOTOOLCHAIN=local
@ -80,7 +80,7 @@ cp go.mod.new go.mod
go get go@1.22.1
stderr '^go: updating go.mod requires go >= 1.22.1; switching to go1.22.9$'
stderr '^go: upgraded go 1.1 => 1.22.1$'
stderr '^go: added toolchain go1.22.9$'
! stderr '^go: added toolchain$'
# go get needgo122 (says 'go 1.22') should use 1.22.0, the earliest release we have available
# (ignoring prereleases).
@ -94,7 +94,7 @@ cp go.mod.new go.mod
go get rsc.io/needgo122
stderr '^go: upgraded go 1.1 => 1.22$'
stderr '^go: rsc.io/needgo122@v0.0.1 requires go >= 1.22; switching to go1.22.9$'
stderr '^go: added toolchain go1.22.9$'
! stderr '^go: added toolchain$'
# go get needgo1223 (says 'go 1.22.3') should use go 1.22.3
env GOTOOLCHAIN=local
@ -107,7 +107,7 @@ cp go.mod.new go.mod
go get rsc.io/needgo1223
stderr '^go: upgraded go 1.1 => 1.22.3$'
stderr '^go: rsc.io/needgo1223@v0.0.1 requires go >= 1.22.3; switching to go1.22.9$'
stderr '^go: added toolchain go1.22.9$'
! stderr '^go: added toolchain$'
# go get needgo124 (says 'go 1.24') should use go 1.24rc1, the only version available
env GOTOOLCHAIN=local
@ -120,7 +120,7 @@ cp go.mod.new go.mod
go get rsc.io/needgo124
stderr '^go: rsc.io/needgo124@v0.0.1 requires go >= 1.24; switching to go1.24rc1$'
stderr '^go: upgraded go 1.1 => 1.24$'
stderr '^go: added toolchain go1.24rc1$'
! stderr '^go: added toolchain$'
# The -C flag should not happen more than once due to switching.
mkdir dir dir/dir
@ -132,7 +132,7 @@ cp p.go dir/dir/p.go
go get -C dir rsc.io/needgo124
stderr '^go: rsc.io/needgo124@v0.0.1 requires go >= 1.24; switching to go1.24rc1$'
stderr '^go: upgraded go 1.1 => 1.24$'
stderr '^go: added toolchain go1.24rc1$'
! stderr '^go: added toolchain1$'
cmp go.mod.new go.mod
cmp go.mod.new dir/dir/go.mod
grep 'go 1.24$' dir/go.mod

View File

@ -7,28 +7,28 @@ cp go.mod.orig go.mod
go get go
stderr '^go: upgraded go 1.21 => 1.23.9$'
grep 'go 1.23.9' go.mod
grep 'toolchain go1.99rc1' go.mod
! grep toolchain go.mod
# go get go@1.23 should use the latest Go 1.23
cp go.mod.orig go.mod
go get go@1.23
stderr '^go: upgraded go 1.21 => 1.23.9$'
grep 'go 1.23.9' go.mod
grep 'toolchain go1.99rc1' go.mod
! grep toolchain go.mod
# go get go@1.22 should use the latest Go 1.22
cp go.mod.orig go.mod
go get go@1.22
stderr '^go: upgraded go 1.21 => 1.22.9$'
grep 'go 1.22.9' go.mod
grep 'toolchain go1.99rc1' go.mod
! grep toolchain1 go.mod
# go get go@patch should use the latest patch release
go get go@1.22.1
go get go@patch
stderr '^go: upgraded go 1.22.1 => 1.22.9$'
grep 'go 1.22.9' go.mod
grep 'toolchain go1.99rc1' go.mod
! grep toolchain go.mod
# go get go@1.24 does NOT find the release candidate
cp go.mod.orig go.mod
@ -40,17 +40,22 @@ cp go.mod.orig go.mod
go get go@1.24rc1
stderr '^go: upgraded go 1.21 => 1.24rc1$'
grep 'go 1.24rc1' go.mod
grep 'toolchain go1.99rc1' go.mod
! grep toolchain go.mod
# go get go@latest finds the latest Go 1.23
cp go.mod.orig go.mod
go get go@latest
stderr '^go: upgraded go 1.21 => 1.23.9$'
grep 'go 1.23.9' go.mod
grep 'toolchain go1.99rc1' go.mod
! grep toolchain go.mod
# Again, with toolchains.
go get toolchain@go1.99rc1
stderr '^go: added toolchain go1.99rc1$'
grep 'go 1.23.9' go.mod
grep 'toolchain go1.99rc1' go.mod
# go get toolchain should find go1.999testmod.
go get toolchain
stderr '^go: upgraded toolchain go1.99rc1 => go1.999testmod$'

View File

@ -1,5 +1,5 @@
# This test verifies that 'go get' and 'go mod tidy' switch to a newer toolchain
# if needed to process newly-reolved imports.
# if needed to process newly-resolved imports.
env TESTGO_VERSION=go1.21.0
env TESTGO_VERSION_SWITCH=switch
@ -46,7 +46,6 @@ go: trying upgrade to example.net/b@v0.1.0
go: accepting indirect upgrade from go@1.20 to 1.22.0
go: trying upgrade to example.net/c@v0.1.0
go: upgraded go 1.20 => 1.22.0
go: added toolchain go1.22.9
go: added example.net/b v0.1.0
go: added example.net/c v0.1.0
go: added example.net/d v0.1.0
@ -67,7 +66,6 @@ go: trying upgrade to example.net/c@v0.1.0
go: trying upgrade to example.net/d@v0.2.0
go: accepting indirect upgrade from go@1.22.0 to 1.23.0
go: upgraded go 1.20 => 1.23.0
go: added toolchain go1.23.9
go: upgraded example.net/a v0.1.0 => v0.2.0
go: added example.net/b v0.1.0
go: added example.net/c v0.1.0
@ -92,8 +90,6 @@ module example
go 1.22.0
toolchain go1.22.9
require (
example.net/a v0.1.0
example.net/b v0.1.0
@ -117,8 +113,6 @@ module example
go 1.23.0
toolchain go1.23.9
require (
example.net/a v0.2.0
example.net/b v0.1.0

View File

@ -131,7 +131,7 @@ cmpenv go.mod go.mod.latest
cp go.mod.117 go.mod
go mod tidy -go=1.21.0 # lower than $goversion
cmpenv go.mod go.mod.121toolchain
cmp go.mod go.mod.121toolchain
-- go.mod --
@ -334,8 +334,6 @@ module example.com/m
go 1.21.0
toolchain $TESTGO_VERSION
require example.net/a v0.1.0
require (

View File

@ -18,9 +18,12 @@ grep 'toolchain go1.22.1' go.mod
go get go@1.22.3
stderr '^go: upgraded go 1.10 => 1.22.3$'
stderr '^go: upgraded toolchain go1.22.1 => go1.100.0$'
! stderr '^go: upgraded toolchain$'
grep 'go 1.22.3' go.mod
go get toolchain@go1.100.0
stderr '^go: added toolchain go1.100.0$'
go get go@1.22.3 toolchain@1.22.3
stderr '^go: removed toolchain go1.100.0$'
! grep toolchain go.mod
@ -65,6 +68,10 @@ stderr '^go: removed toolchain go1.23.9'
! stderr ' go 1'
grep 'go 1.23.5' go.mod
go get toolchain@go1.23.0 go@1.22.0
go get go@1.24.0
! grep toolchain go.mod
-- go.mod --
module m
go 1.10

View File

@ -1,4 +1,4 @@
# go get should update the go and toolchain lines in go.work
# go get should update the go line in go.work
env TESTGO_VERSION=go1.21
env TESTGO_VERSION_SWITCH=switch
env GOTOOLCHAIN=auto
@ -9,8 +9,8 @@ stderr '^go: rsc.io/needall@v0.0.1 requires go >= 1.23; switching to go1.23.9$'
stderr '^go: added rsc.io/needall v0.0.1'
grep 'go 1.23$' go.mod
grep 'go 1.23$' go.work
grep 'toolchain go1.23.9' go.mod
grep 'toolchain go1.23.9' go.work
! grep toolchain go.mod
! grep toolchain go.work
-- go.mod.new --
module m

View File

@ -34,7 +34,7 @@ env GOTOOLCHAIN=auto
go work sync
stderr '^go: m1_22_1'${/}'go.mod requires go >= 1.22.1; switching to go1.22.9$'
grep '^go 1.22.1$' go.work
grep '^toolchain go1.22.9$' go.work
! grep toolchain go.work
# work sync with newer modules should update go 1.22.1 -> 1.24rc1 and drop toolchain
go work edit -use=./m1_24_rc0
@ -42,4 +42,4 @@ go work sync
stderr '^go: m1_24_rc0'${/}'go.mod requires go >= 1.24rc0; switching to go1.24rc1$'
cat go.work
grep '^go 1.24rc0$' go.work
grep '^toolchain go1.24rc1$' go.work
! grep toolchain go.work

View File

@ -32,13 +32,13 @@ env GOTOOLCHAIN=auto
go work use ./m1_22_0
stderr '^go: m1_22_0'${/}'go.mod requires go >= 1.22.0; switching to go1.22.9$'
grep '^go 1.22.0$' go.work
grep '^toolchain go1.22.9$' go.work
! grep toolchain go.work
# work use with an even newer module should bump go again.
go work use ./m1_22_1
! stderr switching
stderr '^go: m1_22_1'${/}'go.mod requires go >= 1.22.1; switching to go1.22.9$'
grep '^go 1.22.1$' go.work
grep '^toolchain go1.22.9$' go.work # unchanged
! grep toolchain go.work
# work use with an even newer module should bump go and toolchain again.
env GOTOOLCHAIN=go1.22.9
@ -48,4 +48,4 @@ env GOTOOLCHAIN=auto
go work use ./m1_24_rc0
stderr '^go: m1_24_rc0'${/}'go.mod requires go >= 1.24rc0; switching to go1.24rc1$'
grep '^go 1.24rc0$' go.work
grep '^toolchain go1.24rc1$' go.work
! grep 'toolchain' go.work