Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

CI 連携

Recipes for wiring bca into a build pipeline. The bca check command ships every output shape a CI system consumes (Checkstyle, SARIF, GitLab Code Climate JSON, clang/GCC warning lines, MSVC warning lines), plus bca report markdown for humans. This page maps each goal to the subcommand, flags, and platform glue that serve it. Copy-paste snippets come first; the design notes behind them (live worked example, self-scan gate, trust boundary) come after.

出力の選び方

The matrix below maps each common goal to the bca invocation that feeds the corresponding CI surface. The linked sections have the runnable example.

目的コマンド + フラグ
しきい値リグレッションに対するハードゲートbca check (自動検出された bca.toml のしきい値を使用)
既存コードベースでしきい値をラチェットするbca check --baseline .bca-baseline.toml (‡)
インライン PR アノテーション(GitHub)bca check … --report-format clang-warning --no-fail + GCC problem matcher
Code Scanning アラート(GitHub)bca check … --report-format sarif --no-fail + github/codeql-action/upload-sarif
マージリクエストウィジェット(GitLab Code Quality)bca check … --report-format code-climate --no-fail
Jenkins / SonarQube への取り込みbca check … --report-format checkstyle
人間可読な PR/MR コメントまたはダウンロード可能な成果物bca report -O markdown --top 20 --strip-prefix "$PWD/"
ダッシュボード向けの機械可読アーティファクトbca metrics --format json --output-dir ./out

(‡)既存の違反があるコードベースにしきい値を導入する際に推奨される導入パスです。ブートストラップ・リフレッシュ・リタイアのワークフローについてはベースラインのレシピを参照してください。

bca check の出力形式、終了コード(0 はクリーン、2 は違反、1 はツールエラー)、しきい値設定の完全なリファレンスは Check コマンドのページ にあります。Markdown レポートの形式については、Report コマンドのページ と品質レポートのレシピを参照してください。

GitHub Actions

For a reproducible install without a Rust toolchain, use the prebuilt tarball from this repository’s GitHub Releases. It is a single curl | sha256sum | tar and produces byte-identical binaries across runs. Pair it with actions/cache keyed by version so a green-path rerun skips the download entirely:

CLI-artifact schema compatibility. The BCA_VERSION you pin here must support the schema version of every CLI artifact your repo commits, above all .bca-baseline.toml (which carries its own version field) and the bca.toml manifest. A baseline file written by a newer bca (carrying a newer schema version) is not loadable by an older bca, and the gate fails with baseline version N is not supported by this bca. When tracking main or regenerating baselines locally with a newer bca, either re-pin to a release that covers the new schema or switch to a cargo install --git build of bca pointed at the same commit your baseline was written from (see the cargo install alternative below). The compatibility contract is recorded in STABILITY.md.

env:
  BCA_VERSION: "2.4.0"
  BCA_TARGET:  "x86_64-unknown-linux-gnu"
  # リリースの SHA256SUMS ファイルにある big-code-analysis-${BCA_VERSION}-${BCA_TARGET}.tar.gz の sha256。
  # BCA_VERSION と合わせて更新してください。
  BCA_SHA256:  "d132c0ef559b4917b7679e17fe3d8f69f5bc14a67692f5d3596d0ec21954d20e"

steps:
  # キャッシュキーには必ず BCA_SHA256(と BCA_TARGET)を含めてください。
  # キーに sha256 が含まれていないと、バージョンを上げずに公開チェックサムを
  # ローテーションした場合、キャッシュヒット時に古いバイナリが返され、
  # インストールステップの `sha256sum --check`(キャッシュミス時のみ実行)を
  # 黙ってバイパスしてしまいます。同じワークフローを複数の `runs-on` で
  # 実行する場合は、BCA_TARGET を含めることが重要です。
  - name: Cache bca binary
    id: bca-cache
    uses: actions/cache@v5
    with:
      path: ~/.local/bin/bca
      key: bca-${{ runner.os }}-${{ env.BCA_TARGET }}-${{ env.BCA_VERSION }}-${{ env.BCA_SHA256 }}

  - name: Install bca from GitHub Releases
    if: steps.bca-cache.outputs.cache-hit != 'true'
    run: |
      set -euo pipefail
      stage="big-code-analysis-${BCA_VERSION}-${BCA_TARGET}"
      tarball="${stage}.tar.gz"
      url="https://github.com/dekobon/big-code-analysis/releases/download/v${BCA_VERSION}/${tarball}"
      mkdir -p "$HOME/.local/bin"
      curl -fsSL --proto '=https' --tlsv1.2 -o "/tmp/${tarball}" "$url"
      echo "${BCA_SHA256}  /tmp/${tarball}" | sha256sum --check --strict -
      tar -xzf "/tmp/${tarball}" -C /tmp
      install -m 0755 "/tmp/${stage}/bca" "$HOME/.local/bin/bca"
      rm -rf "/tmp/${tarball}" "/tmp/${stage}"

  - name: Prepend ~/.local/bin to PATH
    run: echo "$HOME/.local/bin" >> "$GITHUB_PATH"

