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

Support for installing custom Bazel version #81

Merged
merged 1 commit into from Aug 7, 2019
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 13 additions & 4 deletions README.md
Expand Up @@ -15,12 +15,20 @@ Some ideas how to use it:
- Check it into your repository and recommend users to build your software via `./bazelisk.py build //my:software`. That way, even someone who has never used Bazel or doesn't have it installed can build your software.
- As a company using Bazel or as a project owner, add a `.bazelversion` file to your repository. This will tell Bazelisk to use the exact version specified in the file when running in your workspace. The fact that it's versioned inside your repository will then allow for atomic upgrades of Bazel including all necessary changes. If you install Bazelisk as `bazel` on your CI machines, too, you can even test Bazel upgrades via a normal presubmit / pull request. It will also ensure that users will not try to build your project with an incompatible version of Bazel, which is often a cause for frustration and failing builds.

## How does Bazelisk know which version to run?
## How does Bazelisk know which fork and version to run?

It uses a simple algorithm:
- If the environment variable `USE_BAZEL_VERSION` is set, it will use the version specified in the value.
- Otherwise, if a `.bazelversion` file exists in the current directory or recursively any parent directory, it will read the file and use the the version specified in it.
- Otherwise it will check GitHub for the latest version of Bazel, cache the result for an hour and use that version.
- If the environment variable `USE_BAZEL_VERSION` is set, it will use the fork and version specified in the value.
- Otherwise, if a `.bazelversion` file exists in the current directory or recursively any parent directory, it will read the file and use the fork and version specified in it.
- Otherwise it will use the official release from `bazelbuild/bazel` and check GitHub for the latest version of Bazel, cache the result for an hour and use that version.

Bazelisk currently follows the release convention on `bazelbuild/bazel` to build the URL. The URL format looks like `https://github.com/<FORK>/bazel/releases/download/<VERSION>/<FILENAME>`.

The fork and version should be separated by slash `<FORK>/<VERSION>`:
- If the fork and version are `foobar/0.28.0` and the platform is `linux`, the URL will be `https://github.com/foobar/bazel/releases/download/0.28.0/bazel-0.28.0-linux-x86_64`.
- If the version is not provided `foobar/`, it uses the latest version.
- If the fork is not provided `/0.28.0`, it uses the official release from `bazelbuild/bazel`.
- If no `/` is present `0.28.0`, the value is treated as version and it uses the official release from `bazelbuild/bazel`.

Bazelisk currently understands the following formats for version labels:
- `latest` means the latest stable version of Bazel as released on GitHub. Previous
Expand All @@ -31,6 +39,7 @@ Bazelisk currently understands the following formats for version labels:
- `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.

These formats are not supported on a fork.

In the future we will add support for building Bazel from source at a given commit.

Expand Down
49 changes: 41 additions & 8 deletions bazelisk.go
Expand Up @@ -41,6 +41,7 @@ const (
bazelReal = "BAZEL_REAL"
skipWrapperEnv = "BAZELISK_SKIP_WRAPPER"
wrapperPath = "./tools/bazel"
bazelUpstream = "BAZEL_UPSTREAM"
)

var (
Expand All @@ -60,7 +61,7 @@ func findWorkspaceRoot(root string) string {
return findWorkspaceRoot(parentDirectory)
}

func getBazelVersion() (string, error) {
func getBazelForkAndVersion() (string, error) {
// Check in this order:
// - env var "USE_BAZEL_VERSION" is set to a specific version.
// - env var "USE_NIGHTLY_BAZEL" or "USE_BAZEL_NIGHTLY" is set -> latest
Expand Down Expand Up @@ -107,6 +108,29 @@ func getBazelVersion() (string, error) {
return "latest", nil
}

func parseBazelForkAndVersion(bazelForkAndVersion string) (string, string, error) {
var bazelFork, bazelVersion string

versionInfo := strings.Split(bazelForkAndVersion, "/")

if len(versionInfo) == 1 {
bazelFork, bazelVersion = bazelUpstream, versionInfo[0]
} else if len(versionInfo) == 2 {
bazelFork, bazelVersion = versionInfo[0], versionInfo[1]
} else {
return "", "", fmt.Errorf("invalid version \"%s\", could not parse version with more than one slash", bazelForkAndVersion)
}

if len(bazelFork) == 0 {
bazelFork = bazelUpstream
}
if len(bazelVersion) == 0 {
bazelVersion = "latest"
}

return bazelFork, bazelVersion, nil
}

type release struct {
TagName string `json:"tag_name"`
Prerelease bool `json:"prerelease"`
Expand Down Expand Up @@ -353,7 +377,7 @@ func determineBazelFilename(version string) (string, error) {
return fmt.Sprintf("bazel-%s-%s-%s%s", version, osName, machineName, filenameSuffix), nil
}

func determineURL(version string, isCommit bool, filename string) string {
func determineURL(fork string, version string, isCommit bool, filename string) string {
if isCommit {
var platforms = map[string]string{"darwin": "macos", "linux": "ubuntu1404", "windows": "windows"}
// No need to check the OS thanks to determineBazelFilename().
Expand All @@ -369,16 +393,20 @@ func determineURL(version string, isCommit bool, filename string) string {
kind = "rc" + versionComponents[1]
}

return fmt.Sprintf("https://releases.bazel.build/%s/%s/%s", version, kind, filename)
if fork == bazelUpstream {
return fmt.Sprintf("https://releases.bazel.build/%s/%s/%s", version, kind, filename)
} else {
return fmt.Sprintf("https://github.com/%s/bazel/releases/download/%s/%s", fork, version, filename)
}
}

func downloadBazel(version string, isCommit bool, directory string) (string, error) {
func downloadBazel(fork string, version string, isCommit bool, directory string) (string, error) {
filename, err := determineBazelFilename(version)
if err != nil {
return "", fmt.Errorf("could not determine filename to use for Bazel binary: %v", err)
}

url := determineURL(version, isCommit, filename)
url := determineURL(fork, version, isCommit, filename)
destinationPath := filepath.Join(directory, filename)

if _, err := os.Stat(destinationPath); err != nil {
Expand Down Expand Up @@ -637,9 +665,14 @@ func main() {
log.Fatalf("could not create directory %s: %v", bazeliskHome, err)
}

bazelVersion, err := getBazelVersion()
bazelForkAndVersion, err := getBazelForkAndVersion()
if err != nil {
log.Fatalf("could not get Bazel fork and version: %v", err)
}

bazelFork, bazelVersion, err := parseBazelForkAndVersion(bazelForkAndVersion)
if err != nil {
log.Fatalf("could not get Bazel version: %v", err)
log.Fatalf("could not parse Bazel fork and version: %v", err)
}

resolvedBazelVersion, isCommit, err := resolveVersionLabel(bazeliskHome, bazelVersion)
Expand All @@ -653,7 +686,7 @@ func main() {
log.Fatalf("could not create directory %s: %v", bazelDirectory, err)
}

bazelPath, err := downloadBazel(resolvedBazelVersion, isCommit, bazelDirectory)
bazelPath, err := downloadBazel(bazelFork, resolvedBazelVersion, isCommit, bazelDirectory)
if err != nil {
log.Fatalf("could not download Bazel: %v", err)
}
Expand Down