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

> Migrate the Rosetta lint task to aspect lint with rules_lint aspects, SARIF diagnostics, and GithubLintComments PR annotations from the Aspect CLI.

The legacy Rosetta `lint` task ran `bazel lint` with environment-specific flags injected automatically, parsed a proprietary JSON diagnostics file, and posted GitHub annotations directly from the task runner. `aspect lint` replaces it.

`aspect lint` runs a standard Bazel build with [rules\_lint](https://github.com/aspect-build/rules_lint) aspects enabled, reads SARIF output, and exposes lifecycle hooks for custom reporting.

<Note>
  GitHub PR annotations are no longer baked into the task — they are posted by the **`GithubLintComments`** feature, which authenticates via the [Aspect Workflows GitHub App](/docs/cli/authentication-github) and is enabled by default on GitHub Actions.
</Note>

## What changed

| Area                                                   | YAML-configured tasks                                                                                       | Aspect CLI tasks                                                                                                                                                                                           |
| ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Invocation                                             | `bazel lint` (legacy Aspect CLI command); aspects configured in `.aspect/cli/config.yaml` (`lint.aspects:`) | `aspect lint`; aspects configured per-invocation via `--aspect=//tools/lint:linters.bzl%eslint` (repeatable) or in `.aspect/config.axl` (`ctx.tasks["lint"].args.aspects = […]`)                           |
| Diagnostics format                                     | Proprietary JSON (`--lint_diagnostics_file`)                                                                | SARIF (via `rules_lint_machine` output group)                                                                                                                                                              |
| GitHub annotations                                     | Built into the task                                                                                         | `GithubLintComments` feature via the Aspect Workflows app (on by default)                                                                                                                                  |
| Configuration                                          | `.aspect/workflows/config.yaml` task block                                                                  | `config.axl` / CLI flags                                                                                                                                                                                   |
| Failure strategy                                       | `failure_strategy` string                                                                                   | `--strategy` CLI flag                                                                                                                                                                                      |
| Deduplication                                          | Built-in (highest severity wins)                                                                            | Handled by `rules_lint`                                                                                                                                                                                    |
| Annotation / comment limit                             | 25 annotations, 10 suggestions                                                                              | No built-in limit; `GithubLintComments` enforces its own per-PR cap (configurable via `--github-lint-comments:max-pr-comments`)                                                                            |
| Fix application                                        | `--fix` via task config                                                                                     | `aspect lint --fix`                                                                                                                                                                                        |
| Diff base                                              | `git diff HEAD^..HEAD`                                                                                      | `git diff` against the merge-base of `HEAD` and `--base-ref` (default `origin/main`), with the GitHub PR Files API (Aspect Workflows app authenticated) as fallback when `git diff` can't resolve the base |
| [Workspaces](/docs/cli/migration#top-level-workspaces) | `workspaces:` top-level list (one or more directories)                                                      | `cd <dir>` in your CI config; run `aspect lint` 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 lint --strategy=hard //...`). 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 lint` 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**.

### Lint aspects (`--aspect`)

In the legacy Aspect CLI, the `bazel lint` command picked up its [rules\_lint](https://github.com/aspect-build/rules_lint) aspects from the `lint.aspects` list in `.aspect/cli/config.yaml`:

```yaml theme={null}
lint:
  aspects:
    - //tools/lint:linters.bzl%buf
    - //tools/lint:linters.bzl%eslint
    - //tools/lint:linters.bzl%vale
    - //tools/lint:linters.bzl%tfsec
```

`aspect lint` makes the aspect list explicit at the task site. **At least one `--aspect=<label>` is required**; repeat the flag to run multiple aspects in a single invocation. Each `--aspect=<label>` becomes a Bazel `--aspects=<label>` on the underlying build.

**Recommended: declare aspects in `.aspect/config.axl`** so the same `aspect lint` invocation runs the same aspects locally as in CI. Your CI step doesn't need to enumerate the aspect list at all, and developers running `aspect lint //...` on their machines pick up the exact same set:

```python .aspect/config.axl theme={null}
def config(ctx: ConfigContext):
    ctx.tasks["lint"].args.aspects = [
        "//tools/lint:linters.bzl%buf",
        "//tools/lint:linters.bzl%eslint",
        "//tools/lint:linters.bzl%vale",
        "//tools/lint:linters.bzl%tfsec",
    ]
```

The CLI flag is also fine — useful for one-off invocations, parallel CI jobs that need *different* aspect sets, or experimenting with a new linter before committing it:

```shell theme={null}
aspect lint \
  --aspect=//tools/lint:linters.bzl%buf \
  --aspect=//tools/lint:linters.bzl%eslint \
  --aspect=//tools/lint:linters.bzl%vale \
  --aspect=//tools/lint:linters.bzl%tfsec \
  //...
```

CLI flags override `.aspect/config.axl` defaults, so a job that wants only the fast linters can pass `--aspect=//tools/lint:linters.bzl%shellcheck` and skip the rest.

`linters.bzl` defines the aspects via rules\_lint helpers (e.g. `lint_shellcheck_aspect`, `lint_eslint_aspect`). See the [rules\_lint setup docs](https://github.com/aspect-build/rules_lint) for the canonical pattern, or the [aspect-cli lint example](https://github.com/aspect-build/aspect-cli/tree/main/examples/lint) for a minimal working setup.

### Failure strategy

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

```yaml theme={null}
tasks:
  lint:
    failure_strategy: hold_the_line   # or: soft, hard
```

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

<CodeGroup>
  ```shell CLI theme={null}
  aspect lint --strategy=hold-the-line   # default
  aspect lint --strategy=hard
  aspect lint --strategy=soft
  ```

  ```python .aspect/config.axl theme={null}
  def config(ctx: ConfigContext):
      ctx.tasks["lint"].args.strategy = "hard"
  ```
</CodeGroup>

Strategy reference:

| Legacy `failure_strategy` | New `--strategy`          | Behavior                                                                                                                                                                                                                                                          |
| ------------------------- | ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `hold_the_line` (default) | `hold-the-line` (default) | Fail only on error-severity diagnostics in changed files. "Changed" comes from `git diff` against the merge-base of `HEAD` and `--base-ref` (default `origin/main`), falling back to the GitHub PR Files API on PR builds when `git diff` can't resolve the base. |
| `hard`                    | `hard`                    | Fail on any error-severity diagnostic across all linted files; also fails if a linter exits non-zero                                                                                                                                                              |
| `soft`                    | `soft`                    | Never fail; diagnostics are surfaced but do not affect exit code                                                                                                                                                                                                  |

### Lint targets

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

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

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

<CodeGroup>
  ```shell CLI theme={null}
  aspect lint --aspect=//tools/lint:linters.bzl%eslint //services/... //libs/...
  ```

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

The default (no arguments) is `...` — expands to all rule targets in the package at and beneath your current directory. To lint the entire workspace explicitly, run `aspect lint //...` from the repo root (equivalent to the legacy `targets: ['//...']`).

## GitHub PR review comments

On GitHub Actions, the **`GithubLintComments`** feature posts lint findings to PRs as inline review comments. It is **enabled by default**, so no extra configuration is needed beyond authenticating the runner (see [Authentication](#authentication) below).

What it does:

* Posts inline review comments on the lines of the PR diff that the linter flagged.
* Includes fix suggestions as GitHub suggestion blocks where the linter provides them, so reviewers can apply the fix with one click.
* Deduplicates comments across re-runs of the lint task on the same PR, and cleans up stale ones once a violation is fixed.

<Tip>
  `GithubLintComments` computes per-line changed regions from `git diff` against the merge-base of `HEAD` and `--base-ref` (default `origin/main`); override with `--merge-base=<sha>` to skip the merge-base resolution. On a PR build where `git diff` can't resolve the base — shallow clones or fetch-depth-restricted runners — it falls back to the GitHub PR Files API when the Aspect Workflows app is authenticated.
</Tip>

### Authentication

`GithubLintComments` 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 review comments.

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.

When the workflow is running on a GitHub Actions PR but authentication is missing or misconfigured, the CLI logs one `GitHub lint comments: authentication failed for <owner>/<repo> — <reason>` line per `aspect lint` invocation and the run continues normally. The reason explains what's missing and includes a link to the setup guide, for example:

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

Outside a GitHub Actions PR run (push events, non-GitHub CI, local development) the feature has nothing to post to and stays silent.

### Disabling

<CodeGroup>
  ```shell CLI theme={null}
  aspect lint --github-lint-comments:enabled=false //...
  ```

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

  def config(ctx: ConfigContext):
      ctx.features[GithubLintComments].enabled = False
  ```
</CodeGroup>

## CI integration examples

### Post comments

Run `aspect lint` on pull requests so `GithubLintComments` can post annotations as review comments.

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

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

  jobs:
    lint:
      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: Lint
          run: aspect lint --task:name lint //...
  ```

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

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

  lint:
    extends: .setup-aspect
    tags:
      - aspect-workflows
      - aspect-default
    script:
      - aspect lint --task:name lint //...
  # 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:
    lint:
      machine: true
      resource_class: circleci-org/aspect-default
      steps:
        - checkout
        - setup-aspect/setup
        - run:
            name: Lint
            command: aspect lint --task:name lint //...

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

### Auto commit fixes

Run `aspect lint --fix` on feature branches and push the resulting diff back so developers don't have to re-run locally. Each platform needs a push credential with write access to the branch.

<CodeGroup>
  ```yaml GitHub Actions theme={null}
  jobs:
    lint:
      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: Lint with fix
          run: aspect lint --fix --task:name lint //...
        - name: Commit fixes
          uses: stefanzweifel/git-auto-commit-action@v5
          with:
            commit_message: "lint: apply automatic fixes"
  ```

  ```yaml Buildkite theme={null}
  steps:
    - label: ":broom: Lint (fix)"
      if: build.branch != "main"
      plugins:
        - aspect-build/setup-aspect#1d5768c5d28b72bf523b4722fc9177d2cc2d85c7: ~ # v2026.26.8
      command: |
        aspect lint --fix --task:name lint //...
        if [ -n "$$(git status --porcelain)" ]; then
          git config user.email "buildkite@example.com"
          git config user.name "buildkite"
          git commit -am "lint: apply automatic fixes"
          git push origin HEAD:$$BUILDKITE_BRANCH
        fi
      agents:
        queue: aspect-default
  ```

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

  lint-fix:
    extends: .setup-aspect
    tags:
      - aspect-workflows
      - aspect-default
    rules:
      - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
    script:
      - aspect lint --fix --task:name lint //...
      - |
        if [ -n "$(git status --porcelain)" ]; then
          git config user.email "gitlab@example.com"
          git config user.name "gitlab"
          git commit -am "lint: apply automatic fixes"
          git push "https://oauth2:${GITLAB_PUSH_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" HEAD:${CI_COMMIT_REF_NAME}
        fi
  # Set ASPECT_API_TOKEN as a masked CI/CD variable in Settings → CI/CD → Variables.
  ```

  ```yaml CircleCI theme={null}
  orbs:
    setup-aspect: aspect-build/setup-aspect@2026.26.10

  jobs:
    lint-fix:
      machine: true
      resource_class: circleci-org/aspect-default
      steps:
        - checkout
        - setup-aspect/setup
        - run:
            name: Lint with fix
            command: |
              aspect lint --fix --task:name lint //...
              if [ -n "$(git status --porcelain)" ]; then
                git config user.email "circleci@example.com"
                git config user.name "circleci"
                git commit -am "lint: apply automatic fixes"
                git push origin HEAD:$CIRCLE_BRANCH
              fi
  # Set ASPECT_API_TOKEN as a project environment variable or context secret.
  ```
</CodeGroup>
