Skip to content

Commit e049c5c

Browse files
h9jianggopherbot
authored andcommitted
internal/task: add step to update gopls version in vscode-go project
1. Add new method ListBranches in gerrit client interface returns all the branches in a project. a. implement the real gerrit client follow rest api doc. b. implement the fake gerrit client using git for-each-ref. 2. Add util function returns the current active release branch from vscode-go. 3. Add step at the end of gopls pre-release process to update gopls version info in vscode-go project. A local relui screenshot is at golang/vscode-go#3500 (comment) For golang/vscode-go#3500 Change-Id: Ib88b950df729a2f28133809f5a54311c420b447b Reviewed-on: https://go-review.googlesource.com/c/build/+/608416 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> Reviewed-by: Robert Findley <[email protected]> Reviewed-by: Hyang-Ah Hana Kim <[email protected]> Auto-Submit: Hongxiang Jiang <[email protected]>
1 parent d7fe890 commit e049c5c

File tree

7 files changed

+277
-20
lines changed

7 files changed

+277
-20
lines changed

gerrit/gerrit.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,15 @@ func (c *Client) GetProjectBranches(ctx context.Context, name string) (map[strin
867867
return m, nil
868868
}
869869

870+
// ListBranches lists all branches in the project.
871+
//
872+
// See https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#list-branches.
873+
func (c *Client) ListBranches(ctx context.Context, project string) ([]BranchInfo, error) {
874+
var res []BranchInfo
875+
err := c.do(ctx, &res, "GET", fmt.Sprintf("/projects/%s/branches", url.PathEscape(project)))
876+
return res, err
877+
}
878+
870879
// GetBranch gets a particular branch in project.
871880
//
872881
// See https://gerrit-review.googlesource.com/Documentation/rest-api-projects.html#get-branch.

internal/task/fakes.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,23 @@ func (g *FakeGerrit) ReadBranchHead(ctx context.Context, project, branch string)
230230
return strings.TrimSpace(string(out)), nil
231231
}
232232

