Skip to content

Commit 337f7b1

Browse files
committed
cmd/go: update default go directive in mod or work init
This commit updates the default go directive when initializing a new module. The current logic is to use the latest version supported by the toolchain. This behavior is simple, predictable, and importantly, it can work while completely offline (i.e., no internet connection required). This commit changes the default version to the following behavior: * If the current toolchain version is a stable version of Go 1.N.M, default to go 1.(N-1).0 * If the current toolchain version is a pre-release version of Go 1.N (Release Candidate M) or a development version of Go 1.N, default to go 1.(N-2).0 This behavior maintains the property of being able to work offline. Fixes #74748. Change-Id: I81f62eef29f1dd51060067c8075f61e7bcf57c20 Reviewed-on: https://go-review.googlesource.com/c/go/+/720480 Commit-Queue: Ian Alexander <jitsu@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org> TryBot-Bypass: Dmitri Shuralyov <dmitshur@golang.org> Reviewed-by: Michael Matloob <matloob@golang.org> Reviewed-by: Michael Matloob <matloob@google.com>
1 parent 3c26aef commit 337f7b1

File tree

11 files changed

+171
-60
lines changed

11 files changed

+171
-60
lines changed

src/cmd/go/internal/modload/init.go

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"cmd/go/internal/lockedfile"
3030
"cmd/go/internal/modfetch"
3131
"cmd/go/internal/search"
32+
igover "internal/gover"
3233

3334
"golang.org/x/mod/modfile"
3435
"golang.org/x/mod/module"
@@ -826,7 +827,7 @@ func WriteWorkFile(path string, wf *modfile.WorkFile) error {
826827
wf.Cleanup()
827828
out := modfile.Format(wf.Syntax)
828829

829-
return os.WriteFile(path, out, 0666)
830+
return os.WriteFile(path, out, 0o666)
830831
}
831832

832833
// UpdateWorkGoVersion updates the go line in wf to be at least goVers,
@@ -1200,7 +1201,7 @@ func CreateModFile(loaderstate *State, ctx context.Context, modPath string) {
12001201
modFile := new(modfile.File)
12011202
modFile.AddModuleStmt(modPath)
12021203
loaderstate.MainModules = makeMainModules(loaderstate, []module.Version{modFile.Module.Mod}, []string{modRoot}, []*modfile.File{modFile}, []*modFileIndex{nil}, nil)
1203-
addGoStmt(modFile, modFile.Module.Mod, gover.Local()) // Add the go directive before converted module requirements.
1204+
addGoStmt(modFile, modFile.Module.Mod, DefaultModInitGoVersion()) // Add the go directive before converted module requirements.
12041205

12051206
rs := requirementsFromModFiles(loaderstate, ctx, nil, []*modfile.File{modFile}, nil)
12061207
rs, err := updateRoots(loaderstate, ctx, rs.direct, rs, nil, nil, false)
@@ -1811,9 +1812,7 @@ Run 'go help mod init' for more information.
18111812
return "", fmt.Errorf(msg, dir, reason)
18121813
}
18131814

1814-
var (
1815-
importCommentRE = lazyregexp.New(`(?m)^package[ \t]+[^ \t\r\n/]+[ \t]+//[ \t]+import[ \t]+(\"[^"]+\")[ \t]*\r?\n`)
1816-
)
1815+
var importCommentRE = lazyregexp.New(`(?m)^package[ \t]+[^ \t\r\n/]+[ \t]+//[ \t]+import[ \t]+(\"[^"]+\")[ \t]*\r?\n`)
18171816

18181817
func findImportComment(file string) string {
18191818
data, err := os.ReadFile(file)
@@ -2252,3 +2251,29 @@ func CheckGodebug(verb, k, v string) error {
22522251
}
22532252
return fmt.Errorf("unknown %s %q", verb, k)
22542253
}
2254+
2255+
// DefaultModInitGoVersion returns the appropriate go version to include in a
2256+
// newly initialized module or work file.
2257+
//
2258+
// If the current toolchain version is a stable version of Go 1.N.M, default to
2259+
// go 1.(N-1).0
2260+
//
2261+
// If the current toolchain version is a pre-release version of Go 1.N (Release
2262+
// Candidate M) or a development version of Go 1.N, default to go 1.(N-2).0
2263+
func DefaultModInitGoVersion() string {
2264+
v := gover.Local()
2265+
if isPrereleaseOrDevelVersion(v) {
2266+
v = gover.Prev(gover.Prev(v))
2267+
} else {
2268+
v = gover.Prev(v)
2269+
}
2270+
if strings.Count(v, ".") < 2 {
2271+
v += ".0"
2272+
}
2273+
return v
2274+
}
2275+
2276+
func isPrereleaseOrDevelVersion(s string) bool {
2277+
v := igover.Parse(s)
2278+
return v.Kind != "" || v.Patch == ""
2279+
}

