Skip to content

Commit

Permalink
Support 'last_rc' in Bazelisk [Go-only] (bazelbuild#60)
Browse files Browse the repository at this point in the history
* Support last_rc version in bazelisk.go

* Document 'last_rc' version

* Test 'last_rc' version feature

* Make test_bazel_last_rc a Go-only test
  • Loading branch information
fweikert authored and philwo committed May 28, 2019
1 parent a690e4f commit a89e659
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 9 deletions.
2 changes: 2 additions & 0 deletions BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ sh_test(
srcs = ["bazelisk_test.sh"],
data = ["bazelisk.py", "releases_for_tests.json"],
deps = ["@bazel_tools//tools/bash/runfiles"],
args = ["PY"]
)

sh_test(
name = "go_bazelisk_test",
srcs = ["bazelisk_test.sh"],
data = [":bazelisk", "releases_for_tests.json"],
deps = ["@bazel_tools//tools/bash/runfiles"],
args = ["GO"]
)

go_library(
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ Bazelisk currently understands the following formats for version labels:
be a release candidate version like `0.20.0rc3`.
- `last_green` refers to the Bazel binary that was built at the most recent commit that passed [Bazel CI](https://buildkite.com/bazel/bazel-bazel). Ideally this binary should be very close to Bazel-at-head.
- `last_downstream_green` points to the most recent Bazel binary that builds and tests all [downstream projects](https://buildkite.com/bazel/bazel-at-head-plus-downstream) successfully.
- `last_rc` points to the most recent release candidate. If there is no active release candidate, Bazelisk uses the latest Bazel release instead. Currently only the Go version of Bazelisk supports this value.


In the future I will add support for release candidates and for building Bazel from source at a given commit.
In the future I will add support for building Bazel from source at a given commit.

## Other features

Expand Down
100 changes: 92 additions & 8 deletions bazelisk.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,22 +165,101 @@ func resolveLatestVersion(bazeliskHome string, offset int) (string, error) {
return "", fmt.Errorf("could not parse JSON into list of releases: %v", err)
}

var versions []*version.Version
var tags []string
for _, release := range releases {
if release.Prerelease {
continue
}
v, err := version.NewVersion(release.TagName)
tags = append(tags, release.TagName)
}
return getNthMostRecentVersion(tags, offset)
}

func getNthMostRecentVersion(versions []string, offset int) (string, error) {
if offset >= len(versions) {
return "", fmt.Errorf("cannot resolve version \"latest-%d\": There are only %d Bazel versions", offset, len(versions))
}

wrappers := make([]*version.Version, len(versions))
for i, v := range versions {
wrapper, err := version.NewVersion(v)
if err != nil {
log.Printf("WARN: Could not parse version: %s", release.TagName)
log.Printf("WARN: Could not parse version: %s", v)
}
versions = append(versions, v)
wrappers[i] = wrapper
}
sort.Sort(version.Collection(versions))
if offset >= len(versions) {
return "", fmt.Errorf("cannot resolve version \"latest-%d\": There are only %d Bazel releases", offset, len(versions))
sort.Sort(version.Collection(wrappers))
return wrappers[len(wrappers)-1-offset].Original(), nil
}

type gcsListResponse struct {
Prefixes []string `json:"prefixes"`
}

func resolveLatestRcVersion() (string, error) {
versions, err := listDirectoriesInReleaseBucket("")
if err != nil {
return "", fmt.Errorf("could not list Bazel versions in GCS bucket: %v", err)
}

latestVersion, err := getHighestBazelVersion(versions)
if err != nil {
return "", fmt.Errorf("got invalid version number: %v", err)
}
return versions[len(versions)-1-offset].Original(), nil

// Append slash to match directories
rcVersions, err := listDirectoriesInReleaseBucket(latestVersion + "/")
if err != nil {
return "", fmt.Errorf("could not list release candidates for latest release: %v", err)
}
return getHighestRcVersion(rcVersions)
}

func listDirectoriesInReleaseBucket(prefix string) ([]string, error) {
url := "https://www.googleapis.com/storage/v1/b/bazel/o?delimiter=/"
if prefix != "" {
url = fmt.Sprintf("%s&prefix=%s", url, prefix)
}
content, err := readRemoteFile(url)
if err != nil {
return nil, 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, fmt.Errorf("could not parse GCS index JSON: %v", err)
}
return response.Prefixes, nil
}

func getHighestBazelVersion(versions []string) (string, error) {
for i, v := range versions {
versions[i] = strings.TrimSuffix(v, "/")
}
return getNthMostRecentVersion(versions, 0)
}

func getHighestRcVersion(versions []string) (string, error) {
var version string
var lastRc int
re := regexp.MustCompile(`(\d+.\d+.\d+)/rc(\d+)/`)
for _, v := range versions {
// Fallback: use latest release if there is no active RC.
if strings.Index(v, "release") > -1 {
return strings.Split(v, "/")[0], nil
}

m := re.FindStringSubmatch(v)
version = m[1]
rc, err := strconv.Atoi(m[2])
if err != nil {
return "", fmt.Errorf("Invalid version number %s: %v", strings.TrimSuffix(v, "/"), err)
}
if rc > lastRc {
lastRc = rc
}
}
return fmt.Sprintf("%src%d", version, lastRc), nil
}

func resolveVersionLabel(bazeliskHome, bazelVersion string) (string, bool, error) {
Expand All @@ -198,6 +277,11 @@ func resolveVersionLabel(bazeliskHome, bazelVersion string) (string, bool, error
return commit, true, nil
}

if bazelVersion == "last_rc" {
version, err := resolveLatestRcVersion()
return version, false, err
}

r := regexp.MustCompile(`^latest(?:-(?P<offset>\d+))?$`)

match := r.FindStringSubmatch(bazelVersion)
Expand Down
20 changes: 20 additions & 0 deletions bazelisk_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ else
fi
# --- end runfiles.bash initialization ---

BAZELISK_VERSION=$1
shift 1

function setup() {
BAZELISK_HOME="$(mktemp -d $TEST_TMPDIR/home.XXXXXX)"

Expand Down Expand Up @@ -127,6 +130,17 @@ function test_bazel_last_downstream_green() {
(echo "FAIL: 'bazelisk version' of an unreleased binary must not print a build label."; exit 1)
}

function test_bazel_last_rc() {
setup

USE_BAZEL_VERSION="last_rc" \
BAZELISK_HOME="$BAZELISK_HOME" \
bazelisk version 2>&1 | tee log

grep "Build label:" log || \
(echo "FAIL: Expected to find 'Build label' in the output of 'bazelisk version'"; exit 1)
}

echo "# test_bazel_version"
test_bazel_version
echo
Expand All @@ -150,3 +164,9 @@ echo
echo "# test_bazel_last_downstream_green"
test_bazel_last_downstream_green
echo

if [[$BAZELISK_VERSION == "GO"]]; then
echo "# test_bazel_last_rc"
test_bazel_last_rc
echo
fi

0 comments on commit a89e659

Please sign in to comment.