ankuro.dev
← ブログ一覧に戻る
GitHub Actions + Claude Code でPRレビューを自動化する——設定から運用まで
2026-04-06#Claude Code#GitHub Actions#CI/CD#自動化#AI

GitHub Actions + Claude Code でPRレビューを自動化する——設定から運用まで

PRレビューには「見落とし」が起きる。SQLインジェクションの穴、nullが来たときの未処理、命名の揺れ——レビュアーが疲れているとき、急いでいるときに漏れる。

この記事では、PRを開いた瞬間にClaudeが自動でレビューコメントを投稿する仕組みを作る。人間のレビュアーが見るころには定型的な指摘が出揃っているため、議論すべき設計上の問題に集中できるようになる。GitHub ActionsにClaude Code CLIを組み込む構成で、ワークフローファイル1つから始められる。

この記事の構成:

  1. 最小構成:とにかく動かす
  2. 推奨構成:実用レベルに育てる(サイズスキップ + レビュー指示の管理)
  3. 発展:インラインコメントに変換する

準備: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 commentPost 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.yml 1ファイルで動き始める。-p フラグと fetch-depth: 0 が必須
  • 推奨構成:サイズスキップ・CLAUDE.mdレビュー指示・コメント重複防止を追加
  • 発展--output-format json + --json-schema でインラインコメントに変換できる

→ Claude Code CLIの詳細は Claude Code SDK を参照。