-
Notifications
You must be signed in to change notification settings - Fork 155
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
internal/task: add workflow to create vscode-go stable release candidate
1. Add an input selection parameter allowing the coordinator to choose between targeting the next minor or patch version. 2. Add a step to automatically determine the appropriate version number based on the coordinator's selection and prompt for release approval. A local relui screenshot is at golang/vscode-go#3500 (comment) For golang/vscode-go#3500 Change-Id: I38fcd861ff864dc3683fc571e9a39bccf4e9cb63 Reviewed-on: https://go-review.googlesource.com/c/build/+/607176 Reviewed-by: Hyang-Ah Hana Kim <[email protected]> Auto-Submit: Hongxiang Jiang <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
- Loading branch information
Showing
3 changed files
with
249 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
// Copyright 2020 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package task | ||
|
||
import ( | ||
"fmt" | ||
|
||
"golang.org/x/build/internal/relui/groups" | ||
"golang.org/x/build/internal/workflow" | ||
wf "golang.org/x/build/internal/workflow" | ||
) | ||
|
||
// VSCode extensions and semantic versioning have different understandings of | ||
// release and pre-release. | ||
|
||
// From the VSCode extension guidelines: | ||
// - Pre-releases contain the latest changes and use odd-numbered minor versions | ||
// (e.g. v0.45.0). | ||
// - Releases are more stable and use even-numbered minor versions (e.g. | ||
// v0.44.0). | ||
// See: https://code.visualstudio.com/api/working-with-extensions/publishing-extension#prerelease-extensions | ||
|
||
// In semantic versioning: | ||
// - Pre-releases use a hyphen and label (e.g. v0.44.0-rc.1). | ||
// - Releases have no hyphen (e.g. v0.44.0). | ||
// See: https://semver.org/spec/v2.0.0.html | ||
|
||
// To avoid confusion, vscode-go release flow will use terminology below without | ||
// overloading the term "pre-release": | ||
|
||
// - Stable version: VSCode extension's release version (even minor, e.g. v0.44.0) | ||
// - Insider version: VSCode extension's pre-release version (odd minor, e.g. v0.45.0) | ||
// - Release version: Semantic versioning release (no hyphen, e.g. v0.44.0) | ||
// - Pre-release version: Semantic versioning pre-release (with hyphen, e.g. v0.44.0-rc.1) | ||
|
||
// ReleaseVSCodeGoTasks implements a set of vscode-go release workflow definitions. | ||
// | ||
// * pre-release workflow: creates a pre-release version of a stable version. | ||
// * release workflow: creates a release version of a stable version. | ||
// * insider workflow: creates a insider version. There are no pre-releases for | ||
// insider versions. | ||
type ReleaseVSCodeGoTasks struct { | ||
Gerrit GerritClient | ||
ApproveAction func(*wf.TaskContext) error | ||
} | ||
|
||
var nextVersionParam = wf.ParamDef[string]{ | ||
Name: "next version", | ||
ParamType: workflow.ParamType[string]{ | ||
HTMLElement: "select", | ||
HTMLSelectOptions: []string{ | ||
"next minor", | ||
"next patch", | ||
}, | ||
}, | ||
} | ||
|
||
// NewPrereleaseDefinition create a new workflow definition for vscode-go pre-release. | ||
func (r *ReleaseVSCodeGoTasks) NewPrereleaseDefinition() *wf.Definition { | ||
wd := wf.New(wf.ACL{Groups: []string{groups.ToolsTeam}}) | ||
|
||
versionBumpStrategy := wf.Param(wd, nextVersionParam) | ||
|
||
version := wf.Task1(wd, "find the next pre-release version", r.nextPrereleaseVersion, versionBumpStrategy) | ||
_ = wf.Action1(wd, "await release coordinator's approval", r.approveVersion, version) | ||
|
||
return wd | ||
} | ||
|
||
// nextPrereleaseVersion determines the next pre-release version for the | ||
// upcoming stable release of vscode-go by examining all existing tags in the | ||
// repository. | ||
// | ||
// The versionBumpStrategy input indicates whether the pre-release should target | ||
// the next minor or next patch version. | ||
func (r *ReleaseVSCodeGoTasks) nextPrereleaseVersion(ctx *wf.TaskContext, versionBumpStrategy string) (semversion, error) { | ||
tags, err := r.Gerrit.ListTags(ctx, "vscode-go") | ||
if err != nil { | ||
return semversion{}, err | ||
} | ||
|
||
semv := lastReleasedVersion(tags, true) | ||
switch versionBumpStrategy { | ||
case "next minor": | ||
semv.Minor += 2 | ||
semv.Patch = 0 | ||
case "next patch": | ||
semv.Patch += 1 | ||
default: | ||
return semversion{}, fmt.Errorf("unknown version selection strategy: %q", versionBumpStrategy) | ||
} | ||
|
||
// latest to track the latest pre-release for the given semantic version. | ||
latest := 0 | ||
for _, v := range tags { | ||
cur, ok := parseSemver(v) | ||
if !ok { | ||
continue | ||
} | ||
|
||
if cur.Pre == "" { | ||
continue | ||
} | ||
|
||
if cur.Major != semv.Major || cur.Minor != semv.Minor || cur.Patch != semv.Patch { | ||
continue | ||
} | ||
|
||
pre, err := cur.prereleaseVersion() | ||
if err != nil { | ||
continue | ||
} | ||
if pre > latest { | ||
latest = pre | ||
} | ||
} | ||
|
||
semv.Pre = fmt.Sprintf("rc.%v", latest+1) | ||
return semv, err | ||
} | ||
|
||
func lastReleasedVersion(versions []string, onlyStable bool) semversion { | ||
latest := semversion{} | ||
for _, v := range versions { | ||
semv, ok := parseSemver(v) | ||
if !ok { | ||
continue | ||
} | ||
|
||
if semv.Pre != "" { | ||
continue | ||
} | ||
|
||
if semv.Minor%2 == 0 && onlyStable { | ||
if semv.Minor > latest.Minor { | ||
latest = semv | ||
} | ||
|
||
if semv.Minor == latest.Minor && semv.Patch > latest.Patch { | ||
latest = semv | ||
} | ||
} | ||
|
||
if semv.Minor%2 == 1 && !onlyStable { | ||
if semv.Minor > latest.Minor { | ||
latest = semv | ||
} | ||
|
||
if semv.Minor == latest.Minor && semv.Patch > latest.Patch { | ||
latest = semv | ||
} | ||
} | ||
} | ||
|
||
return latest | ||
} | ||
|
||
func (r *ReleaseVSCodeGoTasks) approveVersion(ctx *wf.TaskContext, semv semversion) error { | ||
ctx.Printf("The next release candidate will be v%v.%v.%v-%s", semv.Major, semv.Minor, semv.Patch, semv.Pre) | ||
return r.ApproveAction(ctx) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
// Copyright 2020 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package task | ||
|
||
import ( | ||
"context" | ||
"testing" | ||
|
||
"golang.org/x/build/internal/workflow" | ||
) | ||
|
||
func TestNextPrereleaseVersion(t *testing.T) { | ||
tests := []struct { | ||
name string | ||
existingTags []string | ||
versionRule string | ||
wantVersion string | ||
}{ | ||
{ | ||
name: "v0.44.0 have not released, have no release candidate", | ||
existingTags: []string{"v0.44.0", "v0.43.0", "v0.42.0"}, | ||
versionRule: "next minor", | ||
wantVersion: "v0.46.0-rc.1", | ||
}, | ||
{ | ||
name: "v0.44.0 have not released but already have two release candidate", | ||
existingTags: []string{"v0.44.0-rc.1", "v0.44.0-rc.2", "v0.43.0", "v0.42.0"}, | ||
versionRule: "next minor", | ||
wantVersion: "v0.44.0-rc.3", | ||
}, | ||
{ | ||
name: "v0.44.3 have not released, have no release candidate", | ||
existingTags: []string{"v0.44.2-rc.1", "v0.44.2", "v0.44.1", "v0.44.1-rc.1"}, | ||
versionRule: "next patch", | ||
wantVersion: "v0.44.3-rc.1", | ||
}, | ||
{ | ||
name: "v0.44.3 have not released but already have one release candidate", | ||
existingTags: []string{"v0.44.3-rc.1", "v0.44.2", "v0.44.2-rc.1", "v0.44.1", "v0.44.1-rc.1"}, | ||
versionRule: "next patch", | ||
wantVersion: "v0.44.3-rc.2", | ||
}, | ||
} | ||
|
||
for _, tc := range tests { | ||
t.Run(tc.name, func(t *testing.T) { | ||
vscodego := NewFakeRepo(t, "vscode-go") | ||
commit := vscodego.Commit(map[string]string{ | ||
"go.mod": "module github.com/golang/vscode-go\n", | ||
"go.sum": "\n", | ||
}) | ||
|
||
for _, tag := range tc.existingTags { | ||
vscodego.Tag(tag, commit) | ||
} | ||
|
||
gerrit := NewFakeGerrit(t, vscodego) | ||
|
||
tasks := &ReleaseVSCodeGoTasks{ | ||
Gerrit: gerrit, | ||
} | ||
|
||
got, err := tasks.nextPrereleaseVersion(&workflow.TaskContext{Context: context.Background(), Logger: &testLogger{t, ""}}, tc.versionRule) | ||
if err != nil { | ||
t.Fatal(err) | ||
} | ||
|
||
want, ok := parseSemver(tc.wantVersion) | ||
if !ok { | ||
t.Fatalf("failed to parse the want version: %q", tc.wantVersion) | ||
} | ||
|
||
if want != got { | ||
t.Errorf("nextPrereleaseVersion(%q) = %v but want %v", tc.versionRule, got, want) | ||
} | ||
}) | ||
} | ||
} |