Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor repositories API #631

Merged
merged 1 commit into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions bazelisk.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ func main() {
gcs := &repositories.GCSRepo{}
config := core.MakeDefaultConfig()
gitHub := repositories.CreateGitHubRepo(config.Get("BAZELISK_GITHUB_TOKEN"))
// Fetch LTS releases, release candidates, rolling releases and Bazel-at-commits from GCS, forks from GitHub.
repos := core.CreateRepositories(gcs, gcs, gitHub, gcs, gcs, true)
// Fetch LTS releases & candidates, rolling releases and Bazel-at-commits from GCS, forks from GitHub.
repos := core.CreateRepositories(gcs, gitHub, gcs, gcs, true)

exitCode, err := core.RunBazeliskWithArgsFuncAndConfig(func(string) []string { return os.Args[1:] }, repos, config)
if err != nil {
Expand Down
35 changes: 23 additions & 12 deletions bazelisk_version_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"log"
"os"
"slices"
"strings"
"testing"

Expand All @@ -23,6 +24,16 @@ var (
tmpDir = ""
)

func TestGetInAscendingOrder(t *testing.T) {
input := []string{"7.0.1rc2", "6.1.0", "6.0.1", "10.11.12", "6.0.0", "7.0.1", "6.0.0rc2", "7.0.1rc1", "10.11.12rc1", "6.0.0rc1"}
want := []string{"6.0.0rc1", "6.0.0rc2", "6.0.0", "6.0.1", "6.1.0", "7.0.1rc1", "7.0.1rc2", "7.0.1", "10.11.12rc1", "10.11.12"}

got := versions.GetInAscendingOrder(input)
if !slices.Equal(got, want) {
t.Errorf("GetInAscendingOrder(): got %s, want %s", got, want)
}
}

func TestMain(m *testing.M) {
var err error
tmpDir, err = os.MkdirTemp("", "version_test")
Expand All @@ -41,7 +52,7 @@ func TestResolveVersion(t *testing.T) {
s.Finish()

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

if err != nil {
Expand All @@ -59,7 +70,7 @@ func TestResolvePatchVersion(t *testing.T) {
s.Finish()

gcs := &repositories.GCSRepo{}
repos := core.CreateRepositories(nil, gcs, nil, nil, nil, false)
repos := core.CreateRepositories(gcs, nil, nil, nil, false)
version, _, err := repos.ResolveVersion(tmpDir, versions.BazelUpstream, "4.0.0-patch1", config.Null())

if err != nil {
Expand All @@ -81,7 +92,7 @@ func TestResolveLatestRcVersion(t *testing.T) {
s.Finish()

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

if err != nil {
Expand All @@ -99,7 +110,7 @@ func TestResolveLatestRcVersion_WithFullRelease(t *testing.T) {
s.Finish()

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

if err != nil {
Expand All @@ -119,7 +130,7 @@ func TestResolveLatestVersion_TwoLatestVersionsDoNotHaveAReleaseYet(t *testing.T
s.Finish()

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

if err != nil {
Expand All @@ -141,7 +152,7 @@ func TestResolveLatestVersion_ShouldOnlyReturnStableReleases(t *testing.T) {
s.Finish()

gcs := &repositories.GCSRepo{}
repos := core.CreateRepositories(gcs, nil, nil, nil, nil, false)
repos := core.CreateRepositories(gcs, nil, nil, nil, false)
version, _, err := repos.ResolveVersion(tmpDir, versions.BazelUpstream, "latest-1", config.Null())

if err != nil {
Expand All @@ -160,7 +171,7 @@ func TestResolveLatestVersion_ShouldFailIfNotEnoughReleases(t *testing.T) {
s.Finish()

gcs := &repositories.GCSRepo{}
repos := core.CreateRepositories(gcs, nil, nil, nil, nil, false)
repos := core.CreateRepositories(gcs, nil, nil, nil, false)
_, _, err := repos.ResolveVersion(tmpDir, versions.BazelUpstream, "latest-1", config.Null())

if err == nil {
Expand All @@ -177,7 +188,7 @@ func TestResolveLatestVersion_GCSIsDown(t *testing.T) {
g.Transport.AddResponse("https://www.googleapis.com/storage/v1/b/bazel/o?delimiter=/", 500, "", nil)

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

if err == nil {
Expand All @@ -194,7 +205,7 @@ func TestResolveLatestVersion_GitHubIsDown(t *testing.T) {
transport.AddResponse("https://api.github.com/repos/bazelbuild/bazel/releases", 500, "", nil)

gh := repositories.CreateGitHubRepo("test_token")
repos := core.CreateRepositories(nil, nil, gh, nil, nil, false)
repos := core.CreateRepositories(nil, gh, nil, nil, false)

_, _, err := repos.ResolveVersion(tmpDir, "some_fork", "latest", config.Null())

Expand All @@ -209,7 +220,7 @@ func TestResolveLatestVersion_GitHubIsDown(t *testing.T) {

func TestAcceptRollingReleaseName(t *testing.T) {
gcs := &repositories.GCSRepo{}
repos := core.CreateRepositories(nil, nil, nil, nil, gcs, false)
repos := core.CreateRepositories(nil, nil, nil, gcs, false)

for _, version := range []string{"10.0.0-pre.20201103.4", "10.0.0-pre.20201103.4.2"} {
resolvedVersion, _, err := repos.ResolveVersion(tmpDir, "", version, config.Null())
Expand All @@ -231,7 +242,7 @@ func TestResolveLatestRollingRelease(t *testing.T) {
s.Finish()

gcs := &repositories.GCSRepo{}
repos := core.CreateRepositories(nil, nil, nil, nil, gcs, false)
repos := core.CreateRepositories(nil, nil, nil, gcs, false)

version, _, err := repos.ResolveVersion(tmpDir, "", rollingReleaseIdentifier, config.Null())

Expand All @@ -256,7 +267,7 @@ func TestAcceptFloatingReleaseVersions(t *testing.T) {
s.Finish()

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

if err != nil {
Expand Down
140 changes: 52 additions & 88 deletions core/repositories.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,40 +22,24 @@ const (
// DownloadFunc downloads a specific Bazel binary to the given location and returns the absolute path.
type DownloadFunc func(destDir, destFile string) (string, error)

// ReleaseFilter filters Bazel versions based on specific criteria.
type ReleaseFilter func(matchesSoFar int, currentVersion string) bool
// LTSFilter filters Bazel versions based on specific criteria.
type LTSFilter func(string) bool

func lastNReleases(max int) ReleaseFilter {
return func(matchesSoFar int, currentVersion string) bool {
return max < 1 || matchesSoFar < max
}
}

// filterReleasesByTrack only works reliably if iterating on Bazel versions in descending order.
func filterReleasesByTrack(track int) ReleaseFilter {
prefix := fmt.Sprintf("%d.", track)
return func(matchesSoFar int, currentVersion string) bool {
return matchesSoFar == 0 && strings.HasPrefix(currentVersion, prefix)
}
}

// ReleaseRepo represents a repository that stores LTS Bazel releases.
type ReleaseRepo interface {
// GetReleaseVersions returns a list of all available release versions that match the given filter function.
// Warning: the filter only works reliably if the versions are processed in descending order!
GetReleaseVersions(bazeliskHome string, filter ReleaseFilter) ([]string, error)

// DownloadRelease downloads the given Bazel version into the specified location and returns the absolute path.
DownloadRelease(version, destDir, destFile string, config config.Config) (string, error)
// FilterOpts represents options relevant to filtering Bazel versions.
type FilterOpts struct {
MaxResults int
Track int
Filter LTSFilter
}

// CandidateRepo represents a repository that stores Bazel release candidates.
type CandidateRepo interface {
// GetCandidateVersions returns the versions of all available release candidates.
GetCandidateVersions(bazeliskHome string) ([]string, error)
// LTSRepo represents a repository that stores LTS Bazel releases and their candidates.
type LTSRepo interface {
// GetLTSVersions returns a list of all available LTS release (candidates) that match the given filter options.
// Warning: Filters only work reliably if the versions are processed in descending order!
GetLTSVersions(bazeliskHome string, opts *FilterOpts) ([]string, error)

// DownloadCandidate downloads the given Bazel release candidate into the specified location and returns the absolute path.
DownloadCandidate(version, destDir, destFile string, config config.Config) (string, error)
// DownloadLTS downloads the given Bazel version into the specified location and returns the absolute path.
DownloadLTS(version, destDir, destFile string, config config.Config) (string, error)
}

// ForkRepo represents a repository that stores a fork of Bazel (releases).
Expand Down Expand Up @@ -88,8 +72,7 @@ type RollingRepo interface {

// Repositories offers access to different types of Bazel repositories, mainly for finding and downloading the correct version of Bazel.
type Repositories struct {
Releases ReleaseRepo
Candidates CandidateRepo
LTS LTSRepo
Fork ForkRepo
Commits CommitRepo
Rolling RollingRepo
Expand All @@ -105,17 +88,15 @@ func (r *Repositories) ResolveVersion(bazeliskHome, fork, version string, config

if vi.IsFork {
return r.resolveFork(bazeliskHome, vi, config)
} else if vi.IsRelease {
return r.resolveRelease(bazeliskHome, vi, config)
} else if vi.IsCandidate {
return r.resolveCandidate(bazeliskHome, vi, config)
} else if vi.IsLTS {
return r.resolveLTS(bazeliskHome, vi, config)
} else if vi.IsCommit {
return r.resolveCommit(bazeliskHome, vi, config)
} else if vi.IsRolling {
return r.resolveRolling(bazeliskHome, vi, config)
}

return "", nil, fmt.Errorf("Unsupported version identifier '%s'", version)
return "", nil, fmt.Errorf("unsupported version identifier '%s'", version)
}

func (r *Repositories) resolveFork(bazeliskHome string, vi *versions.Info, config config.Config) (string, DownloadFunc, error) {
Expand All @@ -135,35 +116,36 @@ func (r *Repositories) resolveFork(bazeliskHome string, vi *versions.Info, confi
return version, downloader, nil
}

func (r *Repositories) resolveRelease(bazeliskHome string, vi *versions.Info, config config.Config) (string, DownloadFunc, error) {
lister := func(bazeliskHome string) ([]string, error) {
var filter ReleaseFilter
if vi.TrackRestriction > 0 {
// Optimization: only fetch matching releases if an LTS track is specified.
filter = filterReleasesByTrack(vi.TrackRestriction)
} else {
// Optimization: only fetch last (x+1) releases if the version is "latest-x".
filter = lastNReleases(vi.LatestOffset + 1)
}
return r.Releases.GetReleaseVersions(bazeliskHome, filter)
}
version, err := resolvePotentiallyRelativeVersion(bazeliskHome, lister, vi)
if err != nil {
return "", nil, err
var IsRelease = func(version string) bool {
return !strings.Contains(version, "rc")
}

var IsCandidate = func(version string) bool {
return strings.Contains(version, "rc")
}

func (r *Repositories) resolveLTS(bazeliskHome string, vi *versions.Info, config config.Config) (string, DownloadFunc, error) {
opts := &FilterOpts{
// Optimization: only fetch last (x+1) releases if the version is "latest-x".
MaxResults: vi.LatestOffset + 1,
Track: vi.TrackRestriction,
}
downloader := func(destDir, destFile string) (string, error) {
return r.Releases.DownloadRelease(version, destDir, destFile, config)

if vi.IsRelease {
opts.Filter = IsRelease
} else {
opts.Filter = IsCandidate
}
return version, downloader, nil
}

func (r *Repositories) resolveCandidate(bazeliskHome string, vi *versions.Info, config config.Config) (string, DownloadFunc, error) {
version, err := resolvePotentiallyRelativeVersion(bazeliskHome, r.Candidates.GetCandidateVersions, vi)
lister := func(bazeliskHome string) ([]string, error) {
return r.LTS.GetLTSVersions(bazeliskHome, opts)
}
version, err := resolvePotentiallyRelativeVersion(bazeliskHome, lister, vi)
if err != nil {
return "", nil, err
}
downloader := func(destDir, destFile string) (string, error) {
return r.Candidates.DownloadCandidate(version, destDir, destFile, config)
return r.LTS.DownloadLTS(version, destDir, destFile, config)
}
return version, downloader, nil
}
Expand Down Expand Up @@ -295,19 +277,13 @@ func (r *Repositories) DownloadFromFormatURL(config config.Config, formatURL, ve
}

// CreateRepositories creates a new Repositories instance with the given repositories. Any nil repository will be replaced by a dummy repository that raises an error whenever a download is attempted.
func CreateRepositories(releases ReleaseRepo, candidates CandidateRepo, fork ForkRepo, commits CommitRepo, rolling RollingRepo, supportsBaseURL bool) *Repositories {
func CreateRepositories(lts LTSRepo, fork ForkRepo, commits CommitRepo, rolling RollingRepo, supportsBaseURL bool) *Repositories {
repos := &Repositories{supportsBaseURL: supportsBaseURL}

if releases == nil {
repos.Releases = &noReleaseRepo{err: errors.New("Bazel LTS releases are not supported")}
} else {
repos.Releases = releases
}

if candidates == nil {
repos.Candidates = &noCandidateRepo{err: errors.New("Bazel release candidates are not supported")}
if lts == nil {
repos.LTS = &noLTSRepo{err: errors.New("Bazel LTS releases & candidates are not supported")}
} else {
repos.Candidates = candidates
repos.LTS = lts
}

if fork == nil {
Expand All @@ -331,31 +307,19 @@ func CreateRepositories(releases ReleaseRepo, candidates CandidateRepo, fork For
return repos
}

// The whole point of the structs below this line is that users can simply call repos.Releases.GetReleaseVersions()
// (etc) without having to worry whether `Releases` points at an actual repo.

type noReleaseRepo struct {
err error
}

func (nrr *noReleaseRepo) GetReleaseVersions(bazeliskHome string, filter ReleaseFilter) ([]string, error) {
return nil, nrr.err
}

func (nrr *noReleaseRepo) DownloadRelease(version, destDir, destFile string, config config.Config) (string, error) {
return "", nrr.err
}
// The whole point of the structs below this line is that users can simply call repos.LTS.GetLTSVersions()
// (etc) without having to worry whether `LTS` points at an actual repo.

type noCandidateRepo struct {
type noLTSRepo struct {
err error
}

func (ncc *noCandidateRepo) GetCandidateVersions(bazeliskHome string) ([]string, error) {
return nil, ncc.err
func (nolts *noLTSRepo) GetLTSVersions(bazeliskHome string, opts *FilterOpts) ([]string, error) {
return nil, nolts.err
}

func (ncc *noCandidateRepo) DownloadCandidate(version, destDir, destFile string, config config.Config) (string, error) {
return "", ncc.err
func (nolts *noLTSRepo) DownloadLTS(version, destDir, destFile string, config config.Config) (string, error) {
return "", nolts.err
}

type noForkRepo struct {
Expand Down
Loading