# https://golang.org/issue/45094: 'go mod tidy' now accepts a '-go' flag # to change the language version in use. # # The package import graph used in this test looks like: # # m --- a --- b # | # b_test --- c # | # c_test --- d # # The module diagram looks like: # # m --- a --- b # | # + --- c # | # + --- d # # Module b omits its dependency on c, and module c omits its dependency on d. # # In go 1.15, the tidy main module must require a (because it is direct), # c (because it is a missing test dependency of an imported package), # and d (because it is a missing transitive test dependency). # # In go 1.16, the tidy main module can omit d because it is no longer # included in "all". # # In go 1.17, the main module must explicitly require b # (because it is transitively imported by the main module). cp go.mod go.mod.orig # Pretend we're a release version so that we can theoretically # write our version in toolchain lines. env goversion=1.99.0 env TESTGO_VERSION=go${goversion} # An invalid argument should be rejected. ! go mod tidy -go=bananas stderr '^invalid value "bananas" for flag -go: expecting a Go version like "'$goversion'"$' cmp go.mod go.mod.orig ! go mod tidy -go=0.9 stderr '^invalid value "0.9" for flag -go: expecting a Go version like "'$goversion'"$' ! go mod tidy -go=2000.0 stderr '^invalid value "2000.0" for flag -go: maximum supported Go version is '$goversion'$' # Supported versions should change the go.mod file to be tidy according to the # indicated version. go mod tidy -go=1.15 cmp go.mod go.mod.115 go mod tidy cmp go.mod go.mod.115 go mod tidy -go=1.16 cmp go.mod go.mod.116 go mod tidy cmp go.mod go.mod.116 go mod tidy -go=1.17 cmp go.mod go.mod.117 go mod tidy cmp go.mod go.mod.117 # If we downgrade back to 1.15, we should re-resolve d to v0.2.0 instead # of the original v0.1.0 (because the original requirement is lost). go mod tidy -go=1.15 cmp go.mod go.mod.115-2 # -go= (with an empty argument) maintains the existing version or adds the # default version (just like omitting the flag). go mod tidy -go='' cmp go.mod go.mod.115-2 cp go.mod.orig go.mod go mod tidy -go='' cmpenv go.mod go.mod.latest # Repeat with go get go@ instead of mod tidy. # Go 1.16 -> 1.17 should be a no-op. cp go.mod.116 go.mod go get go@1.16 cmp go.mod go.mod.116 # Go 1.17 -> 1.16 should leave b (go get is not tidy). cp go.mod.117 go.mod go get go@1.16 cmp go.mod go.mod.116from117 # Go 1.15 -> 1.16 should leave d (go get is not tidy). cp go.mod.115 go.mod go get go@1.16 cmp go.mod go.mod.116from115 # Go 1.16 -> 1.17 should add b. cp go.mod.116 go.mod go get go@1.17 stderr '^\tnote: expanded dependencies to upgrade to go 1.17 or higher; run ''go mod tidy'' to clean up' cmp go.mod go.mod.117 # Go 1.16 -> 1.15 should add d, # but 'go get' doesn't load enough packages to know that. # (This leaves the module untidy, but the user can fix it by running 'go mod tidy'.) cp go.mod.116 go.mod go get go@1.15 toolchain@none cmp go.mod go.mod.115from116 go mod tidy cmp go.mod go.mod.115-2 # Updating the go line to 1.21 or higher also updates the toolchain line, # only if the toolchain is higher than what would be implied by the go line. cp go.mod.117 go.mod go mod tidy -go=$goversion 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 -- go.mod -- module example.com/m require example.net/a v0.1.0 require ( example.net/c v0.1.0 // indirect example.net/d v0.1.0 // indirect ) replace ( example.net/a v0.1.0 => ./a example.net/a v0.2.0 => ./a example.net/b v0.1.0 => ./b example.net/b v0.2.0 => ./b example.net/c v0.1.0 => ./c example.net/c v0.2.0 => ./c example.net/d v0.1.0 => ./d example.net/d v0.2.0 => ./d ) -- m.go -- package m import _ "example.net/a" -- go.mod.115 -- module example.com/m go 1.15 require example.net/a v0.1.0 require ( example.net/c v0.1.0 // indirect example.net/d v0.1.0 // indirect ) replace ( example.net/a v0.1.0 => ./a example.net/a v0.2.0 => ./a example.net/b v0.1.0 => ./b example.net/b v0.2.0 => ./b example.net/c v0.1.0 => ./c example.net/c v0.2.0 => ./c example.net/d v0.1.0 => ./d example.net/d v0.2.0 => ./d ) -- go.mod.115from116 -- module example.com/m go 1.15 require example.net/a v0.1.0 require example.net/c v0.1.0 // indirect replace ( example.net/a v0.1.0 => ./a example.net/a v0.2.0 => ./a example.net/b v0.1.0 => ./b example.net/b v0.2.0 => ./b example.net/c v0.1.0 => ./c example.net/c v0.2.0 => ./c example.net/d v0.1.0 => ./d example.net/d v0.2.0 => ./d ) -- go.mod.116from115 -- module example.com/m go 1.16 require example.net/a v0.1.0 require ( example.net/c v0.1.0 // indirect example.net/d v0.1.0 // indirect ) replace ( example.net/a v0.1.0 => ./a example.net/a v0.2.0 => ./a example.net/b v0.1.0 => ./b example.net/b v0.2.0 => ./b example.net/c v0.1.0 => ./c example.net/c v0.2.0 => ./c example.net/d v0.1.0 => ./d example.net/d v0.2.0 => ./d ) -- go.mod.115-2 -- module example.com/m go 1.15 require example.net/a v0.1.0 require ( example.net/c v0.1.0 // indirect example.net/d v0.2.0 // indirect ) replace ( example.net/a v0.1.0 => ./a example.net/a v0.2.0 => ./a example.net/b v0.1.0 => ./b example.net/b v0.2.0 => ./b example.net/c v0.1.0 => ./c example.net/c v0.2.0 => ./c example.net/d v0.1.0 => ./d example.net/d v0.2.0 => ./d ) -- go.mod.116 -- module example.com/m go 1.16 require example.net/a v0.1.0 require example.net/c v0.1.0 // indirect replace ( example.net/a v0.1.0 => ./a example.net/a v0.2.0 => ./a example.net/b v0.1.0 => ./b example.net/b v0.2.0 => ./b example.net/c v0.1.0 => ./c example.net/c v0.2.0 => ./c example.net/d v0.1.0 => ./d example.net/d v0.2.0 => ./d ) -- go.mod.117 -- module example.com/m go 1.17 require example.net/a v0.1.0 require ( example.net/b v0.1.0 // indirect example.net/c v0.1.0 // indirect ) replace ( example.net/a v0.1.0 => ./a example.net/a v0.2.0 => ./a example.net/b v0.1.0 => ./b example.net/b v0.2.0 => ./b example.net/c v0.1.0 => ./c example.net/c v0.2.0 => ./c example.net/d v0.1.0 => ./d example.net/d v0.2.0 => ./d ) -- go.mod.116from117 -- module example.com/m go 1.16 require example.net/a v0.1.0 require ( example.net/b v0.1.0 // indirect example.net/c v0.1.0 // indirect ) replace ( example.net/a v0.1.0 => ./a example.net/a v0.2.0 => ./a example.net/b v0.1.0 => ./b example.net/b v0.2.0 => ./b example.net/c v0.1.0 => ./c example.net/c v0.2.0 => ./c example.net/d v0.1.0 => ./d example.net/d v0.2.0 => ./d ) -- go.mod.latest -- module example.com/m go $goversion require example.net/a v0.1.0 require ( example.net/b v0.1.0 // indirect example.net/c v0.1.0 // indirect ) replace ( example.net/a v0.1.0 => ./a example.net/a v0.2.0 => ./a example.net/b v0.1.0 => ./b example.net/b v0.2.0 => ./b example.net/c v0.1.0 => ./c example.net/c v0.2.0 => ./c example.net/d v0.1.0 => ./d example.net/d v0.2.0 => ./d ) -- go.mod.121toolchain -- module example.com/m go 1.21.0 toolchain $TESTGO_VERSION require example.net/a v0.1.0 require ( example.net/b v0.1.0 // indirect example.net/c v0.1.0 // indirect ) replace ( example.net/a v0.1.0 => ./a example.net/a v0.2.0 => ./a example.net/b v0.1.0 => ./b example.net/b v0.2.0 => ./b example.net/c v0.1.0 => ./c example.net/c v0.2.0 => ./c example.net/d v0.1.0 => ./d example.net/d v0.2.0 => ./d ) -- a/go.mod -- module example.net/a go 1.15 require example.net/b v0.1.0 -- a/a.go -- package a import _ "example.net/b" -- b/go.mod -- module example.net/b go 1.15 -- b/b.go -- package b -- b/b_test.go -- package b_test import _ "example.net/c" -- c/go.mod -- module example.net/c go 1.15 -- c/c.go -- package c -- c/c_test.go -- package c_test import _ "example.net/d" -- d/go.mod -- module example.net/d go 1.15 -- d/d.go -- package d