Skip to content

Commit dbf1467

Browse files
dmitshurgopherbot
authored andcommitted
internal/task: replace use of go.dev/dl/?mode=json API with local params
The tweet tasks are some of the earliest we've automated for relui. In fact, they were ready for use with the release process ahead of the rest of relui, so the only place to add them was the CLI-based x/build/cmd/releasebot command. Since release coordinators needed to provide all of the task inputs as a JSON object over CLI, at the time, it was easier to fetch information about the Go release artifacts that had been published for the very Go release being made by making a network call to the go.dev/dl/?mode=json API. By now, relui is complete and has all the steps interconnected without toil of invoking individual release steps manually, so it's easy for it to pass the release artifacts to the tweeting tasks directly, and avoid having to ask the website for the release it itself published (silly!). We can also drop the lightweight checking of bad version strings done in oneOrTwoGoVersions. Those typos can't happen now since relui picks the next version to release automatically. This CL does the re-wiring to drop the use of API from TweetRelease but makes almost no changes to the current comprehensive tweet tests, and they're still passing. The next change simplifies the tests to drop their temporary use of the API. For golang/go#57062. Change-Id: I60efc8c8c96152ff52c9fb5cc0f91262f0d8f494 Reviewed-on: https://go-review.googlesource.com/c/build/+/504519 Reviewed-by: Dmitri Shuralyov <[email protected]> TryBot-Result: Gopher Robot <[email protected]> Auto-Submit: Dmitri Shuralyov <[email protected]> Reviewed-by: Heschi Kreinick <[email protected]> Run-TryBot: Dmitri Shuralyov <[email protected]>
1 parent 637d5ef commit dbf1467

File tree

7 files changed

+114
-125
lines changed

7 files changed

+114
-125
lines changed

internal/relui/workflows.go

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -296,15 +296,15 @@ func registerProdReleaseWorkflows(ctx context.Context, h *DefinitionHolder, buil
296296

297297
coordinators := wf.Param(wd, releaseCoordinators)
298298

299-
versionPublished := addSingleReleaseWorkflow(build, milestone, version, wd, r.major, r.kind, coordinators)
299+
published := addSingleReleaseWorkflow(build, milestone, version, wd, r.major, r.kind, coordinators)
300300

301301
securitySummary := wf.Const("")
302302
securityFixes := wf.Slice[string]()
303303
if r.kind == task.KindCurrentMinor || r.kind == task.KindPrevMinor {
304304
securitySummary = wf.Param(wd, securitySummaryParameter)
305305
securityFixes = wf.Param(wd, securityFixesParameter)
306306
}
307-
addCommTasks(wd, build, comm, r.kind, wf.Slice(versionPublished), securitySummary, securityFixes, coordinators)
307+
addCommTasks(wd, build, comm, r.kind, wf.Slice(published), securitySummary, securityFixes, coordinators)
308308

309309
h.RegisterDefinition(fmt.Sprintf("Go 1.%d %s", r.major, r.suffix), wd)
310310
}
@@ -343,26 +343,26 @@ func createMinorReleaseWorkflow(build *BuildReleaseTasks, milestone *task.Milest
343343
wd := wf.New()
344344

345345
coordinators := wf.Param(wd, releaseCoordinators)
346-
v1Published := addSingleReleaseWorkflow(build, milestone, version, wd.Sub(fmt.Sprintf("Go 1.%d", currentMajor)), currentMajor, task.KindCurrentMinor, coordinators)
347-
v2Published := addSingleReleaseWorkflow(build, milestone, version, wd.Sub(fmt.Sprintf("Go 1.%d", prevMajor)), prevMajor, task.KindPrevMinor, coordinators)
346+
currPublished := addSingleReleaseWorkflow(build, milestone, version, wd.Sub(fmt.Sprintf("Go 1.%d", currentMajor)), currentMajor, task.KindCurrentMinor, coordinators)
347+
prevPublished := addSingleReleaseWorkflow(build, milestone, version, wd.Sub(fmt.Sprintf("Go 1.%d", prevMajor)), prevMajor, task.KindPrevMinor, coordinators)
348348

349349
securitySummary := wf.Param(wd, securitySummaryParameter)
350350
securityFixes := wf.Param(wd, securityFixesParameter)
351-
addCommTasks(wd, build, comm, task.KindCurrentMinor, wf.Slice(v1Published, v2Published), securitySummary, securityFixes, coordinators)
351+
addCommTasks(wd, build, comm, task.KindCurrentMinor, wf.Slice(currPublished, prevPublished), securitySummary, securityFixes, coordinators)
352352

353353
return wd, nil
354354
}
355355

