
GitHub Actions + Claude Code でPRレビューを自動化する——設定から運用まで
PRレビューには「見落とし」が起きる。SQLインジェクションの穴、nullが来たときの未処理、命名の揺れ——レビュアーが疲れているとき、急いでいるときに漏れる。
この記事では、PRを開いた瞬間にClaudeが自動でレビューコメントを投稿する仕組みを作る。人間のレビュアーが見るころには定型的な指摘が出揃っているため、議論すべき設計上の問題に集中できるようになる。GitHub ActionsにClaude Code CLIを組み込む構成で、ワークフローファイル1つから始められる。
この記事の構成:
- 最小構成:とにかく動かす
- 推奨構成:実用レベルに育てる(サイズスキップ + レビュー指示の管理)
- 発展:インラインコメントに変換する
準備:APIキーをGitHubに登録する
Anthropic Console でAPIキーを発行し、GitHubリポジトリに登録する。
リポジトリページの Settings → Secrets and variables → Actions → New repository secret で追加する。
- Name:
ANTHROPIC_API_KEY - Value: 発行したAPIキー
Step 1:最小構成で動かす
まずPRコメントが届くところまで確認する。リポジトリのルートに .github/workflows/pr-review.yml を作成して以下を貼り付ける。
name: PR Review
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # git diff にベースブランチとの比較が必要なため全履歴を取得
- uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install Claude Code
run: npm install -g @anthropic-ai/claude-code
- name: Run review
id: review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
git diff origin/${{ github.base_ref }}...HEAD > diff.txt
REVIEW=$(claude -p "以下のPR差分をレビューしてください:
$(cat diff.txt)" --allowedTools "Read,Grep")
echo "body<<EOF" >> $GITHUB_OUTPUT
echo "$REVIEW" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Post comment
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: "## Claude Code レビュー\n\n" + `${{ steps.review.outputs.body }}`
});
コミット・プッシュしてPRを開くとActionsが起動し、数十秒後にClaudeのコメントが届く。
-pフラグは必須claudeをそのまま実行するとインタラクティブモードになりCIジョブがハングする。-pを付けることでプロンプトを処理してstdoutに出力して終了するモードになる。
Step 2:推奨構成に育てる
最小構成の問題点は2つある。
- 巨大なdiff:ファイル数が多いPRではdiffが数万行になり、コンテキストウィンドウを超えてエラーになる
- レビュー指示がない:何を見てほしいかを伝えていないため、指摘の質が安定しない
推奨構成への移行は 2つの作業 で完了する。
① CLAUDE.md にレビュー指示を追記する
claude -p はリポジトリ内で実行されるため、ルートに CLAUDE.md があれば自動で読み込む。レビュー観点をここに書いておくことで、ワークフロー側のプロンプトをシンプルに保てる。
CLAUDE.md に以下のセクションを追加する(既存のファイルがあれば末尾に追記する):
## PRレビュー指示
このコードレビューでは以下の観点でチェックする:
- **セキュリティ**:SQLインジェクション・XSS・認証の抜け漏れ
- **バグ**:nullポインタ・境界値・競合状態
- **コード品質**:重複ロジック・命名の一貫性
指摘は「重大度 / ファイルパスと行番号 / 問題の説明と修正案」の形式で返す。
TODOコメント・テストコード内のモックデータ・設定ファイルは対象外とする。
② pr-review.yml を推奨ワークフローに差し替える
.github/workflows/pr-review.yml を以下の内容に 全体を置き換える。
name: PR Review
on:
pull_request:
types: [opened, synchronize, reopened]
paths-ignore:
- "**/*.lock"
- "**/generated/**"
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install Claude Code
run: npm install -g @anthropic-ai/claude-code
- name: Run review
id: review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
git diff origin/${{ github.base_ref }}...HEAD > diff.txt
# 差分が50KBを超える場合はスキップ(コンテキストウィンドウ超過を防ぐ)
DIFF_SIZE=$(wc -c < diff.txt)
if [ "$DIFF_SIZE" -gt 50000 ]; then
echo "body=差分が大きすぎるため自動レビューをスキップしました(${DIFF_SIZE}バイト)。手動でレビューしてください。" >> $GITHUB_OUTPUT
exit 0
fi
# CLAUDE.mdのレビュー指示を参照させる
REVIEW=$(claude -p "CLAUDE.mdのPRレビュー指示に従って以下の差分をレビューしてください:
$(cat diff.txt)" --allowedTools "Read,Grep")
echo "body<<EOF" >> $GITHUB_OUTPUT
echo "$REVIEW" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Delete previous review comments
uses: actions/github-script@v7
with:
script: |
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
for (const comment of comments) {
if (comment.body.startsWith("## Claude Code レビュー")) {
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: comment.id,
});
}
}
- name: Post comment
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: "## Claude Code レビュー\n\n" + `${{ steps.review.outputs.body }}`
});
最小構成(Step 1)からの変更点:
| 変更 | 理由 |
|---|---|
paths-ignore を追加 |
lockファイルや自動生成ファイルの変更でActionsが起動しなくなる |
| diffサイズチェック(50KB) | 巨大なdiffでコンテキストエラーが起きるのを防ぐ |
プロンプトに CLAUDE.mdのPRレビュー指示に従って を追加 |
CLAUDE.mdに書いたレビュー観点をClaudeに参照させる |
Delete previous review comments ステップを追加 |
同じPRにコメントが溜まるのを防ぐ |
Step 3(発展):インラインコメントにする
推奨構成ではレビュー結果をPR全体への1コメントとして投稿している。--output-format json と --json-schema を組み合わせると、ファイルの特定行にインラインコメントとして投稿できる。
.github/workflows/pr-review.yml を以下の内容に 全体を置き換える。
name: PR Review
on:
pull_request:
types: [opened, synchronize, reopened]
paths-ignore:
- "**/*.lock"
- "**/generated/**"
jobs:
review:
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: "20"
- name: Install Claude Code
run: npm install -g @anthropic-ai/claude-code
- name: Run review
id: review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
git diff origin/${{ github.base_ref }}...HEAD > diff.txt
DIFF_SIZE=$(wc -c < diff.txt)
if [ "$DIFF_SIZE" -gt 50000 ]; then
echo "json={\"findings\":[]}" >> $GITHUB_OUTPUT
exit 0
fi
SCHEMA='{
"type": "object",
"properties": {
"findings": {
"type": "array",
"items": {
"type": "object",
"properties": {
"path": { "type": "string" },
"line": { "type": "integer" },
"severity": { "type": "string", "enum": ["critical", "warning", "suggestion"] },
"message": { "type": "string" }
},
"required": ["path", "line", "severity", "message"]
}
}
}
}'
REVIEW_JSON=$(claude -p "CLAUDE.mdのPRレビュー指示に従って以下の差分をレビューしてください:
$(cat diff.txt)" \
--output-format json \
--json-schema "$SCHEMA" \
--allowedTools "Read,Grep")
echo "json<<EOF" >> $GITHUB_OUTPUT
echo "$REVIEW_JSON" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Post inline comments
uses: actions/github-script@v7
env:
REVIEW_JSON: ${{ steps.review.outputs.json }}
with:
script: |
const findings = JSON.parse(process.env.REVIEW_JSON).findings || [];
const { data: pr } = await github.rest.pulls.get({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
});
for (const f of findings) {
const emoji = f.severity === "critical" ? "🚨" : f.severity === "warning" ? "⚠️" : "💡";
try {
await github.rest.pulls.createReviewComment({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: context.issue.number,
commit_id: pr.head.sha,
path: f.path,
line: f.line,
body: `${emoji} **${f.severity}**\n\n${f.message}`,
});
} catch (e) {
// diff範囲外の行はスキップ
console.log(`Skip: ${f.path}:${f.line}`);
}
}
推奨構成(Step 2)からの変更点:
| 変更 | 理由 |
|---|---|
Post comment → Post inline comments に置き換え |
PRコメント1件から、ファイルの特定行へのインラインコメントに変わる |
Delete previous review comments ステップを削除 |
インラインコメントは蓄積しても問題ないため不要 |
--output-format json --json-schema を追加 |
構造化されたJSONで指摘を受け取り、行番号を取り出せる |
インラインコメントはdiffに含まれる変更行にしか投稿できないため、変更されていない文脈行の行番号が返ってきた場合は try/catch でスキップしている。
よくある問題
ジョブがハングして終了しない
-p フラグを付け忘れている。-p なしでは入力待ちになりタイムアウトまで終了しない。
git diff が空になる
fetch-depth: 0 を設定していない場合、シャロークローンのためベースブランチとの差分が取れない。
インラインコメントが 422 Unprocessable Entity になる
指定した行番号がdiff範囲外のとき発生する。try/catch でスキップする処理が必要。
まとめ
- 最小構成:
pr-review.yml1ファイルで動き始める。-pフラグとfetch-depth: 0が必須 - 推奨構成:サイズスキップ・CLAUDE.mdレビュー指示・コメント重複防止を追加
- 発展:
--output-format json+--json-schemaでインラインコメントに変換できる
→ Claude Code CLIの詳細は Claude Code SDK を参照。