bazel at Aspect Cloud.
A vanilla bazel build //... then reads and writes a cache shared across jobs, branches and developers, and every invocation shows up in the Build Results UI. Your pipeline keeps whatever image and executor it has today.
It’s one step on GitHub Actions, Buildkite, CircleCI or GitLab, or a few shell commands anywhere else.
What the setup step does
- Installs the Aspect CLI launcher and Bazelisk, each skipped when the binary is already on
PATH. - Authenticates, exchanging your Aspect API token for a short-lived session JWT. It’s stored where a developer’s login goes: the OS keyring when the CLI can open one (on Linux, the kernel keyring), otherwise a
0600file at~/.aspect/credentials.json. Later steps in the same job reuse it. SetASPECT_CREDENTIALS_FILEto store it in a file at that path instead. - Writes the machine rc with
aspect setup bazelrc --home:~/.aspect/bazelrc, imported from~/.bazelrc, naming Aspect Cloud’s cache and build event endpoints. - Leaves your build alone. Your existing
bazelsteps are unchanged.
--home keeps the rc out of the checkout. Without it the task writes <workspace>/.aspect/bazelrc and a try-import in the repository’s .bazelrc, files meant to be committed, which on a runner would leave the checkout dirty.
Add the setup step
Store your Aspect API token as a CI secret namedASPECT_API_TOKEN. The GitHub Action takes it as the aspect-api-token input; the other integrations read it from the environment. On Buildkite, expose it with the step’s secrets: key (agent v3.106.0 or later) rather than env:, which Buildkite interpolates at upload and so writes the token into the job definition. Either way it reaches aspect auth login on stdin, so it stays out of both the process table and the job log.
- GitHub Actions
- Buildkite
- CircleCI
- GitLab CI
before_script replaces the template’s, and the setup doesn’t run. List the template’s first, with !reference:
Where each integration lives
The versions above were current when this page was written. Each publishes on its own schedule, so take the latest from its own listing:
Buildkite’s shorthand resolves
aspect-build/setup-aspect to the full repository name, which is why the plugin reference looks shorter than the repository it comes from.
The GitHub and Buildkite examples pin a commit; the CircleCI and GitLab ones pin a
version. An Action reference and a Buildkite plugin reference are git refs, so a commit
is the tighter pin. A CircleCI orb resolves from the orb registry and a GitLab component
from the CI/CD Catalog, where an immutable published version is the pin. The trailing
comment gives the version each commit corresponds to.
Label your builds
A vanillabazel call reaches the build event service with no commit, branch or pull request attached, so it arrives in the UI anonymous. aspect setup workspace-data supplies them as Bazel’s workspace status command:
--workspace_status_command, so a repository that already has a status script calls the task from inside it. See composing with an existing script.
Tuning the setup step
Every integration takes the same inputs for the rc. Each is passed toaspect setup bazelrc, and an unset input leaves that flag off the command line, so the CLI applies its own default:
Each CI host passes them its own way:
The GitHub Action reads no environment variables. It takes the credential as an input, and it’s the only integration that manages caches:
disk-cache and repository-cache also take a string, which keys the cache: ${{ github.workflow }} gives each workflow its own.
If both bazelrc-generate and disk-cache are off, the Action warns that the job has no cache.
--remote on CI
Aspect CLI tasks don’t need the flag here. A task’s --remote defaults to auto, which means bare --remote on CI, and nothing off it, so aspect build and aspect test in the same job reach the cache and build events on their own.
auto won’t wire an endpoint the job can’t authenticate to, because Bazel treats a credential helper that returns no token as fatal, so a job without ASPECT_API_TOKEN builds without the cache. An explicit --remote is honored regardless.
Any other CI
Any CI that can run shell can run the same commands. The CLI recognizes the packaged hosts by their own variables, and treats any other host as CI whenCI is set. Jenkins and TeamCity don’t set it, so the snippet does; without it, aspect setup bazelrc would treat the job as a developer’s machine and turn nothing on. Set it in the job’s environment too, so later aspect tasks treat the job as CI.
sudo, so the job needs it. The launcher version is separate from the CLI version, which .aspect/version.axl pins.
What the packaged integrations do that this doesn’t:
- They install Bazelisk too, skipping it when
bazelis already onPATH. If your image already has Bazel, you need nothing here. - They treat every setup failure as a warning. The snippet above fails the job if the install or the login fails. To keep the job going instead, append
|| trueto steps 2 and 3; the rc goes unwritten andbazelruns as before. - The GitHub Action caches the launcher’s downloads between jobs. Without that, each job downloads the CLI again.
Beyond the cache
Remote execution and CI runners are available on Aspect Enterprise today and are coming soon to Aspect Cloud (early access). See choosing an offering.Related
- Local setup: the same cache from a developer’s machine.
aspect setup bazelrc: what the rc contains and how to adjust it.- Build metadata: commit, branch and pull request attribution.
- Generating an API token: where
ASPECT_API_TOKENcomes from.