利用可能な BCA_TARGET の値(runs-on に一致するものを選択してください): x86_64-unknown-linux-gnu、x86_64-unknown-linux-musl、aarch64-unknown-linux-gnu、aarch64-unknown-linux-musl、aarch64-apple-darwin、x86_64-pc-windows-msvc、aarch64-pc-windows-msvc。Windows のアセットは .tar.gz ではなく .zip を使用します。bca-web バイナリは同じアーカイブ内で bca と一緒に配布されます。

代替手段: ビルド済みバイナリ対応アクション経由の cargo install

When a runner cannot reach github.com (air-gapped, custom mirror) but can reach crates.io, the two actions below fall back to cargo install when no prebuilt is published, at the cost of compile time on the cold path. Both pin to the same crates.io release as the GitHub Releases assets, so the CLI-artifact schema compatibility warning applies here unchanged.

If you need a bca ahead of the latest crates.io release (for example, your .bca-baseline.toml is committed at a newer schema than any published bca understands), swap the tool: big-code-analysis-cli@<version> or --version form for cargo install --git https://github.com/dekobon/big-code-analysis --rev <SHA> --locked big-code-analysis-cli against the exact commit the baseline was generated from. This is what the in-tree pages.yml workflow does (against the local checkout via --path). It is a deliberate workaround for bca’s own repo, not a recommended default for downstream adopters.

Use taiki-e/install-action or cargo-binstall.

# 選択肢 1: taiki-e/install-action
- name: Install bca
  uses: taiki-e/install-action@v2
  with:
    tool: big-code-analysis-cli@2.4.0
# 選択肢 2: cargo-binstall
- name: Install cargo-binstall
  uses: cargo-bins/cargo-binstall@main
- name: Install bca
  run: cargo binstall --no-confirm big-code-analysis-cli --version 2.4.0

If either action falls back to compilation, cache the cargo registry and the installed binary so the second run is fast:

- name: Cache cargo registry and bca binary
  uses: actions/cache@v5
  with:
    path: |
      ~/.cargo/registry
      ~/.cargo/git
      ~/.cargo/bin/bca
    # crates.io publishes immutable releases, so a `<version>` key is
    # sufficient here; there is no sha256 to rotate. (The GitHub
    # Releases install path above is different: republished release
    # assets share a version, so its cache key must include the sha256.)
    key: bca-${{ runner.os }}-2.4.0

Pin to a specific version (matching a published big-code-analysis-cli release on crates.io) so reports stay reproducible across runs. A floating install surfaces metric-counting changes as unexplained CI flakes.

しきい値ゲート、SARIF、clang-warning マッチャー

Three recipes live on the Check command page rather than here: the hard threshold gate, the SARIF upload to Code Scanning, and the clang-warning output paired with the GCC problem matcher for inline PR annotations.

Markdown レポートを PR コメントとして投稿する

bca report markdown is built for PR/MR comments: a stable header structure, one row per hot spot, and short paths once you pass --strip-prefix. Pair it with marocchino/sticky-pull-request-comment so each push updates a single comment instead of stacking new ones:

name: bca-pr-report
on:
  pull_request:
    branches: [main]
jobs:
  report:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
    steps:
      - uses: actions/checkout@v4
      - name: Install bca
        uses: taiki-e/install-action@v2
        with:
          tool: big-code-analysis-cli@2.4.0
      - name: Generate report
        run: |
          bca \
            report -O markdown \
            --paths "$PWD" \
            --top 20 \
            --strip-prefix "$PWD/" \
            --output report.md
      - name: Post or update PR comment
        uses: marocchino/sticky-pull-request-comment@v2
        with:
          path: report.md
          header: bca-quality-report

The same Markdown file is suitable for upload as a build artifact (actions/upload-artifact@v7) if you want it downloadable from the workflow run page in addition to the PR comment.

ベースライン / ラチェットパターン

bca check --baseline はネイティブのラチェットです。今日時点の違反をコミット済みの TOML ファイルに記録し、リグレッションと新規違反に対してのみ失敗させ、ファイルを時間をかけて縮小していきます。一度ブートストラップしてコミットし、CI をそのファイルに向けます:

# 開発者マシンで一度だけ実行します。両方のファイルをコミットしてください。
bca check --paths src/ \
    --write-baseline .bca-baseline.toml
git add bca.toml .bca-baseline.toml

This snippet bootstraps from src/ only, which suits a single-crate library. For a multi-crate workspace, see the live worked example: its .github/workflows/pages.yml scans the entire repo with --exclude-from .bcaignore, a checked-in deny-set covering vendored grammars, generated trees, and tests.

Share the exclude list across workflow, recipe, and bootstrap. Put the deny-set in a single file at the repo root (a .bcaignore by convention, mirroring .gitignore / .dockerignore) and point every bca invocation at it with --exclude-from .bcaignore. Patterns from --exclude-from are unioned with any inline --exclude <GLOB> flags into one deny-set; keep --exclude for one-off excludes. Blank lines and #-prefixed comment lines in the file are skipped. Patterns follow the same ./-prefix convention as --exclude arguments (the walker’s emitted form). Pair edits to .bcaignore with a --write-baseline refresh, because the baseline keys depend on which files the walker visits.

- name: Threshold check with baseline
  run: |
    bca check --paths src/ \
        --baseline .bca-baseline.toml

