-
Notifications
You must be signed in to change notification settings - Fork 1
/
main.go
272 lines (232 loc) · 6.34 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
package main
import (
"bytes"
"fmt"
"log"
"os"
"path/filepath"
"strconv"
"time"
"go101.org/gotv/internal/util"
)
func _(x []int) *[1]int {
return (*[1]int)(x) // requires 1.17+ toolchains
}
func main() {
args := make([]string, len(os.Args))
copy(args, os.Args)
program := args[0]
if len(args) < 2 || args[1] == "-h" || args[1] == "--help" {
printUsage(program)
return
}
if args[1] == "release" {
releaseGoTV()
return
}
gotv, err := born()
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
args = args[1:]
err = gotv.tryRunningSpecialCommand(args)
switch err {
case nil:
return
default:
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
case unknownCommand{}:
break
}
tv := parseGoToolchainVersion(args[0])
if tv.kind == kind_Default {
tv = gotv.DefaultVersion()
if invalid, _ := tv.IsInvalid(); invalid {
printSetDefaultVersion(program)
os.Exit(1)
}
// fmt.Println("Use default version:", tv)
if err := gotv.tryRunningGoToolchainCommand(tv, args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
} else if invalid, message := tv.IsInvalid(); invalid {
fmt.Fprintln(os.Stderr, message)
os.Exit(1)
} else {
if err := gotv.tryRunningGoToolchainCommand(tv, args[1:]); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
}
const descToolchainVersion = `where ToolchainVersion might be
* a Go release version, such as 1.17.13, 1.18,
and 1.19rc1, which mean the release tags
go1.17.13, go1.18, go1.19rc1, respectively,
in the Go git repository.
Note:
* 1.N. means the latest release of Go 1.N
versions. If N >= 21, then 1.N also means
the latest release of Go 1.N versions.
* 1. means the latest Go 1 release verison.
* . means the latest Go release version.
* :tip, which means the local latest master
branch in the Go git repository.
* :N.M, such as :1.17, :1.18 and :1.19, which mean
the local latest release-branch.goN.M branch
in the Go git repository.`
func printSetDefaultVersion(program string) {
fmt.Fprintf(os.Stderr, `It looks you want to use the default toolchain version,
but it has not been set yet. Please run the following
command to set it:
%s default-version ToolchainVersion
%s
`,
filepath.Base(program),
descToolchainVersion,
)
}
func printUsage(program string) {
fmt.Printf(`GoTV %s
Usage (to use a specific Go toolchain version):
%s ToolchainVersion [go-arguments...]
%s
A ToolchainVersion suffixed with ! means remote
versions are needed to be fetched firstly.
GoTV specific commands:
gotv fetch-versions
fetch remote versions (sync git repository)
gotv list-versions
list all (local) releases and versions branches
gotv cache-version ToolchainVersion [ToolchainVersion ...]
cache one or more versions
gotv uncache-version ToolchainVersion [ToolchainVersion ...]
uncache one or more versions
gotv pin-version ToolchainVersion
pin a specified version
gotv unpin-version
unpin the current pinned version
gotv default-version ToolchainVersion
set the default version
`,
Version,
filepath.Base(program),
descToolchainVersion,
)
}
const Version = "v0.2.2"
func releaseGoTV() {
if _, err := util.RunShell(time.Minute*3, "", nil, nil, nil, "go", "test", "./..."); err != nil {
log.Println("go test error:", err)
return
}
if _, err := util.RunShell(time.Minute*3, "", nil, nil, nil, "go", "fmt", "./..."); err != nil {
log.Println("go fmt error:", err)
return
}
if _, err := util.RunShell(time.Minute*3, "", nil, nil, nil, "go", "mod", "tidy"); err != nil {
log.Println("go mod tidy error:", err)
return
}
const (
VersionConstPrefix = `const Version = "v`
PreviewSuffix = "-preview"
)
var verisonGoFile = "main.go"
oldContent, err := os.ReadFile(verisonGoFile)
if err != nil {
log.Printf("failed to load version.go: %s", err)
return
}
m, n := bytes.Index(oldContent, []byte(VersionConstPrefix)), 0
if m > 0 {
m += len(VersionConstPrefix)
n = bytes.IndexByte(oldContent[m:], '"')
if n >= 0 {
n += m
}
}
if m <= 0 || n <= 0 {
log.Printf("Version string not found (%d : %d)", m, n)
return
}
oldVersion := bytes.TrimSuffix(oldContent[m:n], []byte(PreviewSuffix))
noPreviewSuffix := len(oldVersion) == n-m
mmp := bytes.SplitN(oldVersion, []byte{'.'}, -1)
if len(mmp) != 3 {
log.Printf("Version string not in MAJOR.MINOR.PATCH format: %s", oldVersion)
return
}
major, err := strconv.Atoi(string(mmp[0]))
if err != nil {
log.Printf("parse MAJOR version (%s) error: %s", mmp[0], err)
return
}
minor, err := strconv.Atoi(string(mmp[1]))
if err != nil {
log.Printf("parse MINOR version (%s) error: %s", mmp[1], err)
return
}
patch, err := strconv.Atoi(string(mmp[2]))
if err != nil {
log.Printf("parse PATCH version (%s) error: %s", mmp[2], err)
return
}
var incVersion = func() {
patch = (patch + 1) % 10
if patch == 0 {
minor = (minor + 1) % 10
if minor == 0 {
major++
}
}
}
newContentLength := len(oldContent) + 1
if noPreviewSuffix {
newContentLength += len(PreviewSuffix)
incVersion()
}
var newVersion, newPreviewVersion []byte
var buf = bytes.NewBuffer(make([]byte, 0, newContentLength))
{
buf.Reset()
fmt.Fprintf(buf, "%d.%d.%d", major, minor, patch)
newVersion = append(newVersion, buf.Bytes()...)
}
{
incVersion()
buf.Reset()
fmt.Fprintf(buf, "%d.%d.%d", major, minor, patch)
buf.WriteString(PreviewSuffix)
newPreviewVersion = append(newPreviewVersion, buf.Bytes()...)
}
var writeNewContent = func(version []byte) error {
buf.Reset()
buf.Write(oldContent[:m])
buf.Write(version)
buf.Write(oldContent[n:])
return os.WriteFile(verisonGoFile, buf.Bytes(), 0644)
}
if err := writeNewContent(newVersion); err != nil {
log.Printf("write release version file error: %s", err)
return
}
var gitTag = fmt.Sprintf("v%s", newVersion)
if output, err := util.RunShellCommand(time.Second*5, "", nil, nil, nil,
"git", "commit", "-a", "-m", gitTag); err != nil {
log.Printf("git commit error: %s\n%s", err, output)
}
if output, err := util.RunShellCommand(time.Second*5, "", nil, nil, nil,
"git", "tag", gitTag); err != nil {
log.Printf("git commit error: %s\n%s", err, output)
}
if err := writeNewContent(newPreviewVersion); err != nil {
log.Printf("write preview version file error: %s", err)
return
}
log.Printf("new version: %s", newVersion)
log.Printf("new preview version: %s", newPreviewVersion)
}