Check
bca check evaluates per-function metrics against thresholds and exits
non-zero when any function exceeds a limit. It is the CI integration
point: wire it into a build step and a regression in code complexity
fails the pipeline before the change lands.
Looking for full CI recipes? The CI integration recipe consolidates the
--output-formatmatrix, runnable GitHub Actions and.gitlab-ci.ymlexamples, the baseline / ratchet pattern, and the GitLab Code Quality path. This page documents the command itself; the recipe documents how to wire it into a pipeline.
Exit codes
| Code | Meaning |
|---|---|
0 | All functions within thresholds (or --no-fail set). |
2 | At least one threshold exceeded. |
1 | Tool error (bad arguments, unreadable config, unknown metric). |
1 is reserved so CI can distinguish a regression (2) from a tool
misconfiguration (1).
Declaring thresholds
Pass --threshold <metric>=<limit> once per metric (repeatable). Metric
names match bca list-metrics; sub-metrics use a dotted form. 0 is a
valid limit and means "no value permitted".
bca --paths src/ check \
--threshold cyclomatic=15 \
--threshold cognitive=20 \
--threshold loc.lloc=200
Or pull thresholds from a TOML config (one place to keep CI thresholds versioned alongside the code):
# bca-thresholds.toml
[thresholds]
cyclomatic = 15
cognitive = 20
"loc.lloc" = 200
"halstead.volume" = 1000
bca --paths src/ check --config bca-thresholds.toml
CLI flags override values from --config for the same metric name, so
you can keep a project-wide default and tighten a single metric for a
specific run.
Accepted metric names
Top-level scalar metrics use their list-metrics names directly:
cognitive, cyclomatic, nargs, nexits, nom, tokens, abc,
wmc, npm, npa. Metric suites with multiple sub-fields use a dotted
form:
| Metric | Accepted threshold names |
|---|---|
| Cyclomatic | cyclomatic, cyclomatic.modified |
| Halstead | halstead.volume, halstead.difficulty, halstead.effort, halstead.time, halstead.bugs |
| Lines of code | loc.sloc, loc.ploc, loc.lloc, loc.cloc, loc.blank |
| Maintainability Index | mi.original, mi.sei, mi.visual_studio |
An unknown threshold name is a tool error (exit 1), not silently
ignored.
Offender output
Every offending (function, metric) pair prints one line to stderr in
this stable format:
<path>:<start_line>-<end_line>: <function_name>: <metric> = <value> (limit <limit>)
For example:
src/parser.rs:42-117: parse_expression: cyclomatic = 22 (limit 15)
src/parser.rs:42-117: parse_expression: cognitive = 31 (limit 20)
Lines are sorted by path, then start line, then metric name, so output is deterministic across runs over the same tree.
Silencing violations with suppression markers
In-source comments can silence threshold violations on individual
functions or whole files without editing the offending code or
excluding it from the walk. The native dialect is bca: suppress /
bca: suppress-file; Lizard's #lizard forgives is recognized as a
compatibility shim. See Suppression markers for
the full reference and the --no-suppress CI-audit flag.
Baselines
When you adopt thresholds on an existing codebase you typically face a binary choice between "raise the limit until nothing fires" and "fix every offender before turning the gate on". A baseline file is the ratchet-down alternative: record today's offenders, fail only on regressions and new offenders, and shrink the file over time as the team pays down debt.
Baselines are complementary to the suppression markers from
Suppression markers, not a substitute. Suppressions
express "this function is intentionally exempt forever" and live in
source; baselines express "this is tech debt we're paying down" and
live in a committed TOML file. bca check honors suppressions first
and applies the baseline filter to whatever remains.
Writing a baseline
bca --paths src/ check \
--config bca-thresholds.toml \
--write-baseline .bca-baseline.toml
This walks the tree, captures every threshold violation that would
otherwise fail the check, and writes them to the file as sorted TOML.
The run exits 0 regardless of offender count — the point is to
capture them.
# bca baseline file. Generated by `bca check --write-baseline`.
# Listed offenders are filtered from threshold checks; a function that
# gets worse than its recorded value still fails. Refresh with
# `--write-baseline` when entries become stale.
version = 1
[[entry]]
path = "src/parser.rs"
function = "parse_expression"
start_line = 42
metric = "cyclomatic"
value = 22.0
Functions already covered by an in-source suppression marker are
excluded. Pass --no-suppress together with --write-baseline to
record every violation (CI-auditor flow).
--write-baseline cannot be combined with --baseline,
--output-format, or --output — the baseline file is the output.
Reading a baseline
bca --paths src/ check \
--config bca-thresholds.toml \
--baseline .bca-baseline.toml
A violation is suppressed when both conditions hold:
- An entry exists at
(path, function, start_line, metric). - The current
valueis less than or equal to the recorded value.
A function that gets worse than its baseline value still fails. New
offenders not listed in the baseline still fail. Improvements pass
silently (the entry remains at its older, higher value until the next
--write-baseline refresh).
A baseline file that does not exist, is empty, has a missing or
unsupported version, or fails to parse is a tool error (exit 1),
not a silent zero-match.
Limitations
- Line drift. The entry key is
(path, function, start_line, metric). Inserting code above a function shifts itsstart_lineand the entry stops matching, surfacing as a "new" offender. Run--write-baselineto refresh and commit the diff. - Path identity. Entries record the path as the walker saw it.
Generate and consume the baseline with the same
--pathsargument from the same working directory; a relative--paths src/and an absolute--paths /repo/src/do not match each other. - OS portability. Paths are stored with forward slashes so a baseline written on one OS matches the same tree on another. Paths that are not valid UTF-8 fall back to a lossy display form (U+FFFD substitution) and may not round-trip exactly.
See the Baselines recipe for the end-to-end adoption flow and CI integration patterns.
Reporting without failing
--no-fail prints offenders to stderr but exits 0. Useful while
adopting baselines without flipping CI red. Other CI tools call this
behavior --report-only or --soft-fail; here the flag is spelled
--no-fail.
bca --paths src/ check \
--config bca-thresholds.toml --no-fail
CI example (GitHub Actions)
- name: Check code complexity thresholds
run: |
bca --paths src/ check --config bca-thresholds.toml
# The default behavior — non-zero exit fails the step — is exactly
# what we want here. No extra wiring needed.
If you want to keep the job green and surface offenders as a build
annotation while you reduce the count, swap in --no-fail:
- name: Surface complexity hot spots (non-blocking)
run: |
bca --paths src/ check \
--config bca-thresholds.toml --no-fail
Exporting offender records
bca check also emits a single CI/IDE document covering every
offender in the walk. Pass --output-format <fmt> to pick the shape
and --output <file> to write it to disk (stdout if omitted). The
exit-code contract is unaffected by these flags: 0 clean, 2 on any
violation (unless --no-fail), 1 on tool error.
| Format | Audience |
|---|---|
checkstyle | Jenkins, SonarQube, GitLab, "warnings plugin" CI |
sarif | GitHub Code Scanning, modern IDEs / security tooling |
code-climate | GitLab MR Code Quality widget |
clang-warning | Editor quickfix parsers, GitHub Actions problem matcher |
msvc-warning | Visual Studio, VS Code, Windows CI runners |
When no offenders exist the writer emits a well-formed but empty
document — empty runs[].results array for SARIF, empty JSON array
([]) for Code Climate, no <file> children under the
<checkstyle> root for Checkstyle, and zero bytes for the two
warning-line formats — so CI consumers can ingest clean runs
unchanged.
Checkstyle (CI integration)
bca --paths src/ check \
--threshold cyclomatic=15 \
--output-format checkstyle \
--output report.checkstyle.xml
The Checkstyle writer emits a single <checkstyle version="4.3">
document containing one <file> element per source path, each
holding one <error> per metric-threshold violation. The schema is
the Checkstyle 4.3 XSD that Jenkins and SonarQube's "Warnings Next
Generation" / "Generic Issue" importers consume directly.
SARIF (GitHub Code Scanning)
bca --paths src/ check \
--threshold cyclomatic=15 \
--output-format sarif \
--output report.sarif.json
The SARIF writer emits a single SARIF 2.1.0 JSON document with one
runs[] element. Each metric-threshold violation becomes a result
under runs[0].results[]; the metric names appearing in the run are
deduplicated into runs[0].tool.driver.rules[] with short
descriptions.
To upload a SARIF file to GitHub Code Scanning from a workflow:
name: bca-sarif
on: [push, pull_request]
jobs:
scan:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v4
- name: Run big-code-analysis
run: |
bca --paths . check \
--config bca-thresholds.toml \
--output-format sarif \
--output report.sarif.json \
--no-fail
- name: Upload SARIF
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: report.sarif.json
--no-fail keeps the job green so the SARIF upload step still runs
when offenders exist; remove it once you want a metric regression to
fail the workflow.
GitLab Code Quality (Code Climate JSON)
bca --paths src/ check \
--threshold cyclomatic=15 \
--output-format code-climate \
--output gl-code-quality-report.json
The Code Climate writer emits a single JSON array of issue objects
matching GitLab's strict subset
of the upstream Code Climate engine spec — one entry per
metric-threshold violation, no byte-order-mark, one trailing
newline (empty input renders as []\n). Each issue carries a
namespaced check_name (big-code-analysis/<metric>), a stable
SHA-256 fingerprint over path \0 function \0 metric (line- and
value-insensitive so cosmetic edits still dedup in the MR widget),
and a severity mapped from the value/threshold ratio onto
GitLab's five-level enum: ≤ 1.5× → minor, ≤ 2× → major,
≤ 4× → critical, > 4× → blocker (inverted for the mi.*
family where lower is worse). The full enum is
info/minor/major/critical/blocker; bca never emits
info — a threshold violation always lands at minor or higher.
To wire the artifact into GitLab's MR Code Quality widget:
code_quality:
stage: quality
script:
- bca --paths "$CI_PROJECT_DIR" check
--config bca-thresholds.toml
--output-format code-climate
--output gl-code-quality-report.json
--no-fail
artifacts:
when: always
reports:
codequality: gl-code-quality-report.json
paths:
- gl-code-quality-report.json
See the
GitLab Code Quality widget recipe
for the full pipeline (combined Code Climate + Checkstyle + Markdown
report) and a local jq smoke check.
--no-fail keeps the job green so the Code Quality report still
uploads when offenders exist; remove it once you want a metric
regression to fail the pipeline.
Clang/GCC warning lines (editor quickfix and CI annotators)
bca --paths src/ check \
--threshold cyclomatic=15 \
--output-format clang-warning \
--output report.txt
The Clang format emits one offender per line in the conventional compiler-warning shape:
path/to/file.rs:42:5: warning: cyclomatic 17 exceeds limit 15 [big-code-analysis-cyclomatic]
This is the format clang -fdiagnostics-format= produces and the
shape every editor quickfix parser (VS Code, IntelliJ, Vim) and most
CI annotators understand without configuration.
GitHub Actions surfaces the lines as inline annotations on the PR
diff via the built-in GCC problem matcher (or any community
compiler-problem-matchers action):
name: bca-clang-warnings
on: [push, pull_request]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Enable GCC problem matcher
run: echo "::add-matcher::$RUNNER_TOOL_CACHE/problem-matchers/gcc.json"
- name: Run big-code-analysis
run: |
bca --paths . check \
--config bca-thresholds.toml \
--output-format clang-warning \
--no-fail
If your runner does not ship a GCC matcher, fall back to streaming
the lines and re-emitting them as ::warning file=...,line=...::
workflow commands.
MSVC warning lines (Visual Studio and Windows CI)
bca --paths src/ check \
--threshold cyclomatic=15 \
--output-format msvc-warning \
--output report.txt
The MSVC format emits one offender per line in Visual Studio's
cl.exe diagnostic shape:
path\to\file.rs(42,5): warning : cyclomatic 17 exceeds limit 15
Note the space before the colon after warning/error — that is
the MSVC convention. On Windows the path is normalized to use \
separators (matching cl.exe output); on other platforms the path is
emitted as-is. Visual Studio, VS Code with the C/C++ extension, and
Windows CI runners (Azure Pipelines, GitHub Actions on
windows-latest) parse these inline without extra configuration.