Quality reports

Recipes for producing aggregated, human-readable Markdown reports.

Wiring reports into CI? See the CI integration recipe for runnable GitHub Actions and GitLab CI examples that post the Markdown report as a PR/MR comment and surface threshold violations through the platform's native code quality widgets.

Live example reports

big-code-analysis publishes the output of bca report -O markdown --vcs and bca report -O html --vcs against its own source tree on every push to main. Open either to see exactly what the recipes on this page produce on a multi-language Rust + Python codebase:

The wiring that produces them lives in .github/workflows/pages.yml. The same workflow runs the threshold gate; see CI integration for the full pipeline shape.

Generate a project-wide quality report

Run from the project root and write the report to a file:

bca report \
    --paths "$PWD" \
    -O markdown \
    --top 20 \
    --strip-prefix "$PWD/" \
    --output report.md
  • --strip-prefix keeps the file paths short and stable across machines — without it every row carries the absolute path of the current checkout.
  • --top controls how many rows appear in each hotspot table. 20 is a good default for a PR comment; drop to 5 for a dashboard tile, or pass 0 to list every row.
  • --jobs defaults to the effective CPU count (cgroup-/cpuset-aware on Linux); pass --jobs 1 only to force serial mode for debugging.

Limit the report to specific languages

bca infers language from extension, so the include/exclude globs do the filtering:

bca report \
    --include "*.rs" --include "*.py" \
    --paths "$PWD" \
    -O markdown --output report.md

To exclude vendored or generated trees, layer in --exclude:

bca report \
    --include "*.rs" \
    --exclude "**/target/**" --exclude "**/vendor/**" \
    --paths "$PWD" \
    -O markdown

Flag arity. --include and --exclude take exactly one glob per occurrence; repeat the flag for additional patterns. The = form works the same way: --include="*.rs" --exclude="**/target/**".

Leading ./ is optional. A bare-relative pattern and its ./-prefixed spelling are equivalent: --exclude "vendor/**" matches exactly what --exclude "./vendor/**" does. This holds for every glob surface — --include, --exclude, --exclude-from, .bcaignore, and the [check.exclude] gate-exemption set.

For a stable repo-wide deny-set, keep the patterns in a file at the repo root (a .bcaignore by convention) and load it with --exclude-from. Patterns are unioned with any inline --exclude values; blank lines and #-prefixed comments are skipped:

bca report \
    --paths . \
    --exclude-from .bcaignore \
    -O markdown --output report.md

Show only the worst offenders

For a quick triage view that highlights the top three problems per section:

bca report -p src/ -O markdown --top 3

The report still includes every section, but each table is short enough to scan at a glance.

Compare two revisions

Aggregate reports do not diff revisions on their own. Run the report on each side and diff the Markdown:

git worktree add /tmp/before main
bca report -p /tmp/before -O markdown \
    --strip-prefix /tmp/before/ --output /tmp/before.md

bca report -p "$PWD" -O markdown \
    --strip-prefix "$PWD/" --output /tmp/after.md

diff -u /tmp/before.md /tmp/after.md | less

Because both reports use the same --strip-prefix shape, the path columns line up and the diff is dominated by metric changes rather than path noise.

C/C++ preprocessor-aware reports

Macro-heavy C/C++ codebases benefit from feeding preprocessor data into the analyzer so that conditional compilation is interpreted the way the compiler sees it. The workflow is two steps:

# 1. Build a preprocessor-data JSON from the headers and sources.
bca preproc \
    --paths src/ include/ \
    --output /tmp/preproc.json

# 2. Run the report (or any other command) with that data attached.
bca report \
    --paths src/ \
    --preproc-data /tmp/preproc.json \
    -O markdown --output report.md

--preproc-data is accepted by every metric-computing walking subcommand (metrics, ops, functions, report, check, …) — anywhere accurate C/C++ analysis matters. Subcommands that do not consume it (vcs, preproc, list-metrics, diff-baseline) reject it as a usage error.

Analyze only files changed in a PR

Pipe a list of changed files into --paths-from - to score just the diff, not the whole tree:

git diff --name-only --diff-filter=AM origin/main...HEAD \
    | bca metrics --paths-from - -O json --output-dir ./out
  • --diff-filter=AM keeps Added and Modified files and drops Deletions — you cannot analyze a file that no longer exists.
  • --paths-from - reads newline-separated paths from stdin. A file argument works the same way: --paths-from changed.txt.
  • Paths fed in this way are treated as explicit, so they bypass any .gitignore rule that would have hidden them in a directory walk. Combine with -I '*.py' -I '*.rs' to filter by language (repeat the flag once per glob).

For a PR-scoped Markdown summary, swap metrics for the report pipeline:

git diff --name-only --diff-filter=AM origin/main...HEAD \
    | bca report --paths-from - -O markdown \
        --top 10 --output pr-report.md

.gitignore is honored automatically when walking a directory, so recipes earlier in this page no longer need an explicit -X "**/target/**" -X "**/node_modules/**" if those paths are already covered by your project's .gitignore. Add --no-ignore if you do need to analyze gitignored trees.