src/cmd/go/internal/workcmd/init.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212

1313
"cmd/go/internal/base"
1414
"cmd/go/internal/fsys"
15-
"cmd/go/internal/gover"
1615
"cmd/go/internal/modload"
1716

1817
"golang.org/x/mod/modfile"
@@ -58,10 +57,9 @@ func runInit(ctx context.Context, cmd *base.Command, args []string) {
5857
base.Fatalf("go: %s already exists", gowork)
5958
}
6059

61-
goV := gover.Local() // Use current Go version by default
6260
wf := new(modfile.WorkFile)
6361
wf.Syntax = new(modfile.FileSyntax)
64-
wf.AddGoStmt(goV)
62+
wf.AddGoStmt(modload.DefaultModInitGoVersion())
6563
workUse(ctx, moduleLoaderState, gowork, wf, args)
6664
modload.WriteWorkFile(gowork, wf)
6765
}

src/cmd/go/testdata/script/mod_edit.txt

Lines changed: 37 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
env GO111MODULE=on
22

3+
# Set go version so that we can test produced mod files for equality.
4+
env TESTGO_VERSION=go1.26.0
5+
36
# Test that go mod edits and related mod flags work.
47
# Also test that they can use a dummy name that isn't resolvable. golang.org/issue/24100
58

@@ -10,16 +13,16 @@ stderr 'cannot determine module path'
1013

1114
go mod init x.x/y/z
1215
stderr 'creating new go.mod: module x.x/y/z'
13-
cmpenv go.mod $WORK/go.mod.init
16+
cmp go.mod $WORK/go.mod.init
1417

1518
! go mod init
16-
cmpenv go.mod $WORK/go.mod.init
19+
cmp go.mod $WORK/go.mod.init
1720

1821
# go mod edits
1922
go mod edit -droprequire=x.1 -require=x.1@v1.0.0 -require=x.2@v1.1.0 -droprequire=x.2 -exclude='x.1 @ v1.2.0' -exclude=x.1@v1.2.1 -exclude=x.1@v2.0.0+incompatible -replace=x.1@v1.3.0=y.1@v1.4.0 -replace='x.1@v1.4.0 = ../z' -retract=v1.6.0 -retract=[v1.1.0,v1.2.0] -retract=[v1.3.0,v1.4.0] -retract=v1.0.0
20-
cmpenv go.mod $WORK/go.mod.edit1
23+
cmp go.mod $WORK/go.mod.edit1
2124
go mod edit -droprequire=x.1 -dropexclude=x.1@v1.2.1 -dropexclude=x.1@v2.0.0+incompatible -dropreplace=x.1@v1.3.0 -require=x.3@v1.99.0 -dropretract=v1.0.0 -dropretract=[v1.1.0,v1.2.0]
22-
cmpenv go.mod $WORK/go.mod.edit2
25+
cmp go.mod $WORK/go.mod.edit2
2326

2427
# -exclude and -retract reject invalid versions.
2528
! go mod edit -exclude=example.com/m@bad
@@ -36,11 +39,11 @@ stderr '^go: -exclude=example.com/m/v2@v1\.0\.0: version "v1\.0\.0" invalid: sho
3639
! go mod edit -exclude=gopkg.in/example.v1@v2.0.0
3740
stderr '^go: -exclude=gopkg\.in/example\.v1@v2\.0\.0: version "v2\.0\.0" invalid: should be v1, not v2$'
3841

39-
cmpenv go.mod $WORK/go.mod.edit2
42+
cmp go.mod $WORK/go.mod.edit2
4043

4144
# go mod edit -json
4245
go mod edit -json
43-
cmpenv stdout $WORK/go.mod.json
46+
cmp stdout $WORK/go.mod.json
4447

4548
# go mod edit -json (retractions with rationales)
4649
go mod edit -json $WORK/go.mod.retractrationale
@@ -56,66 +59,66 @@ cmp stdout $WORK/go.mod.empty.json
5659

