> ## Documentation Index
> Fetch the complete documentation index at: https://site.aspect.build/llms.txt
> Use this file to discover all available pages before exploring further.

# Quickstart

> Install the Aspect CLI, run Bazel build and test on a repo, customize behavior with config.axl, and write your first custom AXL task in under 10 minutes.

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

```shell theme={null}
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](/docs/cli/install) for other installation methods (Homebrew, GitHub Actions, `bazel_env`, manual download).

Verify the install:

```shell theme={null}
aspect --version
# aspect launcher 2026.27.7
```

## 2. Point `aspect` at a Bazel repo

<Tabs>
  <Tab title="I already use Bazel">
    `aspect build`, `aspect test`, and `aspect run` work in any existing Bazel workspace. `cd` into your repo and try them out:

    ```shell theme={null}
    aspect build //...
    aspect test //...

    # Or scope to a specific target pattern
    aspect build //path/to/package/...
    aspect test //path/to/package:my_test

    # Run a binary target
    aspect run //path/to/package:my_binary
    ```

    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)

    Then skip to step 3, the rest of the quickstart works against your existing `MODULE.bazel` / `WORKSPACE`.
  </Tab>

  <Tab title="I'm new to Bazel">
    You'll need `bazel` on your PATH alongside `aspect`. The recommended way is [Bazelisk](https://github.com/bazelbuild/bazelisk), a launcher that reads `.bazelversion` from your repo and downloads the matching Bazel, same idea as the `aspect` launcher.

    ```shell theme={null}
    # macOS (Homebrew)
    brew install bazelisk

    # Linux (download a release binary)
    curl -fsSL -o /usr/local/bin/bazel \
      https://github.com/bazelbuild/bazelisk/releases/latest/download/bazelisk-linux-amd64
    chmod +x /usr/local/bin/bazel
    ```

    Verify:

    ```shell theme={null}
    bazel --version
    ```

    Now create a tiny Bazel repo to play with:

    ```shell theme={null}
    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:

    ```shell theme={null}
    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)
  </Tab>
</Tabs>

## 3. Discover what's available

```shell theme={null}
aspect help
```

The output lists every task the CLI knows about, including any you've defined in this repo. The built-in set:

| Command           | What it does                                        |
| ----------------- | --------------------------------------------------- |
| `aspect build`    | Build Bazel targets                                 |
| `aspect test`     | Run Bazel tests with optional LCOV coverage         |
| `aspect run`      | Build and run a binary target                       |
| `aspect format`   | Format only the files changed in the PR             |
| `aspect lint`     | Run linters with hold-the-line strategy             |
| `aspect gazelle`  | Generate and sync BUILD files                       |
| `aspect delivery` | Deliver 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:

```python title=".aspect/version.axl" theme={null}
version("2026.27.7")
```

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](/docs/cli/version-pinning) 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:

```python title=".aspect/config.axl" theme={null}
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`](/docs/cli/tasks/build_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](/docs/cli/guides/basic#how-tasks-are-registered) for the other registration paths):

```python title=".aspect/build-and-list.axl" theme={null}
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):

```shell theme={null}
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](/docs/cli/guides/basic) for the full walkthrough.

## 7. Run the same tasks in CI

On first invocation, the launcher uses the version pinned in `.aspect/version.axl` to fetch the matching CLI. Local and CI stay in sync as long as the launcher is on PATH.

On **GitHub Actions**, use the [`aspect-build/setup-aspect`](https://github.com/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](/aspect-workflows) runners the launcher is pre-installed regardless of provider, skip the install step.)

### Set up `ASPECT_API_TOKEN` (recommended)

`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](/docs/cli/authentication):

1. **Sign up for a free Aspect account** at [signup.aspect.build](https://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](/docs/cli/authentication#github-app-installation). 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](https://auth.aspect.build/oauth/portal/api-tokens). 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 as a [Buildkite secret](https://buildkite.com/docs/pipelines/security/secrets/buildkite-secrets) named exactly `ASPECT_API_TOKEN`. The Aspect CLI reads it directly from the secret store, so no `env:` mapping is needed in your pipeline.
   * **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. On Buildkite, a secret named `ASPECT_API_TOKEN` is read directly from the secret store — no `env:` mapping, so the raw token never lands in the job environment.

<CodeGroup>
  ```yaml GitHub Actions theme={null}
  permissions:
    id-token: write

  jobs:
    test:
      runs-on: ubuntu-latest
      steps:
        - uses: actions/checkout@v6
        - uses: aspect-build/setup-aspect@2306377a61c45954ab2df7c7311698b109364352 # v2026.26.9
          with:
            aspect-api-token: ${{ secrets.ASPECT_API_TOKEN }}
        - run: |
            aspect build //...
            aspect test //...
  ```

  ```yaml Buildkite theme={null}
  steps:
    - command: |
        curl -fsSL https://install.aspect.build | bash
        aspect build //...
        aspect test //...
  ```

  ```yaml GitLab theme={null}
  default:
    before_script:
      - curl -fsSL https://install.aspect.build | bash
      - export PATH="$HOME/.local/bin:$PATH"
  build:
    script: aspect build //...
  test:
    script: aspect test //...
  # Set ASPECT_API_TOKEN as a masked CI/CD variable in Settings → CI/CD → Variables.
  ```

  ```yaml CircleCI theme={null}
  jobs:
    build:
      docker:
        - image: cimg/base:current
      steps:
        - checkout
        - run: |
            curl -fsSL https://install.aspect.build | bash
            aspect build //...
  # Set ASPECT_API_TOKEN as a project environment variable or context secret.
  ```
</CodeGroup>

[Running tasks in CI](/docs/cli/tasks-ci) has complete pipeline examples for every provider.

## Where to next

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