Bitrise Build Cache CLI - to enable/configure Gradle or Bazel build cache on the machine where you run this CLI.
Important
install/installer.sh and the assets attached to every CLI GitHub release are on the critical path of every Bitrise build — not just builds that opt into the build cache.
The Bitrise default workflow runs the gradle-mirrors activation step (and other CLI-driven steps) unconditionally, and each of those installs the CLI by piping install/installer.sh to sh and fetching the platform tarball + checksum from the latest non-prerelease GitHub release. If any of those (the installer script, the binaries, the checksum file) is broken or missing, the CLI install fails, the mirror activation soft-fails, and Maven Central requests stop going through the Bitrise proxy on the entire fleet — exactly the failure mode behind the 2026-04-28 Maven Central rate-limit incident.
When changing the installer script, the goreleaser config, the release flow, or anything that affects the GitHub release's asset list:
- Smoke-test end-to-end on a real Bitrise build before merging.
- Never publish a CLI GitHub release as anything other than
--prereleaseuntil the binaries have been verified attached. - Treat any failure of the
releaseworkflow as a critical, drop-everything-and-fix incident.
GAR fallback mirror. Every CLI release also mirrors the platform tarballs, the checksums file, and install/installer.sh itself to a public GAR generic repository (build-cache-cli-releases in ip-build-cache-prod, region us-central1). The mirror is used by installer.sh whenever the GitHub primary path fails — both for downloading the binary (already in place) and for resolving the latest tag when github.com is unreachable (via the installer.sh:latest-pointer:VERSION discovery file). The Bitrise preboot init scripts (bitrise-io/build-prebooting-deployments) also fall back to GAR when raw.githubusercontent.com is degraded. Binaries and the pinned installer.sh:<tag>:installer.sh are immutable (describe-or-upload, see #327 postmortem); the documented carve-out is installer.sh:latest-pointer:*, which is mutable by design and only consulted on the already-degraded fallback path. A separate verify-release workflow (chained after release via the release-and-verify pipeline) runs scripts/verify_release.sh to assert both the GH and GAR-only install paths work end-to-end; failures here post to Slack and can be retried independently without re-cutting the tag.
#!/usr/bin/env bash
set -euxo pipefail
# download the Bitrise Build Cache CLI
curl --retry 5 -sSfL 'https://raw.githubusercontent.com/bitrise-io/bitrise-build-cache-cli/main/install/installer.sh' | sh -s -- -b /tmp/bin -d
# run the CLI
/tmp/bin/bitrise-build-cache [COMMAND]If you want to install the CLI to somewhere else you can change the -b PATH parameter.
If you want to install a specific version of the CLI you can use specify the version as the last parameter
of the installer script. For example to install version v0.17.0:
curl --retry 5 -sSfL 'https://raw.githubusercontent.com/bitrise-io/bitrise-build-cache-cli/main/install/installer.sh' | sh -s -- -b /tmp/bin -d v0.17.0Note: DRAFT versions aren't supported by the installer, but releases marked as pre-release are.
To configure Bitrise Build Cache for Gradle on the current machine:
/tmp/bin/bitrise-build-cache activate gradle --cache --cache-push=falseTo configure Bitrise Build Cache for Bazel on the current machine:
/tmp/bin/bitrise-build-cache activate bazel --cache --cache-push=falseFor the options and parameters accepted by the commands call the command with --help flag.
You can also enable the -d flag for more verbose logging. This is helpful for troubleshooting.
The CLI requires the following environment variables to be set for authentication:
- If you're running it on Bitrise CI: no environment variable is required. Bitrise CI generates the necessary authentication config and exposes it as environment variable automatically for builds running on Bitrise CI.
- In any other CI environment or for local development:
- Set
BITRISE_BUILD_CACHE_AUTH_TOKENto a Bitrise Personal Access Token which you can generate on bitrise.io. Related documentation: Bitrise DevCenter. - Set the
BITRISE_BUILD_CACHE_WORKSPACE_IDto the Bitrise Workspace's ID you have Bitrise Build Cache (Trial) enabled for. To find the Workspace ID navigate to the Workspace's page and find the ID in the URL. You can find the related documentation on the Bitrise DevCenter.
- Set
Note: the easiest way to get these parameters and do a Bitrise Build Cache setup is by going to bitrise.io/build-cache, clicking Add new connection on the page and follow the guide there. It'll automatically generate and show the information you need for the setup.
Important: the bitrise-build-cache CLI configures the environment it's running in. If you're running commands in Docker containers you have to run the CLI in the same container in which you run Gradle/Bazel commands in.
It creates the necessary config to enable Build Cache and Command Exec/Invocation Analytics. It does this via adding the config in the $HOME directory.
In case of Gradle it's done via creating or modifying the following two files: $HOME/.gradle/init.d/bitrise-build-cache.init.gradle.kts and $HOME/.gradle/gradle.properties (adding org.gradle.caching=true to gradle.properties).
In case of Bazel it's done via creating or modifying $HOME/.bazelrc.
When activate gradle or activate bazel is called:
- CLI checks whether all the available inputs are available. Inputs (auth token, workspace ID, ...) are read from environment variables or via flags specified for the command.
- Then it checks whether the configuration file(s) already exist in the
$HOMEdirectory. - Then it generates the build cache configuration content (merging with the current content of the configuration file(s) if the file(s) already exist).
- And then it writes the configuration content into the config file(s).
$HOME/.gradle/init.d/bitrise-build-cache.init.gradle.ktsis overwritten when you runactivate gradle. Any modification you do in that file will be overwritten.$HOME/.gradle/gradle.propertiesis modified in the following way: when you runactivate gradlethe CLI will check whether a# [start] generated-by-bitrise-build-cache / # [end] generated-by-bitrise-build-cacheblock is already in the file. If there is, then only the block's content will be modified. If there's no marked block in the properties file yet then the CLI will append it to the file with the necessary content in the block (org.gradle.caching=true).- The CLI will also try to download the Bitrise gradle plugins from the Build Cache to avoid having to rely on maven central.
$HOME/.bazelrcis modified in the following way: when you runactivate bazelthe CLI will check whether a# [start] generated-by-bitrise-build-cache / # [end] generated-by-bitrise-build-cacheblock is already in the file. If there is, then only the block's content will be modified. If there's no marked block in the bazelrc file yet then the CLI will append it to the file with the necessary content in the block.
The codebase follows a three-layer architecture with strict dependency direction:
┌─────────────────────────────────────────────────────┐
│ cmd/ │
│ Thin cobra wrappers — map flags to params, │
│ call pkg/ structs. No business logic. │
│ │
│ cmd/ccache/ cmd/reactnative/ cmd/gradle/ ... │
└──────────────┬──────────────────────────────────────┘
│ imports
▼
┌─────────────────────────────────────────────────────┐
│ pkg/ │
│ Public API for external Go packages (e.g. steps). │
│ Exported structs with public methods. │
│ No cobra dependency. │
│ │
│ pkg/ccache/ pkg/reactnative/ │
│ ├── StorageHelper ├── Activator │
│ ├── Activator ├── Runner │
│ └── InvocationReg. └── (postRunDeps internal) │
└──────────────┬──────────────────────────────────────┘
│ imports
▼
┌─────────────────────────────────────────────────────┐
│ internal/ │
│ Core business logic, config, protocols, analytics. │
│ Not importable outside this module. │
│ │
│ internal/config/ internal/ccache/ │
│ internal/xcelerate/ internal/build_cache/kv/ │
└─────────────────────────────────────────────────────┘
Dependency rules:
cmd/importspkg/andinternal/(for flag types and wiring)pkg/importsinternal/only — nevercmd/internal/never importscmd/orpkg/
Using pkg/ from external Go code (e.g. Bitrise steps):
Instead of shelling out to the CLI binary, Go packages can import the pkg/ structs directly:
import ccachepkg "github.com/bitrise-io/bitrise-build-cache-cli/pkg/ccache"
// Start the ccache storage helper
helper, err := ccachepkg.NewStorageHelper(ccachepkg.StorageHelperParams{
InvocationID: myID,
DebugLogging: true,
})
if err != nil { ... }
err = helper.Start(ctx)import rnpkg "github.com/bitrise-io/bitrise-build-cache-cli/pkg/reactnative"
// Activate React Native build cache
a := &rnpkg.Activator{
Params: rnpkg.ActivatorParams{Gradle: true, Xcode: true, Cpp: true},
}
err := a.Activate(ctx)Refer to the confluence page