5760
# go mod edit -replace
5861
go mod edit -replace=x.1@v1.3.0=y.1/v2@v2.3.5 -replace=x.1@v1.4.0=y.1/v2@v2.3.5
59-
cmpenv go.mod $WORK/go.mod.edit3
62+
cmp go.mod $WORK/go.mod.edit3
6063
go mod edit -replace=x.1=y.1/v2@v2.3.6
61-
cmpenv go.mod $WORK/go.mod.edit4
64+
cmp go.mod $WORK/go.mod.edit4
6265
go mod edit -dropreplace=x.1
63-
cmpenv go.mod $WORK/go.mod.edit5
66+
cmp go.mod $WORK/go.mod.edit5
6467
go mod edit -replace=x.1=../y.1/@v2
65-
cmpenv go.mod $WORK/go.mod.edit6
68+
cmp go.mod $WORK/go.mod.edit6
6669
! go mod edit -replace=x.1=y.1/@v2
6770
stderr '^go: -replace=x.1=y.1/@v2: invalid new path: malformed import path "y.1/": trailing slash$'
6871

6972
# go mod edit -fmt
7073
cp $WORK/go.mod.badfmt go.mod
7174
go mod edit -fmt -print # -print should avoid writing file
72-
cmpenv stdout $WORK/go.mod.goodfmt
75+
cmp stdout $WORK/go.mod.goodfmt
7376
cmp go.mod $WORK/go.mod.badfmt
7477
go mod edit -fmt # without -print, should write file (and nothing to stdout)
7578
! stdout .
76-
cmpenv go.mod $WORK/go.mod.goodfmt
79+
cmp go.mod $WORK/go.mod.goodfmt
7780

7881
# go mod edit -module
7982
cd $WORK/m
8083
go mod init a.a/b/c
8184
go mod edit -module x.x/y/z
82-
cmpenv go.mod go.mod.edit
85+
cmp go.mod go.mod.edit
8386

8487
# golang.org/issue/30513: don't require go-gettable module paths.
8588
cd $WORK/local
8689
go mod init foo
8790
go mod edit -module local-only -require=other-local@v1.0.0 -replace other-local@v1.0.0=./other
88-
cmpenv go.mod go.mod.edit
91+
cmp go.mod go.mod.edit
8992

9093
# go mod edit -godebug
9194
cd $WORK/g
9295
cp go.mod.start go.mod
9396
go mod edit -godebug key=value
94-
cmpenv go.mod go.mod.edit
97+
cmp go.mod go.mod.edit
9598
go mod edit -dropgodebug key2
96-
cmpenv go.mod go.mod.edit
99+
cmp go.mod go.mod.edit
97100
go mod edit -dropgodebug key
98-
cmpenv go.mod go.mod.start
101+
cmp go.mod go.mod.start
99102

100103
# go mod edit -tool
101104
cd $WORK/h
102105
cp go.mod.start go.mod
103106
go mod edit -tool example.com/tool
104-
cmpenv go.mod go.mod.edit
107+
cmp go.mod go.mod.edit
105108
go mod edit -droptool example.com/tool2
106-
cmpenv go.mod go.mod.edit
109+
cmp go.mod go.mod.edit
107110
go mod edit -droptool example.com/tool
108-
cmpenv go.mod go.mod.start
111+
cmp go.mod go.mod.start
109112

110113
# go mod edit -ignore
111114
cd $WORK/i
112115
cp go.mod.start go.mod
113116
go mod edit -ignore example.com/ignore
114-
cmpenv go.mod go.mod.edit
117+
cmp go.mod go.mod.edit
115118
go mod edit -dropignore example.com/ignore2
116-
cmpenv go.mod go.mod.edit
119+
cmp go.mod go.mod.edit
117120
go mod edit -dropignore example.com/ignore
118-
cmpenv go.mod go.mod.start
121+
cmp go.mod go.mod.start
119122

120123
-- x.go --
121124
package x
@@ -126,11 +129,11 @@ package w
126129
-- $WORK/go.mod.init --
127130
module x.x/y/z
128131

129-
go $goversion
132+
go 1.25.0
130133
-- $WORK/go.mod.edit1 --
131134
module x.x/y/z
132135

133-
go $goversion
136+
go 1.25.0
134137

135138
require x.1 v1.0.0
136139

