REST API の操作

bca-web は同じ分析プリミティブを HTTP 経由で公開します。コンシューマーが長時間稼働するサービス(エディタープラグイン、CI ワーカー、Web アプリ)で、ファイルごとに CLI を起動するコストを払うべきでない場合に使ってください。

エンドポイントの完全なリファレンスは Rest API を参照してください。以下のレシピは curl を使った実践的なエンドツーエンドの呼び出しを示します。すべてのエンドポイントは /v1 プレフィックスの下にマウントされます。旧来のプレフィックスなしパスは 2.0 リリース(#637)で削除され、現在は 404 を返します。

サーバーを起動する

bca-web --host 127.0.0.1 --port 8080 -j "$(nproc)"

起動していることを確認します:

curl -sf http://127.0.0.1:8080/v1/ping && echo "ok"
# => ok

/ping は空のボディで 200 OK を返します — curl -sf は成功時に 0、HTTP エラー時に非ゼロで終了し、これはまさにスクリプトが望む挙動です。

ビルド、フラグや環境変数のチューニング、リソース制限、デーモン自体のセキュリティ態勢については、bca-web の運用 を参照してください。

インラインスニペットのメトリクスを計算する

curl -s http://127.0.0.1:8080/v1/metrics \
    -H 'Content-Type: application/json' \
    -d '{
          "id": "snippet-1",
          "file_name": "demo.rs",
          "code": "fn add(a: i32, b: i32) -> i32 { a + b }",
          "scope": "full"
        }' \
  | jq '.root.metrics'

scope: "file" はトップレベルのメトリクスのみを返します。"full"(デフォルト)はスニペット内のすべての関数・クラススペースを歩きます。サーバーは file_name から言語を推測するため、拡張子が重要です。

ディスク上のファイルのメトリクスを計算する

curl --data-binaryjq を組み合わせれば、実ファイルをサーバーが期待する JSON エンベロープへ簡単に詰め込めます:

jq -nc \
    --arg id "$(uuidgen)" \
    --arg file_name "src/lib.rs" \
    --rawfile code src/lib.rs \
    '{id: $id, file_name: $file_name, code: $code, scope: "full"}' \
  | curl -s http://127.0.0.1:8080/v1/metrics \
      -H 'Content-Type: application/json' \
      --data-binary @- \
  | jq '.root.metrics.cyclomatic, .root.metrics.cognitive'

このパターン — リクエストを組み立てる jq -n --rawfile と、それをストリームする curl --data-binary @- — は、複数行のソースコードでの引用符の問題を避ける最も簡単な方法です。

API 経由でコメントを除去する

エンドポイントは /comment(単数形)です。Content-Type で選択される 2 つのバリアントがあります:

  • application/json — リクエストとレスポンスを JSON でラップします。レスポンスの code フィールドは文字列ではなくバイト配列です。下層の API がバイト指向のためです。
  • application/octet-stream — ソースを生のリクエストボディとして受け取り、除去済みソースを生のレスポンスボディとして返します。シェルから使うには断然これが最も簡単なバリアントです。

octet-stream 形式(単発のシェル利用に推奨):

curl -s "http://127.0.0.1:8080/v1/comment?file_name=demo.py" \
    -H 'Content-Type: application/octet-stream' \
    --data-binary $'# leading comment\nprint("hi")  # trailing'
# => print("hi")

JSON 形式(クライアントがネイティブに JSON を扱う場合に使用します)。ASCII / UTF-8 のソースであれば、バイト配列は jq … | implode でデコードできます:

curl -s http://127.0.0.1:8080/v1/comment \
    -H 'Content-Type: application/json' \
    -d '{
          "id": "strip-1",
          "file_name": "demo.py",
          "code": "# leading comment\nprint(\"hi\")  # trailing"
        }' \
  | jq -r '.code | implode'

JSON レスポンスには送信した id がそのまま含まれるため、多数のリクエストを多重化するクライアントでも対応付けができます。

エディタプラグイン向けに関数スパンを抽出する

エンドポイントは /function(単数形)です:

curl -s http://127.0.0.1:8080/v1/function \
    -H 'Content-Type: application/json' \
    -d '{
          "id": "spans-1",
          "file_name": "demo.rs",
          "code": "fn a() {}\nfn b() {}\n"
        }' \
  | jq '.spans'

各エントリには namestart_lineend_line、および error ブール値(パーサーが関数スパンを不正と判定した場合に設定されます)が含まれます。エディタがローカルでファイルを再パースせずに関数ナビゲーターを描画するには十分な情報です。

変更履歴リスクでリポジトリをランク付けする

