チェック
bca check は関数ごとのメトリクスをしきい値に照らして評価し、いずれかの関数が制限を超えると非ゼロで終了します。これは CI の統合ポイントです。ビルドステップに組み込めば、コード複雑度の悪化が変更の取り込み前にパイプラインを失敗させます。
完全な CI レシピをお探しですか? CI 統合レシピには、
--report-formatのマトリクス、実行可能な GitHub Actions と.gitlab-ci.ymlの例、ベースライン / ラチェットのパターン、GitLab Code Quality のパスがまとめられています。本ページはコマンド自体を、レシピはパイプラインへの組み込み方をそれぞれ説明します。
終了コード
| コード | 意味 |
|---|---|
0 | すべての関数がしきい値内(または --no-fail 指定時)。 |
2 | 少なくとも 1 つのしきい値を超過。 |
1 | ツールエラー(不正な引数、読み取れない設定または入力、未知のメトリクス)。 |
1 は予約されており、CI がリグレッション(2)とツールの設定ミス(1)を区別できるようになっています。
A gate that could not read all of its input has no verdict to report, so three input problems exit 1 rather than 0: nothing matched --paths / --include / --exclude, any input file that failed to read, and any directory the walk could not list. The last two are the workspace-wide unreadable-input rule — check is not special here, it is just where the rule matters most, since a gate reporting clean on a tree it could not read is indistinguishable from a gate that passed. All three run before the gate is evaluated and are not suppressed by --no-fail, which suppresses threshold failures, not broken input, so none of them lets --write-baseline record a partial run.
段階的終了コード(--exit-codes=tiered)
--exit-codes=tiered (or [check] exit_codes = "tiered" in bca.toml) splits the single violation code 2 by severity so CI can branch on it without parsing the [new] / [regr +N%] row tags:
| コード | 意味(tiered モード) |
|---|---|
0 | すべての関数がしきい値内(または --no-fail 指定時)。 |
1 | ツールエラー。 |
2 | 新規違反のみ(一致する --baseline エントリなし)。 |
3 | ベースライン退行のみ(ベースライン登録済みの違反が悪化)。 |
4 | 新規違反と退行の両方。 |
5 | ハード制限も超えている --tier=soft 違反。 |
tiered コードはオプトインであり、上記のデフォルト契約は 0/1/2 のままです。すべての失敗状態は非ゼロのままなので、exit != 0 → fail 方式のラッパーは引き続き動作します — $? -eq 2 を明示的に判定するツールだけが 2-5 に広げる必要があります。--no-fail は引き続き終了コード 0 を強制します。コード 5 はソフト層でのみ出力されます。ハード層では定義上すべての違反がハード超過であるため、代わりに 2/3/4 の分割が適用されます。--exit-codes <default|tiered> は値を取るフラグで、CLI の値はどちらの方向でもマニフェストの [check] exit_codes キーを上書きします。無効な exit_codes 値はツールエラー(1)です。--print-effective-config は解決後の exit_codes スタイルを報告します。非推奨の --strict-exit-codes フラグは --exit-codes tiered の 1 サイクル限りのエイリアスです(警告を出し、次のメジャーで削除されます)。
しきい値の宣言
メトリクスごとに --threshold <metric>=<limit> を 1 回ずつ渡します(繰り返し指定可能)。メトリクス名は bca list-metrics と一致し、サブメトリクスはドット区切り形式を使います。0 は有効な制限値で、「いかなる値も許容しない」ことを意味します。
bca check --paths src/ \
--threshold cyclomatic=15 \
--threshold cognitive=20 \
--threshold loc.lloc=200
あるいは、しきい値を bca.toml マニフェストに置くこともできます(CI のしきい値をコードと一緒にバージョン管理できる一元的な場所です)。リポジトリルートに置けば自動検出され、--config フラグなしの素の bca check がそれを読み込みます。
# bca.toml
paths = ["src"]
[thresholds]
cyclomatic = 15
cognitive = 20
"loc.lloc" = 200
"halstead.volume" = 1000
bca check
1 回の実行でマニフェストの上に別のしきい値ファイルをマージするには、--config で明示的に渡します。CLI フラグと --config の値は、同じメトリクス名についてマニフェストを上書きするため、プロジェクト全体のデフォルトを保ちながら、特定の実行でひとつのメトリクスだけを厳しくできます。
bca check --paths src/ --config bca.toml
bca init scaffolds a starting table for you. For where those numbers come from, which ones to override for the language you are gating, and how to pick a different set for agent feedback, legacy triage, or safety-critical work, see Choosing thresholds.
使用できるメトリクス名
トップレベルのスカラーメトリクスは list-metrics の名前をそのまま使います:cognitive、cyclomatic、nargs、nexits、nom、tokens、abc、wmc、npm、npa。複数のサブフィールドを持つメトリクススイートはドット区切り形式を使います。
| メトリクス | 使用できるしきい値名 |
|---|---|
| 循環的複雑度 | cyclomatic、cyclomatic.modified |
| Halstead | halstead.volume、halstead.difficulty、halstead.effort、halstead.time、halstead.bugs |
| コード行数 | loc.sloc、loc.ploc、loc.lloc、loc.cloc、loc.blank |
| 保守容易性指数 | mi.original、mi.sei、mi.visual_studio |
不明なしきい値名はツールエラー(終了コード 1)となり、黙って無視されることはありません。
しきい値のスコープ
しきい値は、そのメトリクスが実際に測定するスペース種別に対してのみチェックされるため、メトリクスのファイル全体や impl 全体の集計値が関数単位の制限と取り違えられることはありません。各メトリクスのスコープは固定で、設定項目はありません。
| スコープ | ゲート対象のスペース | メトリクス |
|---|---|---|
| ファイル | ファイル全体のルートのみ | loc.sloc、loc.ploc、loc.lloc、loc.cloc、loc.blank |
| 関数 | 個々の関数、メソッド、クロージャ | cognitive、cyclomatic、cyclomatic.modified、halstead.*、mi.*、abc、nargs、nexits、tokens |
| コンテナ | クラス、構造体、トレイト、impl、名前空間、インターフェイス | nom、wmc、npm、npa |
関数スコープのメトリクスにはサブツリー合計(nargs、nexits、tokens、halstead.*)が含まれます。これらは引き続き関数自身の入れ子クロージャをその値に繰り入れますが、ファイル全体や impl 全体にわたって合算されることはなくなりました。コンテナスコープのメトリクスは型のメソッド集合(クラスあたりのメソッド数、重み付きメソッド、公開メンバー)を表すため、末端の関数ごとではなくコンテナをゲートします。これにより、個々の関数は問題ないクリーンなファイルが、ファイル全体の合計だけで加算型の制限に引っかかることはなくなりました — かつて bca: suppress-file マーカーが覆い隠していた偽陽性です。
loc サブメトリクスの素の bca diff --metric 表記は、ドット区切り形式のエイリアスとして受け付けられます(sloc は loc.sloc と等価で、ploc/lloc/cloc/blank も同様)。そのため diff 実行からコピーした名前は正しくゲートされます。単一のしきい値スカラーを持たない素のファミリー名(halstead、mi)はあいまいなため拒否され、具体的なサブメトリクスを列挙する「もしかして」ヒントが表示されます — いずれか 1 つ(例:halstead.volume)を選んでください。
The two spellings name one metric, so they override each other wherever limits merge: a [thresholds.lang.c] ploc = 100 replaces the global "loc.ploc", and --threshold ploc=100 replaces either. Within a single table, writing both is an error rather than a silent winner — set the metric once, under whichever spelling you prefer.
Per-language limits ([thresholds.lang.<slug>])
Metric distributions vary by language more than by project — the measured 97.5th-percentile per-function cognitive value runs from 4 in C# to 50 in C. A [thresholds.lang.<slug>] table gives one language its own limits, layered over the project-wide table:
[thresholds]
cognitive = 15
cyclomatic = 15
"loc.ploc" = 600
[thresholds.lang.c]
cognitive = 30
"loc.ploc" = 1200
[thresholds.lang.elixir]
nom = 150
wmc = 300
C is now gated at cognitive = 30 and loc.ploc = 1200 while keeping the project's cyclomatic = 15; every other language keeps all three. The override is per metric, not a replacement table — a language inherits every limit it does not restate. Which numbers to change, and the two cases where an override is a correction rather than tuning, are in Choosing thresholds.
The key is the canonical language slug, the same vocabulary --language accepts: rust, python, cpp, csharp, objc, tsx, mozcpp, mozjs, and so on — bca check --language nonsense prints the full list. Two rules point in opposite directions here:
- An unknown slug in the manifest is a tool error (exit
1) with a did-you-mean hint. A typo'd[thresholds.lang.rust-lang]must not silently leave a gate at the project limit while the author believes it was loosened. - An unrecognised file language falls through to the global table, as does any language with no override of its own. Nothing is silently ungated. (A file whose extension maps to no grammar at all is skipped by the walk before the gate ever sees it — with a warning if you named it explicitly.)
--threshold on the command line stays global and still applies last and absolutely: it overrides the project table and every per-language one, so a limit you type is the limit that runs.
--print-effective-config prints one fully resolved table per overridden language — inherited limits included, not a diff — so the number that will actually fire is the number you read:
[thresholds]
cognitive = 15.0
cyclomatic = 15.0
[thresholds.lang.c]
cognitive = 30.0
cyclomatic = 15.0
二層しきい値(--tier)
--tier <hard|soft|soft=RATIO> selects which threshold tier the gate compares against. hard (the default) uses the [thresholds] table verbatim; soft is an early-warning tier that fires before the hard gate, tightening every limit by RATIO. A bare --tier means soft; soft alone uses the default ratio 0.95; soft=0.90 pins the ratio to 0.90; soft=1.0 disables the blanket scale.
RATIO scales the band, not the number. For most metrics a limit is a ceiling, so tightening it means multiplying: cognitive = 15 with soft=0.9 warns at 13.5. For the lower-is-worse mi.* family a limit is a floor — a value below it is the violation — so the same 0.9 divides: "mi.original" = 20 warns at 20 / 0.9 = 22.2223, rounded up so the band never resolves below the exact quotient. Multiplying a floor would move the warning to 18, under the hard gate, where nothing could reach it first.
[thresholds.soft] テーブルはメトリクスごとのソフト制限を設定します。各値は絶対値の数値か、そのメトリクスのハード制限をスケールする "<ratio>x" 文字列のいずれかです。
[thresholds]
cognitive = 25
cyclomatic = 15
nargs = 7
[thresholds.soft]
cognitive = 22 # 絶対値のソフト制限
cyclomatic = "0.9x" # ハード制限の 90% → 13.5
# nargs は未指定 → ソフト層はハード制限を継承(ソフト帯なし)
bca check --paths src/ --tier=soft
ソフト層は次の固定順序で解決されます。
[thresholds](bca.tomlマニフェスト、--configとマージ済み)から始めます。[thresholds.soft]テーブルが存在する場合は、その上書きを上にマージします。そこに無いメトリクスはハード制限を継承します。一括のRATIOは適用されません(明示的なメトリクスごとの制限が優先されます)。- Otherwise tighten every limit by the soft
RATIO(default0.95for a baresoft;soft=1.0disables scaling). - 繰り返し指定された
--threshold name=valueフラグは最後に、絶対値として適用されます。
Steps 1 to 3 run once per language, against that language's own resolved hard limits. There is no [thresholds.lang.<slug>].soft table and none is needed: with [thresholds] cognitive = 15, [thresholds.lang.c] cognitive = 30, and --tier=soft=0.9, C's soft band is 27 — nine tenths of its limit, and the ceiling a C offender is measured against for the exit-5 escalation is 30, not 15. Derive either from the project's 15 and every C function between 15 and 30 reports "also breaches the hard limit" while sitting inside the limit the project configured for it.
The one combination that can invert the two tiers is an absolute [thresholds.soft] value looser than the hard limit it shadows — [thresholds.soft] cognitive = 12 alongside [thresholds.lang.csharp] cognitive = 4, or an mi.* soft floor below its hard floor. That is a tool error (exit 1) naming the offending table, for the same reason a "<ratio>x" factor above 1 is rejected at parse time: a soft tier that fires after the hard gate is never the intent.
ソフト RATIO(および "<ratio>x" 文字列のスケール係数)は (0, 1] の範囲でなければなりません。[check] headroom マニフェストキーは、素の --tier=soft に比率を供給します。非推奨の --headroom <R> フラグは --tier=soft=<R> の 1 サイクル限りのエイリアスで(警告を出し、次のメジャーで削除されます)、現在はハード実行をソフト層に昇格させます。両方の層は同じ --baseline を通じてラチェットし、--print-effective-config はマージ後の制限と併せて解決後の tier を報告します。移行のヒントと背景についてはローカルしきい値ゲートレシピを参照してください。
Previewing a candidate limit (--explain-threshold)
--explain-threshold <metric>=<limit> reports what a candidate limit would cost — at both tiers — instead of gating. It is repeatable, takes one candidate per metric, and writes nothing.
$ bca check --explain-threshold nargs=6
nargs: candidate limit 6
hard tier (limit 6): 60 offenders, 60 already baselined, 0 new
soft tier (limit 5.7, 0.95x): 135 offenders, 61 already baselined, 74 new
cluster: 75 of 75 soft-band offenders sit at exactly 6 — the candidate
limit itself. The soft tier measures distance to the limit, so a limit
of 6 places them inside the 5.7 band by construction and none of them
can clear it without real work.
The new column is the figure to weigh: it is how many baseline entries adopting the limit would add. Here the hard tier reads as free and the soft tier costs 74 entries, none of which any amount of tidying can retire — which is the whole reason the flag exists.
--threshold nargs=6 cannot tell you this. Its limits are applied last and absolutely, never scaled, so a candidate trialled that way has no soft tier at all. Passing both flags for the same metric is rejected rather than silently resolved.
The soft limit is derived from the candidate exactly as a real run would derive it: a [thresholds.soft] entry for the metric wins, then the --tier=soft=RATIO ratio if one was given, then 0.95. A [thresholds.lang.<slug>] table that overrides the metric keeps its own limit — a candidate global limit does not reach it — and the report says so on its own line.
Everything else matches the run being predicted: exclude_tests, [check] exclude, in-source suppression markers, --changed-only, and the baseline all apply as usual. The one difference is that baseline-covered offenders are counted rather than dropped, which is what makes the already baselined / new split possible.
The preview replaces the gate, so it never fails: the exit code is 0 unless a tool error (exit 1) stops the run, and a one-line reminder of that goes to stderr. It conflicts with --write-baseline, --print-effective-config, --report-format, and --output, each of which would produce a second, different artifact.
See Choosing thresholds for the rule this flag exists to make visible.
違反の出力
Every offending (function, metric) pair prints one line to stdout in this stable format:
<path>:<start_line>-<end_line>: <function_name>: <metric> = <value> (limit <limit>)
例:
src/parser.rs:42-117: parse_expression: cyclomatic = 22 (limit 15)
src/parser.rs:42-117: parse_expression: cognitive = 31 (limit 20)
行はパス、開始行、メトリクス名の順でソートされるため、同じツリーに対する実行間で出力は決定的です。
Which stream
The offender rows are the command's product, so they go to stdout: bca check | wc -l, | head, | rg -c and 2>/dev/null all reach them.
Everything the run says about itself goes to stderr:
- the per-file
--- summary ---footer, - the
--- next steps ---remediation block, - the GitHub Actions
::errorannotations, - the
bca: skipped N violations via [check.exclude]andbca: filtered N violations via baselinecounts, - every
warning:anderror:diagnostic.
One combination inverts this. --report-format <dialect> without --output puts the aggregated SARIF / Checkstyle / Code Climate document on stdout, so the human rows fall back to stderr rather than corrupting it:
bca check --report-format sarif | jq '.runs[0].results | length' # document on stdout
bca check --report-format sarif --output report.sarif | wc -l # rows back on stdout
The --summary-file digest is a file, not a stream, and appears on neither.
Earlier releases sent the rows to stderr along with everything else, which made | wc -l and 2>/dev/null report an empty offender list — indistinguishable from a clean tree. A pipeline that reads the rows through 2>&1 needs no change; one that captured them with 2>file should now use >file.
抑制マーカーによる違反の抑制
ソース内コメントによって、問題のコードを編集したりウォークから除外したりせずに、個々の関数またはファイル全体のしきい値違反を抑制できます。ネイティブの記法は bca: suppress / bca: suppress-file で、Lizard の #lizard forgives は互換シムとして認識されます。完全なリファレンスと --no-suppress CI 監査フラグについては抑制マーカーを参照してください。
ファイルカテゴリ全体の除外([check.exclude])
一部のファイルは分析・報告はするがゲートは決してしない扱いにすべきです。意図的に cognitive/cyclomatic を超過させるテストフィクスチャ、生成されたバインディング、複雑さが構造的で決して「修正」されないマクロディスパッチモジュールなどです。これらを .bcaignore に入れるのは大雑把すぎます — ウォークから完全に除外されるため、bca report からも消えてしまいます。ベースラインに登録するのも誤りです — これらは返済中の負債ではなく、ベースラインの diff を永遠にかき乱すことになります。
[check.exclude] はグロブレベルの中間手段です。マッチしたファイルはウォークされ、パースされ、メトリクスが計算され、bca report にも表示されますが、bca check は違反を出力する前、かつ --write-baseline が何かを記録する前にそれらの違反を破棄するため、構造的な除外対象は .bca-baseline.toml の外に保たれます。
bca.toml の場合:
[check]
exclude = [
"tests/**",
"src/languages/language_*.rs",
"xtask/**",
]
またはコマンドラインで指定します(--check-exclude は繰り返し指定可能で、--check-exclude-from と結合されます)。
bca check --check-exclude "tests/**" --check-exclude "xtask/**"
bca check --check-exclude-from .bcacheckignore
--check-exclude-from は .gitignore 形式のファイルを読み込みます(空行と # コメントはスキップ)。慣例的な名前は、ウォーカー用の .bcaignore に対応する .bcacheckignore です。グロブは、ウォーカーが --exclude でマッチさせたのと全く同じパスにマッチします。「負のフィルター」キーであるため、明示的な --check-exclude リストはマニフェストの [check] exclude リストと結合されます(置き換えではありません)。CLI の除外はプロジェクトの除外に追加されるのであって置き換えではないため、マニフェストが意図的に除外したパスを誤って再ゲートすることはできません。重複はまとめられ、CLI のパターンが先にソートされます。マニフェストの除外を完全に外すには --no-config を渡します。(paths / include のような正のスコープキーは CLI では引き続き「置き換え」です — マージされるのは exclude フィルターだけです。)
他の抑制メカニズムとの優先順位
bca check は、最も特異的なものから順に、次の順序で除外を解決します。
- ソース内マーカー(
bca: suppress/bca: suppress-file)— 常に優先されます。ウォーク中に適用されるため、その関数はそもそも違反になりません。 [check.exclude]グロブ — ファイルの「カテゴリ」(テスト、生成コード)を除外します。.bca-baseline.toml— 返済中の既知の違反。
--print-effective-config は、他のゲート入力と併せて解決後の check_exclude グロブを報告します。
ベースライン
既存のコードベースにしきい値を導入するとき、通常は「何も発火しなくなるまで制限を引き上げる」か「ゲートを有効にする前にすべての違反を修正する」かの二者択一に直面します。ベースラインファイルはラチェットダウン式の代替手段です。今日の違反を記録し、退行と新規違反のみを失敗させ、チームが負債を返済するにつれてファイルを縮小していきます。
ベースラインは、抑制マーカーの抑制マーカーを補完するものであり、代替ではありません。抑制は「この関数は意図的に永久に除外する」を表現し、ソースに置かれます。ベースラインは「これは返済中の技術的負債である」を表現し、コミットされた TOML ファイルに置かれます。bca check はまず抑制を尊重し、残ったものにベースラインフィルターを適用します。
ベースラインの書き出し
bca check --paths src/ \
--write-baseline .bca-baseline.toml
これはツリーをウォークし、そのままではチェックを失敗させるすべてのしきい値違反を捕捉して、ソート済み TOML としてファイルに書き出します。この実行は違反数に関係なく 0 で終了します — 目的は違反を捕捉することだからです。
# 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 = 6
[provenance]
tier = "hard"
[[entry]]
path = "src/parser.rs"
qualified = "Parser::parse_expression"
metric = "cyclomatic"
value = 22.0
The qualified field is the function's qualified symbol (the ::-joined chain of enclosing named containers plus the function name). An entry carries a start_line only when its (path, qualified, metric) identity is shared with another entry, the one case matching consults a line number; recording it elsewhere would only rewrite the file every time an edit above the function shifted it. With --baseline-fuzzy-match, each entry also carries a body_hash for rename-tolerant matching.
ソース内の抑制マーカーで既にカバーされている関数は除外されます。すべての違反を記録するには、--write-baseline と一緒に --no-suppress を渡します(CI 監査フロー)。
--write-baseline は --baseline、--report-format、--output、--since、--changed-only と組み合わせられません — ベースラインファイルが出力「そのもの」だからです。
ベースラインの読み込み
bca check --paths src/ \
--baseline .bca-baseline.toml
違反は、次の両方の条件が成り立つときに抑制されます。
- エントリが
(path, qualified_symbol, metric)で一致する — 行番号には依存しません — か、それで一致しない場合は--baseline-fuzzy-match指定時にボディハッシュで一致する。(完全な解決順序はベースラインレシピを参照してください。) - 現在の
valueが記録された値以下である。
ベースライン値より悪化した関数は引き続き失敗します。ベースラインに載っていない新規違反も引き続き失敗します。改善は黙って通過します(エントリは次の --write-baseline による更新まで、古い高めの値のまま残ります)。
存在しない、空である、version が欠落もしくは未対応である、またはパースに失敗するベースラインファイルはツールエラー(終了コード 1)であり、黙ってゼロマッチ扱いにはなりません。
パスキーはベースラインファイル自身のディレクトリ(アンカー)を基準に正規化されるため、--paths .、--paths src/、--paths "$PWD" はバイト単位で同一のベースラインを生成し、どの --paths 形式でファイルを生成したかに関係なく --baseline 実行はマッチします — --write-baseline を再実行せずに自由に切り替えられます。
制限事項
- あいまいなシンボル / 無名関数。 エントリは修飾シンボルをキーとするため、「名前付き」関数の上にコードを挿入してもキーは変わらなくなりました。例外は、同じ修飾シンボルを共有する関数が
--baseline-line-toleranceを超えて離れた場合と、無名のクロージャ / ラムダ(合成シンボルに行番号が埋め込まれます)です。どちらも移動すると「新規」として再キーされます。--write-baselineで更新してください。 - OS 間の可搬性。 パスはスラッシュ区切りで保存されるため、ある OS で書かれたベースラインは別の OS 上の同じツリーにマッチします。有効な UTF-8 でないパスは損失のある表示形式(U+FFFD 置換)にフォールバックし、正確にラウンドトリップしない場合があります。
エンドツーエンドの導入フローと CI 統合パターンについては、ベースラインレシピを参照してください。
失敗させずに報告する
--no-fail reports offenders as usual 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 check --paths src/ --no-fail
対処しやすい失敗出力
bca check が失敗したとき、5 つのフラグが失敗ストリームを整形し、CI ログを流し読みする開発者が、何が引っかかったのか、PR のどこで引っかかったのか、次に何をすべきかを把握できるようにします。各フラグは独立しており、存在する場合はいずれも GitHub Actions の環境変数から自動検出されるため、一般的な CI のケースでは明示的な設定は不要です。
| フラグ | 効果 | 自動検出する環境変数 |
|---|---|---|
--since <ref> | ファイル別フッターを "Files in this range" と "Other offenders" に分割 | BCA_DIFF_BASE、GITHUB_BASE_REF、GITHUB_EVENT_BEFORE |
--changed-only | diff スコープ外の違反を完全に除外 | 解決可能なベースが必要(--since または上記のいずれか) |
--github-annotations <auto\|always\|never> | インラインのファイル注釈のために ::error file=…::msg ワークフローコマンドを出力(フラグ単独指定 = always) | auto は GITHUB_ACTIONS == "true" を検出 |
--summary-file <path\|auto\|never> | Markdown ダイジェスト(ファイル別集計 + 内訳 + 違反トップ 10)を追記。never で抑止 | auto は GITHUB_STEP_SUMMARY を検出 |
--no-remediation | 末尾の --- next steps --- ブロックを抑止 | このフラグを渡さない限り、失敗時にブロックを出力 |
The per-violation rows and the per-file rollup footer remain unchanged in content when none of the above are active, so CI tooling that grep-anchors on the legacy text keeps working — but see Which stream for where each now lands.
実例については CI 統合レシピを参照してください — 5 つすべてを 1 つのステップにまとめる「全部まとめて」の GHA スニペットも含まれます。また、修復ブロックがリンクする --write-baseline 更新フローについてはベースラインレシピを参照してください。
diff ベース自動検出の優先順位
--since が省略された場合、bca は次の順序で環境変数を参照します。
BCA_DIFF_BASE— ローカルシェルや GHA 以外の CI ランナー向けの明示的なオーバーライド手段。GITHUB_BASE_REF—pull_requestイベントで GHA が設定します。origin/<value>に展開されます。対応するgit fetchはランナーの責任です(actions/checkoutのfetch-depth: 0)。GITHUB_EVENT_BEFORE—pushイベントで GHA が、プッシュ前の HEAD の SHA に設定します。すべてゼロのセンチネル(強制プッシュ、新規ブランチ)はシグナルなしとして扱われます。
ベースの解決に失敗しても致命的ではありません。ただし --changed-only が渡されている場合は別で、その場合ゲートは失敗します — 誤設定されたベースの下ですべての違反を黙って抑制することは、この機能が防ごうとしている最悪の失敗モードだからです。--write-baseline も --since / --changed-only と競合します(部分的なベースラインは、次のフルツリー実行で diff スコープ外のすべての違反を黙って覆い隠してしまいます)。
CI の例(GitHub Actions)
- name: Check code complexity thresholds
run: |
bca check
# しきい値とパスは、リポジトリルートで自動検出される `bca.toml`
# マニフェストから取得されます。デフォルトの挙動 — 非ゼロ終了で
# ステップが失敗する — はまさにここで求めるものです。追加の配線は不要です。
違反数を減らしている間、ジョブをグリーンに保ちつつ違反をビルド注釈として表示したい場合は、--no-fail に置き換えます。
- name: Surface complexity hot spots (non-blocking)
run: |
bca check --paths src/ --no-fail
違反レコードのエクスポート
bca check は、ウォーク内のすべての違反を網羅する単一の CI/IDE 向けドキュメントも出力します。--report-format <fmt> で形式を選び、--output <file> でディスクに書き出します(省略時は stdout)。--format、-O、--output-format の綴りは非推奨のエイリアスとして受け付けられますが、将来のリリースで削除されます。終了コードの契約はこれらのフラグの影響を受けません。クリーンなら 0、違反があれば 2(--no-fail 時を除く)、ツールエラーなら 1 です。
--report-format なしで --output が指定された場合、フォーマットは出力ファイルの拡張子から推定されます。.sarif は sarif を、.xml は checkstyle を選択します。一意なフォーマットに対応しない拡張子(特に sarif と code-climate の両方が生成する .json)や拡張子なしの場合は、--report-format を挙げる使用方法エラー(終了コード 1)になります — 明示的な --output が黙って無視されることは決してありません。明示的な --report-format は常に拡張子より優先されます。
| フォーマット | 対象 |
|---|---|
checkstyle | Jenkins、SonarQube、GitLab、"warnings plugin" 系 CI |
sarif | GitHub Code Scanning、モダンな IDE / セキュリティツール |
code-climate | GitLab MR の Code Quality ウィジェット |
clang-warning | エディタの quickfix パーサー、GitHub Actions の problem matcher |
msvc-warning | Visual Studio、VS Code、Windows CI ランナー |
違反が存在しない場合、ライターは整形式だが空のドキュメントを出力します — SARIF では空の runs[].results 配列、Code Climate では空の JSON 配列([])、Checkstyle では <checkstyle> ルート直下に <file> 子要素なし、2 つの警告行フォーマットでは 0 バイト — これにより CI コンシューマーは違反のない実行結果もそのまま取り込めます。
Checkstyle(CI 統合)
bca check --paths src/ \
--threshold cyclomatic=15 \
--report-format checkstyle \
--output report.checkstyle.xml
Checkstyle ライターは、ソースパスごとに 1 つの <file> 要素を含む単一の <checkstyle version="4.3"> ドキュメントを出力し、各 <file> はメトリクスしきい値違反ごとに 1 つの <error> を保持します。スキーマは Checkstyle 4.3 の XSD で、Jenkins および SonarQube の「Warnings Next Generation」/「Generic Issue」インポーターが直接取り込めます。
SARIF(GitHub Code Scanning)
bca check --paths src/ \
--threshold cyclomatic=15 \
--report-format sarif \
--output report.sarif.json
SARIF ライターは、1 つの runs[] 要素を持つ単一の SARIF 2.1.0 JSON ドキュメントを出力します。各メトリクスしきい値違反は runs[0].results[] 配下の result になり、実行結果に現れるメトリクス名は重複排除されて、短い説明とともに runs[0].tool.driver.rules[] に格納されます。
ワークフローから GitHub Code Scanning に SARIF ファイルをアップロードするには:
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 check --paths . \
--report-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 はジョブを成功(グリーン)のまま保つため、違反が存在しても SARIF アップロードステップは実行されます。メトリクスの悪化でワークフローを失敗させたくなったら外してください。
GitLab Code Quality(Code Climate JSON)
bca check --paths src/ \
--threshold cyclomatic=15 \
--report-format code-climate \
--output gl-code-quality-report.json
Code Climate ライターは、上流の Code Climate エンジン仕様に対する GitLab の厳密なサブセットに適合する issue オブジェクトの単一 JSON 配列を出力します — メトリクスしきい値違反ごとに 1 エントリ、バイトオーダーマークなし、末尾の改行 1 つ(入力が空の場合は []\n になります)。各 issue には、名前空間付きの check_name(big-code-analysis/<metric>)、path \0 function \0 metric に対する安定した SHA-256 の fingerprint(行番号にも値にも依存しないため、体裁だけの編集でも MR ウィジェットでの重複排除が維持されます)、そして値としきい値の比率を GitLab の 5 段階 enum に対応付けた severity が含まれます: ≤ 1.5× → minor、≤ 2× → major、≤ 4× → critical、> 4× → blocker(値が低いほど悪い mi.* ファミリーでは反転)。enum の全体は info/minor/major/critical/blocker ですが、bca が info を出力することはありません — しきい値違反は常に minor 以上になります。
アーティファクトを GitLab の MR Code Quality ウィジェットに接続するには:
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
パイプライン全体(Code Climate + Checkstyle + Markdown レポートの組み合わせ)とローカルでの jq によるスモークチェックについては、GitLab Code Quality ウィジェットのレシピを参照してください。
--no-fail はジョブを成功(グリーン)のまま保つため、違反が存在しても Code Quality レポートはアップロードされます。メトリクスの悪化でパイプラインを失敗させたくなったら外してください。
Clang/GCC 警告行(エディタの quickfix と CI アノテーター)
bca check --paths src/ \
--threshold cyclomatic=15 \
--report-format clang-warning \
--output report.txt
Clang フォーマットは、慣例的なコンパイラ警告の形式で違反を 1 行に 1 件ずつ出力します:
path/to/file.rs:42:5: warning: cyclomatic 17 exceeds limit 15 [big-code-analysis-cyclomatic]
これは clang -fdiagnostics-format= が生成するフォーマットであり、あらゆるエディタの quickfix パーサー(VS Code、IntelliJ、Vim)と大半の CI アノテーターが設定なしで解釈できる形式です。
GitHub Actions は、組み込みの GCC problem matcher(または任意のコミュニティ製 compiler-problem-matchers アクション)を介して、これらの行を PR の差分上のインラインアノテーションとして表示します:
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 check --paths . \
--report-format clang-warning \
--no-fail
ランナーに GCC matcher が同梱されていない場合は、これらの行をストリーム処理して ::warning file=...,line=...:: ワークフローコマンドとして再出力する方法にフォールバックしてください。
MSVC 警告行(Visual Studio と Windows CI)
bca check --paths src/ \
--threshold cyclomatic=15 \
--report-format msvc-warning \
--output report.txt
MSVC フォーマットは、Visual Studio の cl.exe 診断形式で違反を 1 行に 1 件ずつ出力します:
path\to\file.rs(42,5): warning : cyclomatic 17 exceeds limit 15
warning/error の後のコロンの前にスペースがある点に注意してください — これが MSVC の慣例です。Windows ではパスは \ 区切りに正規化されます(cl.exe の出力に一致)。それ以外のプラットフォームではパスはそのまま出力されます。Visual Studio、C/C++ 拡張機能を入れた VS Code、および Windows CI ランナー(Azure Pipelines、windows-latest 上の GitHub Actions)は、追加設定なしでこれらをインラインで解釈します。