> ## 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.

# aspect build and aspect test

> Migrate Rosetta build and test tasks to aspect build and aspect test in CI, covering BazelDefaults, GithubStatusChecks, and ArtifactUpload setup.

`aspect build` and `aspect test` run Bazel builds and tests from your CI pipeline. Environment-specific flags (BES endpoint, build metadata, runner optimizations) are injected by the **`BazelDefaults`** feature. GitHub status checks are posted by the **`GithubStatusChecks`** feature. Artifact uploads are handled by the **`ArtifactUpload`** feature.

<Note>
  Task-level retry, timeout, and conditional execution are now handled by your CI platform rather than the task runner. Bazel's internal retry for transient errors (e.g. `BLAZE_INTERNAL_ERROR`, `LOCAL_ENVIRONMENTAL_ERROR`) is still built into the CLI.
</Note>

## What changed

| Area                                                   | YAML-configured tasks                                                    | Aspect CLI tasks                                                                |
| ------------------------------------------------------ | ------------------------------------------------------------------------ | ------------------------------------------------------------------------------- |
| Invocation                                             | `bazel build` / `bazel test` via task runner                             | `aspect build` / `aspect test`                                                  |
| Targets                                                | `targets:` in `.aspect/workflows/config.yaml` (default `//...`)          | positional args (default `...`)                                                 |
| Bazel flags                                            | `bazel.flags` / `bazel.startup_flags` in `.aspect/workflows/config.yaml` | `--bazel-flag` / `--bazel-startup-flag`                                         |
| BES / remote cache                                     | Auto-configured from platform config                                     | Auto-configured by `BazelDefaults`                                              |
| GitHub status checks                                   | Built into task runner                                                   | `GithubStatusChecks` feature                                                    |
| Artifact upload                                        | Built into task runner                                                   | `ArtifactUpload` feature (opt-in per artifact kind)                             |
| Internal error retry                                   | Bazel retries 3× on transient errors                                     | Bazel retries up to 10× on `BLAZE_INTERNAL_ERROR` / `LOCAL_ENVIRONMENTAL_ERROR` |
| Task timeout                                           | `timeout_in_minutes` in `.aspect/workflows/config.yaml`                  | CI platform job/step timeout                                                    |
| Lifecycle hooks                                        | `before_task` / `after_task` hooks                                       | CI platform steps                                                               |
| Conditional execution                                  | `when:` expressions                                                      | CI platform conditionals                                                        |
| [Workspaces](/docs/cli/migration#top-level-workspaces) | `workspaces:` top-level list (one or more directories)                   | `cd <dir>` in your CI config; run `aspect <task>` from there                    |

## Configuration

Many of the settings below can be applied in either of two ways:

* **CLI flag** — pass the flag at invocation time (e.g. `aspect build --bazel-flag=--config=remote //...`). Good for overrides on individual task invocations, or experimenting with a setting before committing it.
* **`.aspect/config.axl`** — declare it once in your repo's AXL config so every `aspect build` / `aspect test` invocation picks it up. Good for settings that should apply to all invocations of a task type. AXL also lets you compute values programmatically (e.g. branching on CI host or environment), which is more expressive than shell logic wrapped around CLI flags.

Pick whichever fits — you only need one. Where both work, the sections below show them side-by-side under **After**.

### Targets

**Before** — `.aspect/workflows/config.yaml`:

```yaml theme={null}
tasks:
  build:
    targets:
      - //services/...
      - //libs/...
```

**After** — positional CLI args or `config.axl`:

<CodeGroup>
  ```shell CLI theme={null}
  aspect build //services/... //libs/...
  ```

  ```python .aspect/config.axl theme={null}
  def config(ctx: ConfigContext):
      ctx.tasks["build"].args.targets = ["//services/...", "//libs/..."]
  ```
</CodeGroup>

The default (no arguments) is `...` — expands to all rule targets in the package at and beneath your current directory.

### Bazel flags

**Before** — `.aspect/workflows/config.yaml`:

```yaml theme={null}
tasks:
  build:
    bazel:
      flags:
        - --config=remote
        - --jobs=100
      startup_flags:
        - --host_jvm_args=-Xmx4g
```

**After** — CLI flag or `config.axl`:

<CodeGroup>
  ```shell CLI theme={null}
  aspect build \
    --bazel-flag=--config=remote \
    --bazel-flag=--jobs=100 \
    --bazel-startup-flag=--host_jvm_args=-Xmx4g \
    //...
  ```

  ```python .aspect/config.axl theme={null}
  def config(ctx: ConfigContext):
      ctx.tasks["build"].args.bazel_flags = ["--config=remote", "--jobs=100"]
      ctx.tasks["build"].args.bazel_startup_flags = ["--host_jvm_args=-Xmx4g"]
  ```
</CodeGroup>

### Test tag filters

**Before** — `.aspect/workflows/config.yaml`:

```yaml theme={null}
tasks:
  test:
    filters:
      - integration
      - -slow
```

**After** — CLI flag or `config.axl`:

<CodeGroup>
  ```shell CLI theme={null}
  aspect test --bazel-flag=--test_tag_filters=integration,-slow //...
  ```

  ```python .aspect/config.axl theme={null}
  load("@aspect//traits.axl", "BazelTrait")

  def config(ctx: ConfigContext):
      ctx.traits[BazelTrait].extra_flags.append("--test_tag_filters=integration,-slow")
  ```
</CodeGroup>

### Code coverage

**Before** — `.aspect/workflows/config.yaml`:

```yaml theme={null}
tasks:
  test:
    coverage: true
```

**After** — CLI flag or `config.axl`:

<CodeGroup>
  ```shell CLI theme={null}
  aspect test \
    --bazel-flag=--collect_code_coverage \
    --bazel-flag=--instrumentation_filter=^// \
    //...
  ```

  ```python .aspect/config.axl theme={null}
  def config(ctx: ConfigContext):
      ctx.tasks["test"].args.bazel_flags = [
          "--collect_code_coverage",
          "--instrumentation_filter=^//",
      ]
  ```
</CodeGroup>

### Test log upload

**Before** — `.aspect/workflows/config.yaml`:

```yaml theme={null}
tasks:
  test:
    upload_test_logs: failed
```

**After** — CLI flag or `config.axl`:

<CodeGroup>
  ```shell CLI theme={null}
  aspect test --artifact-upload:upload-test-logs=failed //...
  ```

  ```python .aspect/config.axl theme={null}
  load("@aspect//feature/artifacts.axl", "ArtifactUpload")

  def config(ctx: ConfigContext):
      ctx.features[ArtifactUpload].args.upload_test_logs = "failed"
  ```
</CodeGroup>

Upload strategies: `none` (default), `failed`, `executed`, `all`. Test log upload is one knob of the broader [Artifact upload](#artifact-upload) feature — see that section for the full list of artifact kinds and authentication requirements.

## GitHub status checks

The legacy YAML-configured tasks posted build and test results to a **PR comment** via the Aspect Workflows GitHub App (also known as **Marvin**) — there was no per-task GitHub status check.

The new Aspect CLI posts a **GitHub status check** for each `aspect build` / `aspect test` invocation via the **`GithubStatusChecks`** feature. The feature is enabled by default but only posts checks when the CLI is authenticated — without `ASPECT_API_TOKEN` set on the runner and the Aspect Workflows GitHub App installed, nothing reaches GitHub.

`GithubStatusChecks`:

* Creates a check run on the commit as soon as the task starts
* Updates it with live build progress (target counts, failures) during the build
* Completes it with a pass/fail conclusion and a build summary including artifact download links

<Note>
  **Set `--task:name` explicitly** on any invocation that uses `GithubStatusChecks` or `ArtifactUpload`. Status check names and uploaded artifact names are derived from the task name, so a stable, distinct value per invocation makes results identifiable (e.g. `build`, `test`). If you omit `--task:name`, the CLI generates a random one per run and your status checks and artifacts will end up with unpredictable, non-reusable names.
</Note>

### Authentication

`GithubStatusChecks` needs:

* `ASPECT_API_TOKEN` exposed to the runner.
* The **Aspect Workflows GitHub App** (also known as **Marvin**) installed on the GitHub org/repository and linked to your Aspect account, so the CLI can use the App to post the check.

See the [GitHub App](/docs/cli/authentication-github) page for installing and linking the app, and [Authenticating the Aspect CLI](/docs/cli/authentication) for generating the API token.

The most common first-use scenario is a runner without `ASPECT_API_TOKEN` set. In that case the CLI logs one `GitHub status check: authentication failed for <owner>/<repo> — <reason>` line per task and the build continues normally. The reason explains what's missing and includes a link to the setup guide, for example:

```
GitHub status check: authentication failed for acme/widgets — no Aspect credentials — set ASPECT_API_TOKEN (client_id:secret) in the job env. Setup guide: /docs/cli/authentication
```

If checks still don't appear after authentication is in place, search the build log for `GitHub status check:` to see what was reported.

`GithubStatusChecks` failures are isolated from the build/test task: transport errors (DNS, TLS, connection refused), API errors (e.g. GitHub 5xx), and other failures inside the feature are caught and logged but never fail the parent `aspect build` / `aspect test` invocation.

## Artifact upload

The **`ArtifactUpload`** feature uploads build artifacts to the CI platform. All upload kinds are **opt-in** (off by default) because uploaded artifacts are accessible to anyone with repo read access.

<Warning>
  On **public repositories**, uploaded artifacts are downloadable by anyone on the internet. Treat every upload kind as public-by-default and double-check the risk column below before enabling anything.
</Warning>

| Artifact              | Flag                                        | Risk                                                                                                                                   |
| --------------------- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| Test logs             | `--artifact-upload:upload-test-logs=failed` | Medium — test output can leak CI secrets if a test prints, asserts on, or otherwise emits values from env vars (e.g. tokens, API keys) |
| Bazel profile         | `--artifact-upload:upload-profile=true`     | Low — labels and file paths only                                                                                                       |
| Build Event Protocol  | `--artifact-upload:upload-bep=true`         | Low — the CLI redacts env-var values that may hold secrets or sensitive data before upload                                             |
| Compact execution log | `--artifact-upload:upload-exec-log=true`    | High — contains raw action env vars (including secrets); keep off on public repositories                                               |

Enable permanently in `config.axl`:

```python theme={null}
load("@aspect//feature/artifacts.axl", "ArtifactUpload")

def config(ctx: ConfigContext):
    ctx.features[ArtifactUpload].args.upload_test_logs = "failed"
    ctx.features[ArtifactUpload].args.upload_profile   = True
```

<Note>
  `ArtifactUpload` does **not** use the Aspect Workflows GitHub App or `ASPECT_API_TOKEN`. It uploads to your CI platform's native artifact storage using the runner's own credentials: `permissions: id-token: write` on GitHub Actions, the Buildkite agent token, `CI_JOB_TOKEN` on GitLab, the CircleCI runner token. Make sure those credentials are present in the job environment. Supported platforms: Buildkite, CircleCI, GitHub Actions, GitLab.
</Note>

## CI integration examples

<CodeGroup>
  ```yaml GitHub Actions theme={null}
  name: CI

  on:
    pull_request:
      branches: [main]
    push:
      branches: [main]

  jobs:
    build:
      runs-on: [self-hosted, aspect-workflows, aspect-default]
      permissions:
        id-token: write   # ArtifactUpload uses the runner's OIDC token to call the GitHub Actions artifact API
      steps:
        - uses: actions/checkout@v6
        - uses: aspect-build/setup-aspect@2306377a61c45954ab2df7c7311698b109364352 # v2026.26.9
          with:
            aspect-api-token: ${{ secrets.ASPECT_API_TOKEN }}
        - name: Build
          run: aspect build --task:name build //...

    test:
      runs-on: [self-hosted, aspect-workflows, aspect-default]
      permissions:
        id-token: write
      steps:
        - uses: actions/checkout@v6
        - uses: aspect-build/setup-aspect@2306377a61c45954ab2df7c7311698b109364352 # v2026.26.9
          with:
            aspect-api-token: ${{ secrets.ASPECT_API_TOKEN }}
        - name: Test
          run: aspect test --task:name test --artifact-upload:upload-test-logs=failed //...
  ```

  ```yaml Buildkite theme={null}
  steps:
    - label: ":bazel: Build"
      plugins:
        - aspect-build/setup-aspect#1d5768c5d28b72bf523b4722fc9177d2cc2d85c7: ~ # v2026.26.8
      command: aspect build --task:name build //...
      agents:
        queue: aspect-default

    - label: ":bazel: Test"
      plugins:
        - aspect-build/setup-aspect#1d5768c5d28b72bf523b4722fc9177d2cc2d85c7: ~ # v2026.26.8
      command: aspect test --task:name test --artifact-upload:upload-test-logs=failed //...
      agents:
        queue: aspect-default
  ```

  ```yaml GitLab theme={null}
  include:
    - component: $CI_SERVER_FQDN/aspect-build/setup-aspect-gitlab-component/setup@2026.26.8

  build:
    extends: .setup-aspect
    tags:
      - aspect-workflows
      - aspect-default
    script:
      - aspect build --task:name build //...

  test:
    extends: .setup-aspect
    tags:
      - aspect-workflows
      - aspect-default
    script:
      - aspect test --task:name test --artifact-upload:upload-test-logs=failed //...
  # Set ASPECT_API_TOKEN as a masked CI/CD variable in Settings → CI/CD → Variables.
  ```

  ```yaml CircleCI theme={null}
  version: 2.1

  orbs:
    setup-aspect: aspect-build/setup-aspect@2026.26.10

  jobs:
    build:
      machine: true
      resource_class: circleci-org/aspect-default
      steps:
        - checkout
        - setup-aspect/setup
        - run:
            name: Build
            command: aspect build --task:name build //...

    test:
      machine: true
      resource_class: circleci-org/aspect-default
      steps:
        - checkout
        - setup-aspect/setup
        - run:
            name: Test
            command: aspect test --task:name test --artifact-upload:upload-test-logs=failed //...

  workflows:
    ci:
      jobs:
        - build
        - test
  # Set ASPECT_API_TOKEN as a project environment variable or context secret.
  ```
</CodeGroup>