A regressed function (worse than its baseline value in the metric’s own direction: higher, or lower for the mi.* family) still fails. A new offender not in the baseline still fails. An improved function passes silently and stays in the baseline until the next --write-baseline refresh.

Each surviving violation row starts with a tag that distinguishes a new offender from a known one that has worsened:

  • [new]: no baseline entry for this function / metric.
  • [regr …]: the current value is worse than the recorded baseline, measured in the metric’s own direction (for the lower-is-worse mi.* family that is a drop, still rendered +). The amount comes in two shapes: [regr +N%], a percentage of the record, for counts, and [regr +N.N pts], index points, for mi.*, whose arbitrary zero makes a percentage meaningless. Both start [regr , so rg '\[regr ' matches every regressed row. Special forms: [regr from 0] when a count’s baseline value was zero, [regr +>9999%] when a count’s regression exceeds 100× the baseline, [regr NaN] when the current value is NaN. See Reading the gate output for the details.

違反行は stdout に出力され、それより下のものはすべて stderr に出力されます。完全な振り分けはどちらのストリームかを参照してください。

After the per-violation rows, stderr carries a per-file rollup footer with the format <path>: <count> violations (worst: <metric> = <value> vs limit <limit> at L<start>), sorted by violation count descending. It is meant to be the first thing a reader looks at: which file has the most problems, and which metric is the loudest in that file. Pass --no-summary to suppress the footer for tooling that reads the merged streams.

対処しやすい失敗出力

The four sub-sections below turn bca check’s failure output from a wall of offender lines into CI-aware presentations: which files in this PR tripped a threshold (--since / --changed-only), inline file-diff annotations (--github-annotations), a rendered step-summary digest ($GITHUB_STEP_SUMMARY), and a copy-paste-safe remediation block. Each is independent; mix and match per CI surface. A combined worked example is at the end of this group.

差分対応モード(--since / --changed-only)

On a PR or push, the developer’s first question is usually which of my files in this change tripped a threshold, not the whole-tree offender list. Two flags answer that:

  • --since <ref> はファイル別フッターを、「Files in this range:」セクション(<ref> と HEAD の間で変更されたファイルにある違反)と、それに続く「Other offenders:」(それ以外すべて)に分割します。違反ごとの行は変更されないため、grep を前提とした既存のツールはそのまま動作し続けます。
  • --changed-only は、変更されたファイル群の外にあるファイルの違反を完全に除外します。簡潔さが求められる PR ゲートに使用してください。
- uses: actions/checkout@v4
  with:
    # `--since origin/<base>` はマージベースを解決します。デフォルトの
    # `fetch-depth: 1` チェックアウトではその ref に到達できません。`0` を
    # 指定すると全履歴を取得するため、差分ベースが解決できます。
    fetch-depth: 0
- name: 差分対応フッター付きのしきい値チェック
  run: |
    bca check --paths . --exclude-from .bcaignore \
        --baseline .bca-baseline.toml \
        --since "origin/${{ github.base_ref }}"

--since を省略すると、bca は次の優先順位で環境から差分ベースを自動検出します。

  1. BCA_DIFF_BASE: the explicit override. Use this from a local shell or non-GHA CI runner to mimic the auto-detection.
  2. GITHUB_BASE_REF: set by GitHub Actions on pull_request events. Expanded to origin/<value>; the runner is responsible for the corresponding git fetch.
  3. GITHUB_EVENT_BEFORE: set by GitHub Actions on push events to the SHA at HEAD before the push. The all-zeroes SHA (force push, brand-new branch) is treated as no signal.

A failed auto-detection (git missing, ref unresolvable, not a git checkout) is non-fatal without --changed-only: bca prints a warning and falls back to the whole-tree footer. With --changed-only, the same failure is fatal, so a misconfigured CI cannot pass by suppressing every violation.

“Files in this range:” バナーには、解決されたベースとそれを導いたシグナルが表示されるため、CI ログの読者はゲートが期待どおりの ref を捕捉したことを確認できます。

Files in this range (diff base: origin/main via GITHUB_BASE_REF):
./src/a.rs: 1 violation (worst: cyclomatic = 11 vs limit 2 at L1)

Other offenders:
./src/b.rs: 1 violation (worst: cyclomatic = 11 vs limit 2 at L1)

これは bca diff-baseline とは異なります。bca diff-baseline はディスク上の 2 つのパス間でベースラインファイルを比較し、追加・削除・悪化・改善されたエントリを報告します。--since は 2 つの git ref 間でソースファイルを比較します。

GitHub Actions のインラインアノテーション(--github-annotations)

The GHA UI renders ::error file=…,line=…,title=…::msg workflow commands as inline annotations on the file-diff view, which are easier to find than lines in the raw job log. bca check emits one per violation according to the tri-state --github-annotations <auto|always|never>: auto (the default) enables them when $GITHUB_ACTIONS == "true" (set by every GHA workflow step), always forces them on, and never suppresses them even inside a step (useful when a workflow runs bca check twice and wants annotations from only one run). A bare --github-annotations means always.

The annotations are emitted alongside the existing per-violation human stream, not instead of it. To avoid exhausting GitHub’s 10-error-per-step UI quota, annotations are capped at 10 per metric; overflow rolls up to a single ::error::N more <metric> violations not shown line so the count stays visible.

- name: インラインアノテーション付きのしきい値チェック
  run: |
    bca check --paths . --exclude-from .bcaignore \
        --baseline .bca-baseline.toml
  # No `--github-annotations` flag needed: auto-enabled in GHA.

上記の --since と組み合わせると、アノテーションが違反者リスト全体ではなく PR 内のファイルを指すようになります。

ステップサマリーの Markdown ダイジェスト($GITHUB_STEP_SUMMARY)

GitHub Actions exposes $GITHUB_STEP_SUMMARY, a path to a markdown file that, when populated, renders as the step’s summary view in the job UI. Whenever that env var is set, or when --summary-file <path> is passed explicitly, bca check appends a digest containing the per-file rollup table, a per-metric count breakdown, and the top-10 offenders by severity (ranked the same way as the footer’s worst metric). --summary-file never suppresses the digest even when $GITHUB_STEP_SUMMARY is set.

The digest is bracketed by HTML-comment markers (<!-- bca-step-summary-begin --> / <!-- bca-step-summary-end -->) so a retried step replaces the previous block rather than stacking a new one; three retries converge to exactly one up-to-date digest. Content outside the markers (for example, summaries written by other tools earlier in the same step) is preserved.

- name: ステップサマリーダイジェスト付きのしきい値チェック
  run: |
    bca check --paths . --exclude-from .bcaignore \
        --baseline .bca-baseline.toml
  # No flag needed: `$GITHUB_STEP_SUMMARY` is set automatically in GHA.

ローカルユーザーは --summary-file <path> でダイジェストを任意の Markdown ファイルに書き出せます。入力が空(クリーンな実行)でも “✓ No threshold violations.” ブロックが書き込まれるため、ステップサマリーからゲートが実行されたことを明確に確認できます。

修復フッター(常時有効)

ゲートが違反を検出すると、bca check は stderr に(さらに上記のステップサマリーダイジェスト内にも)末尾の --- next steps --- ブロックを出力します。

--- next steps ---
* Detailed reports: bca-reports artifact at https://github.com/<owner>/<repo>/actions/runs/<run-id>
* To refresh baseline: bca check --paths . --exclude-from .bcaignore --write-baseline .bca-baseline.toml
* Adoption guide: https://dekobon.github.io/big-code-analysis/recipes/baselines.html

The refresh invocation mirrors every flag the gate ran with that decides what a baseline records, so a first-time reader of a failing CI log can copy-paste it verbatim. That covers the walk scope (--paths, --include, --exclude, --exclude-from, --paths-from), the walker tuning (--language, --exclude-tests, --cyclomatic-count-try, --no-ignore, --no-skip-generated, --no-config), and the gate’s own inputs (--config, --threshold, --tier, --no-suppress, --check-exclude, --check-exclude-from, --baseline-fuzzy-match, --baseline). Flags that change only reporting or the exit code (--jobs, --report-format, --output, --no-fail, --exit-codes) are left off, since none can move an entry into or out of the file. The artifact URL is derived from $GITHUB_REPOSITORY and $GITHUB_RUN_ID when both are present (always true in GHA). Local runs have no upload to point at, so they suggest running bca report for the detailed view instead.

統合されたストリームを読むツール向けには --no-remediation でブロックを抑制します。素の bca check | ... パイプラインがこのブロックを見ることはありません。

集中的なリファクタリングの後に更新します。

bca check --paths src/ \
    --write-baseline .bca-baseline.toml
git diff .bca-baseline.toml   # ファイルは縮小していくはずです

Two --write-baseline runs over an unchanged tree produce byte-identical output, so a diff appears only when offenders changed. See the Baselines recipe for the full adoption flow, PR-review heuristics, and the suppression composition rules.

すべてを組み合わせる

上記の 4 つのフラグは組み合わせて使えます。PR ゲートのワークフローで推奨される呼び出しは次のとおりです。

- uses: actions/checkout@v4
  with:
    # `--since origin/<base>` はマージベースを解決します。デフォルトの
    # `fetch-depth: 1` ではその ref に到達できません。`0` を指定すると
    # 全履歴を取得するため、差分が解決できます。
    fetch-depth: 0

- name: しきい値ゲート(差分対応 + GHA UX)
  run: |
    bca check --paths . --exclude-from .bcaignore \
        --baseline .bca-baseline.toml \
        --since "origin/${{ github.base_ref }}"
  # `--github-annotations` や `--summary-file` フラグは不要です。
  # どちらも `$GITHUB_ACTIONS == "true"` と `$GITHUB_STEP_SUMMARY` から
  # 自動的に有効になります。末尾の修復ブロックも自動的に
  # 出力されます。

失敗した PR で得られるもの:

  1. Per-violation rows on stdout, in the same shape as the legacy gate, so existing grep tooling keeps working.
  2. Per-file rollup footer with Files in this range: (touched in the PR) listed before Other offenders:, so the developer sees their own contributions first.
  3. インライン GHA アノテーション — ファイル差分ビュー上に表示され、メトリクスごとに 10 件で打ち切られ、超過分はまとめて表示されます。
  4. Step-summary panel with a rendered markdown digest (per-file rollup, per-metric breakdown, top-10 offenders by severity).
  5. 末尾の修復ブロック — アーティファクト名を示し、そのまま使える --write-baseline 更新コマンドを表示し、ベースラインのレシピへリンクします。

調整ノブ:

フラグ効果デフォルト
--since <ref>フッターを分割。省略時は環境から自動検出オフ。BCA_DIFF_BASE / GITHUB_BASE_REF / GITHUB_EVENT_BEFORE により自動検出
--changed-only差分外の違反を完全に除外オフ
--github-annotations <auto|always|never>::error file=…::msg ワークフローコマンドを出力(引数なしのフラグ = always)auto は $GITHUB_ACTIONS == "true" のとき有効化。never で無効化
--summary-file <path|auto|never>Markdown ダイジェストを追記。never で無効化auto は $GITHUB_STEP_SUMMARY を検出
--no-remediation末尾の --- next steps --- ブロックを抑止失敗時にブロックを出力

GHA の外で bca check を実行するローカルユーザーにとって、デフォルトの挙動に変化はありません。環境からのシグナルなしに自動有効化される機能は 4 つのうち 1 つもありません。GHA での体験をローカルでプレビューするには次のようにします。

summary=$(mktemp "${TMPDIR:-/tmp}/bca-summary.XXXXXX.md")
GITHUB_ACTIONS=true GITHUB_STEP_SUMMARY="$summary" \
  BCA_DIFF_BASE=main \
  bca check --paths . --exclude-from .bcaignore \
      --baseline .bca-baseline.toml
cat "$summary"

GHA 以外の CI(GitLab、Buildkite、Jenkins)では、ランナーが公開する環境変数を設定する(またはフラグを明示的に渡す)ことで、同じ出力経路が動作します。

マージベースに対する違反者数の差分(暫定策)

For teams who cannot commit a baseline file (for policy reasons, say), a coarser approximation counts <error> elements in two Checkstyle documents, one on the merge base and one on the PR head, and fails when the count grows:

- name: マージベースに対する違反者の差分を計算
  run: |
    set -euo pipefail
    BASE="$(git merge-base origin/main HEAD)"
    git worktree add /tmp/base "$BASE"

    bca check --paths /tmp/base \
        --report-format checkstyle \
        --output /tmp/base.xml \
        --no-fail
    BASE_COUNT=$(grep -c "<error" /tmp/base.xml || true)

    bca check --paths "$PWD" \
        --report-format checkstyle \
        --output /tmp/head.xml \
        --no-fail
    HEAD_COUNT=$(grep -c "<error" /tmp/head.xml || true)

    echo "Offenders: base=$BASE_COUNT head=$HEAD_COUNT"
    if [ "$HEAD_COUNT" -gt "$BASE_COUNT" ]; then
      echo "::error::Offender count grew from $BASE_COUNT to $HEAD_COUNT"
      exit 1
    fi

これは違反の件数を数えるだけで、その同一性は追跡しません。違反している関数の名前を変えてもリグレッションとして検出されず、ある違反者を改善しつつ別の違反者を悪化させると差し引きゼロになります。上記のネイティブなベースラインフローの方が厳密に高精度であり、推奨されるアプローチです。

実際の動作例

big-code-analysis runs the recipes above against its own source on every push and PR. The workflow source, .github/workflows/pages.yml, exercises the threshold gate, the baseline ratchet, both report formats, and a SARIF upload to GitHub Code Scanning end-to-end against the workspace itself. (The SARIF upload runs on same-repo pushes and PRs only; fork PRs skip it because the upload needs a write-scoped token, exactly as the clippy SARIF job does.) The output sits on GitHub Pages alongside this book:

The bca version quoted in the snippets on this page is the latest published release at the time of writing.

The in-tree workflow installs bca by building it from the current checkout rather than downloading a pinned release. This avoids the CLI-artifact schema-skew failure mode described under Installing bca from a GitHub Release for repos whose .bca-baseline.toml is always written by the same bca that gates it. Downstream adopters tracking a stable release line should stick with the pinned-tarball pattern; only switch to “build from checkout” if you, too, are mutating CLI artifact schemas in lockstep with the binary.

セルフスキャンしきい値ゲート(CI ゲートのローカルミラー)

CI’s threshold gate fires only after push, which is too late if a refactor silently nudged a metric past its limit. The big-code-analysis repo’s Makefile exposes four targets that mirror the CI gate (the Threshold gate step in .github/workflows/pages.yml) locally and add a second tier at 95% of every limit, so encroachment is caught a commit or two before the hard gate trips:

make self-scan                            # ハードゲート。bca.toml のしきい値の 100%
make self-scan-headroom                   # ソフトゲート。デフォルト 95%(BCA_HEADROOM)
make self-scan-write-baseline             # ハードしきい値でベースラインを更新
make self-scan-write-baseline-headroom    # ソフトしきい値でベースラインを更新

Path selection, the .bcaignore deny-set, the per-function thresholds, the cyclomatic ? policy, and the baseline file all live in the repo-root bca.toml manifest, which bca discovers automatically. The hard tier is exactly what CI runs; expanded, it is a bare check with no path, threshold, or baseline flags, because the manifest supplies them:

cargo run --quiet -p big-code-analysis-cli -- check

That is the dev profile: the release relink costs ~100 s after any edit under src/, and both profiles write a byte-identical baseline, which make self-scan-profile-parity checks per PR. The recipes go through a small wrapper that can also run an already-installed bca when it proves it was built from the checkout; that mechanism and its knobs are repository tooling, documented in the Makefile, not part of the gate an adopter copies.

Both tiers consume the same bca.toml thresholds and the same .bca-baseline.toml; the soft tier runs the hard recipe with every threshold value multiplied by BCA_HEADROOM. Both exit 0 clean, 2 on any threshold violation, and 1 on tool error. The soft tier is a real gate, not advisory, so do not wrap make self-scan-headroom in || true. The two gate targets (self-scan, self-scan-headroom) are wired into make pre-commit, make ci, and .pre-commit-config.yaml; those chains run the hard tier before the soft tier, so a true regression always reports before near-limit headroom. The two write-baseline targets are side-effecting and deliberately not wired in.

BCA_HEADROOM=0.90 make self-scan-headroom widens the band; BCA_HEADROOM=0.99 tightens it to the last 1%. When the soft tier fires, absorb the offender into the baseline with make self-scan-write-baseline-headroom, which records every offender at the scaled thresholds (a strict superset of the hard-tier offenders).

The pattern (hard tier mirroring CI, soft tier as an early-warning band, both ratcheted by the same baseline) is project-agnostic. The Local threshold gates recipe documents the underlying principles, drop-in Makefile / just / package.json skeletons, and the helper script that scales thresholds, so you can adopt the same workflow in your own repo. The generic recipe uses the same BCA_* env-var names as the Makefile above, so overrides like BCA_HEADROOM=0.90 work identically across both.

GitLab CI

完全な .gitlab-ci.yml の例

以下のジョブは bca をインストールし、しきい値チェックを実行して Code Climate JSON(MR の Code Quality ウィジェット用)、Checkstyle XML、Markdown レポートを生成し、それらをアーティファクトとしてアップロードします。

The same CLI-artifact schema-compatibility note from the GitHub Actions section applies here: the BCA_VERSION pin must cover the schema version of every CLI artifact you commit.

stages:
  - quality

variables:
  BCA_VERSION: "2.4.0"  # pin a published big-code-analysis-cli release
  BCA_TARGET:  "x86_64-unknown-linux-gnu"
  # リリースの SHA256SUMS ファイルに記載された
  # big-code-analysis-${BCA_VERSION}-${BCA_TARGET}.tar.gz の sha256。BCA_VERSION と一緒に更新します。
  BCA_SHA256:  "d132c0ef559b4917b7679e17fe3d8f69f5bc14a67692f5d3596d0ec21954d20e"

bca-quality:
  stage: quality
  image: debian:stable-slim
  cache:
    # Same key shape as the GitHub Actions snippet: bumping
    # BCA_VERSION invalidates the cache automatically.
    key: "bca-$BCA_VERSION"
    paths:
      - .cache/bca/
  before_script:
    - apt-get update -qq && apt-get install -y --no-install-recommends ca-certificates curl tar
    - |
      set -euo pipefail
      install -d "$CI_PROJECT_DIR/.cache/bca" "$HOME/.local/bin"
      if [ ! -x "$CI_PROJECT_DIR/.cache/bca/bca" ]; then
        stage="big-code-analysis-${BCA_VERSION}-${BCA_TARGET}"
        tarball="${stage}.tar.gz"
        url="https://github.com/dekobon/big-code-analysis/releases/download/v${BCA_VERSION}/${tarball}"
        curl -fsSL --proto '=https' --tlsv1.2 -o "/tmp/${tarball}" "$url"
        echo "${BCA_SHA256}  /tmp/${tarball}" | sha256sum --check --strict -
        tar -xzf "/tmp/${tarball}" -C /tmp
        install -m 0755 "/tmp/${stage}/bca" "$CI_PROJECT_DIR/.cache/bca/bca"
        rm -rf "/tmp/${tarball}" "/tmp/${stage}"
      fi
      install -m 0755 "$CI_PROJECT_DIR/.cache/bca/bca" "$HOME/.local/bin/bca"
      export PATH="$HOME/.local/bin:$PATH"
  script:
    - bca
        check
        --paths "$PWD"
        --report-format code-climate
        --output gl-code-quality-report.json
        --no-fail
    - bca
        check
        --paths "$PWD"
        --report-format checkstyle
        --output bca-checkstyle.xml
        --no-fail
    - bca
        report -O markdown
        --paths "$PWD"
        --top 20
        --strip-prefix "$PWD/"
        --output bca-report.md
    # しきい値ゲートは別に実行するため、上記のアーティファクトは失敗時にも
    # 公開されます。終了コード 2 = 少なくとも 1 件のしきい値超過。
    - bca check --paths "$PWD"
  artifacts:
    when: always
    reports:
      codequality: gl-code-quality-report.json
    paths:
      - gl-code-quality-report.json
      - bca-checkstyle.xml
      - bca-report.md

この例に関する補足です。

  • 最初の 2 回の bca check … --no-fail 呼び出しはアーティファクト用に違反者を収集します。最後の bca check(--no-fail なし)が合否を決めるゲートです。3 回の実行はすべて同じしきい値設定を使うため、アーティファクトは常にゲートの判定と一致します。
  • artifacts:when: always keeps every artifact downloadable even on a red pipeline, which is when you need them most.
  • artifacts:reports:codequality wires the Code Climate JSON directly into GitLab’s MR Code Quality widget; see the Code Quality widget section below for the field-by-field semantics.

GitLab Code Quality ウィジェット

GitLab’s Code Quality view (inline complaints on the MR diff, summary on the MR overview page) consumes Code Climate JSON. bca check emits this via --report-format code-climate, so the integration is a one-liner:

code_quality:
  stage: quality
  script:
    - bca check --paths "$CI_PROJECT_DIR"
          --report-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

Severity bands are derived from how far each metric breaches its configured threshold: value / limit for a count, and for the maintainability-index family, where lower is worse and the index goes negative, one step per maintainability band below the floor. ≤ 1.5 → minor, ≤ 2 → major, ≤ 4 → critical, > 4 → blocker; bca check’s summary ranks offenders by the same key. The widget deduplicates findings by fingerprint; bca hashes path \0 function \0 metric (no line, no value) so a violation that survives an upstream line-drift edit still collapses into the same widget entry across pipeline runs.

生成されたレポートをローカルで検証するには次のようにします。

jq 'all(.[]; has("description") and has("check_name")
     and has("fingerprint") and has("severity")
     and has("location"))' gl-code-quality-report.json
