この記事でできること
PRを作成・更新するたびに、Claudeが差分をレビューして該当行へのインラインコメントとサマリコメントを残す、という状態をGitHub Actionsだけで作ります。
認証にはClaude Code Pro/Maxのサブスクリプションのトークンを使うため、APIキーの従量課金は発生しません。実際に実行ログの total_cost_usd は 0 でした(サブスクの利用枠を消費します)。
自前ホストのOSSレビューbot + LLM無料枠という構成から乗り換えた際の手順です。乗り換えを決めた判断過程はZenn版に書いたので、ここでは動く状態に持っていくまでの手順と、実際に回して分かった挙動をまとめます。
前提
- Claude Code Pro または Max のサブスクリプション(個人開発向け。業務利用は後述の別方式を推奨)
- ローカルにClaude Code CLIがインストール済み
- 対象リポジトリのSecretsを編集できる権限
手順1: サブスクリプション用のトークンを生成する
ローカルで次のコマンドを実行します。対話的な認証が走ります。
claude setup-token
完了するとトークンが表示されるので、これを控えておきます。APIキー(sk-ant-...)とは別物で、こちらはサブスクリプションの枠で動くためのトークンです。
手順2: リポジトリのSecretsに登録する
生成したトークンを、対象リポジトリのSecretsに CLAUDE_CODE_OAUTH_TOKEN という名前で登録します。
gh secret set CLAUDE_CODE_OAUTH_TOKEN --repo <owner>/<repo>
ワークフローだけ置いてもSecretが無ければ動きません。ここを先にやっておきます。
手順3: ワークフローを置く
.github/workflows/claude-review.yml を作成します。以下が実際に運用しているものです。
name: Claude Review
on:
pull_request:
types: [opened, reopened, synchronize, ready_for_review]
jobs:
claude_review:
# 人間が起こしたPRのみ対象(bot起因の自動PRで枠を消費しない)
if: ${{ github.event.sender.type == 'User' }}
runs-on: ubuntu-latest
concurrency:
group: claude-review-${{ github.event.pull_request.number }}
cancel-in-progress: true
permissions:
contents: read
pull-requests: write
id-token: write
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Claude review
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
github_token: ${{ secrets.GITHUB_TOKEN }}
claude_args: |
--model claude-sonnet-5
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)"
prompt: |
REPO: ${{ github.repository }}
PR NUMBER: ${{ github.event.pull_request.number }}
このPRのコードレビューを日本語で行ってください。
(プロンプトの中身は後述)
各設定のポイント
ここが分かっていないとハマる、という箇所を個別に説明します。
github_token は明示的に渡す
一番ハマりやすいのがここです。github_token を省略すると、ActionはClaude GitHub Appのトークン交換を試みます。Appをインストールしていないリポジトリでは401で落ちます。
ワークフロー既定の secrets.GITHUB_TOKEN を明示的に渡すことで、App無しでもPRへのコメントができます。
github_token: ${{ secrets.GITHUB_TOKEN }}
なお、この方法だとコメントの投稿者名は github-actions[bot] になります。公式のClaude GitHub Appを入れると claude[bot] 名義になり、他のCIのコメントと見分けやすくなります。
--model でモデルを指定する
claude_args に渡します。指定するとログの初期化メッセージにも "model": "claude-sonnet-5" として反映されます。
--model claude-sonnet-5
Opusにも変更できます。差分が大きい・重要度が高いリポジトリならOpus、PRの本数が多いならSonnetで十分だと思います。
concurrency は cancel-in-progress: true にする
連続pushで実行が並行すると、古い差分に対するレビューが後から返ってきて紛らわしくなります。レビューは古い実行を完走させる意味がないのでキャンセルしてしまって問題ありません。枠の節約にもなります。
concurrency:
group: claude-review-${{ github.event.pull_request.number }}
cancel-in-progress: true
sender.type == 'User' でbot起因の実行を除外する
依存関係更新botなどが起こしたPRでもレビューが走ると、枠を無駄に消費します。
if: ${{ github.event.sender.type == 'User' }}
--allowedTools は必要なものだけ列挙する
インラインコメント用のMCPツールと、gh コマンドのサブコマンド単位の許可を並べます。
--allowedTools "mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)"
Bash(gh pr diff:*) のようにサブコマンド単位で絞れます。Bash(gh:*) とまとめて許可すると gh pr merge まで通ってしまうので、用途に必要なものだけを個別に書くのが安全です。
なお、ここに Read を列挙していませんが、実行ログを見る限りファイルの読み取りは問題なく動いていました。ファイル読み取りのような基本ツールまで自分で並べる必要はなさそうです(レビュー実行時にClaude自身が「Read が無いと動かないのでは」と指摘してきましたが、実際には動いていた、という偽陽性でした)。
プロンプトの書き方
デフォルトのまま走らせると「一般論として良いことを言うが、指摘の粒度が安定しない」レビューになりがちです。運用上効いたのは次の3点です。
1. 出力先を2系統とも明示する
これを書かないと、インラインコメントだけ付いてサマリが無い(またはその逆)という状態が起きます。「どちらか一方では不十分」と明記します。
結果の出力は必ず以下の両方を行うこと(どちらか一方だけでは不十分):
- 個別の指摘は mcp__github_inline_comment__create_inline_comment で該当行に付ける
- 最後に `gh pr comment` でレビュー結果のサマリを必ず1件投稿する。
指摘が無かった場合も「レビュー完了: 指摘なし」と投稿すること
指摘ゼロのときも必ずコメントさせるのが地味に重要です。無言で終わると、「指摘が無かった」のか「レビュー自体が落ちた」のかがPR上で区別できません。
2. 差分だけでなくファイル全体を読ませる
diffだけを見せると、そのリポジトリの既存の書き方と食い違う指摘が増えます。同じファイル内の兄弟コードと突き合わせさせます。
ロジックに関わる変更(ドキュメント/コメントのみの変更は除く)については、
該当ファイルを丸ごとReadし、同じファイル内の類似関数・類似エンドポイントが
同じ状況で採用しているパターン(エラーハンドリングの有無、命名、戻り値の扱い等)と
矛盾していないか確認する。既存の兄弟コードと一貫していれば指摘しないこと
3. 見る観点を不具合クラスで列挙し、好みの問題は禁止する
以下の典型的な不具合クラスを重点的に確認する:
- fail-open(失敗時に安全側でなく通してしまう)
- 既定値・空入力・境界値の扱い
- ID衝突、一意制約、並行実行時の競合
- リソースリーク(ファイル/セッション/ロックの解放漏れ)
- セキュリティ(危険な動的実行、SQL構築、権限チェック漏れ)
- テストの欠落
指摘は「明確な不具合」または「設計方針との矛盾」に限定すること。
好みの問題や、正しく動いているコードへの些細な改善提案は述べないこと。
あわせて「リポジトリの CLAUDE.md に書かれた設計方針と矛盾していないか」も観点に入れておくと、プロジェクト固有のルール違反を拾ってくれるようになります。
実際に回して分かった挙動
ここからは、ドキュメントを読むだけでは分からなかった実測ベースの話です。
毎pushで「PRの全差分」を見直す
トリガーに synchronize を含めているため、コミットのたびに再実行されます。このとき見るのは差分コミットではなく、gh pr diff、つまりベースブランチとの全差分です。
結果として、
- 直していない指摘は、pushのたびに毎回再指摘される
- 直せば次の実行で消える
という挙動になります。実際、指摘された concurrency 未設定を直した次の実行は「指摘なし」になりました。指摘が残り続けるかどうかが対応状況のチェックリスト代わりになるので、この挙動自体は扱いやすいです。
コメントは蓄積する(use_sticky_comment が効かない)
Actionには use_sticky_comment という入力があり、これを true にすればコメントが1つに保たれる……と期待していたのですが、この構成では効きませんでした。実際、1本のPRにサマリコメントが2件別々に残りました。
理由は、上のプロンプトでClaude自身に gh pr comment でサマリを投稿させているからです。gh pr comment は素直に新規コメントを作るので、Action側のsticky制御の外側で投稿が増えていきます。
対処としては、
- プロンプト側で「既存の自分のコメントを探して編集する」よう指示する
- 公式のClaude GitHub Appを入れて、Action本来のコメント管理に寄せる
のどちらかになります。push回数が多い長いPRだとコメントがどんどん伸びるので、業務で使うなら後者のほうが綺麗だと思います。
外部PRに対しては設定ファイルを差し替えてくれる
実行ログを見ていて感心したのがここです。PRのheadが信頼できない場合、Actionは自動的にこうしていました。
Restoring .claude, .mcp.json, CLAUDE.md ... from origin/main (PR head is untrusted)
Preserved PR's sensitive paths -> .claude-pr/ for review agents (not executed)
.claude/ や CLAUDE.md を origin/main の内容に戻したうえで、PR側のそれらは実行せず別ディレクトリに退避しています。悪意あるPRがレビュー指示そのものを書き換える攻撃を防ぐ作りになっているわけです。外部コントリビュータを受け入れるリポジトリでは重要な性質です。
精度は「良いが完璧ではない」
初回実行のインライン指摘2件の内訳は、真陽性1件・偽陽性1件でした。
- 真陽性:
concurrency未設定の指摘。同じリポジトリの兄弟ワークフローを根拠として挙げたうえでの指摘だった - 偽陽性: 「
--allowedToolsにReadが無いと動かない」という指摘。実際には動いていた
人間のレビューを置き換えるものではなく、一次スクリーニングとして見るのが妥当な精度です。
業務で使うなら認証方式を変える
冒頭のサブスクリプショントークン方式は、個人開発では手軽ですが業務導入では勧めません。理由は2つあります。
- Pro/Maxは個人向けプランなので、会社のCIに個人の認証情報を埋めるのはライセンス面がグレーです(契約条件は必ず自社で確認してください)
- 運用上、全レビューが特定個人のアカウントと枠に紐づきます。その人の退職やトークン失効でCIが止まります
action.yml を読んだ限り、業務向けには以下が用意されています。
| 方式 | 入力 | 備考 |
|---|---|---|
| APIキー | anthropic_api_key |
従量課金。予算管理しやすい |
| Workload Identity Federation |
anthropic_federation_rule_id + anthropic_organization_id
|
静的キー不要。GitHub OIDCトークンを交換。id-token: write が必要 |
| Bedrock / Vertex / Foundry |
use_bedrock / use_vertex / use_foundry
|
既にクラウド契約があるならこれが本命 |
既存のAWS/GCP契約があるならBedrock/Vertex、無ければFederationが、鍵の管理が要らないぶん筋が良いと思います。
動作確認
ワークフローをpushし、適当なPRを立てて確認します。
# 実行状況を見る
gh run list --workflow=claude-review.yml
# 失敗していたらログを見る
gh run view <run-id> --log-failed
401で落ちている場合は、CLAUDE_CODE_OAUTH_TOKEN の登録漏れか、github_token の指定漏れのどちらかであることがほとんどです。
まとめ
-
claude setup-tokenで生成したトークンをCLAUDE_CODE_OAUTH_TOKENとしてSecretsに登録すれば、APIキー課金なしでPR自動レビューが動く(実行ログのtotal_cost_usdは0) -
github_token: ${{ secrets.GITHUB_TOKEN }}を明示しないと、GitHub App未インストール環境では401で落ちる - プロンプトでは「インラインコメントとサマリの両方を出す」「指摘ゼロでもサマリを投稿する」を明示する。レビューが落ちたことに気づけるようにするため
- 毎pushでPRの全差分を見直すため、未対応の指摘は毎回再指摘され、直せば消える
- プロンプトから
gh pr commentさせる構成だとuse_sticky_commentは効かずコメントが蓄積する。嫌なら公式GitHub Appを入れる - 業務導入ではサブスクトークンではなく、Bedrock/Vertex または Workload Identity Federation を検討する
なぜ自前ホストのレビューbot + LLM無料枠という構成をやめたのか、その判断過程はZenn版に書いています。