Skip to content

Commit

Permalink
internal/task: create the branch for the first rc in minor release
Browse files Browse the repository at this point in the history
A local relui screenshot is at golang/vscode-go#3500 (comment)

For golang/vscode-go#3500

Change-Id: Ie6b5650eef8f84d1fe7264e35894f80043cad109
Reviewed-on: https://go-review.googlesource.com/c/build/+/608817
Reviewed-by: Dmitri Shuralyov <[email protected]>
Reviewed-by: Dmitri Shuralyov <[email protected]>
Auto-Submit: Hongxiang Jiang <[email protected]>
LUCI-TryBot-Result: Go LUCI <[email protected]>
  • Loading branch information
h9jiang authored and gopherbot committed Aug 29, 2024
1 parent e049c5c commit a15ffe2
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 1 deletion.
7 changes: 6 additions & 1 deletion internal/task/fakes.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,14 @@ func (g *FakeGerrit) ReadBranchHead(ctx context.Context, project, branch string)
if err != nil {
return "", err
}
// TODO: If the branch doesn't exist, return an error matching gerrit.ErrResourceNotExist.
out, err := repo.dir.RunCommand(ctx, "rev-parse", "refs/heads/"+branch)
if err != nil {
// TODO(hxjiang): switch to git show-ref --exists refs/heads/branch after
// upgrade git to 2.43.0.
// https://git-scm.com/docs/git-show-ref/2.43.0#Documentation/git-show-ref.txt---exists
if strings.Contains(err.Error(), "unknown revision or path not in the working tree") {
return "", gerrit.ErrResourceNotExist
}
// Returns empty string if the error is nil to align the same behavior with
// the real Gerrit client.
return "", err
Expand Down
2 changes: 2 additions & 0 deletions internal/task/releasegopls.go
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,8 @@ func parseSemver(v string) (_ semversion, ok bool) {
return parsed, ok
}

// prereleaseVersion extracts the integer component from a pre-release version
// string in the format "${STRING}.${INT}".
func (s *semversion) prereleaseVersion() (int, error) {
parts := strings.Split(s.Pre, ".")
if len(parts) == 1 {
Expand Down
49 changes: 49 additions & 0 deletions internal/task/releasevscodego.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ package task

import (
_ "embed"
"errors"
"fmt"
"strconv"
"strings"

"github.com/google/go-github/v48/github"
"golang.org/x/build/gerrit"
"golang.org/x/build/internal/relui/groups"
"golang.org/x/build/internal/workflow"
wf "golang.org/x/build/internal/workflow"
Expand Down Expand Up @@ -130,6 +132,7 @@ func (r *ReleaseVSCodeGoTasks) NewPrereleaseDefinition() *wf.Definition {
approved := wf.Action1(wd, "await release coordinator's approval", r.approveVersion, semv)

_ = wf.Task1(wd, "create release milestone and issue", r.createReleaseMilestoneAndIssue, semv, wf.After(approved))
_ = wf.Action1(wd, "create release branch", r.createReleaseBranch, semv, wf.After(approved))

return wd
}
Expand Down Expand Up @@ -173,6 +176,52 @@ func (r *ReleaseVSCodeGoTasks) createReleaseMilestoneAndIssue(ctx *wf.TaskContex
return *issue.Number, nil
}

// createReleaseBranch creates corresponding release branch only for the initial
// release candidate of a minor version.
func (r *ReleaseVSCodeGoTasks) createReleaseBranch(ctx *wf.TaskContext, semv semversion) error {
branch := fmt.Sprintf("release-v%v.%v", semv.Major, semv.Minor)
releaseHead, err := r.Gerrit.ReadBranchHead(ctx, "vscode-go", branch)

if err == nil {
ctx.Printf("Found the release branch %q with head pointing to %s\n", branch, releaseHead)
return nil
}

if !errors.Is(err, gerrit.ErrResourceNotExist) {
return fmt.Errorf("failed to read the release branch: %w", err)
}

// Require vscode release branch existence if this is a non-minor release.
if semv.Patch != 0 {
return fmt.Errorf("release branch is required for patch releases: %w", err)
}

rc, err := semv.prereleaseVersion()
if err != nil {
return err
}

// Require vscode release branch existence if this is not the first rc in
// a minor release.
if rc != 1 {
return fmt.Errorf("release branch is required for non-initial release candidates: %w", err)
}

// Create the release branch using the revision from the head of master branch.
head, err := r.Gerrit.ReadBranchHead(ctx, "vscode-go", "master")
if err != nil {
return err
}

ctx.DisableRetries() // Beyond this point we want retries to be done manually, not automatically.
_, err = r.Gerrit.CreateBranch(ctx, "vscode-go", branch, gerrit.BranchInput{Revision: head})
if err != nil {
return err
}
ctx.Printf("Created branch %q at revision %s.\n", branch, head)
return nil
}

// nextPrereleaseVersion determines the next pre-release version for the
// upcoming stable release of vscode-go by examining all existing tags in the
// repository.
Expand Down
78 changes: 78 additions & 0 deletions internal/task/releasevscodego_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package task

import (
"context"
"fmt"
"testing"

"github.com/google/go-github/v48/github"
Expand Down Expand Up @@ -79,6 +80,83 @@ func TestCreateReleaseMilestoneAndIssue(t *testing.T) {
}
}

func TestCreateReleaseBranch(t *testing.T) {
ctx := context.Background()
testcases := []struct {
name string
version string
existingBranch bool
wantErr bool
}{
{
name: "nil if the release branch does not exist for first rc in a minor release",
version: "v0.44.0-rc.1",
existingBranch: false,
wantErr: false,
},
{
name: "nil if the release branch already exist for non-initial rc in a minor release",
version: "v0.44.0-rc.4",
existingBranch: true,
wantErr: false,
},
{
name: "fail if the release branch does not exist for non-initial rc in a minor release",
version: "v0.44.0-rc.4",
existingBranch: false,
wantErr: true,
},
{
name: "nil if the release branch already exist for a patch version",
version: "v0.44.3-rc.3",
existingBranch: true,
wantErr: false,
},
{
name: "fail if the release branch does not exist for a patch version",
version: "v0.44.3-rc.3",
existingBranch: false,
wantErr: true,
},
}

for _, tc := range testcases {
t.Run(tc.name, func(t *testing.T) {
semv, ok := parseSemver(tc.version)
if !ok {
t.Fatalf("failed to parse the want version: %q", tc.version)
}

vscodego := NewFakeRepo(t, "vscode-go")
commit := vscodego.Commit(map[string]string{
"go.mod": "module github.com/golang/vscode-go\n",
"go.sum": "\n",
})
if tc.existingBranch {
vscodego.Branch(fmt.Sprintf("release-v%v.%v", semv.Major, semv.Minor), commit)
}

gerrit := NewFakeGerrit(t, vscodego)
tasks := &ReleaseVSCodeGoTasks{
Gerrit: gerrit,
}

err := tasks.createReleaseBranch(&workflow.TaskContext{Context: ctx, Logger: &testLogger{t, ""}}, semv)
if tc.wantErr && err == nil {
t.Errorf("createReleaseBranch(%q) should return error but return nil", tc.version)
} else if !tc.wantErr && err != nil {
t.Errorf("createReleaseBranch(%q) should return nil but return err: %v", tc.version, err)
}

if !tc.wantErr {
if _, err := gerrit.ReadBranchHead(ctx, "vscode-go", fmt.Sprintf("release-v%v.%v", semv.Major, semv.Minor)); err != nil {
t.Errorf("createReleaseBranch(%q) should ensure the release branch creation: %v", tc.version, err)
}
}
})
}
}

func TestNextPrereleaseVersion(t *testing.T) {
tests := []struct {
name string
Expand Down

0 comments on commit a15ffe2

Please sign in to comment.