# → true
jq '[.[] | .severity] | unique' gl-code-quality-report.json
# → ["info","minor","major","critical","blocker"] の部分集合

Markdown レポートを MR 限定コメントとして投稿

Markdown レポートを MR ノートとして添付する(GitHub の PR コメントレシピの GitLab 版)には、プロジェクトアクセストークンと Notes API を使用します。

bca-mr-comment:
  stage: quality
  image: alpine:3
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  needs: ["bca-quality"]
  before_script:
    - apk add --no-cache curl jq
  script:
    - |
      BODY=$(jq -Rs '.' < bca-report.md)
      curl --fail --silent --show-error \
        --request POST \
        --header "PRIVATE-TOKEN: $CI_BCA_BOT_TOKEN" \
        --header "Content-Type: application/json" \
        --data "{\"body\": $BODY}" \
        "$CI_API_V4_URL/projects/$CI_PROJECT_ID/merge_requests/$CI_MERGE_REQUEST_IID/notes"

CI_BCA_BOT_TOKEN は api スコープを持つプロジェクトアクセストークンです。このジョブは bca-quality に依存するため、実行前に Markdown アーティファクトが揃っています。

Jenkins / SonarQube

Both Jenkins (via the Warnings Next Generation plugin) and SonarQube (via its Generic Issue importer) consume Checkstyle 4.3 XML directly. The same invocation feeds both:

