Continuous integration
The whole CI surface is the exit code: 0 if nothing is over the threshold, 1 if something
is. No reporter to configure, and on GitHub Actions the annotations are built in.
GitHub Actions
Section titled “GitHub Actions”name: Complexity
on: [push, pull_request]
jobs: bonsai-lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npx bonsai-lint --format github .No toolchain setup, no caching worth bothering with, no matrix. npx fetches a binary under 2 MB
and the scan itself is fast enough that it is not the thing you wait for. A monorepo of more
than a million lines, in four languages, scans in about a second.
Annotations on the pull request
Section titled “Annotations on the pull request”--format github prints each breach as a workflow command, so it shows as an annotation on the
pull request, and fails exactly when the text report does. A marker without a reason warns on its
unit, a loose baseline entry is filed on its baseline file, and config warnings and read errors
on the run. Paths are made relative to GITHUB_WORKSPACE, so the step can run from any directory;
a file outside it is annotated on the run. A repository checked out into a subdirectory with
path: is annotated under that subdirectory, which GitHub cannot place. GitHub annotates at most
10 errors per step; the log lists every one.

GitLab CI
Section titled “GitLab CI”complexity: image: node:22-alpine script: - npx bonsai-lint .Failing on regressions only
Section titled “Failing on regressions only”Most existing codebases have findings on day one. Gating on the absolute number means either a threshold so high it catches nothing, or a red build nobody can fix this sprint.
Record what exists, commit it, and the gate then only fires on things that get worse:
npx bonsai-lint --write-baseline .git add .bonsai-lint-baseline.jsonCI runs the same command it always did. A unit already in the baseline passes at its recorded
score or below; anything new, or anything that climbed, fails. With --format github, a unit
that climbed is annotated with both scores:

See baselines for how entries are keyed and what happens when code moves.
To make the baseline tighten as the code improves, add --strict-baseline, or set
strict-baseline = true in bonsai-lint.toml. An entry that accepts more than the code now
needs then fails the run until the baseline is rewritten over the whole workspace.
Why a broken scan fails the build
Section titled “Why a broken scan fails the build”Exit 1 does not only mean “too complex”, or a strict baseline accepting more than the code
needs. It also means the scan could not be trusted: a path that could not be read, or no
supported files found under the paths given.
That is deliberate. A quality gate that silently checks nothing when it is misconfigured is worse than no gate, because it reports success. If you point the command at a directory that does not exist, you want a red build, not a green one.
Machine-readable output
Section titled “Machine-readable output”For anything that needs to consume results rather than just pass or fail:
npx bonsai-lint --format json . > complexity.jsonThe shape is documented in JSON output. Findings are sorted worst-first and the ordering is deterministic, because the report is assembled after the scan rather than streamed as results arrive. Two runs of the same commit are byte-for-byte identical regardless of how many workers ran. A diff between runs is always a real change.