setup-aspect
June 12, 2026 · View on GitHub
GitHub Action that installs the Aspect CLI launcher, installs Bazelisk (skipped automatically if bazel is already on PATH), configures Bazel for CI caching, and authenticates with the Aspect API — all in one step.
Usage
Minimal — latest launcher, with Bazelisk and caching on by their defaults:
- uses: actions/checkout@v6
- uses: aspect-build/setup-aspect@<commit-sha>
- run: aspect test //...
Full — pin versions, segregate caches per workflow, and authenticate:
permissions:
id-token: write # required for Aspect CLI's ArtifactUpload feature
jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: aspect-build/setup-aspect@<commit-sha>
with:
launcher-version: 2026.22.39
bazelisk-version: 1.x
bazelisk-cache: true
disk-cache: ${{ github.workflow }}
repository-cache: ${{ github.workflow }}
aspect-api-token: ${{ secrets.ASPECT_API_TOKEN }}
- run: aspect build //...
- run: aspect test //...
Pin to a full-length commit SHA, not a branch or tag — tags are mutable and can be repointed at malicious code, so SHA-pinning is the GitHub-recommended way to use third-party actions. Annotate with the version in a trailing comment for readability, and let Dependabot or Renovate keep the SHA fresh:
- uses: aspect-build/setup-aspect@<commit-sha> # v2026.22.3
Find the SHA on the Releases page — each release's notes include a copy-paste snippet pinned to its SHA. (We also push lightweight YYYY.VV weekly tags for discoverability.) Either way, pin to the SHA rather than the tag, since tags are mutable.
What it does
setup-aspect runs in one of two modes depending on the runner:
On a vanilla GHA runner (ubuntu-latest, macos-latest, self-hosted, etc.)
- Install the Aspect CLI launcher (
aspectonPATH). Reads.aspect/version.axlfrom your repo on first invocation to fetch the matching CLI binary. PointsASPECT_LAUNCHER_CACHEandASPECT_CLI_CACHEat GHA-cacheable directories (with distinct roots) and restores them, so the download is skipped on warm runs. - Install Bazelisk (default:
latest). Skipped ifbazelis already on PATH (you don't need bothsetup-bazelandsetup-aspect). Caches the binary viaactions/tool-cacheand viaactions/cache(unlessbazelisk-cache: false). - Configure
~/.bazelrcwith--disk_cache,--repository_cache, and any extra lines from thebazelrcinput. Idempotent — appends only directives that aren't already present. - Restore caches via
@actions/cache. The disk, repository, Bazelisk, and Aspect CLI caches are all on by default on ephemeral runners. The post-job hook saves them on exit. - Authenticate to the Aspect API via
aspect auth login --with-api-tokenifaspect-api-tokenis set. The resulting short-lived JWT is persisted locally; the long-lived<client_id>:<secret>is never written toGITHUB_ENV(see Security below).
On an Aspect Workflows runner (ASPECT_WORKFLOWS_RUNNER env var set)
The runner image already provides aspect and bazel, and aspect <task> invocations always wire themselves into the runner's caching infrastructure on their own. setup-aspect's job on these runners is to extend the same wiring to raw bazel calls outside of aspect <task> — many pipelines mix aspect build with a separate bazel build step, and without setup-aspect those bare bazel invocations would miss the deployment's remote cache, BES backend, and local NVMe disk cache.
- Waits for runner cache warming to complete if it is still in progress — the same wait
aspect <task>performs before running, extended to jobs that go on to run rawbazelso they don't race the warming bootstrap and miss the warmed caches. - Configures raw
bazelto use the runner's caching infrastructure — the same remote cache, BES backend, and NVMe disk cache thataspect <task>already uses. - Authenticates to the Aspect API (same as ephemeral mode).
- Skips launcher install, Bazelisk install, GHA cache wiring, and
~/.bazelrcappend — not needed on Workflows runners (aspect/bazelare already available and Bazel is routed through the runner's own caching).
Detection is based on the ASPECT_WORKFLOWS_RUNNER env var.
Inputs
| Input | Default | Purpose |
|---|---|---|
launcher-version | latest | Aspect CLI launcher version to install (e.g. 2026.22.39). The CLI version is pinned by .aspect/version.axl in your repo — the launcher reads that file and downloads the matching CLI on first aspect invocation. Ignored on Workflows runners or when launcher-install is false. |
launcher-install | true | Whether to install the Aspect CLI launcher. Set to false if you're providing aspect yourself (e.g. via a `curl install.aspect.build |
aspect-api-token | — | Long-lived <CLIENT_ID>:<SECRET> token, typically passed via the GitHub Actions secrets context (e.g. as secrets.ASPECT_API_TOKEN in a with: block). When set, setup-aspect runs aspect auth login --with-api-token (piping the token via stdin) — the short-lived JWT it produces is persisted locally for downstream aspect <task> calls via ctx.aspect.auth.credentials(). The long-lived token is not exported to GITHUB_ENV. Leave empty to skip the auth step. |
bazelisk-version | latest | Bazelisk version to install (semver range or exact, e.g. 1.x or 1.21.0). Default: latest (downloaded via GitHub's /releases/latest/download/<asset> redirect — no API call, no rate-limit risk). The install is skipped regardless of this input if bazel is already on PATH (setup-bazel ran first, you're on an Aspect Workflows runner, etc.). |
bazelisk-cache | true | Cache the Bazelisk binary across runs (keyed on .bazelversion). On by default; set false to disable. |
disk-cache | true | Cache Bazel's --disk_cache outputs across runs. On by default. Set to a string to segregate caches by stage (a common pattern is to key on the GHA github.workflow value so each workflow gets its own cache). false disables. Ignored on Workflows runners (which route Bazel through their own remote cache). |
repository-cache | true | Enable Bazel --repository_cache (external-repo download bytes). On by default. Set to a string to segregate caches by stage (e.g. the GHA github.workflow value). false disables. Ignored on Workflows runners. |
bazelrc | "" | Extra lines appended to ~/.bazelrc. Multiline YAML supported. Append-only and idempotent. Ignored on Workflows runners. |
Security
How ASPECT_API_TOKEN is handled
The typical pattern in GHA workflows is:
# DON'T do this — token visible to every step in the job
env:
ASPECT_API_TOKEN: ${{ secrets.ASPECT_API_TOKEN }}
That exposes the long-lived <client_id>:<secret> to every step in the job, including any untrusted third-party action you call. If a malicious action reads process.env, the secret leaks.
setup-aspect handles the token differently:
- You pass
aspect-api-token: ${{ secrets.ASPECT_API_TOKEN }}as an input — visible only to setup-aspect's step. - setup-aspect calls
aspect auth login --with-api-token, piping the token via stdin. - The CLI exchanges the long-lived token with the Aspect API for a short-lived session JWT, and persists the JWT on disk.
- Downstream
aspect <task>invocations in any later step pick up the persisted JWT viactx.aspect.auth.credentials(). - The long-lived token is never written to
GITHUB_ENV— downstream steps see only the JWT on disk.
The JWT has a bounded TTL (24h by default; see Aspect docs). For jobs longer than the JWT TTL, downstream tasks will need to re-authenticate; in practice that affects vanishingly few CI jobs since GitHub Actions caps individual jobs at 6h on hosted runners.
permissions: id-token: write
Several Aspect CLI features (notably ArtifactUpload, which uses the GHA artifact API) require the workflow to grant id-token: write to the job. setup-aspect can't grant the permission itself — only the workflow YAML can — but it detects when the permission is missing and emits a ::warning:: pointing at the docs:
permissions:
id-token: write
If you don't use any feature that requires OIDC, you can ignore the warning.
Coexistence with setup-bazel
You typically don't need both — setup-aspect handles Bazelisk install + cache wiring itself. But if you do run both:
- Cache keys don't collide: setup-aspect uses
setup-aspect-*prefixes; setup-bazel usessetup-bazel-*. - Cache defaults are inverted. setup-bazel ships its disk/repository/Bazelisk caches off by default; setup-aspect ships them on (on ephemeral runners) — the common case wants caching, so you opt out rather than in. Set any of
disk-cache/repository-cache/bazelisk-cachetofalseto disable. - Bazelisk is shared. Both actions use
${RUNNER_TOOL_CACHE}/bazeliskso they reuse the cached binary when at the same version. ~/.bazelrc— setup-bazel overwrites, setup-aspect appends. Run setup-aspect last if you want both actions' directives in the file.bazelalready on PATH — setup-aspect skips its Bazelisk install ifbazelis already there.
Recommendation: pick one. setup-aspect alone handles every concern setup-bazel does, plus the Aspect-specific install + auth steps.
Requirements
- GitHub Actions runner running Linux or macOS. Windows is not supported (the Aspect CLI launcher is POSIX-only).
- On Aspect Workflows runners only, the action configures the environment such that vanilla
bazelcalls use the Aspect Workflows CI runner NVME drive & deployment remote cache automatically.
What this action does NOT do
- It does not invoke any
aspecttask — you callaspect <task>yourself in subsequent steps. - It does not install raw Bazel. Set
bazelisk-versionto install Bazelisk (recommended), or use bazel-contrib/setup-bazel separately if you need Bazel without Bazelisk.
Credits
Inspired by bazel-contrib/setup-bazel by Alex Rodionov. The caching architecture (key shape, @actions/cache integration, post-hook save) and Bazelisk install flow are adapted from setup-bazel with attribution — see THIRD_PARTY_NOTICES.md for the full MIT license text and per-file headers in index.js, post.js, config.js, and install.js.
Related
- Aspect CLI documentation
- Aspect CLI Quickstart — install to a custom task in 10 minutes.
- Running AXL tasks in CI — pipeline examples for GitHub Actions, Buildkite, GitLab CI, CircleCI.
- Aspect Workflows — managed CI runners with
aspectpre-installed plus remote cache and RBE.