bca check --paths src/ \
    --report-format checkstyle \
    --output report.checkstyle.xml

report.checkstyle.xml を、既存の Jenkins の Record Issues / SonarQube の External Issues ステップに接続してください。Checkstyle ライターは違反者がいないときも空の(整形式の)ドキュメントを出力するため、どちらのツールもクリーンな実行を特別扱いする必要はありません。ライターのスキーマの詳細は check コマンドのページを参照してください。

汎用的な CI ガイダンス

以下はプロバイダーを問わず当てはまります。

  • Pin bca to a specific version. Both cargo install --version and cargo binstall --version accept the published crate version of big-code-analysis-cli. A floating install surfaces metric-counting changes as unexplained CI flakes. Pin to a version whose CLI-artifact schemas (baseline, thresholds) match the files your repo commits; see the schema-compatibility note in the install section.
  • Use the default CPU-based parallelism unless memory requires fewer workers. --jobs defaults to the effective CPU count and honors available_parallelism() (cgroup-, cpuset- and quota-aware on Linux, OS CPU count on macOS/Windows), so CI runners need not thread --jobs "$(nproc)" through every recipe. --jobs 1 remains a debugging knob, not a default. It is not memory-aware. Each worker holds one whole file at a time and a parse-only run measures 25–70× the source size, so the in-flight term is roughly jobs × 70 × the largest source file in the tree, and --jobs bounds that term only. The destination decides the rest: --output-dir writes each document as it is produced and bca check reduces each file to its violations, so both stay at the in-flight bound. metrics --output <FILE> collects every file’s result and serializes it after the walk, and structured stdout holds finished documents while an earlier file is still being analyzed, which is worst when one large file sorts early. A memory-limited container with many vCPUs should pass an explicit --jobs, and prefer --output-dir to --output <FILE> on a large tree.
  • bca report markdown には常に --strip-prefix "$PWD/" を渡してください。 そうすればパス列は、ワークスペースパスが異なるランナー間でも同一になります。これがないと、2 つのレポートの差分は /home/runner/work/... と /builds/group/project/... のノイズに支配されます。
  • bca.toml はリポジトリルートに置いてください — Cargo.toml / pyproject.toml / package.json と同じ場所です。bca はこれを自動的に発見するため、引数なしの bca check がコミット済みのしきい値、パス、ベースラインを読み込みます。これをソースコードとして扱い、しきい値の緩和はコードレビューで審査してください。
  • Handle the exit-code contract. bca check exits 0 clean, 2 on any threshold violation, 1 on tool error (bad config, unknown metric, unreadable path). Reserving 1 for tool errors lets CI distinguish “a function got too complex” from “the analyzer crashed”. Pass --exit-codes=tiered (or set [check] exit_codes = "tiered" in bca.toml) to split the violation case by severity: 2 new offenders only, 3 regressions only, 4 both, 5 a --tier=soft violation that also breaches the hard limit. The tiered codes are opt-in; the default stays 0/1/2. Every fail-state remains non-zero, so exit != 0 → fail wrappers keep working; only tooling that tests $? -eq 2 explicitly needs to widen to 2-5.
  • ソース内の抑制マーカーを尊重し、監査には --no-suppress を使ってください。 デフォルトの bca check は bca: suppress / bca: suppress-file マーカーを尊重します。--no-suppress を渡すとそれらを無視するため、監査者は生の違反者リストを確認できます。