233+
func (g *FakeGerrit) ListBranches(ctx context.Context, project string) ([]gerrit.BranchInfo, error) {
234+
repo, err := g.repo(project)
235+
if err != nil {
236+
return nil, err
237+
}
238+
out, err := repo.dir.RunCommand(ctx, "for-each-ref", "--format=%(refname) %(objectname:short)", "refs/heads/")
239+
if err != nil {
240+
return nil, err
241+
}
242+
var infos []gerrit.BranchInfo
243+
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
244+
branchCommit := strings.Fields(line)
245+
infos = append(infos, gerrit.BranchInfo{Ref: branchCommit[0], Revision: branchCommit[1]})
246+
}
247+
return infos, nil
248+
}
249+
233250
func (g *FakeGerrit) CreateBranch(ctx context.Context, project, branch string, input gerrit.BranchInput) (string, error) {
234251
repo, err := g.repo(project)
235252
if err != nil {

internal/task/gerrit.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ type GerritClient interface {
3838
// ReadBranchHead returns the head of a branch in project.
3939
// If the branch doesn't exist, it returns an error matching gerrit.ErrResourceNotExist.
4040
ReadBranchHead(ctx context.Context, project, branch string) (string, error)
41+
// ListBranches returns the branch info for all the branch in a project.
42+
ListBranches(ctx context.Context, project string) ([]gerrit.BranchInfo, error)
4143
// CreateBranch create the given branch and returns the created branch's revision.
4244
CreateBranch(ctx context.Context, project, branch string, input gerrit.BranchInput) (string, error)
4345
// ListProjects lists all the projects on the server.
@@ -196,6 +198,10 @@ func (c *RealGerritClient) ReadBranchHead(ctx context.Context, project, branch s
196198
return branchInfo.Revision, nil
197199
}
198200

201+
func (c *RealGerritClient) ListBranches(ctx context.Context, project string) ([]gerrit.BranchInfo, error) {
202+
return c.Client.ListBranches(ctx, project)
203+
}
204+
199205
func (c *RealGerritClient) CreateBranch(ctx context.Context, project, branch string, input gerrit.BranchInput) (string, error) {
200206
branchInfo, err := c.Client.CreateBranch(ctx, project, branch, input)
201207
if err != nil {

internal/task/releasegopls.go

Lines changed: 82 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ func (r *ReleaseGoplsTasks) NewPrereleaseDefinition() *wf.Definition {
6161
prereleaseVerified := wf.Action1(wd, "verify installing latest gopls using release branch pre-release version", r.verifyGoplsInstallation, prereleaseVersion)
6262
wf.Action4(wd, "mail announcement", r.mailAnnouncement, semv, prereleaseVersion, dependencyCommit, issue, wf.After(prereleaseVerified))
6363

64+
vsCodeGoChanges := wf.Task3(wd, "update gopls version in vscode-go project", r.updateGoplsVersionInVSCodeGo, reviewers, issue, prereleaseVersion, wf.After(prereleaseVerified))
65+
_ = wf.Task1(wd, "await gopls version update CL submission in vscode-go project", clAwaiter{r.Gerrit}.awaitSubmissions, vsCodeGoChanges)
66+
6467
wf.Output(wd, "version", prereleaseVersion)
6568

6669
return wd
@@ -221,9 +224,9 @@ func (r *ReleaseGoplsTasks) createBranchIfMinor(ctx *wf.TaskContext, semv semver
221224
//
222225
// It returns an empty string if no such CL is found, otherwise it returns the
223226
// CL's change ID.
224-
func openCL(ctx *wf.TaskContext, gerrit GerritClient, branch, title string) (string, error) {
227+
func openCL(ctx *wf.TaskContext, gerrit GerritClient, repo, branch, title string) (string, error) {
225228
// Query for an existing pending config CL, to avoid duplication.
226-
query := fmt.Sprintf(`message:%q status:open owner:[email protected] repo:tools branch:%q -age:7d`, title, branch)
229+
query := fmt.Sprintf(`message:%q status:open owner:[email protected] repo:%s branch:%q -age:7d`, title, repo, branch)
227230
changes, err := gerrit.QueryChanges(ctx, query)
228231
if err != nil {
229232
return "", err
@@ -245,7 +248,7 @@ func (r *ReleaseGoplsTasks) updateCodeReviewConfig(ctx *wf.TaskContext, semv sem
245248
branch := goplsReleaseBranchName(semv)
246249
clTitle := fmt.Sprintf("all: update %s for %s", configFile, branch)
247250

248-
openCL, err := openCL(ctx, r.Gerrit, branch, clTitle)
251+
openCL, err := openCL(ctx, r.Gerrit, "tools", branch, clTitle)
249252
if err != nil {
250253
return "", fmt.Errorf("failed to find the open CL of title %q in branch %q: %w", clTitle, branch, err)
251254
}
@@ -342,7 +345,7 @@ func (r *ReleaseGoplsTasks) updateXToolsDependency(ctx *wf.TaskContext, semv sem
342345

343346
branch := goplsReleaseBranchName(semv)
344347
clTitle := fmt.Sprintf("gopls: update go.mod for v%v.%v.%v-%s", semv.Major, semv.Minor, semv.Patch, pre)
345-
openCL, err := openCL(ctx, r.Gerrit, branch, clTitle)
348+
openCL, err := openCL(ctx, r.Gerrit, "tools", branch, clTitle)
346349
if err != nil {
347350
return "", fmt.Errorf("failed to find the open CL of title %q in branch %q: %w", clTitle, branch, err)
348351
}
@@ -363,7 +366,7 @@ go get golang.org/x/tools@%s
363366
go mod tidy -compat=1.19
364367
`, head)
365368

366-
changedFiles, err := executeAndMonitorChange(ctx, r.CloudBuild, branch, script, []string{"gopls/go.mod", "gopls/go.sum"})
369+
changedFiles, err := executeAndMonitorChange(ctx, r.CloudBuild, "tools", branch, script, []string{"gopls/go.mod", "gopls/go.sum"})
367370
if err != nil {
368371
return "", err
369372
}
@@ -467,6 +470,53 @@ func (r *ReleaseGoplsTasks) mailAnnouncement(ctx *wf.TaskContext, semv semversio
467470
return r.SendMail(r.AnnounceMailHeader, content)
468471
}
469472

473+
func (r *ReleaseGoplsTasks) updateGoplsVersionInVSCodeGo(ctx *wf.TaskContext, reviewers []string, issue int64, version string) ([]string, error) {
474+
releaseBranch, err := vsCodeGoActiveReleaseBranch(ctx, r.Gerrit)
475+
if err != nil {
476+
return nil, err
477+
}
478+
var changes []string
479+
for _, branch := range []string{"master", releaseBranch} {
480+
clTitle := fmt.Sprintf(`extension/src/goToolsInformation: update gopls version %s`, version)
481+
if branch != "master" {
482+
clTitle = "[" + branch + "] " + clTitle
483+
}
484+
openCL, err := openCL(ctx, r.Gerrit, "vscode-go", branch, clTitle)
485+
if err != nil {
486+
return nil, fmt.Errorf("failed to find the open CL of title %q in branch %q: %w", clTitle, branch, err)
487+
}
488+
if openCL != "" {
489+
ctx.Printf("not creating CL: found existing CL %s", openCL)
490+
changes = append(changes, openCL)
491+
continue
492+
}
493+
const script = `go run -C extension tools/generate.go -tools`
494+
changedFiles, err := executeAndMonitorChange(ctx, r.CloudBuild, "vscode-go", branch, script, []string{"extension/src/goToolsInformation.ts"})
495+
if err != nil {
496+
return nil, err
497+
}
498+
499+
// Skip CL creation as nothing changed.
500+
if len(changedFiles) == 0 {
501+
return nil, nil
502+
}
503+
504+
changeInput := gerrit.ChangeInput{
505+
Project: "vscode-go",
506+
Branch: branch,
507+
Subject: fmt.Sprintf("%s\n\nThis is an automated CL which updates the gopls version.\n\nFor golang/go#%v", clTitle, issue),
508+
}
509+
510+
ctx.Printf("creating auto-submit change under branch %q in vscode-go repo.", branch)
511+
changeID, err := r.Gerrit.CreateAutoSubmitChange(ctx, changeInput, reviewers, changedFiles)
512+
if err != nil {
513+
return nil, err
514+
}
515+
changes = append(changes, changeID)
516+
}
517+
return changes, nil
518+
}
519+
470520
func (r *ReleaseGoplsTasks) isValidReleaseVersion(ctx *wf.TaskContext, ver string) error {
471521
if !semver.IsValid(ver) {
472522
return fmt.Errorf("the input %q version does not follow semantic version schema", ver)
@@ -706,7 +756,7 @@ func (r *ReleaseGoplsTasks) updateDependencyIfMinor(ctx *wf.TaskContext, reviewe
706756
}
707757

708758
clTitle := fmt.Sprintf("gopls/go.mod: update dependencies following the v%v.%v.%v release", semv.Major, semv.Minor, semv.Patch)
709-
openCL, err := openCL(ctx, r.Gerrit, "master", clTitle)
759+
openCL, err := openCL(ctx, r.Gerrit, "tools", "master", clTitle)
710760
if err != nil {
711761
return "", fmt.Errorf("failed to find the open CL of title %q in master branch: %w", clTitle, err)
712762
}
@@ -722,7 +772,7 @@ pwd
722772
go get -u all
723773
go mod tidy -compat=1.19
724774
`
725-
changed, err := executeAndMonitorChange(ctx, r.CloudBuild, "master", script, []string{"gopls/go.mod", "gopls/go.sum"})
775+
changed, err := executeAndMonitorChange(ctx, r.CloudBuild, "tools", "master", script, []string{"gopls/go.mod", "gopls/go.sum"})
726776
if err != nil {
727777
return "", err
728778
}
@@ -747,7 +797,7 @@ go mod tidy -compat=1.19
747797
//
748798
// Returns a map where keys are the filenames of modified files and values are
749799
// their corresponding content after script execution.
750-
func executeAndMonitorChange(ctx *wf.TaskContext, cloudBuild CloudBuildClient, branch, script string, watchFiles []string) (map[string]string, error) {
800+
func executeAndMonitorChange(ctx *wf.TaskContext, cloudBuild CloudBuildClient, project, branch, script string, watchFiles []string) (map[string]string, error) {
751801
// Checkout to the provided branch.
752802
fullScript := fmt.Sprintf(`git checkout %s
753803
git rev-parse --abbrev-ref HEAD
@@ -775,7 +825,7 @@ fi
775825
outputFiles = append(outputFiles, file+".before")
776826
outputFiles = append(outputFiles, file)
777827
}
778-
build, err := cloudBuild.RunScript(ctx, fullScript, "tools", outputFiles)
828+
build, err := cloudBuild.RunScript(ctx, fullScript, project, outputFiles)
779829
if err != nil {
780830
return nil, err
781831
}
@@ -801,6 +851,8 @@ type clAwaiter struct {
801851
GerritClient
802852
}
803853

854+
// awaitSubmission waits for the specified change to be submitted, then returns
855+
// the corresponding commit hash.
804856
func (c clAwaiter) awaitSubmission(ctx *wf.TaskContext, changeID string) (string, error) {
805857
if changeID == "" {
806858
ctx.Printf("not awaiting: no CL was created")
@@ -812,3 +864,24 @@ func (c clAwaiter) awaitSubmission(ctx *wf.TaskContext, changeID string) (string
812864
return c.Submitted(ctx, changeID, "")
813865
})
814866
}
867+
868+
// awaitSubmissions waits for the specified changes to be submitted, then
869+
// returns a slice of commit hashes corresponding to the input change IDs,
870+
// maintaining the original input order.
871+
func (c clAwaiter) awaitSubmissions(ctx *wf.TaskContext, changeIDs []string) ([]string, error) {
872+
if len(changeIDs) == 0 {
873+
ctx.Printf("not awaiting: no CL was created")
874+
return nil, nil
875+
}
876+
877+
var commits []string
878+
for _, changeID := range changeIDs {
879+
commit, err := c.awaitSubmission(ctx, changeID)
880+
if err != nil {
881+
return nil, err
882+
}
883+
commits = append(commits, commit)
884+
}
885+
886+
return commits, nil
887+
}

internal/task/releasegopls_test.go

Lines changed: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,10 @@ parent-branch: master
628628
ctx, cancel := context.WithCancel(context.Background())
629629
defer cancel()
630630

631+
vscodego := NewFakeRepo(t, "vscode-go")
632+
initial := vscodego.Commit(map[string]string{"extension/src/goToolsInformation.ts": "foo"})
633+
vscodego.Branch("release-v0.44", initial)
634+
631635
tools := NewFakeRepo(t, "tools")
632636
beforeHead := tools.Commit(map[string]string{
633637
"gopls/go.mod": "module golang.org/x/tools\n",
@@ -655,7 +659,7 @@ parent-branch: master
655659
tools.Branch(goplsReleaseBranchName(tc.semv), beforeHead)
656660
}
657661

658-
gerrit := NewFakeGerrit(t, tools)
662+
gerrit := NewFakeGerrit(t, tools, vscodego)
659663

660664
// fakeGo handles multiple arguments in gopls pre-release flow.
661665
// - go get will write fake go.sum and go.mod to simulate pining the
@@ -664,6 +668,8 @@ parent-branch: master
664668
// permission to it to simulate gopls installation.
665669
// - go env will return the current dir so gopls will point to the fake
666670
// script that is written by go install.
671+
// - go run will write "bar" content to file in vscode-go project
672+
// containing gopls versions.
667673
// - go mod will exit without any error.
668674
var fakeGo = fmt.Sprintf(`#!/bin/bash -exu
669675
@@ -702,6 +708,10 @@ EOF
702708
"mod")
703709
exit 0
704710
;;
711+
"run")
712+
echo -n "bar" > extension/src/goToolsInformation.ts
713+
exit 0
714+
;;
705715
*)
706716
echo unexpected command $@
707717
exit 1
@@ -744,24 +754,48 @@ esac`, tc.wantVersion)
744754

745755
// Verify the content of following files are expected.
746756
contentChecks := []struct {
747-
path string
748-
want string
757+
repo string
758+
branch string
759+
path string
760+
want string
749761
}{
750762
{
751-
path: "codereview.cfg",
752-
want: tc.wantConfig,
763+
repo: "tools",
764+
branch: goplsReleaseBranchName(tc.semv),
765+
path: "codereview.cfg",
766+
want: tc.wantConfig,
767+
},
768+
{
769+
repo: "tools",
770+
branch: goplsReleaseBranchName(tc.semv),
771+
path: "gopls/go.sum",
772+
want: "test go sum",
753773
},
754774
{
755-
path: "gopls/go.sum",
756-
want: "test go sum",
775+
repo: "tools",
776+
branch: goplsReleaseBranchName(tc.semv),
777+
path: "gopls/go.mod",
778+
want: "test go mod",
757779
},
758780
{
759-
path: "gopls/go.mod",
760-
want: "test go mod",
781+
repo: "vscode-go",
782+
branch: "master",
783+
path: "extension/src/goToolsInformation.ts",
784+
want: "bar",
785+
},
786+
{
787+
repo: "vscode-go",
788+
branch: "release-v0.44",
789+
path: "extension/src/goToolsInformation.ts",
790+
want: "bar",
761791
},
762792
}
763793
for _, check := range contentChecks {
764-
got, err := gerrit.ReadFile(ctx, "tools", afterHead, check.path)
794+
commit, err := gerrit.ReadBranchHead(ctx, check.repo, check.branch)
795+
if err != nil {
796+
t.Fatal(err)
797+
}
798+
got, err := gerrit.ReadFile(ctx, check.repo, commit, check.path)
765799
if err != nil {
766800
t.Fatal(err)
767801
}
@@ -1058,7 +1092,11 @@ echo -n "foo" > file_b
10581092
}
10591093

10601094
cloudBuild := NewFakeCloudBuild(t, NewFakeGerrit(t, tools), "", nil, fakeGo)
1061-
got, err := executeAndMonitorChange(&workflow.TaskContext{Context: context.Background(), Logger: &testLogger{t, ""}}, cloudBuild, tc.branch, tc.script, tc.watch)
1095+
ctx := &workflow.TaskContext{
1096+
Context: context.Background(),
1097+
Logger: &testLogger{t, ""},
1098+
}
1099+
got, err := executeAndMonitorChange(ctx, cloudBuild, "tools", tc.branch, tc.script, tc.watch)
10621100
if err != nil {
10631101
t.Fatal(err)
10641102
}

0 commit comments

Comments
 (0)