@@ -43,10 +43,10 @@ func (r *PrereleaseGoplsTasks) NewDefinition() *wf.Definition {
43
43
branchCreated := wf .Action1 (wd , "creating new branch if minor release" , r .createBranchIfMinor , semv , wf .After (issue ))
44
44
45
45
configChangeID := wf .Task3 (wd , "updating branch's codereview.cfg" , r .updateCodeReviewConfig , semv , reviewers , issue , wf .After (branchCreated ))
46
- configCommit := wf .Task1 (wd , "await config CL submission" , r . AwaitSubmission , configChangeID )
46
+ configCommit := wf .Task1 (wd , "await config CL submission" , clAwaiter { r . Gerrit }. awaitSubmission , configChangeID )
47
47
48
48
dependencyChangeID := wf .Task4 (wd , "updating gopls' x/tools dependency" , r .updateXToolsDependency , semv , prerelease , reviewers , issue , wf .After (configCommit ))
49
- dependencyCommit := wf .Task1 (wd , "await gopls' x/tools dependency CL submission" , r . AwaitSubmission , dependencyChangeID )
49
+ dependencyCommit := wf .Task1 (wd , "await gopls' x/tools dependency CL submission" , clAwaiter { r . Gerrit }. awaitSubmission , dependencyChangeID )
50
50
51
51
verified := wf .Action1 (wd , "verify installing latest gopls using release branch dependency commit" , r .verifyGoplsInstallation , dependencyCommit )
52
52
prereleaseVersion := wf .Task3 (wd , "tag pre-release" , r .tagPrerelease , semv , dependencyCommit , prerelease , wf .After (verified ))
@@ -154,10 +154,10 @@ func (r *PrereleaseGoplsTasks) createBranchIfMinor(ctx *wf.TaskContext, semv sem
154
154
//
155
155
// It returns an empty string if no such CL is found, otherwise it returns the
156
156
// CL's change ID.
157
- func ( r * PrereleaseGoplsTasks ) openCL (ctx * wf.TaskContext , branch , title string ) (string , error ) {
157
+ func openCL (ctx * wf.TaskContext , gerrit GerritClient , branch , title string ) (string , error ) {
158
158
// Query for an existing pending config CL, to avoid duplication.
159
159
query := fmt .
Sprintf (
`message:%q status:open owner:[email protected] repo:tools branch:%q -age:7d` ,
title ,
branch )
160
- changes , err := r . Gerrit .QueryChanges (ctx , query )
160
+ changes , err := gerrit .QueryChanges (ctx , query )
161
161
if err != nil {
162
162
return "" , err
163
163
}
@@ -178,7 +178,7 @@ func (r *PrereleaseGoplsTasks) updateCodeReviewConfig(ctx *wf.TaskContext, semv
178
178
branch := goplsReleaseBranchName (semv )
179
179
clTitle := fmt .Sprintf ("all: update %s for %s" , configFile , branch )
180
180
181
- openCL , err := r . openCL (ctx , branch , clTitle )
181
+ openCL , err := openCL (ctx , r . Gerrit , branch , clTitle )
182
182
if err != nil {
183
183
return "" , fmt .Errorf ("failed to find the open CL of title %q in branch %q: %w" , clTitle , branch , err )
184
184
}
@@ -275,7 +275,7 @@ func (r *PrereleaseGoplsTasks) updateXToolsDependency(ctx *wf.TaskContext, semv
275
275
276
276
branch := goplsReleaseBranchName (semv )
277
277
clTitle := fmt .Sprintf ("gopls: update go.mod for v%v.%v.%v-%s" , semv .Major , semv .Minor , semv .Patch , pre )
278
- openCL , err := r . openCL (ctx , branch , clTitle )
278
+ openCL , err := openCL (ctx , r . Gerrit , branch , clTitle )
279
279
if err != nil {
280
280
return "" , fmt .Errorf ("failed to find the open CL of title %q in branch %q: %w" , clTitle , branch , err )
281
281
}
@@ -284,38 +284,23 @@ func (r *PrereleaseGoplsTasks) updateXToolsDependency(ctx *wf.TaskContext, semv
284
284
return openCL , nil
285
285
}
286
286
287
- outputFiles := []string {"gopls/go.mod.before" , "gopls/go.mod" , "gopls/go.sum.before" , "gopls/go.sum" }
288
- const scriptFmt = `git checkout %s
289
- git rev-parse --abbrev-ref HEAD
290
- cp gopls/go.mod gopls/go.mod.before
291
- cp gopls/go.sum gopls/go.sum.before
292
- cd gopls
293
- go mod edit -dropreplace=golang.org/x/tools
294
- go get golang.org/x/tools@%s
295
- go mod tidy -compat=1.19
296
- `
297
287
head , err := r .Gerrit .ReadBranchHead (ctx , "tools" , branch )
298
288
if err != nil {
299
289
return "" , err
300
290
}
301
- build , err := r .CloudBuild .RunScript (ctx , fmt .Sprintf (scriptFmt , branch , head ), "tools" , outputFiles )
302
- if err != nil {
303
- return "" , err
304
- }
291
+ // TODO(hxjiang): Remove -compat flag once gopls no longer supports building
292
+ // with older Go versions.
293
+ script := fmt .Sprintf (`cd gopls
294
+ go mod edit -dropreplace=golang.org/x/tools
295
+ go get golang.org/x/tools@%s
296
+ go mod tidy -compat=1.19
297
+ ` , head )
305
298
306
- outputs , err := buildToOutputs (ctx , r .CloudBuild , build )
299
+ changedFiles , err := executeAndMonitorChange (ctx , r .CloudBuild , branch , script , [] string { "gopls/go.mod" , "gopls/go.sum" } )
307
300
if err != nil {
308
301
return "" , err
309
302
}
310
303
311
- changedFiles := map [string ]string {}
312
- for i := 0 ; i < len (outputFiles ); i += 2 {
313
- before , after := outputs [outputFiles [i ]], outputs [outputFiles [i + 1 ]]
314
- if before != after {
315
- changedFiles [outputFiles [i + 1 ]] = after
316
- }
317
- }
318
-
319
304
// Skip CL creation as nothing changed.
320
305
if len (changedFiles ) == 0 {
321
306
return "" , nil
@@ -324,7 +309,7 @@ go mod tidy -compat=1.19
324
309
changeInput := gerrit.ChangeInput {
325
310
Project : "tools" ,
326
311
Branch : branch ,
327
- Subject : fmt .Sprintf ("%s\n \n This is an automated CL which updates the go.mod go.sum.\n \n For golang/go#%v" , clTitle , issue ),
312
+ Subject : fmt .Sprintf ("%s\n \n This is an automated CL which updates the go.mod and go.sum.\n \n For golang/go#%v" , clTitle , issue ),
328
313
}
329
314
330
315
ctx .Printf ("creating auto-submit change under branch %q in x/tools repo." , branch )
@@ -391,21 +376,6 @@ func (r *PrereleaseGoplsTasks) tagPrerelease(ctx *wf.TaskContext, semv semversio
391
376
return version , nil
392
377
}
393
378
394
- // AwaitSubmission waits for the CL with the given change ID to be submitted.
395
- //
396
- // The return value is the submitted commit hash, or "" if changeID is "".
397
- func (r * PrereleaseGoplsTasks ) AwaitSubmission (ctx * wf.TaskContext , changeID string ) (string , error ) {
398
- if changeID == "" {
399
- ctx .Printf ("not awaiting: no CL was created" )
400
- return "" , nil
401
- }
402
-
403
- ctx .Printf ("awaiting review/submit of %v" , ChangeLink (changeID ))
404
- return AwaitCondition (ctx , 10 * time .Second , func () (string , bool , error ) {
405
- return r .Gerrit .Submitted (ctx , changeID , "" )
406
- })
407
- }
408
-
409
379
func (r * PrereleaseGoplsTasks ) isValidVersion (ctx * wf.TaskContext , ver string ) (semversion , error ) {
410
380
if ! semver .IsValid (ver ) {
411
381
return semversion {}, fmt .Errorf ("the input %q version does not follow semantic version schema" , ver )
@@ -529,17 +499,22 @@ func (r *PrereleaseGoplsTasks) possibleGoplsVersions(ctx *wf.TaskContext) ([]str
529
499
// ReleaseGoplsTasks implements a new workflow definition include all the tasks
530
500
// to release gopls.
531
501
type ReleaseGoplsTasks struct {
532
- Gerrit GerritClient
502
+ Gerrit GerritClient
503
+ CloudBuild CloudBuildClient
533
504
}
534
505
535
506
// NewDefinition create a new workflow definition for gopls release.
536
507
func (r * ReleaseGoplsTasks ) NewDefinition () * wf.Definition {
537
508
wd := wf .New (wf.ACL {Groups : []string {groups .ToolsTeam }})
538
509
539
510
version := wf .Param (wd , wf.ParamDef [string ]{Name : "pre-release version" })
511
+ reviewers := wf .Param (wd , reviewersParam )
540
512
541
513
semv := wf .Task1 (wd , "validating input version" , r .isValidPrereleaseVersion , version )
542
- _ = wf .Action1 (wd , "tag the release" , r .tagRelease , semv )
514
+ tagged := wf .Action1 (wd , "tag the release" , r .tagRelease , semv )
515
+
516
+ changeID := wf .Task2 (wd , "updating x/tools dependency in master branch in gopls sub dir" , r .updateDependencyIfMinor , reviewers , semv , wf .After (tagged ))
517
+ _ = wf .Task1 (wd , "await x/tools gopls dependency CL submission in gopls sub dir" , clAwaiter {r .Gerrit }.awaitSubmission , changeID )
543
518
544
519
return wd
545
520
}
@@ -637,3 +612,120 @@ func (r *ReleaseGoplsTasks) tagRelease(ctx *wf.TaskContext, semv semversion) err
637
612
ctx .Printf ("tagged commit %s with tag %s" , info .Revision , releaseTag )
638
613
return nil
639
614
}
615
+
616
+ // updateDependencyIfMinor update the dependency of x/tools repo in master
617
+ // branch.
618
+ //
619
+ // Returns the change ID.
620
+ func (r * ReleaseGoplsTasks ) updateDependencyIfMinor (ctx * wf.TaskContext , reviewers []string , semv semversion ) (string , error ) {
621
+ if semv .Patch != 0 {
622
+ return "" , nil
623
+ }
624
+
625
+ clTitle := fmt .Sprintf ("gopls/go.mod: update dependencies following the v%v.%v.%v release" , semv .Major , semv .Minor , semv .Patch )
626
+ openCL , err := openCL (ctx , r .Gerrit , "master" , clTitle )
627
+ if err != nil {
628
+ return "" , fmt .Errorf ("failed to find the open CL of title %q in master branch: %w" , clTitle , err )
629
+ }
630
+ if openCL != "" {
631
+ ctx .Printf ("not creating CL: found existing CL %s" , openCL )
632
+ return openCL , nil
633
+ }
634
+
635
+ // TODO(hxjiang): Remove -compat flag once gopls no longer supports building
636
+ // with older Go versions.
637
+ const script = `cd gopls
638
+ pwd
639
+ go get -u all
640
+ go mod tidy -compat=1.19
641
+ `
642
+ changed , err := executeAndMonitorChange (ctx , r .CloudBuild , "master" , script , []string {"gopls/go.mod" , "gopls/go.sum" })
643
+ if err != nil {
644
+ return "" , err
645
+ }
646
+
647
+ // Skip CL creation as nothing changed.
648
+ if len (changed ) == 0 {
649
+ return "" , nil
650
+ }
651
+
652
+ changeInput := gerrit.ChangeInput {
653
+ Project : "tools" ,
654
+ Branch : "master" ,
655
+ Subject : fmt .Sprintf ("%s\n \n This is an automated CL which updates the go.mod and go.sum." , clTitle ),
656
+ }
657
+
658
+ ctx .Printf ("creating auto-submit change under master branch in x/tools repo." )
659
+ return r .Gerrit .CreateAutoSubmitChange (ctx , changeInput , reviewers , changed )
660
+ }
661
+
662
+ // executeAndMonitorChange runs the specified script on the designated branch,
663
+ // tracking changes to the provided files.
664
+ //
665
+ // Returns a map where keys are the filenames of modified files and values are
666
+ // their corresponding content after script execution.
667
+ func executeAndMonitorChange (ctx * wf.TaskContext , cloudBuild CloudBuildClient , branch , script string , watchFiles []string ) (map [string ]string , error ) {
668
+ // Checkout to the provided branch.
669
+ fullScript := fmt .Sprintf (`git checkout %s
670
+ git rev-parse --abbrev-ref HEAD
671
+ git rev-parse --ref HEAD
672
+ ` , branch )
673
+ // Make a copy of all file that need to watch.
674
+ // If the file does not exist, create a empty file and a empty before file.
675
+ for _ , file := range watchFiles {
676
+ if strings .Contains (file , "'" ) {
677
+ return nil , fmt .Errorf ("file name %q contains '" , file )
678
+ }
679
+ fullScript += fmt .Sprintf (`if [ -f '%[1]s' ]; then
680
+ cp '%[1]s' '%[1]s.before'
681
+ else
682
+ touch '%[1]s' '%[1]s.before'
683
+ fi
684
+ ` , file )
685
+ }
686
+ // Execute the script provided.
687
+ fullScript += script
688
+
689
+ // Output files before the script execution and after the script execution.
690
+ outputFiles := []string {}
691
+ for _ , file := range watchFiles {
692
+ outputFiles = append (outputFiles , file + ".before" )
693
+ outputFiles = append (outputFiles , file )
694
+ }
695
+ build , err := cloudBuild .RunScript (ctx , fullScript , "tools" , outputFiles )
696
+ if err != nil {
697
+ return nil , err
698
+ }
699
+
700
+ outputs , err := buildToOutputs (ctx , cloudBuild , build )
701
+ if err != nil {
702
+ return nil , err
703
+ }
704
+
705
+ changed := map [string ]string {}
706
+ for i := 0 ; i < len (outputFiles ); i += 2 {
707
+ if before , after := outputs [outputFiles [i ]], outputs [outputFiles [i + 1 ]]; before != after {
708
+ changed [outputFiles [i + 1 ]] = after
709
+ }
710
+ }
711
+
712
+ return changed , nil
713
+ }
714
+
715
+ // A clAwaiter closes over a GerritClient to provide a reusable workflow task
716
+ // for awaiting the submission of a Gerrit change.
717
+ type clAwaiter struct {
718
+ GerritClient
719
+ }
720
+
721
+ func (c clAwaiter ) awaitSubmission (ctx * wf.TaskContext , changeID string ) (string , error ) {
722
+ if changeID == "" {
723
+ ctx .Printf ("not awaiting: no CL was created" )
724
+ return "" , nil
725
+ }
726
+
727
+ ctx .Printf ("awaiting review/submit of %v" , ChangeLink (changeID ))
728
+ return AwaitCondition (ctx , 10 * time .Second , func () (string , bool , error ) {
729
+ return c .Submitted (ctx , changeID , "" )
730
+ })
731
+ }
0 commit comments