@@ -11,6 +11,7 @@ import (
11
11
"context"
12
12
"crypto/sha1"
13
13
"crypto/sha256"
14
+ "encoding/json"
14
15
"errors"
15
16
"flag"
16
17
"fmt"
@@ -30,6 +31,7 @@ import (
30
31
"golang.org/x/build/buildenv"
31
32
"golang.org/x/build/internal/envutil"
32
33
"golang.org/x/build/internal/task"
34
+ "golang.org/x/build/internal/workflow"
33
35
"golang.org/x/build/maintner"
34
36
)
35
37
@@ -69,13 +71,19 @@ var releaseTargets = []Target{
69
71
}
70
72
71
73
var releaseModes = map [string ]bool {
72
- "prepare" : true ,
73
- "release" : true ,
74
+ "prepare" : true ,
75
+ "release" : true ,
76
+
74
77
"mail-dl-cl" : true ,
78
+
79
+ "tweet-minor" : true ,
80
+ "tweet-beta" : true ,
81
+ "tweet-rc" : true ,
82
+ "tweet-major" : true ,
75
83
}
76
84
77
85
func usage () {
78
- fmt .Fprintln (os .Stderr , "usage: releasebot -mode {prepare|release|mail-dl-cl} [-security] [-dry-run] {go1.8.5|go1.10beta2|go1.11rc1}" )
86
+ fmt .Fprintln (os .Stderr , "usage: releasebot -mode {prepare|release|mail-dl-cl|tweet-{minor,beta,rc,major} } [-security] [-dry-run] {go1.8.5|go1.10beta2|go1.11rc1}" )
79
87
flag .PrintDefaults ()
80
88
os .Exit (2 )
81
89
}
@@ -101,6 +109,10 @@ func main() {
101
109
} else if * modeFlag == "mail-dl-cl" {
102
110
mailDLCL ()
103
111
return
112
+ } else if strings .HasPrefix (* modeFlag , "tweet-" ) {
113
+ kind := (* modeFlag )[len ("tweet-" ):]
114
+ postTweet (kind )
115
+ return
104
116
} else if flag .NArg () != 1 {
105
117
fmt .Fprintln (os .Stderr , "need to provide a release name" )
106
118
usage ()
@@ -200,9 +212,11 @@ func mailDLCL() {
200
212
}
201
213
changeURL , err := task .MailDLCL (context .Background (), versions )
202
214
if err != nil {
203
- log .Fatalf (`task.MailDLCL(ctx, %#v) failed: %v
215
+ log .Fatalf (`task.MailDLCL(ctx, %#v) failed:
216
+
217
+ %v
204
218
205
- If it's neccessary to perform it manually as a workaround,
219
+ If it's necessary to perform it manually as a workaround,
206
220
consider the following steps:
207
221
208
222
git clone https://go.googlesource.com/dl && cd dl
@@ -216,6 +230,71 @@ Discuss with the secondary release coordinator as needed.`, versions, err)
216
230
fmt .Printf ("\n Please review and submit %s\n and then refer to the playbook for the next steps.\n \n " , changeURL )
217
231
}
218
232
233
+ // postTweet parses command-line arguments for the tweet-* modes,
234
+ // and runs it.
235
+ // kind must be one of "minor", "beta", "rc", or "major".
236
+ func postTweet (kind string ) {
237
+ if flag .NArg () != 1 {
238
+ fmt .Fprintln (os .Stderr , "need to provide 1 release tweet JSON object" )
239
+ usage ()
240
+ }
241
+ var tweet task.ReleaseTweet
242
+ err := json .Unmarshal ([]byte (flag .Arg (0 )), & tweet )
243
+ if err != nil {
244
+ log .Fatalln ("error parsing release tweet JSON object:" , err )
245
+ }
246
+
247
+ versions := []string {tweet .Version }
248
+ if tweet .SecondaryVersion != "" {
249
+ versions = append (versions , tweet .SecondaryVersion + " (secondary)" )
250
+ }
251
+ fmt .Printf ("About to tweet about the release of the following Go versions:\n \n \t • %s\n \n " , strings .Join (versions , "\n \t • " ))
252
+ if tweet .Security != "" {
253
+ fmt .Printf ("with the following security sentence (%d characters long):\n \n \t %s\n \n " , len ([]rune (tweet .Security )), tweet .Security )
254
+ } else {
255
+ fmt .Print ("with no security fixes being mentioned,\n \n " )
256
+ }
257
+ if tweet .Announcement != "" {
258
+ fmt .Printf ("and with the following announcement URL:\n \n \t %s\n \n " , tweet .Announcement )
259
+ }
260
+ fmt .Print ("Ok? (Y/n) " )
261
+ var response string
262
+ _ , err = fmt .Scanln (& response )
263
+ if err != nil {
264
+ log .Fatalln (err )
265
+ }
266
+ if response != "Y" && response != "y" {
267
+ log .Fatalln ("stopped as requested" )
268
+ }
269
+ tweetRelease := map [string ]func (workflow.TaskContext , task.ReleaseTweet , bool ) (string , error ){
270
+ "minor" : task .TweetMinorRelease ,
271
+ "beta" : task .TweetBetaRelease ,
272
+ "rc" : task .TweetRCRelease ,
273
+ "major" : task .TweetMajorRelease ,
274
+ }[kind ]
275
+ tweetURL , err := tweetRelease (workflow.TaskContext {Context : context .Background (), Logger : log .Default ()}, tweet , dryRun )
276
+ if errors .Is (err , task .ErrTweetTooLong ) && len ([]rune (tweet .Security )) > 120 {
277
+ log .Fatalf (`A tweet was not created because it's too long.
278
+
279
+ The provided security sentence is somewhat long (%d characters),
280
+ so try making it shorter to avoid exceeding Twitter's limits.` , len ([]rune (tweet .Security )))
281
+ } else if err != nil {
282
+ log .Fatalf (`tweetRelease(ctx, %#v) failed:
283
+
284
+ %v
285
+
286
+ If it's necessary to perform it manually as a workaround,
287
+ consider the following options:
288
+
289
+ • use the template displayed in the log above (if any)
290
+ • use the same format as the last tweet for the release
291
+ of the same kind
292
+
293
+ Discuss with the secondary release coordinator as needed.` , tweet , err )
294
+ }
295
+ fmt .Printf ("\n Please check that %s looks okay\n and then refer to the playbook for the next steps.\n \n " , tweetURL )
296
+ }
297
+
219
298
// checkForGitCodereview exits the program if git-codereview is not installed
220
299
// in the user's path.
221
300
func checkForGitCodereview () {
0 commit comments