356356
func addCommTasks(
357357
wd *wf.Definition, build *BuildReleaseTasks, comm task.CommunicationTasks,
358-
kind task.ReleaseKind, versions wf.Value[[]string], securitySummary wf.Value[string], securityFixes, coordinators wf.Value[[]string],
358+
kind task.ReleaseKind, published wf.Value[[]task.Published], securitySummary wf.Value[string], securityFixes, coordinators wf.Value[[]string],
359359
) {
360-
okayToAnnounceAndTweet := wf.Action0(wd, "Wait to Announce", build.ApproveAction, wf.After(versions))
360+
okayToAnnounceAndTweet := wf.Action0(wd, "Wait to Announce", build.ApproveAction, wf.After(published))
361361

362362
// Announce that a new Go release has been published.
363-
sentMail := wf.Task4(wd, "mail-announcement", comm.AnnounceRelease, wf.Const(kind), versions, securityFixes, coordinators, wf.After(okayToAnnounceAndTweet))
363+
sentMail := wf.Task4(wd, "mail-announcement", comm.AnnounceRelease, wf.Const(kind), published, securityFixes, coordinators, wf.After(okayToAnnounceAndTweet))
364364
announcementURL := wf.Task1(wd, "await-announcement", comm.AwaitAnnounceMail, sentMail)
365-
tweetURL := wf.Task3(wd, "post-tweet", comm.TweetRelease, versions, securitySummary, announcementURL, wf.After(okayToAnnounceAndTweet))
365+
tweetURL := wf.Task3(wd, "post-tweet", comm.TweetRelease, published, securitySummary, announcementURL, wf.After(okayToAnnounceAndTweet))
366366

367367
wf.Output(wd, "Announcement URL", announcementURL)
368368
wf.Output(wd, "Tweet URL", tweetURL)
@@ -379,7 +379,7 @@ func now(_ context.Context) (time.Time, error) {
379379
func addSingleReleaseWorkflow(
380380
build *BuildReleaseTasks, milestone *task.MilestoneTasks, version *task.VersionTasks,
381381
wd *wf.Definition, major int, kind task.ReleaseKind, coordinators wf.Value[[]string],
382-
) (versionPublished wf.Value[string]) {
382+
) wf.Value[task.Published] {
383383
kindVal := wf.Const(kind)
384384
branch := fmt.Sprintf("release-branch.go1.%d", major)
385385
if kind == task.KindBeta {
@@ -425,14 +425,14 @@ func addSingleReleaseWorkflow(
425425
uploadedMods := wf.Action2(wd, "Upload modules to CDN", build.uploadModules, nextVersion, modules, wf.After(tagged))
426426
availableOnProxy := wf.Action2(wd, "Wait for modules on proxy.golang.org", build.awaitProxy, nextVersion, modules, wf.After(uploadedMods))
427427
pushed := wf.Action3(wd, "Push issues", milestone.PushIssues, milestones, nextVersion, kindVal, wf.After(tagged))
428-
versionPublished = wf.Task2(wd, "Publish to website", build.publishArtifacts, nextVersion, signedAndTestedArtifacts, wf.After(uploaded, availableOnProxy, pushed))
428+
published := wf.Task2(wd, "Publish to website", build.publishArtifacts, nextVersion, signedAndTestedArtifacts, wf.After(uploaded, availableOnProxy, pushed))
429429
if kind == task.KindMajor {
430-
goimportsCL := wf.Task2(wd, fmt.Sprintf("Mail goimports CL for 1.%d", major), version.CreateUpdateStdlibIndexCL, coordinators, versionPublished)
430+
goimportsCL := wf.Task2(wd, fmt.Sprintf("Mail goimports CL for 1.%d", major), version.CreateUpdateStdlibIndexCL, coordinators, nextVersion, wf.After(published))
431431
goimportsCommit := wf.Task2(wd, "Wait for goimports CL submission", version.AwaitCL, goimportsCL, wf.Const(""))
432432
wf.Output(wd, "goimports CL submitted", goimportsCommit)
433433
}
434-
wf.Output(wd, "Released version", versionPublished)
435-
return versionPublished
434+
wf.Output(wd, "Published to website", published)
435+
return published
436436
}
437437

438438
type moduleArtifact struct {
@@ -1334,11 +1334,12 @@ func uploadFile(scratchFS, servingFS fs.FS, scratch, filename string) error {
13341334
}
13351335

13361336
// publishArtifacts publishes artifacts for version (typically so they appear at https://go.dev/dl/).
1337-
// It returns version, the Go version that has been successfully published.
1338-
//
1339-
// The version string uses the same format as Go tags. For example, "go1.19rc1".
1340-
func (tasks *BuildReleaseTasks) publishArtifacts(ctx *wf.TaskContext, version string, artifacts []artifact) (publishedVersion string, _ error) {
1341-
for _, a := range artifacts {
1337+
// It returns the Go version and files that have been successfully published.
1338+
func (tasks *BuildReleaseTasks) publishArtifacts(ctx *wf.TaskContext, version string, artifacts []artifact) (task.Published, error) {
1339+
// Each release artifact corresponds to a single website file.
1340+
var files = make([]task.WebsiteFile, len(artifacts))
1341+
for i, a := range artifacts {
1342+
// Define website file metadata.
13421343
f := task.WebsiteFile{
13431344
Filename: a.Filename,
13441345
Version: version,
@@ -1360,12 +1361,16 @@ func (tasks *BuildReleaseTasks) publishArtifacts(ctx *wf.TaskContext, version st
13601361
case "msi", "pkg":
13611362
f.Kind = "installer"
13621363
}
1364+
1365+
// Publish it.
13631366
if err := tasks.PublishFile(f); err != nil {
1364-
return "", err
1367+
return task.Published{}, err
13651368
}
1369+
ctx.Printf("Published %q.", f.Filename)
1370+
files[i] = f
13661371
}
1367-
ctx.Printf("Published %v artifacts for %v", len(artifacts), version)
1368-
return version, nil
1372+
ctx.Printf("Published all %d files for %s.", len(files), version)
1373+
return task.Published{Version: version, Files: files}, nil
13691374
}
13701375

13711376
// cutPrefix returns s without the provided leading prefix string

internal/task/announce.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -131,13 +131,14 @@ type SentMail struct {
131131
Subject string // Subject of the email. Expected to be unique so it can be used to identify the email.
132132
}
133133

134-
// AnnounceRelease sends an email announcing a Go release to Google Groups.
135-
func (t AnnounceMailTasks) AnnounceRelease(ctx *workflow.TaskContext, kind ReleaseKind, versions []string, security []string, users []string) (SentMail, error) {
134+
// AnnounceRelease sends an email to Google Groups
135+
// announcing that a Go release has been published.
136+
func (t AnnounceMailTasks) AnnounceRelease(ctx *workflow.TaskContext, kind ReleaseKind, published []Published, security []string, users []string) (SentMail, error) {
136137
if deadline, ok := ctx.Deadline(); ok && time.Until(deadline) < time.Minute {
137138
return SentMail{}, fmt.Errorf("insufficient time for announce release task; a minimum of a minute left on context is required")
138139
}
139-
if err := oneOrTwoGoVersions(versions); err != nil {
140-
return SentMail{}, err
140+
if len(published) < 1 || len(published) > 2 {
141+
return SentMail{}, fmt.Errorf("got %d published Go releases, AnnounceRelease supports only 1 or 2 at once", len(published))
141142
}
142143
names, err := coordinatorFirstNames(users)
143144
if err != nil {
@@ -146,12 +147,12 @@ func (t AnnounceMailTasks) AnnounceRelease(ctx *workflow.TaskContext, kind Relea
146147

147148
r := releaseAnnouncement{
148149
Kind: kind,
149-
Version: versions[0],
150+
Version: published[0].Version,
150151
Security: security,
151152
Names: names,
152153
}
153-
if len(versions) == 2 {
154-
r.SecondaryVersion = versions[1]
154+
if len(published) == 2 {
155+
r.SecondaryVersion = published[1].Version
155156
}
156157

157158
// Generate the announcement email.
@@ -198,8 +199,8 @@ func (t AnnounceMailTasks) PreAnnounceRelease(ctx *workflow.TaskContext, version
198199
if deadline, ok := ctx.Deadline(); ok && time.Until(deadline) < time.Minute {
199200
return SentMail{}, fmt.Errorf("insufficient time for pre-announce release task; a minimum of a minute left on context is required")
200201
}
201-
if err := oneOrTwoGoVersions(versions); err != nil {
202-
return SentMail{}, err
202+
if len(versions) < 1 || len(versions) > 2 {
203+
return SentMail{}, fmt.Errorf("got %d planned Go releases, PreAnnounceRelease supports only 1 or 2 at once", len(versions))
203204
}
204205
now := time.Now().UTC()
205206
if t.testHookNow != nil {

internal/task/announce_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import (
2424
func TestAnnounceReleaseShortContext(t *testing.T) {
2525
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
2626
defer cancel()
27-
_, err := (AnnounceMailTasks{}).AnnounceRelease(&workflow.TaskContext{Context: ctx}, KindCurrentMinor, []string{"go1.18.1", "go1.17.8"}, nil, nil)
27+
_, err := (AnnounceMailTasks{}).AnnounceRelease(&workflow.TaskContext{Context: ctx}, KindCurrentMinor, []Published{{Version: "go1.18.1"}, {Version: "go1.17.8"}}, nil, nil)
2828
if err == nil {
2929
t.Errorf("want non-nil error")
3030
} else if !strings.HasPrefix(err.Error(), "insufficient time") {
@@ -195,7 +195,7 @@ func TestAnnounceRelease(t *testing.T) {
195195
tests := [...]struct {
196196
name string
197197
kind ReleaseKind
198-
versions []string
198+
published []Published
199199
security []string
200200
coordinators []string
201201
want SentMail
@@ -204,7 +204,7 @@ func TestAnnounceRelease(t *testing.T) {
204204
{
205205
name: "minor",
206206
kind: KindCurrentMinor,
207-
versions: []string{"go1.18.1", "go1.17.8"}, // Intentionally not 1.17.9 so the real email doesn't get in the way.
207+
published: []Published{{Version: "go1.18.1"}, {Version: "go1.17.8"}}, // Intentionally not 1.17.9 so the real email doesn't get in the way.
208208
coordinators: []string{"heschi", "dmitshur"},
209209
want: SentMail{Subject: "Go 1.18.1 and Go 1.17.8 are released"},
210210
wantLog: `announcement subject: Go 1.18.1 and Go 1.17.8 are released
@@ -264,7 +264,7 @@ Heschi and Dmitri for the Go team` + "\n",
264264
}
265265
var buf bytes.Buffer
266266
ctx := &workflow.TaskContext{Context: context.Background(), Logger: fmtWriter{&buf}}
267-
sentMail, err := tasks.AnnounceRelease(ctx, tc.kind, tc.versions, tc.security, tc.coordinators)
267+
sentMail, err := tasks.AnnounceRelease(ctx, tc.kind, tc.published, tc.security, tc.coordinators)
268268
if err != nil {
269269
if fe := (fetchError{}); errors.As(err, &fe) && fe.PossiblyRetryable {
270270
t.Skip("test run produced no actionable signal due to a transient network error:", err) // See go.dev/issue/60541.

internal/task/dlcl.go

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -63,24 +63,6 @@ func (t *VersionTasks) MailDLCL(ctx *workflow.TaskContext, major int, kind Relea
6363
return t.Gerrit.CreateAutoSubmitChange(ctx, changeInput, reviewers, files)
6464
}
6565

66-
// oneOrTwoGoVersions returns true iff len(versions) is exactly 1 or 2
67-
// and each version passes some lightweight checks that catch problems.
68-
func oneOrTwoGoVersions(versions []string) error {
69-
if len(versions) < 1 || len(versions) > 2 {
70-
return fmt.Errorf("got %d Go versions, want 1 or 2", len(versions))
71-
}
72-
for _, ver := range versions {
73-
if ver != strings.ToLower(ver) {
74-
return fmt.Errorf("version %q is not lowercase", ver)
75-
} else if strings.Contains(ver, " ") {
76-
return fmt.Errorf("version %q contains a space", ver)
77-
} else if !strings.HasPrefix(ver, "go") {
78-
return fmt.Errorf("version %q doesn't have the 'go' prefix", ver)
79-
}
80-
}
81-
return nil
82-
}
83-
8466
func docLink(major int, kind ReleaseKind, ver string) string {
8567
if kind == KindCurrentMinor || kind == KindPrevMinor {
8668
return fmt.Sprintf("https://go.dev/doc/devel/release#%v", ver)

internal/task/task.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,18 @@ import (
1414
wf "golang.org/x/build/internal/workflow"
1515
)
1616

17+
// Published holds information for a Go release
18+
// that by this time has already been published.
19+
//
20+
// Published in this context refers to it being
21+
// available for download at https://go.dev/dl/.
22+
// It doesn't mean it has been announced by now;
23+
// that step happens sometime after publication.
24+
type Published struct {
25+
Version string // Version that's published, in the same format as Go tags. For example, "go1.21rc1".
26+
Files []WebsiteFile // Files that are published.
27+
}
28+
1729
// CommunicationTasks combines communication tasks together.
1830
type CommunicationTasks struct {
1931
AnnounceMailTasks

internal/task/tweet.go

Lines changed: 26 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -84,20 +84,20 @@ type TweetTasks struct {
8484
RandomSeed int64
8585
}
8686

87-
// TweetRelease posts a tweet announcing a Go release.
87+
// TweetRelease posts a tweet announcing that a Go release has been published.
8888
// ErrTweetTooLong is returned if the inputs result in a tweet that's too long.
89-
func (t TweetTasks) TweetRelease(ctx *workflow.TaskContext, versions []string, security string, announcement string) (tweetURL string, _ error) {
90-
if err := oneOrTwoGoVersions(versions); err != nil {
91-
return "", err
89+
func (t TweetTasks) TweetRelease(ctx *workflow.TaskContext, published []Published, security string, announcement string) (tweetURL string, _ error) {
90+
if len(published) < 1 || len(published) > 2 {
91+
return "", fmt.Errorf("got %d published Go releases, TweetRelease supports only 1 or 2 at once", len(published))
9292
}
9393

9494
r := releaseTweet{
95-
Version: versions[0],
95+
Version: published[0].Version,
9696
Security: security,
9797
Announcement: announcement,
9898
}
99-
if len(versions) == 2 {
100-
r.SecondaryVersion = versions[1]
99+
if len(published) == 2 {
100+
r.SecondaryVersion = published[1].Version
101101
}
102102

103103
seed := t.RandomSeed
@@ -114,7 +114,7 @@ func (t TweetTasks) TweetRelease(ctx *workflow.TaskContext, versions []string, s
114114
ctx.Printf("tweet text:\n%s\n", tweetText)
115115

116116
// Generate tweet image.
117-
imagePNG, imageText, err := tweetImage(r.Version, rnd)
117+
imagePNG, imageText, err := tweetImage(published[0], rnd)
118118
if err != nil {
119119
return "", err
120120
}
@@ -320,19 +320,18 @@ var emoji = map[string][]string{
320320
}
321321

322322
// tweetImage generates an image to use in the announcement
323-
// tweet for goVersion. It returns the image encoded as PNG,
323+
// tweet for published. It returns the image encoded as PNG,
324324
// and the text displayed in the image.
325325
//
326-
// tweetImage makes an HTTP GET request to the go.dev/dl/?mode=json
327-
// read-only API to select a random release archive to highlight.
328-
func tweetImage(goVersion string, rnd *rand.Rand) (imagePNG []byte, imageText string, _ error) {
329-
a, err := fetchRandomArchive(goVersion, rnd)
326+
// tweetImage selects a random release archive to highlight.
327+
func tweetImage(published Published, rnd *rand.Rand) (imagePNG []byte, imageText string, _ error) {
328+
a, err := pickRandomArchive(published, rnd)
330329
if err != nil {
331330
return nil, "", err
332331
}
333332
var buf bytes.Buffer
334333
if err := goCmdTmpl.Execute(&buf, map[string]string{
335-
"GoVer": goVersion,
334+
"GoVer": published.Version,
336335
"GOOS": a.OS,
337336
"GOARCH": a.GOARCH(),
338337
"Filename": a.Filename,
@@ -375,57 +374,22 @@ func digits(i int64) int {
375374
return n
376375
}
377376

378-
// fetchRandomArchive downloads all release archives for Go version goVer,
379-
// and selects a random archive to showcase in the image that displays
380-
// sample output from the 'go install golang.org/dl/...@latest' command.
381-
func fetchRandomArchive(goVer string, rnd *rand.Rand) (archive WebsiteFile, _ error) {
382-
archives, err := fetchReleaseArchives(goVer)
383-
if err != nil {
384-
return WebsiteFile{}, err
385-
}
386-
return archives[rnd.Intn(len(archives))], nil
387-
}
388-
389-
func fetchReleaseArchives(goVer string) (archives []WebsiteFile, _ error) {
390-
url := "https://go.dev/dl/?mode=json"
391-
if strings.Contains(goVer, "beta") || strings.Contains(goVer, "rc") ||
392-
goVer == "go1.17" || goVer == "go1.17.1" || goVer == "go1.11.1" /* For TestTweetRelease. */ {
393-
394-
url += "&include=all"
395-
}
396-
resp, err := http.Get(url)
397-
if err != nil {
398-
return nil, err
399-
}
400-
defer resp.Body.Close()
401-
if resp.StatusCode != http.StatusOK {
402-
return nil, fmt.Errorf("non-200 OK status code: %v", resp.Status)
403-
} else if ct := resp.Header.Get("Content-Type"); ct != "application/json" {
404-
return nil, fmt.Errorf("got Content-Type %q, want %q", ct, "application/json")
405-
}
406-
var releases []WebsiteRelease
407-
err = json.NewDecoder(resp.Body).Decode(&releases)
408-
if err != nil {
409-
return nil, err
410-
}
411-
for _, r := range releases {
412-
if r.Version != goVer {
377+
// pickRandomArchive picks one random release archive
378+
// to showcase in an image showing sample output from
379+
// 'go install golang.org/dl/...@latest'.
380+
func pickRandomArchive(published Published, rnd *rand.Rand) (archive WebsiteFile, _ error) {
381+
var archives []WebsiteFile
382+
for _, f := range published.Files {
383+
if f.Kind != "archive" {
384+
// Not an archive type of file, skip. The golang.org/dl commands use archives only.
413385
continue
414386
}
415-
var archives []WebsiteFile
416-
for _, f := range r.Files {
417-
if f.Kind != "archive" {
418-
continue
419-
}
420-
archives = append(archives, f)
421-
}
422-
if len(archives) == 0 {
423-
return nil, fmt.Errorf("release version %q has 0 archive files", goVer)
424-
}
425-
// Return archives.
426-
return archives, nil
387+
archives = append(archives, f)
427388
}
428-
return nil, fmt.Errorf("release version %q not found", goVer)
389+
if len(archives) == 0 {
390+
return WebsiteFile{}, fmt.Errorf("release version %q has 0 archive files", published.Version)
391+
}
392+
return archives[rnd.Intn(len(archives))], nil
429393
}
430394

431395
// drawTerminal draws an image of a terminal window

0 commit comments

Comments
 (0)