/vcs エンドポイントは、サーバー側のファイルシステム上に既に存在する git 作業ツリーを分析し(変更履歴はリクエスト内で表現できません)、複合リスクスコアでランク付けしたファイル一覧を返します。シグナルと計算式のリファレンスは変更履歴(VCS)メトリクスを参照してください。

セキュリティ: 他のエンドポイントと異なり、/vcs はサーバー側の repo_path を受け取り、サーバープロセスが読み取れる任意の git リポジトリを走査して、そのリポジトリのファイルパスと変更シグナルを返します。認可レイヤーなしで /vcs を信頼できないクライアントに公開しないでください。デフォルトの 127.0.0.1 バインドであればローカルに閉じたままになります。

curl -s http://127.0.0.1:8080/v1/vcs \
    -H 'Content-Type: application/json' \
    -d '{
          "id": "risk-1",
          "repo_path": "/srv/checkouts/my-project",
          "top": 20
        }' \
  | jq '.files[] | {path, risk_score, churn_recent}'

本文には bca vcs と同じ調整項目(long_windowrecent_windowtoprefrisk_formulafile_typesfull_historyinclude_mergesfollow_renamesexclude_botsbot_patternas_ofemit_author_detailsinclude_deletedbus_factor_thresholdno_cachecache_dir)を省略可能なフィールドとして指定できます。file_types はランキングの対象範囲を絞り込み(デフォルトの metrics / all / rs,py 形式の拡張子リスト)、CLI の --file-types に対応します。git 作業ツリーでない repo_path や、不正なウィンドウ / タイムスタンプ / 計算式 / 対象範囲を指定した場合は、統一 JSON エラー本文とともに 400 が返ります。

時系列トレンド

POST /vcs/trend は同じメトリクスを複数の時点でサンプリングし、ファイルごとの時系列を返します(履歴トレンドを参照)。本文には /vcs のフィールドに加えて points(2 以上)、span(デフォルト 12mo)、top_deltas を指定します。

curl -s http://127.0.0.1:8080/v1/vcs/trend \
    -H 'Content-Type: application/json' \
    -d '{
          "id": "trend-1",
          "repo_path": "/srv/checkouts/my-project",
          "points": 12,
          "span": "24mo",
          "top": 20
        }' \
  | jq '.deltas.regressed[] | {path, delta}'

ポイント数が 2 未満(またはサポートされる上限を超える場合)は、他の不正リクエストの場合と同様に、統一 JSON エラー本文とともに 400 が返ります。

ジャストインタイムのコミット / diff スコアリング

POST /vcs/jit は単一のコミットをスコアリングします(ジャストインタイムスコアリングを参照)。本文には repo_pathcommit(デフォルト HEAD)と、long_window / recent_window / full_history / include_merges / follow_renames / as_of の各調整項目を指定します。エコーバックされた id を含むコミットの JitReport JSON が返ります。

curl -s http://127.0.0.1:8080/v1/vcs/jit \
    -H 'Content-Type: application/json' \
    -d '{ "id": "jit-1", "repo_path": "/srv/checkouts/my-project",
          "commit": "HEAD" }' \
  | jq '{risk_score, purpose: .commit.purpose}'

代わりに任意の diff をスコアリングするには、unified diff を格納した diff フィールドを送信します(repo_path は不要です)。この場合のレスポンスは 部分 レポートになります。すなわち source: "diff"partial_risk_score を含み、履歴 / 経験 / 目的の各グループは含まれません(ゼロではなく欠落します)。部分スコアはコミットスコアと比較できません

git diff | jq -Rs '{id: "jit-diff", diff: .}' \
  | curl -s http://127.0.0.1:8080/v1/vcs/jit \
      -H 'Content-Type: application/json' -d @- \
  | jq '{source, partial_risk_score}'

不正な diff(あるいは解決できない commit、git 作業ツリーでない repo_path)を指定すると、統一 JSON エラー本文とともに 400 が返ります。

CI から API を呼び出す

サーバーはミリ秒単位で起動するため、短時間で終わる CI ジョブでは、ジョブ内でバックグラウンドプロセスとして起動し、最後に停止するのが最も簡単なことが多いです:

bca-web --port 8080 &
SERVER_PID=$!
trap 'kill "$SERVER_PID"' EXIT

# 起動するまで待ちます。
until curl -sf http://127.0.0.1:8080/v1/ping >/dev/null; do sleep 0.1; done

# … ここで分析の呼び出しを実行します …

長時間稼働するワーカーでは、サーバーを systemd ユニット(またはコンテナ)として実行し、ジョブからそのホスト / ポートを参照してください。