internal/task: add GetNextVersions
Add a function that gets the next version number for each of the
possible release types. Maybe it should be parameterized by the Kind
enum I added in CL 408295, at least for single releases like
major/beta/rc. That will become clearer when it's wired up.
For golang/go#51797.
Change-Id: Iccb94333d967ee620a9d2ae8ceacc87c7ea7007d
Reviewed-on: https://go-review.googlesource.com/c/build/+/408955
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Alex Rakoczy <alex@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
Auto-Submit: Heschi Kreinick <heschi@google.com>
diff --git a/internal/task/gerrit.go b/internal/task/gerrit.go
index ccf5fa4..7874e2b 100644
--- a/internal/task/gerrit.go
+++ b/internal/task/gerrit.go
@@ -19,6 +19,8 @@
AwaitSubmit(ctx context.Context, changeID string) (string, error)
// Tag creates a tag on project at the specified commit.
Tag(ctx context.Context, project, tag, commit string) error
+ // ListTags returns all the tags on project.
+ ListTags(ctx context.Context, project string) ([]string, error)
}
type realGerritClient struct {
@@ -89,6 +91,18 @@
return err
}
+func (c *realGerritClient) ListTags(ctx context.Context, project string) ([]string, error) {
+ tags, err := c.client.GetProjectTags(ctx, project)
+ if err != nil {
+ return nil, err
+ }
+ var tagNames []string
+ for _, tag := range tags {
+ tagNames = append(tagNames, strings.TrimPrefix(tag.Ref, "refs/tags/"))
+ }
+ return tagNames, nil
+}
+
// changeLink returns a link to the review page for the CL with the specified
// change ID. The change ID must be in the project~cl# form.
func changeLink(changeID string) string {
diff --git a/internal/task/milestones.go b/internal/task/milestones.go
index 4601186..f8ac385 100644
--- a/internal/task/milestones.go
+++ b/internal/task/milestones.go
@@ -4,7 +4,6 @@
"context"
"fmt"
"sort"
- "strconv"
"strings"
"github.com/google/go-github/github"
@@ -76,16 +75,6 @@
return ReleaseMilestones{Current: currentMilestone, Next: nextMilestone}, nil
}
-func nextVersion(version string) (string, error) {
- parts := strings.Split(version, ".")
- n, err := strconv.Atoi(parts[len(parts)-1])
- if err != nil {
- return "", err
- }
- parts[len(parts)-1] = strconv.Itoa(n + 1)
- return strings.Join(parts, "."), nil
-}
-
func uppercaseVersion(version string) string {
return strings.Replace(version, "go", "Go", 1)
}
diff --git a/internal/task/version.go b/internal/task/version.go
index e83c687..d9f9a20 100644
--- a/internal/task/version.go
+++ b/internal/task/version.go
@@ -2,6 +2,8 @@
import (
"fmt"
+ "strconv"
+ "strings"
"golang.org/x/build/gerrit"
"golang.org/x/build/internal/workflow"
@@ -13,6 +15,70 @@
Project string
}
+type NextVersions struct {
+ CurrentMinor string
+ PreviousMinor string
+ Beta string
+ RC string
+ Major string
+}
+
+// GetNextVersions returns the next version for each type of release.
+func (t *VersionTasks) GetNextVersions(ctx *workflow.TaskContext) (NextVersions, error) {
+ tags, err := t.Gerrit.ListTags(ctx, t.Project)
+ if err != nil {
+ return NextVersions{}, err
+ }
+ tagSet := map[string]bool{}
+ for _, tag := range tags {
+ tagSet[tag] = true
+ }
+ // Find the most recently released major version.
+ currentMajor := 0
+ for ; ; currentMajor++ {
+ if !tagSet[fmt.Sprintf("go1.%d", currentMajor+1)] {
+ break
+ }
+ }
+ var savedError error
+ findUnused := func(v string) string {
+ for {
+ if !tagSet[v] {
+ return v
+ }
+ var err error
+ v, err = nextVersion(v)
+ if err != nil {
+ savedError = err
+ return ""
+ }
+ }
+ }
+ // Find the next missing tag for each release type.
+ result := NextVersions{
+ CurrentMinor: findUnused(fmt.Sprintf("go1.%d.1", currentMajor)),
+ PreviousMinor: findUnused(fmt.Sprintf("go1.%d.1", currentMajor-1)),
+ Beta: findUnused(fmt.Sprintf("go1.%dbeta1", currentMajor+1)),
+ RC: findUnused(fmt.Sprintf("go1.%drc1", currentMajor+1)),
+ Major: fmt.Sprintf("go1.%d", currentMajor+1),
+ }
+ return result, savedError
+}
+
+func nextVersion(version string) (string, error) {
+ lastNonDigit := strings.LastIndexFunc(version, func(r rune) bool {
+ return r < '0' || r > '9'
+ })
+ if lastNonDigit == -1 || len(version) == lastNonDigit {
+ return "", fmt.Errorf("malformatted Go version %q", version)
+ }
+ n, err := strconv.Atoi(version[lastNonDigit+1:])
+ if err != nil {
+ return "", fmt.Errorf("malformatted Go version %q (%v)", version, err)
+ }
+ return fmt.Sprintf("%s%d", version[:lastNonDigit+1], n+1), nil
+}
+
// CreateAutoSubmitVersionCL mails an auto-submit change to update VERSION on branch.
func (t *VersionTasks) CreateAutoSubmitVersionCL(ctx *workflow.TaskContext, branch, version string) (string, error) {
return t.Gerrit.CreateAutoSubmitChange(ctx, gerrit.ChangeInput{
diff --git a/internal/task/version_test.go b/internal/task/version_test.go
index 0880983..6d1fd32 100644
--- a/internal/task/version_test.go
+++ b/internal/task/version_test.go
@@ -6,12 +6,77 @@
"strings"
"testing"
+ "github.com/google/go-cmp/cmp"
"golang.org/x/build/gerrit"
"golang.org/x/build/internal/workflow"
)
var flagRunVersionTest = flag.Bool("run-version-test", false, "run version test, which will submit CLs to go.googlesource.com/scratch. Must have a Gerrit cookie in gitcookies.")
+func TestGetNextVersionsLive(t *testing.T) {
+ if !*flagRunVersionTest {
+ t.Skip("Not enabled by flags")
+ }
+
+ cl := gerrit.NewClient("https://go-review.googlesource.com", gerrit.GitCookiesAuth())
+ tasks := &VersionTasks{
+ Gerrit: &realGerritClient{client: cl},
+ Project: "go",
+ }
+ ctx := &workflow.TaskContext{
+ Context: context.Background(),
+ Logger: &testLogger{t},
+ }
+
+ versions, err := tasks.GetNextVersions(ctx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ // It's hard to check correctness automatically.
+ t.Errorf("manually verify results: %#v", versions)
+}
+
+func TestGetNextVersions(t *testing.T) {
+ tasks := &VersionTasks{
+ Gerrit: &versionsClient{
+ tags: []string{
+ "go1.1", "go1.2",
+ "go1.3beta1", "go1.3beta2", "go1.3rc1", "go1.3", "go1.3.1", "go1.3.2", "go1.3.3",
+ "go1.4beta1", "go1.4beta2", "go1.4rc1", "go1.4", "go1.4.1",
+ "go1.5beta1", "go1.5rc1",
+ },
+ },
+ Project: "go",
+ }
+ ctx := &workflow.TaskContext{
+ Context: context.Background(),
+ Logger: &testLogger{t},
+ }
+ versions, err := tasks.GetNextVersions(ctx)
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := NextVersions{
+ CurrentMinor: "go1.4.2",
+ PreviousMinor: "go1.3.4",
+ Beta: "go1.5beta2",
+ RC: "go1.5rc2",
+ Major: "go1.5",
+ }
+ if diff := cmp.Diff(versions, want); diff != "" {
+ t.Fatalf("GetNextVersions mismatch (-want +got):\n%s", diff)
+ }
+}
+
+type versionsClient struct {
+ tags []string
+ GerritClient
+}
+
+func (c *versionsClient) ListTags(ctx context.Context, project string) ([]string, error) {
+ return c.tags, nil
+}
+
func TestVersion(t *testing.T) {
if !*flagRunVersionTest {
t.Skip("Not enabled by flags")