REST API
bca-web は、REST API を通じてソースコードを分析できる Web サーバーです。このサービスは、HTTP 経由でコード分析を行いたいすべての人に役立ちます。
サーバーは任意のホストとポートで実行でき、次の主要な機能をサポートします:
- ソースコードからのコメント除去。
- 指定したコードの関数スパンの取得。
- 指定したコードの AST(抽象構文木)の取得。
- 提供されたソースコードのメトリクスの計算。
サーバーの実行
サーバーを実行するには、次のコマンドを使用します:
bca-web --host 127.0.0.1 --port 9090
--hostはサーバーを実行する IP アドレスを指定します(デフォルトは 127.0.0.1)。--portは使用するポートを指定します(デフォルトは 8080)。-jは並列ジョブ数を指定します(省略可能)。--corsはブラウザベースのツール向けに CORS を有効にします(デフォルトは無効)。
フラグの全一覧、環境変数、リソース制限、およびデーモンを公開する前に守るべき信頼境界については、bca-web の運用 を参照してください。
CORS
デフォルトでは bca-web は CORS ヘッダーを一切送出しません。別のオリジンから配信されたブラウザスクリプトは、この API のレスポンスを読み取れません。これにより、ローカルの bca-web(デフォルトの 127.0.0.1 バインド)が、運用者がたまたま閲覧している任意の Web サイトにリポジトリのパスやメトリクスを晒すことを防ぎます。
オプトインするには --cors を渡します。引数は オリジン の明示的なカンマ区切り許可リストです。許可リストに含まれるオリジンだけが Access-Control-Allow-Origin ヘッダーを受け取り、マッチしたオリジンがそのままエコーバックされます(それ以外のオリジンからのリクエストはヘッダーを受け取らず、ブラウザによってブロックされます):
bca-web --cors https://app.example,https://tools.example
すべてのオリジンに Access-Control-Allow-Origin: * で応答するには、リテラルの * を渡します:
bca-web --cors '*'
全開放の * は、サーバーのメトリクスとリポジトリのパスをあらゆるオリジンに公開するため、信頼できるネットワークでのみ使用してください。
CORS が有効な場合、プリフライトの OPTIONS リクエストには 204 No Content で応答し、Access-Control-Allow-Origin、Access-Control-Allow-Methods(そのリソース自身が受け付けるメソッド。Allow ヘッダーが広告するのと同じ集合)、および Access-Control-Allow-Headers(リクエストの Access-Control-Request-Headers をエコーし、素のプローブに対しては Content-Type, Accept)を付与します。この API には認証も Cookie もないため、Access-Control-Allow-Credentials は決して送信されません。
API バージョニング
すべてのエンドポイントは /v1 プレフィックスの下にマウントされます(例えば /v1/metrics)。ルートの全集合は /v1/ping、/v1/version、/v1/languages、/v1/ast、/v1/comment、/v1/function、/v1/metrics、/v1/vcs、/v1/vcs/trend、/v1/vcs/jit、およびルートインデックス /v1 です。プログラムから発見するには GET /v1 を使用します(ルートインデックス を参照)。
以前の 1.x リリースが非推奨エイリアスとして提供していたプレフィックスなしのパス(/metrics、/comment、/ast、/、…)は 2.0 で削除されました。現在それらをリクエストすると 404 が返ります。どこでも /v1 形式を使用してください。
エラーレスポンス
エラーは 200 のボディ内ではなく、HTTP ステータスコードで報告されます。すべてのエラー(JSON エンドポイント、raw/octet-stream エンドポイント、415/405/404 フォールバックのいずれでも)は統一された機械可読の JSON ボディを 1 つ返すため、クライアントは成功時のコンテンツタイプに関わらず単一のエラー形式をパースすれば済みます:
{
"error": "human-readable message",
"error_kind": "stable_machine_token",
"id": "echoed-request-id"
}
error は具体的な人間可読の原因で、error_kind は安定した snake_case の機械向けトークン(例:unknown_field、unsupported_language、bad_request、parse_timeout)です。これによりクライアントは説明文の文字列マッチングをせずに原因で分岐できます(issue #631)。トークンの語彙は閉じており、STABILITY.md によって管理されています。
id キーは常に存在します。リクエストにクライアント指定の相関 ID があった場合(JSON エンドポイント)はそれを保持し、それ以外の場合は空文字列になります(octet-stream / クエリのエンドポイントは ID を持たず、コンテンツタイプ / メソッド / not-found のフォールバック、および ID が読み取られる前にボディのパースに失敗したリクエストには、エコーすべきパース済み ID が存在しません)。
ステータスコード:
400 Bad Request— 不正なボディまたはクエリパラメータ。無効な JSON、必須フィールドの欠落、認識されないキー(厳格なdeny_unknown_fieldsパース。削除されたunitフラグを含みます — 後述の Compute Metrics を参照)、またはfull/file以外のscope値。422 Unprocessable Entity—file_nameの拡張子(およびコンテンツスニッフィング)が対応言語のいずれにもマップされない場合。ルートはマッチしボディはパースされており、送信されたエンティティだけが処理できません。レスポンスは安定した機械向けトークン"error": "unsupported_language"を含みます。対応言語の集合はGET /v1/languagesで照会してください。(2.0 より前はこれが404であり、未知の URL と区別できませんでした — issue #634 を参照。)404 Not Found— URL がどのエンドポイントにもマッチしない場合。415 Unsupported Media Type— 既知のPOSTエンドポイントが、application/jsonでもapplication/octet-streamでもないContent-Typeを受け取った場合(charsetパラメータは許可されます)。405 Method Not Allowed— 既知のエンドポイントが誤った HTTP メソッドで呼ばれた場合(分析エンドポイントはPOST専用、/ping、/version、/languagesはGET専用です)。406 Not Acceptable— リクエストのAcceptヘッダーが、サーバーが生成できないメディアタイプのみを指定していた場合。構造化分析エンドポイントはapplication/json、application/yaml、application/cborを提供します。それ以外の具体的なタイプ(例:application/xml)はnot_acceptableトークンを伴う406になります。コンテンツネゴシエーション を参照してください。413 Payload Too Large— リクエストボディがサーバーの制限を超えた場合。500 Internal Server Error— それ以外は有効なリクエストに対してメトリクス計算または AST 構築が失敗した場合、あるいは/vcsの履歴ウォークがサーバー側で失敗した場合。ネストが深すぎてシリアライズできないレスポンス(serialize_failed)もこれに含まれます — 下記のネストの上限を参照してください。503 Service Unavailable— パースプールが孤立した(タイムアウトした)タスクで飽和している場合。後で再試行してください。504 Gateway Timeout— パース(または履歴ウォーク)がサーバーに設定された期限を超過した場合。
ネストの上限
レスポンスはツリーであり、どのシリアライザもレベルごとに 1 つのネイティブスタックフレームを使わずにツリーを出力することはできません。深くネストしたレスポンスにスタックを溢れさせ、プロセス全体を — 実行中のすべてのリクエストもろとも — 落としてしまうのを避けるため、サーバーは固定の深さを超えるシリアライズを拒否し、serialize_failed トークンとともに 500 を返します。
| レスポンス | 上限 | 1 レベルとみなされるもの |
|---|---|---|
/metrics | 128 | ネストした関数スペース 1 つ(関数の中の関数の中の…) |
/ast | 512 | AST ノード 1 つ |
どちらの上限も、実在のソースが到達する水準をはるかに上回っています。本プロジェクトがテスト対象とする 14,450 ファイルのコーパス(TensorFlow、DeepSpeech、serde など)全体で、最も深い AST は 188 ノード、最も深い関数スペースのネストは 10 です。/metrics の上限は読み取り側よりも余裕があります — ネストしたスペースの JSON ドキュメントは約 61 レベルを超えると読み戻せません。serde_json 自身の 128 レベルの再帰上限が、スペース 1 つあたり 2 レベルを消費するためです。
コンテンツネゴシエーション
構造化分析エンドポイント(/v1/ast、/v1/comment(JSON バリアント)、/v1/function、/v1/metrics、/v1/vcs、/v1/vcs/trend、/v1/vcs/jit)は、CLI の -O json|yaml|cbor 出力を反映して、リクエストの Accept ヘッダーからレスポンスのシリアライズ形式を選択します。同じ値は、CLI 由来でもサーバー由来でも、バイト単位で同一にシリアライズされます。
Accept の値 | レスポンスの Content-Type |
|---|---|
なし、*/*、application/*、application/json | application/json |
application/yaml(または text/yaml、application/x-yaml) | application/yaml |
application/cbor | application/cbor |
| その他の任意の具体的なタイプ | 406 Not Acceptable |
ルール:
- JSON がデフォルトです。
Acceptヘッダーのないリクエスト、または*/*/application/*/application/jsonを含むリクエストには JSON が返ります。これは以前のリリースが常に返していたのと同じボディとContent-Typeであり、既存のクライアントに変更は不要です。 q重みは尊重されます。 サポートされるタイプの中でq重みが最も高いエントリが選ばれます(Accept: application/json;q=0.5, application/yaml;q=0.9は YAML を返します)。q=0はそのタイプを拒否します。同点の場合は先に列挙されたエントリが維持されます。- サポートされないタイプは
406になり、 暗黙の JSON フォールバックは行われません。ボディはerror_kind: "not_acceptable"を持つ統一の{error, error_kind, id}エンベロープで、メッセージにはサポートされるメディアタイプが列挙されます。 - 構造化シリアライズのみが提供されます。 TOML と CSV は除外されています。TOML は深くネストしたスペースツリーには不向きで、CSV はフラットな表形式だからです。エラーエンベロープのボディと
/v1/commentの octet-stream バリアント(生バイト入力 / 生バイト出力)は、それぞれ常に JSON / 生バイトであり、ネゴシエーションを行いません。イントロスペクションルート(/v1、/v1/version、/v1/languages)は JSON メタデータのみを返します。
# YAML メトリクス
curl --silent \
--header 'Content-Type: application/json' \
--header 'Accept: application/yaml' \
--data '{"file_name": "foo.py", "code": "def f():\n pass\n"}' \
http://127.0.0.1:8080/v1/metrics
# CBOR メトリクス(バイナリ。デコーダーにパイプしてください)
curl --silent \
--header 'Content-Type: application/json' \
--header 'Accept: application/cbor' \
--data '{"file_name": "foo.py", "code": "def f():\n pass\n"}' \
http://127.0.0.1:8080/v1/metrics --output metrics.cbor
エンドポイント
1. サーバーの Ping
このエンドポイントは、サーバーが稼働しているかどうかの確認に使用します。
リクエスト:
GET http://127.0.0.1:8080/v1/ping
レスポンス:
- ステータスコード:
200 OK - ボディ:空。
curl -sf http://127.0.0.1:8080/v1/ping && echo ok を使うと死活チェックをスクリプト化できます。-f により、curl は HTTP エラー時に非ゼロの終了コードで終了します。
2. コメントの除去
このエンドポイントは、提供されたソースコードからコメントを除去します。2 つの Content-Type バリアントを受け付けます。生バイト入力 / 生バイト出力には application/octet-stream を、JSON エンベロープには application/json を使用します。
リクエスト:
POST http://127.0.0.1:8080/v1/comment
ペイロード:
{
"id": "unique-id",
"file_name": "filename.ext",
"code": "source code with comments"
}
id: リクエストの一意な識別子です。省略可能です(issue #645)。省略すると空文字列にデフォルトされ、「相関 ID なし」として扱われます。file_name: 解析対象のファイル名です。code: コメントを含むソースコードです。
レスポンス(JSON バリアント):
{
"id": "unique-id",
"language": "cpp",
"code": "print"
}
レスポンスのエンベロープは id、検出された language(正規の小文字スラッグ — 後述の メトリクスの計算 を参照)、および結果キー code を報告します。code フィールドはコメント除去後のソースを保持する文字列です。リクエストの code は JSON 文字列として届くため、除去後の出力は有効な UTF-8 であることが保証され、リクエストや他のすべての JSON エンドポイントと同様に文字列として返されます。application/octet-stream バリアントは、除去後のソースをそのまま(エンベロープなしの)生のレスポンスボディとして返します。これはバイナリを忠実に往復させる用途に適した形であり、シェルパイプラインでも扱いやすくなります。その エラー には引き続き上記の統一 JSON エラーボディが使われます。
ソースに除去可能なコメントが含まれない場合、どちらのバリアントも 200 ステータスと空のペイロードで空の結果を通知します。JSON バリアントは "code": ""(空文字列)を返し、octet-stream バリアントは空のボディを返します。したがって、要求された Content-Type にかかわらずステータスコードとエンベロープの形は同一です。octet-stream バリアントは 204 No Content ではなく空の 200 ボディを返します。
3. 関数スパンの取得
このエンドポイントは、指定されたソースコード内の関数のスパンを取得します。
リクエスト:
POST http://127.0.0.1:8080/v1/function
ペイロード:
{
"id": "unique-id",
"file_name": "filename.ext",
"code": "source code with functions"
}
id: リクエストの一意な識別子です。省略可能です(issue #645)。省略すると空文字列にデフォルトされ、「相関 ID なし」として扱われます。file_name: 解析対象のファイル名です。code: 関数を含むソースコードです。
レスポンス:
{
"id": "unique-id",
"language": "cpp",
"spans": [
{
"name": "function_name",
"start_line": 1,
"end_line": 10
}
]
}
エンベロープは id、検出された language スラッグ、および結果キー spans を報告します。パーサーが AST から関数名を解決できなかった場合(無名の定義や不正な形式の定義など)、name は null になります。null の name は不正な形式のスパンを示すシグナルです。
4. AST の取得
このエンドポイントは、指定されたソースコードの完全な tree-sitter 抽象構文木(AST)を、再帰的な JSON ノードツリーとして返します。
リクエスト:
POST http://127.0.0.1:8080/v1/ast
ペイロード:
{
"id": "unique-id",
"file_name": "filename.ext",
"code": "source code to parse",
"comment": false,
"span": true
}
id: リクエストの一意な識別子です。省略可能です。省略すると空文字列にデフォルトされ、「相関 ID なし」として扱われます。file_name: 解析対象のファイル名です。code: パースするソースコードです。comment:trueの場合、コメントノードがツリーから省かれます。省略可能で、デフォルトはfalseです。span:trueの場合、各ノードはソース上のspanを持ちます。falseの場合、spanはnullになります。省略可能で、デフォルトはfalseです。
id、comment、span は省略可能で、デフォルトは上記のとおりです(issue #645)。file_name と code は必須です。未知のキーは 400 で拒否されます(issue #633)。
レスポンス:
{
"id": "unique-id",
"language": "rust",
"root": {
"type": "source_file",
"value": "",
"span": { "start_line": 1, "start_col": 1, "end_line": 2, "end_col": 1 },
"field_name": null,
"children": [
{
"type": "function_item",
"value": "",
"span": { "start_line": 1, "start_col": 1, "end_line": 1, "end_col": 13 },
"field_name": null,
"children": [
{
"type": "identifier",
"value": "main",
"span": { "start_line": 1, "start_col": 4, "end_line": 1, "end_col": 8 },
"field_name": "name",
"children": []
}
]
}
]
}
}
エンベロープは id、検出された language スラッグ、およびルート AST ノードである root を報告します。各ノードは次の情報を持ちます。
type: tree-sitter 文法のノード種別です(文法固有です。どの文法が生成したかはlanguageスラッグで分かります)。value: リーフ/名前付きトークンのソーステキストです(内部ノードでは空です)。span:{ start_line, start_col, end_line, end_col }オブジェクト(すべて 1 始まり)です。spanがfalseの場合はnullになります。これらのスパンキーは/functionおよび/metricsと共通の*_line語彙を使用します(issue #638 で旧来の*_rowキーから改名されました)。field_name: 親がこのノードに到達する際に経由する tree-sitter 文法フィールドです(例:name、left、body)。ルート、無名トークン、フィールドを持たない子ではnullになります。children: ノードの子ノードです(再帰的に続きます)。
メトリクスや関数のエンドポイントと異なり、AST エンドポイントはクライアントが送信した正確なバイト列に対するノード座標を報告します。ソースの改行コードは正規化されないため、スパンはクライアント側の手元のコピーと一致します(issue #640)。
5. メトリクスの計算
このエンドポイントは、指定されたソースコードに対して各種メトリクスを計算します。
リクエスト:
POST http://127.0.0.1:8080/v1/metrics
ペイロード:
{
"id": "unique-id",
"file_name": "filename.ext",
"code": "source code for metrics",
"scope": "full"
}
-
id: リクエストの一意な識別子です。省略可能です(issue #645)。省略すると空文字列にデフォルトされ、「相関 ID なし」として扱われます。 -
file_name: ソースコードファイルのファイル名です。 -
code: 解析するソースコードです。 -
scope: スペースツリーをどこまで返すかを指定します。full(デフォルト)は完全にネストされたスペースツリー、すなわちファイルレベルのルートに加えて、すべての関数・クラス・その他のユニットの再帰的なspacesリストを返します。fileはファイルレベルのルートのみを返し、そのspacesの子は空になります。このフィールドは 2.0 より前のブール値unitフラグを置き換えるものです(issue#638)。旧来の
unitキーを送信すると、現在は400で失敗します。
ペイロードは厳密に検証されます。認識されないキー(タイプミスや、削除済みの unit)は 400 と統一 JSON エラーボディで拒否され、問題のフィールド名が示されます(issue #633)。
application/octet-stream バリアントでは、ソースは生のリクエストボディで渡し、scope はクエリパラメータとして指定します(?file_name=…&scope=full)。パラメータを省略するとデフォルトの full になり、認識されない値は 400 で拒否されます。
レスポンス:
{
"id": "unique-id",
"language": "rust",
"root": {
"name": "sample.rs",
"start_line": 1,
"end_line": 7,
"kind": "unit",
"spaces": [
{
"name": "double",
"start_line": 1,
"end_line": 7,
"kind": "function",
"spaces": [],
"metrics": {
"cyclomatic": { "sum": 2, "average": 2.0, "min": 2, "max": 2 },
"loc": { "sloc": 7, "ploc": 7, "lloc": 1, "cloc": 0, "blank": 0 },
"nom": { "functions": 1, "closures": 0, "total": 1 }
}
}
],
"metrics": { "...": "the same metric block, aggregated over the file" }
}
}
レスポンスのエンベロープは id、検出された language スラッグ、および root — 単一のファイルレベルのスペースオブジェクト — を報告します(issue #638 で、誤解を招く複数形の spaces からこのキーに改名されました)。root は name(リクエストの file_name)、start_line / end_line のスパン、kind 判別子(unit、function、class など)、その metrics ブロック、および子ユニットの再帰的な spaces リストを持ちます。上の例は省略されています。実際の各 metrics ブロックには、すべてのメトリクスファミリー — cyclomatic、cognitive、halstead、loc、nom、nargs、nexits、tokens、mi、abc、wmc — が、bca CLI が出力するのと同じネストされたフィールドとともに含まれます。scope が file の場合、root.spaces は空のリストになります。
language の値は正規の小文字スラッグ(例: rust、cpp、csharp、tsx)です。これは言語語彙が受け付けるトークンと同じもので、人間向けの表示名ではありません。すべての解析エンドポイント(/ast、/comment、/function、/metrics)がこの language フィールドを報告するため、クライアントはどの文法が選択されたかを確認できます。
6. サーバーとライブラリのバージョン
実行中のサーバーのバージョンと、そのビルドに使用された big-code-analysis ライブラリのバージョンを報告します。
リクエスト:
GET http://127.0.0.1:8080/v1/version
レスポンス:
{
"server": "2.0.0",
"library": "2.0.0"
}
7. 対応言語
対応言語と、登録されているファイル拡張子の一覧を返します。名前は正規の小文字スラッグです。一覧と拡張子はライブラリの言語テーブルから取得され、ハードコードされることはありません。
リクエスト:
GET http://127.0.0.1:8080/v1/languages
レスポンス:
{
"languages": [
{ "name": "cpp", "extensions": ["cpp", "cc", "hpp", "..."] },
{ "name": "rust", "extensions": ["rs"] }
]
}
他のすべてのエンドポイントと同様に、/version と /languages は /v1 プレフィックスの下でのみ提供されます。プレフィックスなしの 1.x エイリアスは 2.0 で削除されました(API バージョニングを参照)。
8. ルートインデックス
登録されているすべてのルートの機械可読なインデックス — パス、受け付ける HTTP メソッド、1 行の説明 — を返します。これによりクライアントは、本章をスクレイピングすることなく API サーフェスを発見できます。インデックスはサーバーが登録するのと同じルートテーブルから生成されるため、実際のルーティングから乖離することはありません。
リクエスト:
GET http://127.0.0.1:8080/v1
レスポンス:
{
"service": "bca-web",
"version": "2.0.0",
"routes": [
{ "path": "/v1", "methods": ["GET", "HEAD"], "description": "This route index." },
{ "path": "/v1/metrics", "methods": ["POST"], "description": "Compute maintainability metrics for the source." }
]
}
service は常に bca-web です。version は GET /v1/version の server フィールドと一致します。以前のリリースでこのエンドポイントのエイリアスとして提供されていたプレフィックスなしのルート / は、2.0 で削除されました。
変更履歴(VCS)メトリクス
3 つのエンドポイントが変更履歴(バージョン管理)メトリクス — CLI の bca vcs が計算するのと同じ数値 — を公開します。他のすべてのエンドポイントと異なり、これらはリクエストボディで運ばれるソースコードではなく、サーバーのファイルシステム上に既に存在する git リポジトリを解析します。VCS メトリクスはコミット履歴から導出されるものであり、リクエスト内に表現を持たないためです。
運用者向け警告 —
repo_pathは信頼境界です。repo_pathフィールドはサーバー側のファイルシステムパスです。これらのエンドポイントは、サーバーが読み取れる あらゆる git リポジトリをサーバーに走査させ、そのリポジトリの相対ファイルパス、チャーン、作者シグナルを返します。これは、クライアントが送信したコードしか見ないソース同梱型のエンドポイントとは本質的に異なります。省略可能なcache_dirフィールドは、呼び出し側が指定するもう 1 つのサーバー側パスで、書き込み 能力を付与します。キャッシュが有効な場合(デフォルト)、サーバーはその配下にディレクトリを作成して JSON キャッシュファイルを書き込む(<cache_dir>/<repo>/<head_sha>.json)ため、cache_dirを制御する呼び出し側は、サーバープロセスが書き込める任意のパスにキャッシュファイルを書かせることができます。(cache_dirを受け付けるのは/vcsのみで、/vcs/trendと/vcs/jitはキャッシュしません。)したがって、このエンドポイントのファイルシステム到達範囲は、読み取り可能な任意の git リポジトリの任意読み取り および 書き込み可能な任意のパス配下へのキャッシュファイルの任意書き込みです。bca-webの前段に認可レイヤーを置かないまま、/vcs、/vcs/trend、/vcs/jitを信頼できないクライアントに公開しないでください。 デフォルトの127.0.0.1バインドはこれらをローカルに留めます。各走査は、解析エンドポイントと同じパースタイムアウトとブロッキングプールのガードの下で実行されます。
3 つのエンドポイントはいずれも POST 専用で、application/json を受け付け、リクエストの id をエコーバックし、統一の {error, error_kind, id} ボディでエラーを報告します(その error_kind トークンは vcs_* ファミリーです — 例: vcs_not_a_repository、vcs_invalid_window)。クライアント側の誤り — repo_path が存在しない、または git 作業ツリーでない(どちらも vcs_not_a_repository を伴います)、解決できない ref/commit、不正な形式または diff でない diff、不正な形式のウィンドウ / タイムスタンプ / 式 / ファイルタイプ / しきい値 / トレンドのパラメータ — は 400 です。履歴走査自体の失敗は 500 です。存在しない repo_path はタイプミス — ここで最も多いクライアントエラー — であるため、存在するもののリポジトリではないパスと同様に、500 ではなく 400 を返します(issue 653)。
9. リスク順のファイルランキング — /vcs
リポジトリの履歴を一度だけ走査し、複合リスクスコアでランク付けしたファイル一覧を返します(issue #328)。
リクエスト:
POST http://127.0.0.1:8080/v1/vcs
ペイロード:
{
"id": "unique-id",
"repo_path": "/srv/repos/my-project"
}
repo_path は必須です。他のすべてのフィールドは省略可能で、デフォルトは bca vcs のデフォルト値です。id も省略可能で(issue #645)、省略すると空文字列となり、そのままエコーバックされます。省略可能なフィールドは次のとおりです。
long_window/recent_window: ウィンドウ指定です(例:12mo、90d)。デフォルトは12mo/90dです。top: リスク上位 N 件のファイルのみを保持します。未指定の場合のデフォルトは50(bca vcs --topのデフォルト)で、明示的に0を指定するとすべてのファイルを返します。ref: 解析対象のリビジョンです(デフォルトはHEAD)。risk_formula:weighted(デフォルト)またはpercentileです。file_types:metrics(デフォルト — bca がメトリクスを持つファイルのみ)、all(追跡されているすべてのテキストファイル)、またはカンマ区切りの拡張子許可リスト(rs,py)です。full_history: 第一親のみではなく、完全な DAG を走査します。include_merges: マージコミットを含めます。follow_renames: リネームを追跡します(デフォルトtrue)。exclude_bots: ボットの識別情報を除外します(デフォルトtrue)。bot_pattern: ボット作者を除外する正規表現を上書きします。as_of: スナップショットの基準となる「現在」(RFC 3339 /@unix/ git の日付形式)を指定します。emit_author_details: SHA-256 でハッシュ化した作者の識別情報を出力します。author_hash_key:emit_author_detailsを鍵付き HMAC-SHA256 に強化する秘密鍵です(emit_author_detailsが必要です。空の鍵、またはフラグなしでの指定は400になります)。include_deleted: 対象の ref で削除済みのファイルを含めます。bus_factor_threshold:(0, 1)の範囲で指定するバスファクターのカバレッジしきい値です(デフォルト0.5)。no_cache: このリクエストで永続的な変更履歴キャッシュを無効にします(デフォルトfalse)。cache_dir: サーバー側のキャッシュディレクトリを上書きします。
レスポンス:
{
"id": "unique-id",
"vcs_schema_version": 2,
"risk_score_version": 2,
"long_window_days": 365,
"recent_window_days": 90,
"truncated_shallow_clone": false,
"vcs_aggregate": { "...": "directory- / repo-level bus factor" },
"files": [
{
"path": "src/main.rs",
"vcs": {
"commits_long": 12,
"commits_recent": 3,
"churn_long": 540,
"churn_recent": 80,
"authors_long": 4,
"authors_recent": 2,
"risk_score": 1.42
}
}
]
}
files は vcs.risk_score の降順に並びます。各エントリはリポジトリ相対の path と、入れ子の vcs メトリクスブロック(bca vcs が出力するものと同じ形式、issue #684)を持ちます。含まれるのは、長期・直近ウィンドウでのコミット数とチャーン数、作者数、所有権シェア、バースト、バグ修正 / セキュリティ修正 / リバートの各カウント、経過期間、変更エントロピーと共変更エントロピー、そして合成値の risk_score です。hotspot_score とハッシュ化された author_ids は、計算可能な場合 / 要求された場合にのみそのブロック内に現れます。4 つの定数スタンプ vcs_schema_version、risk_score_version、long_window_days、recent_window_days はトップレベルに一度だけ置かれ、行ごとには現れません(issue #635)。vcs_aggregate はディレクトリレベルおよびリポジトリレベルのバスファクターを持ちます(issue #332)。
10. 履歴トレンド — /vcs/trend
変更履歴メトリクスを等間隔の複数時点でサンプリングし、ファイルごとの時系列を返します(issue #333)。応答はランク付けされたスナップショットではなく系列であるため、/vcs とは別のルートになっています。
リクエスト:
POST http://127.0.0.1:8080/v1/vcs/trend
ペイロード: 上記の /vcs フィールドすべて(ただしキャッシュ制御 no_cache / cache_dir を除きます — trend は永続キャッシュを使わないため、どちらかを送ると黙って無視されるのではなく 400 になります。issue #961)に加えて、次を指定できます:
points: 等間隔のサンプル点の数(>= 2)。省略時は12(bca vcs trend --pointsのデフォルト)になります。span: サンプル点全体がカバーする遡及期間(デフォルト12mo)。top_deltas: 改善 / 悪化それぞれのリストに載せる上位 N ファイル数。未指定の場合は10、明示的な0は全件を返します。
レスポンス:
{
"id": "unique-id",
"trend_schema_version": 1,
"vcs_schema_version": 2,
"risk_score_version": 2,
"long_window_days": 365,
"recent_window_days": 90,
"truncated_shallow_clone": false,
"as_of_points": [1704067200, 1711929600],
"files": {
"src/main.rs": [ { "as_of": 1704067200, "vcs": { "risk_score": 1.1 } }, null ]
},
"deltas": { "improved": [], "regressed": [] }
}
as_of_points はサンプル時刻を古い順に列挙します。files 内の各ファイルの配列はこれと 1:1 で対応し、その時点でファイルがまだ存在しなかった場合は null 要素になります。存在する各要素は { "as_of": ..., "vcs": { ... } } の形で、その時点のそのファイルの VCS ブロックが vcs の下に入れ子になります(issue #684)。4 つの定数スタンプはトップレベルに一度だけ置かれ、時点ごとには現れません(issue #635)。deltas は、系列全体でのリスクスコアの変動に基づいて、最も改善したファイルと最も悪化したファイルをランク付けします。
11. ジャストインタイムリスク — /vcs/jit
単一の変更のジャストインタイムリスクをスコアリングします — 対象はサーバー側リポジトリ上の 1 コミット、またはリクエストボディで渡される任意の unified diff のいずれかです(issue #331 / #580)。この 2 つのモードは相互排他です。
コミットモードは repo_path 上のコミットをスコアリングします:
{
"id": "unique-id",
"repo_path": "/srv/repos/my-project",
"commit": "HEAD"
}
コミットモードは、経験ウィンドウを調整する long_window、recent_window、full_history、include_merges、follow_renames、as_of も受け付けます。応答は source が "commit" の完全なレポートで、その risk_score は 5 つの特徴グループ(サイズ、拡散、履歴、経験、目的)すべてを織り込みます:
{
"id": "unique-id",
"jit_schema_version": 3,
"jit_score_version": 1,
"source": "commit",
"long_window_days": 365,
"recent_window_days": 90,
"risk_score": 0.87,
"commit": { "id": "…", "parent_count": 1, "is_merge": false, "purpose": {} },
"features": { "size": {}, "diffusion": {}, "history": {}, "experience": {} },
"contributions": { "size": 0.4, "diffusion": 0.2, "history": 0.1, "purpose": 0.0, "experience": -0.1 }
}
diff モードはリポジトリなしで任意の unified diff をスコアリングします:
{
"id": "unique-id",
"diff": "--- a/x\n+++ b/x\n@@ -1 +1 @@\n-old\n+new\n"
}
素の diff には作者・親・履歴の情報がないため、計算できるのは サイズ と 拡散 のグループだけです。diff レポートの source は "diff" で、risk_score ではなく partial_risk_score を報告します。欠けているグループは ボディから完全に省かれ、ゼロとして現れることは決してないためです:
{
"id": "unique-id",
"jit_schema_version": 3,
"jit_score_version": 1,
"source": "diff",
"partial_risk_score": 0.6,
"size": {},
"diffusion": {},
"contributions": { "size": 0.4, "diffusion": 0.2 }
}
source 判別子("commit" か "diff" か)で分岐して、正しいスコアフィールドを読み取ってください。partial_risk_score は、同じ変更に対するコミットの risk_score より常に低く、異なるスケール上にあります。diff は他の diff とだけ比較し、コミットのスコアとは決して比較しないでください。
モードの競合。 diff をいずれかのコミットモードのフィールド(repo_path、commit、ウィンドウ、履歴、リネーム、as_of の各設定)と同時に指定すると、diff を黙って優先して残りを捨てるのではなく 400 で拒否されます。2 つのモードは互いに比較できない別々の問いに答えるものであり、この組み合わせはクライアントの誤りとして扱われます(issue 632)。
diff でない diff。 git の unified diff ではない diff 値 — フィールドの取り違え、途中で壊れた文字列、任意のテキストなど — は、確信を持った partial_risk_score の 0.0 としてスコアリングされるのではなく、400(vcs_invalid_diff)で拒否されます。リスクを「ゲート」するエンドポイントでは、誤った「リスクゼロ」が最も危険な失敗モードであるため、diff でない入力はハードエラーになります(issue 652)。唯一の例外は空または空白のみの diff です。これは正当に「変更なし」を意味するため、有効な 0.0 を返します — 空の diff を計算した CI ステップは、期待どおりのリスクゼロという答えを得られます。