チェック

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)を区別できるようになっています。

入力をすべて読み取れなかったゲートには報告すべき判定が存在しないため、3 つの入力の問題は 0 ではなく 1 で終了します。--paths / --include / --exclude に何も一致しなかった場合、入力ファイルの読み取りに失敗した場合、そして走査がディレクトリを一覧できなかった場合です。後者 2 つはワークスペース全体に適用される読み取れない入力の規則であり、check が特別なわけではありません — この規則が最も効いてくる場所というだけです。読み取れなかったツリーに対してクリーンと報告するゲートは、パスしたゲートと区別が付かないからです。3 つのチェックはいずれもゲートの評価前に走り、--no-fail では抑制されません。--no-fail が抑制するのはしきい値の失敗であって壊れた入力ではないためです。したがっていずれの場合も --write-baseline が部分的な実行を記録することはありません。

段階的終了コード(--exit-codes=tiered

--exit-codes=tiered(または bca.toml[check] exit_codes = "tiered")は、単一の違反コード 2 を重大度別に分割し、CI が [new] / [regr +N%] の行タグをパースせずに分岐できるようにします。

コード意味(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 は出発点となるテーブルをスキャフォールドします。それらの数値の出どころ、ゲート対象の言語に合わせて上書きすべき項目、エージェントフィードバック・レガシートリアージ・セーフティクリティカルな作業向けに別のセットを選ぶ方法については、しきい値の選択を参照してください。

使用できるメトリクス名

トップレベルのスカラーメトリクスは list-metrics の名前をそのまま使います:cognitivecyclomaticnargsnexitsnomtokensabcwmcnpmnpa。複数のサブフィールドを持つメトリクススイートはドット区切り形式を使います。

メトリクス使用できるしきい値名
循環的複雑度cyclomaticcyclomatic.modified
Halsteadhalstead.volumehalstead.difficultyhalstead.efforthalstead.timehalstead.bugs
コード行数loc.slocloc.plocloc.llocloc.clocloc.blank
保守容易性指数mi.originalmi.seimi.visual_studio

不明なしきい値名はツールエラー(終了コード 1)となり、黙って無視されることはありません。

しきい値のスコープ

しきい値は、そのメトリクスが実際に測定するスペース種別に対してのみチェックされるため、メトリクスのファイル全体や impl 全体の集計値が関数単位の制限と取り違えられることはありません。各メトリクスのスコープは固定で、設定項目はありません。

スコープゲート対象のスペースメトリクス
ファイルファイル全体のルートのみloc.slocloc.plocloc.llocloc.clocloc.blank
関数個々の関数、メソッド、クロージャcognitivecyclomaticcyclomatic.modifiedhalstead.*mi.*abcnargsnexitstokens
コンテナクラス、構造体、トレイト、impl、名前空間、インターフェイスnomwmcnpmnpa

関数スコープのメトリクスにはサブツリー合計(nargsnexitstokenshalstead.*)が含まれます。これらは引き続き関数自身の入れ子クロージャをその値に繰り入れますが、ファイル全体や impl 全体にわたって合算されることはなくなりました。コンテナスコープのメトリクスは型のメソッド集合(クラスあたりのメソッド数、重み付きメソッド、公開メンバー)を表すため、末端の関数ごとではなくコンテナをゲートします。これにより、個々の関数は問題ないクリーンなファイルが、ファイル全体の合計だけで加算型の制限に引っかかることはなくなりました — かつて bca: suppress-file マーカーが覆い隠していた偽陽性です。

loc サブメトリクスの素の bca diff --metric 表記は、ドット区切り形式のエイリアスとして受け付けられます(slocloc.sloc と等価で、ploc/lloc/cloc/blank も同様)。そのため diff 実行からコピーした名前は正しくゲートされます。単一のしきい値スカラーを持たない素のファミリー名(halsteadmi)はあいまいなため拒否され、具体的なサブメトリクスを列挙する「もしかして」ヒントが表示されます — いずれか 1 つ(例:halstead.volume)を選んでください。

2 つの綴りは 1 つのメトリクスを指すため、制限がマージされるあらゆる場所で互いを上書きします。[thresholds.lang.c] ploc = 100 はグローバルの "loc.ploc" を置き換え、--threshold ploc=100 はどちらも置き換えます。単一のテーブル内で両方を書くと、どちらかが黙って勝つのではなくエラーになります — 好きな方の綴りで、メトリクスを一度だけ設定してください。

言語別の制限([thresholds.lang.<slug>]

メトリクスの分布は、プロジェクトによる違いよりも言語による違いの方が大きいものです — 実測した関数ごとの cognitive の 97.5 パーセンタイル値は、C# の 4 から C の 50 まで幅があります。[thresholds.lang.<slug>] テーブルは、プロジェクト全体のテーブルの上に重ねる形で、1 つの言語に独自の制限を与えます。

[thresholds]
cognitive = 15
cyclomatic = 15
"loc.ploc" = 600

[thresholds.lang.c]
cognitive = 30
"loc.ploc" = 1200

[thresholds.lang.elixir]
nom = 150
wmc = 300

これで C は、プロジェクトの cyclomatic = 15 を保ったまま cognitive = 30loc.ploc = 1200 でゲートされ、他のすべての言語は 3 つすべてを保ちます。上書きは置き換えテーブルではなくメトリクス単位です — 言語は、言い直さなかったすべての制限を継承します。どの数値を変えるべきか、そして上書きがチューニングではなく是正である 2 つのケースについては、しきい値の選択にあります。

キーは正規の言語スラッグで、--language が受け付けるのと同じ語彙です。rustpythoncppcsharpobjctsxmozcppmozjs など — bca check --language nonsense を実行すると完全なリストが表示されます。ここでは 2 つの規則が逆方向を向いています。

  • マニフェスト内の未知のスラッグは、did-you-mean ヒント付きのツールエラー(終了コード 1)です。タイポした [thresholds.lang.rust-lang] が、作者は緩めたつもりでいるのにゲートを黙ってプロジェクトの制限のままにしておく、という事態があってはなりません。
  • 認識されないファイル言語は、独自の上書きを持たない言語と同様に、グローバルテーブルにフォールバックします。黙ってゲートが外されるものはありません。(拡張子がどの文法にも対応しないファイルは、ゲートが見る前に走査によってスキップされます — 明示的に名指しした場合は警告付きです。)

コマンドラインの --threshold はグローバルのままで、これまでどおり最後に絶対的に適用されます。プロジェクトのテーブル「と」すべての言語別テーブルの両方を上書きするため、あなたが入力した制限が実際に動く制限になります。

--print-effective-config は、上書きされた言語ごとに完全に解決されたテーブルを 1 つずつ出力します — 差分ではなく、継承された制限も含めてです。そのため、実際に発動する数値は、あなたが読んだその数値になります。

[thresholds]
cognitive = 15.0
cyclomatic = 15.0

[thresholds.lang.c]
cognitive = 30.0
cyclomatic = 15.0

二層しきい値(--tier

--tier <hard|soft|soft=RATIO> は、ゲートが比較に使うしきい値の層を選択します。hard(デフォルト)は [thresholds] テーブルをそのまま使います。soft はハードゲートの「前に」発火する早期警告の層で、すべての制限を RATIO 倍に引き締めます。--tier 単独指定は soft を意味し、soft だけならデフォルト比率 0.95 を使い、soft=0.90 は比率を 0.90 に固定し、soft=1.0 は一括スケールを無効にします。

RATIO がスケールするのは数値ではなく「帯」です。ほとんどのメトリクスでは制限は上限なので、引き締めるとは乗算することです。cognitive = 15soft=0.9 なら 13.5 で警告します。低いほど悪い mi.* ファミリーでは制限は下限であり — それを下回る値が違反です — 同じ 0.9 は「除算」になります。"mi.original" = 2020 / 0.9 = 22.2223 で警告し、帯が正確な商を下回って解決されることのないよう切り上げられます。下限を乗算すると警告は 18、つまりハードゲートの「下」に移動してしまい、何も先にそこへ到達できなくなります。

[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

ソフト層は次の固定順序で解決されます。

  1. [thresholds]bca.toml マニフェスト、--config とマージ済み)から始めます。
  2. [thresholds.soft] テーブルが存在する場合は、その上書きを上にマージします。そこに無いメトリクスはハード制限を継承します。一括の RATIO は適用されません(明示的なメトリクスごとの制限が優先されます)。
  3. それ以外の場合は、すべての制限をソフト RATIO で引き締めます(素の soft のデフォルトは 0.95soft=1.0 はスケールを無効化します)。
  4. 繰り返し指定された --threshold name=value フラグは最後に、絶対値として適用されます。

ステップ 1 から 3 は、その言語自身の解決済みハード制限に対して、言語ごとに 1 回実行されます。[thresholds.lang.<slug>].soft テーブルは存在せず、必要でもありません。[thresholds] cognitive = 15[thresholds.lang.c] cognitive = 30--tier=soft=0.9 の場合、C のソフト帯は 27 — 「C 自身の」制限の 10 分の 9 — であり、終了コード 5 へのエスカレーションのために C の違反者が測られる上限は 15 ではなく 30 です。どちらかをプロジェクトの 15 から導出してしまうと、15 から 30 の間にあるすべての C 関数が、プロジェクトがその言語のために構成した制限の内側にいながら「ハード制限も超過」と報告することになります。

2 つの層を逆転させ「うる」唯一の組み合わせは、覆い隠す対象のハード制限より緩い [thresholds.soft] の絶対値です — [thresholds.lang.csharp] cognitive = 4 と並ぶ [thresholds.soft] cognitive = 12 や、ハードの下限を「下回る」mi.* のソフト下限がそれに当たります。これは問題のテーブルを名指しするツールエラー(終了コード 1)です。理由は、1 を超える "<ratio>x" 係数がパース時に拒否されるのと同じで、ハードゲートの「後に」発動するソフト層が意図であることは決してないからです。

ソフト RATIO(および "<ratio>x" 文字列のスケール係数)は (0, 1] の範囲でなければなりません。[check] headroom マニフェストキーは、素の --tier=soft に比率を供給します。非推奨の --headroom <R> フラグは --tier=soft=<R> の 1 サイクル限りのエイリアスで(警告を出し、次のメジャーで削除されます)、現在はハード実行をソフト層に昇格させます。両方の層は同じ --baseline を通じてラチェットし、--print-effective-config はマージ後の制限と併せて解決後の tier を報告します。移行のヒントと背景についてはローカルしきい値ゲートレシピを参照してください。

候補制限のプレビュー(--explain-threshold

--explain-threshold <metric>=<limit> は、ゲートする代わりに、候補の制限が両方の層でどれだけのコストになるかを報告します。繰り返し指定でき、メトリクスごとに 1 つの候補を取り、何も書き込みません。

$ 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.

重視すべき数字は new 列です。これは、その制限を採用するとベースラインエントリが何件増えるかを表します。この例ではハード層はコストゼロに見えますが、ソフト層は 74 エントリのコストが掛かり、そのいずれも、どれだけコードを整理しても退役させられません — これこそが、このフラグが存在する理由そのものです。

--threshold nargs=6 はこれを教えてくれません。その制限は最後に、絶対的に、決してスケールされずに適用されるため、この方法で試した候補にはソフト層がまったくありません。同じメトリクスに両方のフラグを渡すと、黙って解決されるのではなく拒否されます。

ソフト制限は、実際の実行が導出するのとまったく同じ方法で候補から導出されます。そのメトリクスの [thresholds.soft] エントリが優先され、次に指定されていれば --tier=soft=RATIO の比率、その次が 0.95 です。そのメトリクスを上書きする [thresholds.lang.<slug>] テーブルは自身の制限を保ち — 候補の「グローバル」制限はそこには届きません — レポートは専用の行でそのことを伝えます。

それ以外はすべて、予測対象の実行と一致します。exclude_tests[check] exclude、ソース内の抑制マーカー、--changed-only、ベースラインはいずれも通常どおり適用されます。唯一の違いは、ベースラインでカバーされた違反者が、落とされるのではなくカウントされることです。これによって already baselined / new の分割が可能になっています。

プレビューはゲートを置き換えるため、失敗することはありません。ツールエラー(終了コード 1)が実行を止めない限り終了コードは 0 で、その旨の 1 行のリマインダーが stderr に出力されます。--write-baseline--print-effective-config--report-format--output とは競合します。これらはいずれも、別のもう 1 つの成果物を生成することになるからです。

このフラグが可視化するために存在する規則については、しきい値の選択を参照してください。

違反の出力

違反した (function, metric) の各ペアは、次の安定したフォーマットで 1 行ずつ stdout に出力されます。

<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)

行はパス、開始行、メトリクス名の順でソートされるため、同じツリーに対する実行間で出力は決定的です。

どちらのストリームか

違反行はこのコマンドの成果物なので stdout に出力されます。bca check | wc -l| head| rg -c2>/dev/null のいずれでも違反行に届きます。

実行が「自分自身について」語る内容はすべて stderr に出力されます:

  • ファイルごとの --- summary --- フッター、
  • --- next steps --- の修復ブロック、
  • GitHub Actions の ::error アノテーション、
  • bca: skipped N violations via [check.exclude]bca: filtered N violations via baseline の件数表示、
  • すべての warning: および error: 診断メッセージ。

これを逆転させる組み合わせが 1 つあります。--output なしの --report-format <dialect> は、集約された SARIF / Checkstyle / Code Climate ドキュメントを stdout に出力するため、人間向けの行はドキュメントを壊さないよう stderr 側に退避します:

bca check --report-format sarif | jq '.runs[0].results | length'   # ドキュメントは stdout に
bca check --report-format sarif --output report.sarif | wc -l      # 違反行は stdout に戻る

--summary-file のダイジェストはストリームではなくファイルであり、どちらのストリームにも現れません。

以前のリリースでは違反行もその他すべてと一緒に stderr に送られていたため、| wc -l2>/dev/null は空の違反者リストを報告し、クリーンなツリーと見分けが付きませんでした。2>&1 を通して違反行を読んでいるパイプラインは変更不要です。2>file で捕捉していたものは、今後は >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/**",
    "big-code-analysis-ast/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 フィルターだけです。)

相対グロブは何に対して相対か

2 つの供給元は異なるルートを基準点とします。ここは誰もが推測を誤る部分です:

ソース相対グロブが解決される基準
bca.toml [check] exclude / exclude_frombca.toml を置いているディレクトリ
--check-exclude / --check-exclude-frombca を実行したディレクトリ

したがって、リポジトリルートのマニフェストにある exclude = ["./vendor/**"] は、bca check をどのディレクトリから起動しても — たとえ vendor/ の中から起動しても — <repo>/vendor/ を意味します。同じグロブを --check-exclude './vendor/**' として渡すと、それは「シェルの現在のディレクトリ配下の」vendor/ を意味します。そこがそのグロブを入力した場所だからです。

これが最も重要になるのはファイル単位の呼び出し元 — エディタ統合、pre-commit フック、エージェントへのメトリクス供給のエージェントフック — です。これらは、たまたま居合わせたディレクトリから bca check <one file> を実行します。その除外設定はマニフェストに置くべきです。マニフェストなら基準点が呼び出し元のものではなくプロジェクトのものになるからです。

ウォーカーの exclude / --exclude の組も同じように由来で分割されますが、それは明示的なパスが関わる場合に限られます。名前で指定されたファイルが除外を上書きしたという警告では、マニフェストのグロブはマニフェストルートに対して解決されます。一方、「ディレクトリ」のウォークでは、マニフェストのグロブは依然としてウォーク自身のルートに対して照合されます。両者が同じディレクトリになるのは、ウォークがマニフェストの場所から始まる場合 — 通常の paths = ["."] のケース — だけです。そのため、サブディレクトリを指定する(bca metrics -p sub)とマニフェストの exclude が一致しなくなることがあります。このギャップは #1189 で追跡されています。

他の抑制メカニズムとの優先順位

bca check は、最も特異的なものから順に、次の順序で除外を解決します。

  1. ソース内マーカーbca: suppress / bca: suppress-file)— 常に優先されます。ウォーク中に適用されるため、その関数はそもそも違反になりません。
  2. [check.exclude] グロブ — ファイルの「カテゴリ」(テスト、生成コード)を除外します。
  3. .bca-baseline.toml — 返済中の既知の違反。

--print-effective-config は、他のゲート入力と併せて解決後の check_exclude グロブを報告します。

グロブは由来に応じて異なるルートに対して解決されます。コマンドラインで渡したものは作業ディレクトリに対して、bca.toml 由来のものはそのファイルのディレクトリに対して解決されます。解決後の check_exclude 配列は両者の和集合であり、この区別を表現できないため、manifest_check_exclude キーがマニフェスト由来の部分集合を示します。その基準点は、報告される manifest ファイルのディレクトリです。ウォーカー自身の exclude 面も同じ組を報告します。マニフェストがグロブを 1 つも寄与していない場合、両方のキーは省略されます。

manifest_exclude_frommanifest_check_exclude_from は、exclude_from の「ファイル」について同じ考え方に従いますが、知っておくべき違いが 1 つあります。コマンドラインの --exclude-from はマニフェストのファイルに追加するのではなく置き換えるため、マニフェストキーが現れるのは、マニフェストのファイルが実際に有効になっている場合だけです。インラインのグロブリストは和集合になりますが、ファイルはそうなりません。

信頼できない入力に対するモード(--strict

ゲートが検査するファイル集合を狭める既定の動作が 2 つあり、どちらもテスト対象のツリーから入力を読み取ります。生成コード検出器はヘッダにマーカー語句を含むファイルをスキップし、ウォーカーは走査対象ツリー内の .gitignore ファイルを解釈します。どちらも信頼できるチェックアウトの分析には適切な既定値です。しかしプルリクエストのゲートでは、レビュー対象の変更が引けるレバーになります。コメント 1 行の追加、または .gitignore への 1 エントリで、ファイルがゲートから外れます。CI レシピに両方の再現手順があります。

--strict は両者を 1 つのフラグで無効にします。--no-skip-generated --no-ignore を渡すのと等価です。

bca check --strict --paths src/

プロジェクトはワークフローごとではなく、bca.toml で一度だけオプトインします。

[check]
strict = true

このキーはトップレベルの exclude_tests と同様に presence-only です。プロファイルを有効にすることはできますが、無効に戻すことはできません(--no-strict はありません)。そのため CI ワークフローがコミット済みのポリシーを黙って弱めることはできません。

プロファイルが無効でも、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>、または note: skipped (ignored directory): <path> の行で一覧表示します。--strict の下では何もスキップされないため、要約するものもありません。

カウントはウォークの枝刈り地点で測定されます。ウォーカーが入った各ディレクトリについて、直下の子のうち ignore ルールが除外したものをゲートが確認します。どちらの側でも、いずれかのパーサが扱うファイルだけがカウントされます。無視されたログファイルや、生成された Cargo.lock(ヘッダが @generated にマッチします)は、検査されるのではなく読み込まれて破棄されるだけであり、これらを数えるとサマリーがすべての実行で表示されてしまいます。コマンドラインで明示的に指定されたファイルは ignore ルールを迂回して分析されるため、カウントされません。無視された「ディレクトリ」は --report-skipped の下でのみ N ignored directories not walked のエントリとして報告され、中には入りません。無視されたビルドツリーはほぼすべてのチェックアウトに存在し、target/ 規模のツリーを列挙すればまるごともう 1 回の走査コストで意味のない数百万ファイルのカウントが生まれるためです。なお ignore 由来の集計は、ツリー内の .gitignore/.ignore だけでなく、グローバル gitignore や祖先ディレクトリの ignore ファイルなど、ウォーカーが解釈するすべてのソースを反映するため、ローカルと CI でカウントが異なることがあります。

コンテンツ判定を無効にした場合でも、本当に生成されたツリーには除外設定が必要です。提出者が管理するマーカーではなく、レビュアーが管理する仕組み、すなわちコミット済みのウォーカー用 deny-set(--exclude-from .bcaignore)か [check.exclude] のグロブを使ってください。

ベースライン

既存のコードベースにしきい値を導入するとき、通常は「何も発火しなくなるまで制限を引き上げる」か「ゲートを有効にする前にすべての違反を修正する」かの二者択一に直面します。ベースラインファイルはラチェットダウン式の代替手段です。今日の違反を記録し、退行と新規違反のみを失敗させ、チームが負債を返済するにつれてファイルを縮小していきます。

ベースラインは、抑制マーカーの抑制マーカーを補完するものであり、代替ではありません。抑制は「この関数は意図的に永久に除外する」を表現し、ソースに置かれます。ベースラインは「これは返済中の技術的負債である」を表現し、コミットされた TOML ファイルに置かれます。bca check はまず抑制を尊重し、残ったものにベースラインフィルターを適用します。

ベースラインの書き出し

bca check --paths src/ \
    --write-baseline .bca-baseline.toml

これはツリーをウォークし、そのままではチェックを失敗させるすべてのしきい値違反を捕捉して、ソート済み TOML としてファイルに書き出します。この実行は違反数に関係なく 0 で終了します — 目的は違反を捕捉することだからです。

# bca ベースラインファイル。`bca check --write-baseline` により生成。
# 記載された違反はしきい値チェックから除外されます。記録値より
# 悪化した関数は引き続き失敗します。エントリが古くなったら
# `--write-baseline` で更新してください。
version = 6

[provenance]
tier = "hard"

[[entry]]
path = "src/parser.rs"
qualified = "Parser::parse_expression"
metric = "cyclomatic"
value = 22.0

qualified フィールドは関数の修飾シンボル(囲んでいる名前付きコンテナを :: で連結した連鎖に関数名を加えたもの)です。エントリが start_line を持つのは、その (path, qualified, metric) の同一性が別のエントリと重複する場合 — マッチングが行番号を参照する唯一のケース — に限られます。それ以外で記録しても、関数より上の編集で行がずれるたびにファイルが書き換わるだけです。--baseline-fuzzy-match を使うと、各エントリはリネーム耐性のあるマッチングのための body_hash も持ちます。

ソース内の抑制マーカーで既にカバーされている関数は除外されます。すべての違反を記録するには、--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 は違反を通常どおり報告しますが、0 で終了します。CI を赤くせずにベースラインを導入している間に便利です。他の CI ツールではこの挙動を --report-only--soft-fail と呼びますが、ここでは --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_BASEGITHUB_BASE_REFGITHUB_EVENT_BEFORE
--changed-onlydiff スコープ外の違反を完全に除外解決可能なベースが必要(--since または上記のいずれか)
--github-annotations <auto\|always\|never>インラインのファイル注釈のために ::error file=…::msg ワークフローコマンドを出力(フラグ単独指定 = alwaysautoGITHUB_ACTIONS == "true" を検出
--summary-file <path\|auto\|never>Markdown ダイジェスト(ファイル別集計 + 内訳 + 違反トップ 10)を追記。never で抑止autoGITHUB_STEP_SUMMARY を検出
--no-remediation末尾の --- next steps --- ブロックを抑止このフラグを渡さない限り、失敗時にブロックを出力

上記のいずれも有効でない場合、違反ごとの行とファイル別集計フッターは「内容」としては変わらないため、従来のテキストに grep でアンカーしている CI ツールは引き続き動作します — ただし、それぞれが現在どのストリームに出力されるかはどのストリームかを参照してください。

実例については CI 統合レシピを参照してください — 5 つすべてを 1 つのステップにまとめる「全部まとめて」の GHA スニペットも含まれます。また、修復ブロックがリンクする --write-baseline 更新フローについてはベースラインレシピを参照してください。

diff ベース自動検出の優先順位

--since が省略された場合、bca は次の順序で環境変数を参照します。

  1. BCA_DIFF_BASE — ローカルシェルや GHA 以外の CI ランナー向けの明示的なオーバーライド手段。
  2. GITHUB_BASE_REFpull_request イベントで GHA が設定します。origin/<value> に展開されます。対応する git fetch はランナーの責任です(actions/checkoutfetch-depth: 0)。
  3. GITHUB_EVENT_BEFOREpush イベントで 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 が指定された場合、フォーマットは出力ファイルの拡張子から推定されます。.sarifsarif を、.xmlcheckstyle を選択します。一意なフォーマットに対応しない拡張子(特に sarifcode-climate の両方が生成する .json)や拡張子なしの場合は、--report-format を挙げる使用方法エラー(終了コード 1)になります — 明示的な --output が黙って無視されることは決してありません。明示的な --report-format は常に拡張子より優先されます。

フォーマット対象
checkstyleJenkins、SonarQube、GitLab、"warnings plugin" 系 CI
sarifGitHub Code Scanning、モダンな IDE / セキュリティツール
code-climateGitLab MR の Code Quality ウィジェット
clang-warningエディタの quickfix パーサー、GitHub Actions の problem matcher
msvc-warningVisual 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_namebig-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 ですが、bcainfo を出力することはありません — しきい値違反は常に 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)は、追加設定なしでこれらをインラインで解釈します。