Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.aspect.build/llms.txt

Use this file to discover all available pages before exploring further.

The Aspect CLI (aspect) is a free, open-source task runner built on top of Bazel. It absorbs the pile of shell scripts and CI YAML that every Bazel monorepo eventually grows — format pre-submits, lint enforcement, BUILD-file generation, release-tag delivery — and replaces it with a single command-line tool that behaves identically on a developer laptop and in CI. Built-in tasks like aspect build and aspect test work out of the box; custom tasks are Starlark functions you drop into your repo and invoke as aspect <name>. This page walks you from a fresh install to a custom task in about ten minutes. Apache-licensed, no Aspect account required — everything below works locally and your existing bazel workflow keeps working alongside it.

1. Install the CLI

curl -fsSL https://install.aspect.build | bash
Works on macOS and Linux, no extra package manager required. The script installs the aspect-launcher binary as aspect on your PATH. The launcher is a thin shim that downloads and runs the actual CLI — like bazelisk for Bazel, or nvm for Node. See How to install the Aspect CLI for other installation methods (Homebrew, GitHub Actions, bazel_env, manual download). Verify the install:
aspect --version
# aspect launcher 2026.22.39

2. Build and test a Bazel repo

aspect build, aspect test, and aspect run work in any Bazel workspace. If you have an existing repo, jump there. Otherwise, create a tiny one to play with:
mkdir hello && cd hello
cat > MODULE.bazel <<'EOF'
module(name = "hello", version = "0.0.0")
bazel_dep(name = "rules_shell", version = "0.8.0")
EOF
cat > BUILD.bazel <<'EOF'
load("@rules_shell//shell:sh_binary.bzl", "sh_binary")
load("@rules_shell//shell:sh_test.bzl", "sh_test")

sh_binary(name = "hello", srcs = ["hello.sh"])
sh_test(name = "hello_test", srcs = ["hello_test.sh"])
EOF
cat > hello.sh <<'EOF'
#!/usr/bin/env bash
echo "hello, aspect!"
EOF
cat > hello_test.sh <<'EOF'
#!/usr/bin/env bash
[ "$(echo hi)" = "hi" ] && echo "ok"
EOF
chmod +x hello.sh hello_test.sh
Now run the three core tasks:
aspect build //...
aspect test //...
aspect run //:hello
Each invocation runs the equivalent bazel command under the hood — same Bazel flags pass through, same exit code — with these additions on top:
  • A status line summarising the task and its phases (🔨 Build, 🧪 Test, 🚀 Run)
  • Automatic retry on transient Bazel errors (BLAZE_INTERNAL_ERROR, LOCAL_ENVIRONMENTAL_ERROR)
  • A consistent task lifecycle that CI integrations can hook into (status checks, annotations, artifact upload)

3. Discover what’s available

aspect help
The output lists every task the CLI knows about, including any you’ve defined in this repo. The built-in set:
CommandWhat it does
aspect buildBuild Bazel targets
aspect testRun Bazel tests with optional LCOV coverage
aspect runBuild and run a binary target
aspect formatFormat only the files changed in the PR
aspect lintRun linters with hold-the-line strategy
aspect gazelleGenerate and sync BUILD files
aspect deliveryDeliver only targets whose outputs actually changed
aspect <task> --help shows every flag for a specific task with its current default.

4. Pin the CLI version

For reproducible builds across developers and CI, pin the CLI version in your repo:
.aspect/version.axl
version("2026.22.39")
The launcher reads this file and downloads the matching aspect-cli binary the first time it’s needed, then caches it. Every developer and every CI runner ends up on the same version regardless of when they installed the launcher. See How to pin the Aspect CLI version for custom sources (local builds, internal mirrors, alternate registries).

5. Customize tasks in config.axl

Built-in tasks accept configuration via .aspect/config.axl at the repo root. The file is evaluated once per invocation — locally and in CI — so a single edit changes behaviour everywhere. This config activates a --config=ci group from your .bazelrc when the CI env var is set, and turns on test-log artifact upload for failures:
.aspect/config.axl
load("@aspect//traits.axl", "BazelTrait")
load("@aspect//feature/artifacts.axl", "ArtifactUpload")

def config(ctx: ConfigContext):
    if bool(ctx.std.env.var("CI")):
        ctx.traits[BazelTrait].extra_flags.extend(["--config=ci"])

    ctx.features[ArtifactUpload].args.upload_test_logs = "failed"