ゲートの信頼境界

プルリクエストのゲートは、その作成者が管理するコードを分析します。そして「ゲートがどのファイルを見るか」を決める入力のうち 2 つが、その同じブランチの中にあります。生成コード検出器が判定するファイル内容と、ウォーカーが解釈する .gitignore ファイルです。どちらの既定値も、信頼できるチェックアウトの分析には適切です。しかし PR ゲートでは、レビュー対象の変更が引けるレバーになります。bca check は既定でどちらも報告し(後述のサマリー)、--strict の下ではどちらも無効にします。

生成コードマーカーはファイルをゲートから外す

f() が cyclomatic=2 を超える C ファイルを、bca check --no-config --paths . --threshold cyclomatic=2 で検査した場合:

状態終了コード
ベースライン2(違反が報告される)
1 行目に // @generated を追加0
同じファイルで --no-skip-generated を指定2
マーカーを再び削除2

コメント 1 行でファイルがゲートから外れます。認識されるマーカー語句はどれでも有効で(@generated、DO NOT EDIT、GENERATED CODE、大文字小文字を区別しない)、検出器は 1 行目だけでなく先頭の約 50 行 / 5 KiB を走査します。差分レビューの際に知っておくべき形が 2 つあります。

  • // DO NOT EDIT, see docs/schema.md before changing は日常的な整備コメントに見え、指摘されずにレビューを通過します。
  • 5 行のライセンスヘッダの 4 行目に埋め込まれた @generated もマッチします。不審な 1 行目を探すレビュアーはこれを見つけられません。

