Skip to main content
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

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 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:
After — CLI flag or config.axl (only needed if your formatter target isn’t at the new default //tools/format: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:
After — default behavior already formats changed files only. Pass --scope=all to run on every file the formatter handles:
aspect format detects changed files in this order:
  1. Local git diff <merge-base> — the default. Defaults to --base-ref=origin/main; override with --base-ref or pass an explicit --merge-base=<sha>.
  2. GitHub PR Files API — fallback when git diff can’t resolve the diff base (shallow clones, fetch-depth-restricted runners), on a PR with the Aspect Workflows GitHub App authenticated.

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:
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:
After--soft-fail (default false):
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):
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:
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:
After — CI configuration:

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:

Format the whole tree (--scope=all)

Useful for nightly maintenance jobs or when bringing a new formatter into a repo for the first time:

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