BazelTrait.extra_flags are forwarded to every bazel invocation the CLI makes. ArtifactUpload uploads test logs, BEP, Bazel profiles, and execution logs to your CI platform’s native artifact storage. Both are documented in aspect build & aspect test.

6. Write your first custom task

When the built-ins don’t cover what you need, write your own. Custom tasks are Starlark functions you register with the task() built-in. Drop them in any .axl file inside .aspect/ and they’re auto-discovered as CLI commands (see How tasks are registered for the other registration paths):
.aspect/build-and-list.axl
def _impl(ctx: TaskContext) -> int:
    events = bazel.build_events.iterator(kinds = ["named_set_of_files"])
    build = ctx.bazel.build(
        build_events = [events],
        *ctx.args.targets,
    )

    for event in events:
        for f in event.payload.files:
            ctx.std.io.stdout.write("Built {}\n".format(f.name))

    return build.wait().code

build_and_list = task(
    summary = "Build targets and list every output file.",
    implementation = _impl,
    args = {
        "targets": args.positional(default = ["..."], maximum = 512),
    },
)
Re-run aspect help — your new build-and-list task appears alongside the built-ins (the variable name’s underscores become dashes in the command):
aspect build-and-list //...
# Built hello.sh
# Built hello
# Built hello_test.sh
# Built hello_test
Tasks can call any subprocess, read and write files, query the build graph, subscribe to Bazel’s Build Event Stream, and define their own typed arguments. See How to run and define tasks for the full walkthrough.

7. Run the same tasks in CI

The version of the CLI pinned in .aspect/version.axl is used by the launcher to fetch the matching CLI on first invocation, so local and CI stay in sync as long as the launcher is on PATH. On GitHub Actions, use the aspect-build/setup-aspect action — it installs the launcher (a no-op on Aspect Workflows CI runners, where aspect is pre-installed), installs Bazelisk, and wires up GHA caching. On Buildkite, GitLab, and CircleCI you install the launcher inline with the curl one-liner. (On Aspect Workflows runners the launcher is pre-installed regardless of provider — skip the install step.) ASPECT_API_TOKEN is optional, but setting it up unlocks the CI features that make aspect worth running in CI in the first place: GitHub Status Checks on every PR, inline lint findings as PR review comments, one-click suggested fixes, and more accurate changed-file detection for aspect format and aspect lint. Without it the tasks still build and test normally — the platform integrations just silently skip. One-time setup, all of which is walked through in detail in Authenticating the Aspect CLI:
  1. Sign up for a free Aspect account at signup.aspect.build (no credit card required).
  2. Install the Aspect Workflows GitHub App on your GitHub org and link it to your Aspect account from the authentication page. The App is what lets the CLI post status checks, PR comments, and suggested fixes back to GitHub.
  3. Generate an ASPECT_API_TOKEN in the API Tokens portal. The token is a <CLIENT_ID>:<SECRET> string — copy it as soon as it’s displayed (the secret half is shown only once).
  4. Store the token as a CI secret on each provider you use:
    • GitHub Actions — Settings → Secrets and variables → Actions → New repository secret, named ASPECT_API_TOKEN.
    • Buildkite — store it in your secret manager and reference it from env: (your-buildkite-secret-ref in the example below).
    • GitLab CI — Settings → CI/CD → Variables → Add variable, masked, named ASPECT_API_TOKEN.
    • CircleCI — Project Settings → Environment Variables → Add Environment Variable (or a Context if you want to share across projects).
Then pass the token to each provider as shown below. On GitHub Actions, hand it to setup-aspect via with: aspect-api-token: — this keeps the long-lived secret out of GITHUB_ENV so other steps in the job can’t see it.
permissions:
  id-token: write

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v6
      - uses: aspect-build/setup-aspect@c22a8f64fb38f82f59ce809cd7eb9f8ae096da44 # v2026.23.2
        with:
          aspect-api-token: ${{ secrets.ASPECT_API_TOKEN }}
      - run: |
          aspect build //...
          aspect test //...
Running tasks in CI has complete pipeline examples for every provider.

Where to next

  • Tasks overview — full reference for every built-in task with config options and CI examples.
  • How to run and define tasks — deep dive on writing custom tasks: arguments, processes, filesystem, build queries, phases, cleanup.
  • How to use external AXL modules — pull in extension libraries like rules_lint via MODULE.aspect.
  • AXL reference — every type, function, and module that AXL exposes.
  • Aspect Workflows — purpose-built Bazel CI runners that detect aspect tasks automatically and apply remote cache, RBE, and pre-warmed output bases.