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.

This is the per-task migration guide for aspect format. See Migrating from legacy YAML-configured tasks for shared setup (CLI version pinning, the recommended two-step migration path, top-level workspaces:).
The legacy Rosetta format task ran a rules_lint format_multirun target against changed files, archived a diff, and surfaced an annotation if the formatter modified anything. aspect format replaces it. aspect format builds your formatter target, determines the changed file set, and runs the formatter against it. Formatters edit files in place, so a non-zero git status after the run signals that formatting was required. Whether to fail the CI step on that condition is up to your CI script.

What changed

AreaYAML-configured tasksAspect CLI tasks
Invocationbazel format (legacy Aspect CLI command)aspect format
Formatter targettarget: (default //:format)--formatter-target (default //tools/format:format)
Scopeformat_changed: true formats changed files only--scope=changed (default) formats changed files only; pass --scope=all for every file the formatter handles
Changed-file detectionLocal git diff-tree / git diff-index against the head commit, with a GitHub PR Files API fallbackGitHub PR Files API when in a PR + authenticated; otherwise local git diff <merge-base>
DetectionChecked git diff after the formatter to detect “formatting required”aspect format snapshots the tree pre-run via git stash create, then diffs against that snapshot — non-destructive, robust to pre-existing dirty state
Diff archivalArchived to artifacts, plus a CI annotation pointing to it--upload-format-diff (off by default) uploads the patch as a CI artifact via the same uploader build/test use; download URL is recorded on ArtifactsTrait.artifact_urls["format_diff"]
Soft-failsoft_fail: true downgraded the failure to a warning--soft-fail (off by default) — same semantics: WARNING + exit 0 instead of failure. The formatter binary’s own non-zero exit still fails the task regardless
Workspacesrespect_workspaces: true filtered changed files to the current workspace--ignore-pattern='nested/**' (or in .aspect/config.axl) filters nested Bazel workspaces. Combine with cd <dir> in CI when you want different formatter targets per workspace
Ignore patternsignore_patterns: [glob, …] excluded matching files from the diff outcome--ignore-pattern (repeatable) — same effect, plus also drops the matched files from the formatter’s input in changed-files mode so it never runs on them. Pattern syntax: *, **, ?; case-sensitive

Configuration migration

Many of the settings below can be applied in either of two ways:
  • CLI flag — pass the flag at invocation time (e.g. aspect format --scope=all //...). 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 format invocation picks it up. Good for settings that should apply to all invocations of a task type.
Pick whichever fits — you only need one. Where both work, the sections below show them side-by-side under After.

Formatter target

Before.aspect/workflows/config.yaml:
tasks:
  format:
    target: //:format
After — CLI flag or config.axl (only needed if your formatter target isn’t at the new default //tools/format:format):
aspect format --formatter-target=//:format
The default formatter target moved from //:format to //tools/format:format. Putting format_multirun in a sibling package keeps its load(...) out of your root BUILD.bazel — loads in the root package fire on every invocation that touches it (any wildcard, query, or rule that resolves a root-package label), so keeping format-only machinery out of that path saves fetch and analysis overhead on commands that have nothing to do with formatting.If your formatter still lives at the root, set --formatter-target=//:format (or pin it in config.axl). Repos that already have their formatter at //tools/format:format don’t need to set the flag at all.

Scope: changed files vs. the whole tree

Before.aspect/workflows/config.yaml:
tasks:
  format:
    format_changed: true   # default
After — default behavior already formats changed files only. Pass --scope=all to run on every file the formatter handles:
aspect format                     # --scope=changed (default)
aspect format --scope=all //...   # every file the formatter knows how to handle
aspect format detects changed files in this order:
  1. GitHub PR Files API — when running on a PR (any CI host) with the Aspect Workflows GitHub App authenticated. Most accurate.
  2. Local git diff <merge-base> — fallback. Defaults to --base-ref=origin/main; override with --base-ref or pass an explicit --merge-base=<sha>.

Detection: when does aspect format decide formatting is required?

Most rules_lint formatters rewrite files in place and exit 0 even when they made changes. So “did the formatter exit non-zero?” isn’t a useful signal. aspect format instead takes a tree snapshot before running the formatter, then diffs against that snapshot afterward:
pre_snap = git stash create        # SHA, or "" if working tree was clean
run formatter
formatter_diff = git diff <pre_snap | HEAD>
if formatter_diff is non-empty:
    formatting required → fail (or warn under --soft-fail)
git stash create is non-destructive — it writes a commit object to the object store and returns its SHA without touching the working tree, the index, or the stash list. As a result, the detection works correctly on a dirty local clone: any pre-existing changes are absorbed into the snapshot and don’t false-positive as “formatter modified”.

Soft-fail

Before.aspect/workflows/config.yaml:
tasks:
  format:
    soft_fail: true
After--soft-fail (default false):
aspect format --soft-fail
When --soft-fail is set and the formatter modified files, the task prints a WARNING listing the affected files and exits 0. Useful for teams introducing automated formatting incrementally so the CI step doesn’t block merges. The formatter binary’s own non-zero exit (e.g. config error) still fails the task regardless.

Diff archival

Before.aspect/workflows/config.yaml: the legacy task auto-archived format.diff as a CI artifact and pointed an annotation at it. After--upload-format-diff (default false):
aspect format --upload-format-diff
When the formatter modified files, the task writes the diff to a job-scoped tmpfile and uploads it as format.patch via the same uploader build/test use for testlogs. Works on GitHub Actions, Buildkite, CircleCI, and GitLab. The download URL (where the CI provider returns one) lands on ArtifactsTrait.artifact_urls["format_diff"] so future status-check features can link to it. No-op when the working tree is clean post-format, or when not running on a recognized CI host.

Ignore patterns / workspace awareness

The legacy ignore_patterns: [glob, …] excluded matching files from the diff outcome; respect_workspaces: true filtered changed files to the current Bazel workspace. Both map to --ignore-pattern in the new task:
aspect format \
  --ignore-pattern='vendor/**' \
  --ignore-pattern='**/*.generated.go' \
  --ignore-pattern='nested-bazel-workspace/**'
Pattern syntax: * (one segment), ** (zero or more segments), ? (one char). Patterns are case-sensitive and matched against repo-relative file paths. In --scope=changed (default), ignored paths are dropped from the file list passed to the formatter, so it never runs on them. In --scope=all, the formatter walks the tree itself, so ignored files may still be rewritten on disk; ignored paths are excluded only from the post-format diff used to decide whether the task fails. The legacy format task had the same constraint — ignore_patterns filtered the failure-decision diff but couldn’t stop the formatter from rewriting files it discovered itself — so this isn’t a regression. If you need a hard guarantee the formatter never touches a directory in --scope=all, configure exclusion at the formatter level too (most rules_lint helpers accept include/exclude lists).
The “nested Bazel workspace” case (where aspect format from the parent would otherwise stomp the child’s files) is a common motivator for --ignore-pattern. List each nested workspace’s directory and the parent’s formatter leaves them untouched.

Examples

Standard format check on PRs

Before.aspect/workflows/config.yaml:
tasks:
  format:
    target: //tools/format:format
    format_changed: true
After — CI configuration:
on:
  pull_request:
    branches: [main]

jobs:
  format:
    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
    env:
      ASPECT_API_TOKEN: ${{ secrets.ASPECT_API_TOKEN }}
    steps:
      - uses: actions/checkout@v6
        with: { fetch-depth: 0 }
      - name: Format
        run: aspect format --task-key format

Soft-fail (annotate but don’t fail)

For teams introducing automated formatting incrementally, downgrade the failure to a warning so the CI step doesn’t block merges:
jobs:
  format:
    runs-on: [self-hosted, aspect-workflows, aspect-default]
    permissions:
      id-token: write
    env:
      ASPECT_API_TOKEN: ${{ secrets.ASPECT_API_TOKEN }}
    steps:
      - uses: actions/checkout@v6
        with: { fetch-depth: 0 }
      - name: Format (soft-fail)
        run: aspect format --task-key format --soft-fail

Format the whole tree (--scope=all)

Useful for nightly maintenance jobs or when bringing a new formatter into a repo for the first time:
aspect format --task-key format-all --scope=all //...

Archive the diff as a CI artifact (--upload-format-diff)

--upload-format-diff writes the formatter’s patch to a tmpfile and uploads it as format.patch via the same uploader build/test use, so reviewers can apply it locally without re-running the formatter:
jobs:
  format:
    runs-on: [self-hosted, aspect-workflows, aspect-default]
    permissions:
      id-token: write
    env:
      ASPECT_API_TOKEN: ${{ secrets.ASPECT_API_TOKEN }}
    steps:
      - uses: actions/checkout@v6
        with: { fetch-depth: 0 }
      - name: Format
        run: aspect format --task-key format --upload-format-diff
The download URL (when the CI provider returns one — GitHub, GitLab, CircleCI) is recorded on ArtifactsTrait.artifact_urls["format_diff"] for downstream status-check or annotation features to surface.