Skip to content

Commit

Permalink
Support floating release identifiers (e.g. "5.x") (bazelbuild#278)
Browse files Browse the repository at this point in the history
This version format allows users to work with the most recent stable release from a particular LTS track.

Fixes bazelbuild#277
  • Loading branch information
fweikert authored Nov 24, 2021
1 parent cbb333c commit e87a993
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 5 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Bazelisk currently understands the following formats for version labels:
Previous releases can be specified via `latest-1`, `latest-2` etc.
- A version number like `0.17.2` means that exact version of Bazel.
It can also be a release candidate version like `0.20.0rc3`, or a rolling release version like `5.0.0-pre.20210317.1`.
- A floating version identifier like `4.x` that returns the latest release from the LTS series started by Bazel 4.0.0.
- The hash of a Git commit. Please note that Bazel binaries are only available for commits that passed [Bazel CI](https://buildkite.com/bazel/bazel-bazel).

Additionally, a few special version names are supported for our official releases only (these formats do not work when using a fork):
Expand Down
23 changes: 23 additions & 0 deletions bazelisk_version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,29 @@ func TestResolveLatestRollingRelease(t *testing.T) {
}
}

func TestAcceptFloatingReleaseVersions(t *testing.T) {
s := setUp(t)
s.AddVersion("3.0.0", true, nil, []string{"4.0.0-pre.20210504.1"})
s.AddVersion("4.0.0", true, nil, nil)
s.AddVersion("4.1.0", true, nil, nil)
s.AddVersion("4.2.0", true, nil, nil)
s.AddVersion("4.2.1", true, []int{1, 2}, nil)
s.AddVersion("5.0.0", true, nil, nil)
s.Finish()

gcs := &repositories.GCSRepo{}
repos := core.CreateRepositories(gcs, nil, nil, nil, nil, false)
version, _, err := repos.ResolveVersion(tmpDir, versions.BazelUpstream, "4.x")

if err != nil {
t.Fatalf("Version resolution failed unexpectedly: %v", err)
}
expectedVersion := "4.2.1"
if version != expectedVersion {
t.Fatalf("Expected version %s, but got %s", expectedVersion, version)
}
}

type gcsSetup struct {
baseURL string
versionPrefixes []string
Expand Down
25 changes: 24 additions & 1 deletion core/repositories.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package core
import (
"errors"
"fmt"
"strings"

"github.com/bazelbuild/bazelisk/httputil"
"github.com/bazelbuild/bazelisk/platforms"
Expand Down Expand Up @@ -116,7 +117,13 @@ func (r *Repositories) resolveFork(bazeliskHome string, vi *versions.Info) (stri

func (r *Repositories) resolveRelease(bazeliskHome string, vi *versions.Info) (string, DownloadFunc, error) {
lister := func(bazeliskHome string) ([]string, error) {
return r.Releases.GetReleaseVersions(bazeliskHome, vi.LatestOffset+1)
var lastN int
// Optimization: only fetch last (x+1) releases if the version is "latest-x".
// This does not work if the version is "4.x", i.e. if TrackRestriction is set.
if vi.TrackRestriction == 0 {
lastN = vi.LatestOffset + 1
}
return r.Releases.GetReleaseVersions(bazeliskHome, lastN)
}
version, err := resolvePotentiallyRelativeVersion(bazeliskHome, lister, vi)
if err != nil {
Expand Down Expand Up @@ -179,6 +186,11 @@ func resolvePotentiallyRelativeVersion(bazeliskHome string, lister listVersionsF
if err != nil {
return "", fmt.Errorf("unable to determine latest version: %v", err)
}

if vi.TrackRestriction > 0 {
available = restrictToTrack(available, vi.TrackRestriction)
}

index := len(available) - 1 - vi.LatestOffset
if index < 0 {
return "", fmt.Errorf("cannot resolve version \"%s\": There are only %d Bazel versions", vi.Value, len(available))
Expand All @@ -187,6 +199,17 @@ func resolvePotentiallyRelativeVersion(bazeliskHome string, lister listVersionsF
return sorted[index], nil
}

func restrictToTrack(versions []string, track int) []string {
filtered := make([]string, 0)
prefix := fmt.Sprintf("%d.", track)
for _, v := range versions {
if strings.HasPrefix(v, prefix) {
filtered = append(filtered, v)
}
}
return filtered
}

// DownloadFromBaseURL can download Bazel binaries from a specific URL while ignoring the predefined repositories.
func (r *Repositories) DownloadFromBaseURL(baseURL, version, destDir, destFile string) (string, error) {
if !r.supportsBaseURL {
Expand Down
16 changes: 12 additions & 4 deletions versions/versions.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const (
)

var (
releasePattern = regexp.MustCompile(`^(\d+\.\d+\.\d+)$`)
releasePattern = regexp.MustCompile(`^(\d+)\.(x|\d+\.\d+)$`)
candidatePattern = regexp.MustCompile(`^(\d+\.\d+\.\d+)rc(\d+)$`)
rollingPattern = regexp.MustCompile(`^\d+\.0\.0-pre\.\d{8}(\.\d+){1,2}$`)
latestReleasePattern = regexp.MustCompile(`^latest(?:-(?P<offset>\d+))?$`)
Expand All @@ -27,16 +27,24 @@ var (
// Info represents a structured Bazel version identifier.
type Info struct {
IsRelease, IsCandidate, IsCommit, IsFork, IsRolling, IsRelative, IsDownstream bool
Fork, Value string
LatestOffset int
Fork, Value string
LatestOffset, TrackRestriction int
}

// Parse extracts and returns structured information about the given Bazel version label.
func Parse(fork, version string) (*Info, error) {
vi := &Info{Fork: fork, Value: version, IsFork: isFork(fork)}

if releasePattern.MatchString(version) {
if m := releasePattern.FindStringSubmatch(version); m != nil {
vi.IsRelease = true
if m[2] == "x" {
track, err := strconv.Atoi(m[1])
if err != nil {
return nil, fmt.Errorf("invalid version %q, expected something like '5.2.1' or '5.x'", version)
}
vi.IsRelative = true
vi.TrackRestriction = track
}
} else if m := latestReleasePattern.FindStringSubmatch(version); m != nil {
vi.IsRelease = true
vi.IsRelative = true
Expand Down

0 comments on commit e87a993

Please sign in to comment.