コミットされた .gitignore はファイルをウォークから外す

同じゲートで、違反ファイルに無害な兄弟ファイルを 1 つ添えた場合:

状態終了コード
違反ファイル + 無害な兄弟ファイル2
+ violator.c を含む .gitignore0
同じ構成で --no-ignore を指定2

ウォーカーは走査対象ツリー内で見つかった ignore ファイルを解釈するため、テスト対象のブランチにコミットされた .gitignore は検査対象のファイル集合を狭めます。1 つ注意があります。無視されたファイルが唯一の分析可能な入力だった場合、実行は 0 ではなく 1(“no input files matched”)で終了します。つまりクリーンなバイパスには、少なくとも 1 つ他のファイルが残っている必要があります。

ゲートはスキップしたものを報告する

bca check は、いずれかの仕組みがファイルを除外した場合に 1 行のサマリーを stderr に出力するため、どちらのバイパスも無音ではありません。

bca: 2 files not checked (1 generated, 1 ignored) — pass --report-skipped to list them

クリーンな実行では何も出力されません。--report-skipped はファイルごとに note: skipped (generated): <path> / note: skipped (ignored): <path> の行を追加します。サマリーは終了コードを変えません。バイパスされた実行は依然として 0 で終了し、その事実を表示するだけです。

The ignored count covers analyzable files at the directories the walk entered. A whole directory dropped by an ignore rule (a build tree, or an attacker ignoring a source directory outright) is never entered and appears only under --report-skipped, as its own clause (3 ignored directories not walked), because ignored build trees are present in nearly every checkout and would otherwise make the default summary permanent noise. A PR gate that must see whole-directory drops should pass --report-skipped or, better, --strict. See the check page for the exact rules.

--strict は両方の仕組みを無効にする

信頼できない入力に対するゲートでは、stderr を監査するのではなく、両方の仕組みを無効にしてください。

- name: Threshold gate (PR)
  run: bca check --strict --paths src/

--strict は --no-skip-generated --no-ignore と等価です。2 つの構成フラグのうち片方だけを覚えているレシピにはもう片方のバイパスが残るため、単一のフラグを選んでください。プロジェクトは各ワークフローを編集する代わりに、bca.toml の [check] に strict = true を書いて一度だけオプトインします。本当に生成されたツリーは、提出者が管理するコンテンツ判定ではなく、レビュアーが管理する明示的な deny-set(コミット済みの .bcaignore パターン)に置くべきです。