Repository: bazelbuild/bazelisk
Pull request #231 (2021-04-05 06:21:12)

Bump github.com/hashicorp/go-version from 1.2.1 to 1.3.0
Bumps [github.com/hashicorp/go-version](https://github.com/hashicorp/go-version) from 1.2.1 to 1.3.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/hashicorp/go-version/releases">github.com/hashicorp/go-version's releases</a>.</em></p>
<blockquote>
<h2>v1.3.0</h2>
<p>FEATURES:</p>
<ul>
<li>Add <code>Core</code> function to return a version without prerelease or metadata (<a href="https://github-redirect.dependabot.com/hashicorp/go-version/pull/85">#85</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a href="https://github.com/hashicorp/go-version/blob/master/CHANGELOG.md">github.com/hashicorp/go-version's changelog</a>.</em></p>
<blockquote>
<h1>1.3.0 (March 31, 2021)</h1>
<p>Please note that CHANGELOG.md does not exist in the source code prior to this release.</p>
<p>FEATURES:</p>
<ul>
<li>Add <code>Core</code> function to return a version without prerelease or metadata (<a href="https://github-redirect.dependabot.com/hashicorp/go-version/pull/85">#85</a>)</li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/hashicorp/go-version/commit/c2de2312d363381e4254abec260c6df2b1226483"><code>c2de231</code></a> create CHANGELOG.md</li>
<li><a href="https://github.com/hashicorp/go-version/commit/33073403b8a0c0d49e1c0552534c63f7c5ddb148"><code>3307340</code></a> Merge pull request <a href="https://github-redirect.dependabot.com/hashicorp/go-version/issues/85">#85</a> from hashicorp/base-version-func</li>
<li><a href="https://github.com/hashicorp/go-version/commit/7da126b9f5d830386b27fa8e2f04fbbddcfe04e8"><code>7da126b</code></a> add Core func</li>
<li><a href="https://github.com/hashicorp/go-version/commit/9e6930792fb43ef7fd0fe527de1c7d83188f8d2e"><code>9e69307</code></a> Use docker mirror in CI (<a href="https://github-redirect.dependabot.com/hashicorp/go-version/issues/82">#82</a>)</li>
<li><a href="https://github.com/hashicorp/go-version/commit/d16dd1c406fd0935240da46b64b3c774fb70a0b3"><code>d16dd1c</code></a> Switch to docker mirror</li>
<li>See full diff in <a href="https://github.com/hashicorp/go-version/compare/v1.2.1...v1.3.0">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=github.com/hashicorp/go-version&package-manager=go_modules&previous-version=1.2.1&new-version=1.3.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>


Patch:
diff --git a/build.sh b/build.sh
index 157a0eb..239d570 100755
--- a/build.sh
+++ b/build.sh
@@ -54,5 +54,5 @@ echo
 readonly REGISTRY=${NPM_REGISTRY:-https://wombat-dressing-room.appspot.com}
 echo "== NPM releases =="
 echo "After testing, publish to NPM via these commands:"
-echo "$ npm config set registry https://wombat-dressing-room.appspot.com"
+echo "$ npm login --registry $REGISTRY"
 echo "$ ./bazelisk run --config=release //:npm_package.publish -- --access=public --tag latest --registry $REGISTRY"
diff --git a/go.mod b/go.mod
index 420baa0..9e49ff6 100644
--- a/go.mod
+++ b/go.mod
@@ -4,6 +4,6 @@ go 1.15
 
 require (
 	github.com/bazelbuild/rules_go v0.25.1
-	github.com/hashicorp/go-version v1.2.1
+	github.com/hashicorp/go-version v1.3.0
 	github.com/mitchellh/go-homedir v1.1.0
 )
diff --git a/go.sum b/go.sum
index becb0c2..1fc7681 100644
--- a/go.sum
+++ b/go.sum
@@ -1,6 +1,6 @@
 github.com/bazelbuild/rules_go v0.25.1 h1:kFMT+OgT62SY3tBTRYFJBTphOHHoCfyn5wbRFNauKUQ=
 github.com/bazelbuild/rules_go v0.25.1/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M=
-github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
-github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
+github.com/hashicorp/go-version v1.3.0 h1:McDWVJIU/y+u1BRV06dPaLfLCaT7fUTJLp5r04x7iNw=
+github.com/hashicorp/go-version v1.3.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
 github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
 github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
diff --git a/repositories/gcs.go b/repositories/gcs.go
index dbdf235..6f74b36 100644
--- a/repositories/gcs.go
+++ b/repositories/gcs.go
@@ -63,27 +63,45 @@ func getVersionHistoryFromGCS() ([]string, error) {
 }
 
 func listDirectoriesInReleaseBucket(prefix string) ([]string, bool, error) {
-	url := "https://www.googleapis.com/storage/v1/b/bazel/o?delimiter=/"
+	baseURL := "https://www.googleapis.com/storage/v1/b/bazel/o?delimiter=/"
 	if prefix != "" {
-		url = fmt.Sprintf("%s&prefix=%s", url, prefix)
-	}
-	content, err := httputil.ReadRemoteFile(url, "")
-	if err != nil {
-		return nil, false, fmt.Errorf("could not list GCS objects at %s: %v", url, err)
+		baseURL = fmt.Sprintf("%s&prefix=%s", baseURL, prefix)
 	}
 
-	var response GcsListResponse
-	if err := json.Unmarshal(content, &response); err != nil {
-		return nil, false, fmt.Errorf("could not parse GCS index JSON: %v", err)
+	var prefixes []string
+	var isRelease = false
+	var nextPageToken = ""
+	for {
+		var url = baseURL
+		if nextPageToken != "" {
+			url = fmt.Sprintf("%s&pageToken=%s", baseURL, nextPageToken)
+		}
+		content, err := httputil.ReadRemoteFile(url, "")
+		if err != nil {
+			return nil, false, fmt.Errorf("could not list GCS objects at %s: %v", url, err)
+		}
+
+		var response GcsListResponse
+		if err := json.Unmarshal(content, &response); err != nil {
+			return nil, false, fmt.Errorf("could not parse GCS index JSON: %v", err)
+		}
+
+		prefixes = append(prefixes, response.Prefixes...)
+		isRelease = isRelease || len(response.Items) > 0
+
+		if response.NextPageToken == "" {
+			break
+		}
+		nextPageToken = response.NextPageToken
 	}
-	return response.Prefixes, len(response.Items) > 0, nil
+	return prefixes, isRelease, nil
 }
 
 func getVersionsFromGCSPrefixes(versions []string) []string {
 	result := make([]string, len(versions))
 	for i, v := range versions {
 		noSlashes := strings.Replace(v, "/", "", -1)
-		result[i] =  strings.TrimSuffix(noSlashes, "release")
+		result[i] = strings.TrimSuffix(noSlashes, "release")
 	}
 	return result
 }
@@ -96,6 +114,9 @@ type GcsListResponse struct {
 
 	// Items contains the names of available objects in the current GCS bucket.
 	Items []interface{} `json:"items"`
+
+	// Optional token when the result is paginated.
+	NextPageToken string `json:"nextPageToken"`
 }
 
 // DownloadRelease downloads the given Bazel release into the specified location and returns the absolute path.
@@ -156,22 +177,28 @@ func (gcs *GCSRepo) GetCandidateVersions(bazeliskHome string) ([]string, error)
 		return []string{}, errors.New("could not find any Bazel versions")
 	}
 
-	latestVersion := history[len(history)-1]
-
-	// Append slash to match directories
-	rcPrefixes, _, err := listDirectoriesInReleaseBucket(latestVersion + "/")
-	if err != nil {
-		return []string{}, fmt.Errorf("could not list release candidates for latest release: %v", err)
-	}
+	// Find most recent directory that contains any release candidates.
+	// Typically it should be the last or second-to-last, depending on whether there are new rolling releases.
+	for pos := len(history) - 1; pos >= 0; pos-- {
+		// Append slash to match directories
+		bucket := fmt.Sprintf("%s/", history[pos])
+		rcPrefixes, _, err := listDirectoriesInReleaseBucket(bucket)
+		if err != nil {
+			return []string{}, fmt.Errorf("could not list release candidates for latest release: %v", err)
+		}
 
-	rcs := make([]string, 0)
-	for _, v := range getVersionsFromGCSPrefixes(rcPrefixes) {
-		// Remove full releases
-		if strings.Contains(v, "rc") {
-			rcs = append(rcs, v)
+		rcs := make([]string, 0)
+		for _, v := range getVersionsFromGCSPrefixes(rcPrefixes) {
+			// Remove full and rolling releases
+			if strings.Contains(v, "rc") {
+				rcs = append(rcs, v)
+			}
+		}
+		if len(rcs) > 0 {
+			return rcs, nil
 		}
 	}
-	return rcs, nil
+	return nil, nil
 }
 
 // DownloadCandidate downloads the given release candidate into the specified location and returns the absolute path.
diff --git a/versions/versions.go b/versions/versions.go
index acb3f87..4ef6245 100644
--- a/versions/versions.go
+++ b/versions/versions.go
@@ -19,7 +19,7 @@ const (
 var (
 	releasePattern       = regexp.MustCompile(`^(\d+\.\d+\.\d+)$`)
 	candidatePattern     = regexp.MustCompile(`^(\d+\.\d+\.\d+)rc(\d+)$`)
-	rollingPattern       = regexp.MustCompile(`\d+\.0\.0-pre\.\d{6}(\.\d+)?`)
+	rollingPattern       = regexp.MustCompile(`^\d+\.0\.0-pre\.\d{8}(\.\d+){1,2}$`)
 	latestReleasePattern = regexp.MustCompile(`^latest(?:-(?P<offset>\d+))?$`)
 	commitPattern        = regexp.MustCompile(`^[a-z0-9]{40}$`)
 )


Test patch:
diff --git a/bazelisk_version_test.go b/bazelisk_version_test.go
index b4d4a3a..9884d9f 100644
--- a/bazelisk_version_test.go
+++ b/bazelisk_version_test.go
@@ -17,6 +17,10 @@ import (
 	"github.com/bazelbuild/bazelisk/versions"
 )
 
+const (
+	rollingReleaseIdentifier = "rolling"
+)
+
 var (
 	transport = &fakeTransport{responses: make(map[string]*http.Response)}
 	tmpDir    = ""
@@ -40,10 +44,11 @@ func TestMain(m *testing.M) {
 
 func TestResolveLatestRcVersion(t *testing.T) {
 	s := setUp(t)
-	s.AddVersion("4.0.0", false)
-	s.AddVersion("10.0.0", false)
-	s.AddVersion("11.0.0", true)
-	s.AddVersion("11.11.0", false, 1, 2)
+	s.AddVersion("4.0.0", false, nil, nil)
+	s.AddVersion("10.0.0", false, nil, nil)
+	s.AddVersion("11.0.0", true, nil, nil)
+	s.AddVersion("11.11.0", false, []int{1, 2}, nil)
+	s.AddVersion("12.0.0", false, nil, []string{"12.0.0-pre.20210504.1"})
 	s.Finish()
 
 	gcs := &repositories.GCSRepo{}
@@ -61,7 +66,7 @@ func TestResolveLatestRcVersion(t *testing.T) {
 
 func TestResolveLatestRcVersion_WithFullRelease(t *testing.T) {
 	s := setUp(t)
-	s.AddVersion("4.0.0", true, 1, 2, 3)
+	s.AddVersion("4.0.0", true, []int{1, 2, 3}, nil)
 	s.Finish()
 
 	gcs := &repositories.GCSRepo{}
@@ -79,9 +84,9 @@ func TestResolveLatestRcVersion_WithFullRelease(t *testing.T) {
 
 func TestResolveLatestVersion_TwoLatestVersionsDoNotHaveAReleaseYet(t *testing.T) {
 	s := setUp(t)
-	s.AddVersion("4.0.0", true)
-	s.AddVersion("5.0.0", false)
-	s.AddVersion("6.0.0", false)
+	s.AddVersion("4.0.0", true, nil, nil)
+	s.AddVersion("5.0.0", false, nil, nil)
+	s.AddVersion("6.0.0", false, nil, nil)
 	s.Finish()
 
 	gcs := &repositories.GCSRepo{}
@@ -97,12 +102,13 @@ func TestResolveLatestVersion_TwoLatestVersionsDoNotHaveAReleaseYet(t *testing.T
 	}
 }
 
-func TestResolveLatestVersion_FilterReleaseCandidates(t *testing.T) {
+func TestResolveLatestVersion_ShouldOnlyReturnStableReleases(t *testing.T) {
 	s := setUp(t)
-	s.AddVersion("3.0.0", true)
-	s.AddVersion("4.0.0", false)
-	s.AddVersion("5.0.0", false)
-	s.AddVersion("6.0.0", true)
+	s.AddVersion("3.0.0", true, []int{1}, nil)
+	s.AddVersion("4.0.0", false, nil, nil)
+	s.AddVersion("5.0.0", false, nil, nil)
+	s.AddVersion("6.0.0", true, []int{1, 2}, nil)
+	s.AddVersion("7.0.0", false, nil, []string{"12.0.0-pre.20210504.1"})
 	s.Finish()
 
 	gcs := &repositories.GCSRepo{}
@@ -120,8 +126,8 @@ func TestResolveLatestVersion_FilterReleaseCandidates(t *testing.T) {
 
 func TestResolveLatestVersion_ShouldFailIfNotEnoughReleases(t *testing.T) {
 	s := setUp(t)
-	s.AddVersion("3.0.0", true)
-	s.AddVersion("4.0.0", false)
+	s.AddVersion("3.0.0", true, nil, nil)
+	s.AddVersion("4.0.0", false, nil, nil)
 	s.Finish()
 
 	gcs := &repositories.GCSRepo{}
@@ -176,17 +182,16 @@ func TestAcceptRollingReleaseName(t *testing.T) {
 	gh := repositories.CreateGitHubRepo("test_token")
 	repos := core.CreateRepositories(nil, nil, nil, nil, gh, false)
 
-	version := "10.0.0-pre.20201103.4"
-
-	resolvedVersion, _, err := repos.ResolveVersion(tmpDir, "", version)
+	for _, version := range []string{"10.0.0-pre.20201103.4", "10.0.0-pre.20201103.4.2"} {
+		resolvedVersion, _, err := repos.ResolveVersion(tmpDir, "", version)
 
-	details := fmt.Sprintf("ResolveVersion(%s, \"\", %s)", tmpDir, version)
-	if err != nil {
-		t.Fatalf("Unexpected error in %s: %v", details, err)
-	}
+		if err != nil {
+			t.Fatalf("ResolveVersion(%q, \"\", %q): expected no error, but got %v", tmpDir, version, err)
+		}
 
-	if resolvedVersion != version {
-		t.Fatalf("Expected version %q from %s, but got %q", version, details, resolvedVersion)
+		if resolvedVersion != version {
+			t.Fatalf("ResolveVersion(%q, \"\", %q) = %v, but expected %v", tmpDir, version, resolvedVersion, version)
+		}
 	}
 }
 
@@ -198,7 +203,7 @@ func TestResolveLatestRollingRelease(t *testing.T) {
 		"prerelease": false
 	  },
 	  {
-		"tag_name": "5.0.0-pre.20210319",
+		"tag_name": "5.0.0-pre.20210319.1",
 		"prerelease": true
 	  },
 	  {
@@ -217,16 +222,15 @@ func TestResolveLatestRollingRelease(t *testing.T) {
 	gh := repositories.CreateGitHubRepo("test_token")
 	repos := core.CreateRepositories(nil, nil, nil, nil, gh, false)
 
-	version, _, err := repos.ResolveVersion(tmpDir, "", "rolling")
+	version, _, err := repos.ResolveVersion(tmpDir, "", rollingReleaseIdentifier)
 
-	details := fmt.Sprintf("ResolveVersion(%s, \"\", \"rolling\")", tmpDir)
 	if err != nil {
-		t.Fatalf("Unexpected error in %s: %v", details, err)
+		t.Fatalf("ResolveVersion(%q, \"\", %q): expected no error, but got %v", tmpDir, rollingReleaseIdentifier, err)
 	}
 
 	want := "5.0.0-pre.20210322.4"
 	if version != want {
-		t.Fatalf("Expected version %q from %s, but got %q", want, details, version)
+		t.Fatalf("ResolveVersion(%q, \"\", %q) = %v, but expected %v", tmpDir, rollingReleaseIdentifier, version, want)
 	}
 }
 
@@ -237,13 +241,22 @@ type gcsSetup struct {
 	test            *testing.T
 }
 
-func (g *gcsSetup) AddVersion(version string, hasRelease bool, rcs ...int) *gcsSetup {
+func (g *gcsSetup) AddVersion(version string, hasRelease bool, rcs []int, rolling []string) *gcsSetup {
 	g.versionPrefixes = append(g.versionPrefixes, fmt.Sprintf("%s/", version))
 	prefixes := make([]string, 0)
+
+	register := func(subDir string) {
+		path := fmt.Sprintf("%s/%s/", version, subDir)
+		prefixes = append(prefixes, path)
+		g.addURL(path, false)
+	}
+
 	for _, rc := range rcs {
-		prefix := fmt.Sprintf("%s/rc%d/", version, rc)
-		prefixes = append(prefixes, prefix)
-		g.addURL(prefix, false)
+		register(fmt.Sprintf("rc%d", rc))
+	}
+
+	for _, r := range rolling {
+		register(r)
 	}
 
 	// The /release/ URLs have to exist, even if there is no release. In this case GCS returns no items, though.