@@ -154,7 +157,7 @@ retract (
154157
-- $WORK/go.mod.edit2 --
155158
module x.x/y/z
156159

157-
go $goversion
160+
go 1.25.0
158161

159162
exclude x.1 v1.2.0
160163

@@ -171,7 +174,7 @@ require x.3 v1.99.0
171174
"Module": {
172175
"Path": "x.x/y/z"
173176
},
174-
"Go": "$goversion",
177+
"Go": "1.25.0",
175178
"Require": [
176179
{
177180
"Path": "x.3",
@@ -211,7 +214,7 @@ require x.3 v1.99.0
211214
-- $WORK/go.mod.edit3 --
212215
module x.x/y/z
213216

214-
go $goversion
217+
go 1.25.0
215218

216219
exclude x.1 v1.2.0
217220

@@ -229,7 +232,7 @@ require x.3 v1.99.0
229232
-- $WORK/go.mod.edit4 --
230233
module x.x/y/z
231234

232-
go $goversion
235+
go 1.25.0
233236

234237
exclude x.1 v1.2.0
235238

@@ -244,7 +247,7 @@ require x.3 v1.99.0
244247
-- $WORK/go.mod.edit5 --
245248
module x.x/y/z
246249

247-
go $goversion
250+
go 1.25.0
248251

249252
exclude x.1 v1.2.0
250253

@@ -257,7 +260,7 @@ require x.3 v1.99.0
257260
-- $WORK/go.mod.edit6 --
258261
module x.x/y/z
259262

260-
go $goversion
263+
go 1.25.0
261264

262265
exclude x.1 v1.2.0
263266

@@ -272,7 +275,7 @@ replace x.1 => ../y.1/@v2
272275
-- $WORK/local/go.mod.edit --
273276
module local-only
274277

275-
go $goversion
278+
go 1.25.0
276279

277280
require other-local v1.0.0
278281

@@ -304,7 +307,7 @@ retract [v1.8.1, v1.8.2]
304307
-- $WORK/m/go.mod.edit --
305308
module x.x/y/z
306309

307-
go $goversion
310+
go 1.25.0
308311
-- $WORK/go.mod.retractrationale --
309312
module x.x/y/z
310313

@@ -405,4 +408,4 @@ module g
405408

406409
go 1.24
407410

408-
ignore example.com/ignore
411+
ignore example.com/ignore
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
env TESTGO_VERSION=go1.28-devel
2+
go mod init example.com
3+
cmp go.mod go.mod.want-1.26.0
4+
rm go.mod
5+
env TESTGO_VERSION=go1.26.0
6+
go mod init example.com
7+
cmp go.mod go.mod.want-1.25.0
8+
rm go.mod
9+
env TESTGO_VERSION=go1.22.2
10+
go mod init example.com
11+
cmp go.mod go.mod.want-1.21.0
12+
rm go.mod
13+
env TESTGO_VERSION=go1.25.0-xyzzy
14+
go mod init example.com
15+
cmp go.mod go.mod.want-1.24.0
16+
rm go.mod
17+
env TESTGO_VERSION=go1.23rc3
18+
go mod init example.com
19+
cmp go.mod go.mod.want-1.21.0
20+
rm go.mod
21+
env TESTGO_VERSION=go1.18beta2
22+
go mod init example.com
23+
cmp go.mod go.mod.want-1.16.0
24+
-- go.mod.want-1.26.0 --
25+
module example.com
26+
27+
go 1.26.0
28+
-- go.mod.want-1.25.0 --
29+
module example.com
30+
31+
go 1.25.0
32+
-- go.mod.want-1.24.0 --
33+
module example.com
34+
35+
go 1.24.0
36+
-- go.mod.want-1.22.0 --
37+
module example.com
38+
39+
go 1.22.0
40+
-- go.mod.want-1.21.0 --
41+
module example.com
42+
43+
go 1.21.0
44+
-- go.mod.want-1.16.0 --
45+
module example.com
46+
47+
go 1.16.0

src/cmd/go/testdata/script/work.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[short] skip 'runs go run'
2+
env TESTGO_VERSION=go1.26.0
23

34
! go work init doesnotexist
45
stderr 'go: directory doesnotexist does not exist'
@@ -74,7 +75,7 @@ use (
7475
../src/a
7576
)
7677
-- go.work.want --
77-
go $goversion
78+
go 1.25.0
7879

7980
use (
8081
./a

src/cmd/go/testdata/script/work_edit.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Test editing go.work files.
2+
env TESTGO_VERSION=go1.26.0
23

34
go work init m
45
cmpenv go.work go.work.want_initial
@@ -54,11 +55,11 @@ module m
5455

5556
go 1.18
5657
-- go.work.want_initial --
57-
go $goversion
58+
go 1.25.0
5859

5960
use ./m
6061
-- go.work.want_use_n --
61-
go $goversion
62+
go 1.25.0
6263

6364
use (
6465
./m

0 commit comments

Comments
 (0)