Skip to main content
aspect lint runs one or more aspect_rules_lint aspects as a Bazel build, reads the SARIF diagnostics they produce, and decides whether to fail the CI step based on which findings are in scope. It posts inline review comments on the diff through the GithubLintComments (GitHub) and GitlabLintComments (GitLab) features. aspect lint doesn’t implement linting itself. It drives Bazel, which caches lint results like any other action: a file that hasn’t changed gets its lint result from the remote cache instead of running the linter again, so aspect lint //... re-lints only what changed.

Hold-the-line strategy

The default strategy, hold-the-line, makes lint practical on a large codebase that already has violations. Instead of failing on any finding anywhere, it:
  1. Finds the change’s changed lines, with git diff or, as a fallback, the GitHub PR Files API.
  2. Fails only on error-severity findings inside the diff hunks the change touched.
Existing violations in untouched code are reported but don’t block the change; new ones do.
If change detection can’t resolve a base — a shallow CI checkout is the usual cause — every strategy is overridden to hard, because the alternative is silently hiding findings. A repo that set soft to keep lint non-blocking gets a failing gate. The warning names the fix:

Configuration

Lint aspects

aspect lint needs at least one aspect_rules_lint aspect, from --aspect, Bazel’s own --aspects flag, or a --config in your .bazelrc:
Declare them in config.axl so developers running aspect lint //... locally use the same set as CI:
.aspect/config.axl
Command-line flags override config.axl, so a CI job that needs only the fast linters can pass its own --aspect.

Apply auto-fix patches

Some rules_lint linters produce fix patches. Pass --fix to apply them to the working tree:
Without --fix the patches are still produced for the features that use them, such as review-comment suggestions, and aren’t applied locally.

Base ref

The changed lines are diffed against the merge base with --base-ref. When it’s empty, the base comes from the CI environment (GITHUB_BASE_REF, BUILDKITE_PULL_REQUEST_BASE_BRANCH or CI_MERGE_REQUEST_TARGET_BRANCH_NAME), then the remote’s default branch, then origin/main. --merge-base=<sha> pins an explicit commit instead.
The Bazel build covers the target patterns you pass either way; the base ref only decides which findings are in scope.

Options

It also takes the Bazel flag options common to every task that runs Bazel (--bazel-flag, --bazel-startup-flag, --announce-bazel-*, --bes-backend, --bes-header); see aspect run.

GitHub PR annotations

GithubLintComments posts auto-fix suggestions as inline review comments on the diff line, as one-click Commit suggestion buttons. By default other findings appear as check-run annotations; set findings_destination to comments (below) to post every finding as a comment. It needs the Aspect Workflows GitHub App and an Aspect credential. Cap the comments per run with --github-lint-comments:max-pr-comments=<n> (default 25).

GitLab MR comments

GitlabLintComments is the GitLab counterpart. It posts each finding as a discussion on the merge request diff, so reviewers see it inline in the Changes view. It needs the Aspect Workflows GitLab App and ASPECT_API_TOKEN, and posts only on merge request pipelines. It differs from the GitHub feature:
  • Suggestion blocks: auto-fix patches render as GitLab ```suggestion blocks that reviewers apply with one click.
  • Dedup across runs: re-running the lint task replaces a stale discussion at the same position instead of adding a duplicate.
  • Thread auto-resolve: on a clean pass, discussions Aspect opened on earlier runs are marked Resolved rather than deleted, which keeps the review history. GitHub review comments have no resolve, so the GitHub feature deletes them instead.
Cap the discussions per run with --gitlab-lint-comments:max-pr-comments=<n> (default 25; 0 posts none). Errors are posted first, then warnings, then notes; findings past the cap are left off the MR but still count toward the lint result.

Routing findings: findings_destination

Where findings land is set by LintTrait.findings_destination, which both the GitHub and GitLab features read. The default, auto, posts auto-fix suggestions as inline comments and other findings as check-run annotations on GitHub. GitLab has no per-finding annotation surface, so there auto posts only the auto-fix suggestions. To post every finding as an inline comment (recommended on GitLab, so non-fix findings show up too), set it to comments:
.aspect/config.axl
The config hook must be named config (def config(ctx: ConfigContext):). The CLI doesn’t call a function named configure.

How it works

aspect lint reads two output groups from aspect_rules_lint:
  • rules_lint_machine: SARIF and per-target exit codes, used for the verdict and for posting findings.
  • rules_lint_human: colored terminal output, streamed to your terminal.
After the build, the task:
  1. Reads every diagnostic from every SARIF file.
  2. Computes the change’s changed lines.
  3. Filters diagnostics by the strategy. hold-the-line keeps findings inside the displayed diff hunks, changed lines plus their surrounding context.
  4. Posts the remaining findings, through GithubLintComments on GitHub or GitlabLintComments on GitLab.
  5. Exits 1 if any remaining finding is error severity, otherwise 0.

CI example

.github/workflows/lint.yaml
Complete pipeline examples shows the same setup on Buildkite, GitLab CI and CircleCI, and on Aspect Workflows CI runners.