Claude Code × GitHub Actions でCI/CDを賢くする——AIレビュー・自動修正・コスト管理の実装パターン

Claude Code × GitHub Actions でCI/CDを賢くする——AIレビュー・自動修正・コスト管理の実装パターン

Claude Code と GitHub Actions を連携してCI/CDパイプラインにAIを組み込む実践ガイド。PRレビュー自動化・テスト失敗の自動分析・コスト最適化まで、settings.jsonとワークフローファイルを丸ごと公開。

エンジニアのゆとです。

Claude Codeをローカルで使っている人は多いと思うけど、CI/CDに組み込んでいる人はまだ少ない印象がある。

僕は3ヶ月前から GitHub Actions のワークフローにClaude Codeを差し込んでいる。PRのレビュー自動化・テスト失敗の原因分析・Lintエラーの自動修正が主な用途で、「CIを通すためだけに何往復もする」みたいな時間がかなり減った。

この記事ではその実装を全部公開する。トークンコストの話もきちんとする。


なぜCI/CDにClaude Codeを入れるのか

ローカルのClaude Codeとの違いを先に整理しておく。

用途ローカルCCCI上のCC
インタラクティブ作業向いている向いていない
PR全体のレビュー手間がかかる自動化できる
テスト失敗の分析その都度手動自動でissueに貼れる
コード修正のpush手動ブランチにpushできる
コスト管理個人の裁量Secrets + Budgetで制御

CI上ではインタラクティブ性は不要で、「決まったタスクを確実にこなす」用途がメインになる。

そこでClaude Codeの --print モード(非対話型)と Anthropic SDK を組み合わせるアーキテクチャが現実的になる。


前提環境

  • GitHub Actions(標準ランナーで動く)
  • Anthropic API キー(ANTHROPIC_API_KEY をRepositoryのSecretsに登録)
  • Node.js or Python(SDKを使う場合)

Claude CodeをそのままCI上で動かす方法と、Anthropic SDKをスクリプト経由で呼ぶ方法の2通りある。シンプルさで言えば後者の方が安定しやすい。


パターン1: PR差分をAIにレビューさせる

最初にやる価値が高いのはこれ。

PRが開かれたタイミングで差分を取ってClaudeに食わせ、コメントをPRに投稿する。

ワークフローファイル

.github/workflows/ai-review.yml:

name: AI Code Review

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  ai-review:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
      contents: read

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Get PR diff
        id: diff
        run: |
          git diff origin/${{ github.base_ref }}...HEAD > pr_diff.txt
          echo "lines=$(wc -l < pr_diff.txt)" >> $GITHUB_OUTPUT

      - name: Skip if diff too large
        if: steps.diff.outputs.lines > 800
        run: |
          echo "差分が大きすぎるためAIレビューをスキップ(${{ steps.diff.outputs.lines }}行)"
          exit 0

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: '3.12'

      - name: Install dependencies
        run: pip install anthropic

      - name: Run AI review
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: python .github/scripts/ai_review.py

      - name: Post review comment
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            const review = fs.readFileSync('ai_review_result.md', 'utf8');
            github.rest.issues.createComment({
              issue_number: context.issue.number,
              owner: context.repo.owner,
              repo: context.repo.repo,
              body: review
            });

レビュースクリプト

.github/scripts/ai_review.py:

import anthropic
import os

diff = open('pr_diff.txt').read()

client = anthropic.Anthropic()

prompt = f"""以下のPR差分をコードレビューしてください。

## レビューの観点
1. バグや論理的な誤りはないか
2. セキュリティ上の問題点(特に入力バリデーション、認証系)
3. パフォーマンスへの影響
4. 可読性・保守性の改善点
5. テストが不足していそうな箇所

## 出力形式
Markdown形式で。指摘がない場合は「特に問題なし」と書く。
重大な問題には⚠️、軽微な提案には💡、良い実装には✅ をつける。

## PR差分
""" + diff[:6000]

message = client.messages.create(
    model="claude-opus-4-5",
    max_tokens=2048,
    messages=[
        {"role": "user", "content": prompt}
    ],
    system="あなたはシニアエンジニアのコードレビュアーです。日本語で、具体的かつ建設的なフィードバックをしてください。"
)

with open('ai_review_result.md', 'w') as f:
    f.write("## 🤖 AIコードレビュー\n\n")
    f.write(message.content[0].text)
    f.write("\n\n---\n*このレビューはClaude (Anthropic)によって自動生成されました。最終判断は人間のレビュアーが行ってください。*")

差分が800行を超えるとスキップする条件を入れているのは、コスト爆発を防ぐため。大きなPRはそもそも分割すべきというメッセージも込めている。


パターン2: テスト失敗の原因をAIが分析してPRにコメント

テストが落ちた時の「なぜ落ちたか」をAIに分析させて、PRに自動コメントする。

name: Test with AI Analysis

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write

    steps:
      - uses: actions/checkout@v4

      - name: Run tests
        id: tests
        run: |
          npm test 2>&1 | tee test_output.txt
          echo "exit_code=${PIPESTATUS[0]}" >> $GITHUB_OUTPUT
        continue-on-error: true

      - name: AI failure analysis
        if: steps.tests.outputs.exit_code != '0'
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          python3 << 'EOF'
          import anthropic, os

          output = open('test_output.txt').read()[-4000:]  # 末尾4000文字
          client = anthropic.Anthropic()
          msg = client.messages.create(
              model="claude-haiku-4-5",  # 分析だけなのでHaikuで十分
              max_tokens=1024,
              messages=[{"role": "user", "content": f"テスト失敗のログを分析して、原因と修正方針を簡潔に教えてください:\n\n{output}"}]
          )
          analysis = msg.content[0].text
          with open('failure_analysis.md', 'w') as f:
              f.write(f"## ⚠️ テスト失敗の分析\n\n{analysis}")
          EOF

      - name: Comment on PR
        if: steps.tests.outputs.exit_code != '0' && github.event_name == 'pull_request'
        uses: actions/github-script@v7
        with:
          script: |
            const fs = require('fs');
            if (fs.existsSync('failure_analysis.md')) {
              const analysis = fs.readFileSync('failure_analysis.md', 'utf8');
              github.rest.issues.createComment({
                issue_number: context.issue.number,
                owner: context.repo.owner,
                repo: context.repo.repo,
                body: analysis
              });
            }

テスト分析はHaikuを使うのがポイント。分析タスクはOpusやSonnetを使わなくてもHaikuで十分な精度が出る。コストが1/10以下になる。


パターン3: Lintエラーの自動修正をブランチにpush

これは少し攻めた設定。Lintが落ちたら自動修正してcommitする。

name: Auto Lint Fix

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  lint-fix:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write

    steps:
      - uses: actions/checkout@v4
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          ref: ${{ github.head_ref }}

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '20'

      - run: npm ci

      - name: Run lint
        id: lint
        run: npx eslint . --format json > lint_result.json 2>&1 || true

      - name: AI fix lint errors
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          python3 << 'EOF'
          import anthropic, json, os, subprocess

          with open('lint_result.json') as f:
              results = json.load(f)

          # エラーがある最初の3ファイルだけ修正(コスト制御)
          files_with_errors = [r for r in results if r['errorCount'] > 0][:3]

          if not files_with_errors:
              print("Lintエラーなし")
              exit(0)

          client = anthropic.Anthropic()

          for file_result in files_with_errors:
              filepath = file_result['filePath']
              errors = file_result['messages']
              
              if not os.path.exists(filepath):
                  continue
                  
              code = open(filepath).read()
              error_desc = '\n'.join([f"L{e['line']}: {e['message']} ({e['ruleId']})" for e in errors])
              
              prompt = "以下のファイルのLintエラーを修正してください。\n\nエラー一覧:\n" + error_desc + "\n\nファイル内容:\n" + code + "\n\n修正済みのファイル全体をコードブロックで返してください。"

              msg = client.messages.create(
                  model="claude-haiku-4-5",
                  max_tokens=4096,
                  messages=[{
                      "role": "user",
                      "content": prompt
                  }]
              )
              
              response = msg.content[0].text
              # コードブロックを抽出
              import re
              match = re.search(r'```(?:\w+)?\n(.*?)```', response, re.DOTALL)
              if match:
                  with open(filepath, 'w') as f:
                      f.write(match.group(1))
                  print(f"修正: {filepath}")
          EOF

      - name: Commit fixes
        run: |
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          if git diff --quiet; then
            echo "変更なし"
          else
            git add -A
            git commit -m "fix: AI auto-fix lint errors [skip ci]"
            git push
          fi

[skip ci] をコミットメッセージに入れているのは無限ループを防ぐため。自動pushがまた同じワークフローをトリガーしてしまう。


コスト管理の設計

CI上でAPIを使う場合、コストが青天井になりやすい。対策をいくつか。

月次予算アラート

Anthropic Consoleの「Budgets」でプロジェクト別に上限を設定できる(2026年4月時点でGA済み)。CIからのAPI呼び出しは専用のAPIキーを発行して、その分だけ予算を切るのが安全。

トークン使用量の記録

- name: Log token usage
  run: |
    # ai_review.pyの出力からusageを記録
    echo "$(date): PR #${{ github.event.number }}" >> .github/ai_usage.log

実際に3ヶ月運用してわかったコスト感:

ワークフロー使用モデル1回あたりコスト月間(50PR想定)
PRレビューclaude-opus-4-5$0.05〜0.15$2.5〜7.5
テスト分析claude-haiku-4-5$0.003〜0.01$0.15〜0.5
Lint修正claude-haiku-4-5$0.01〜0.05$0.5〜2.5

月$10〜15以内で収まる。PRレビューに人間が費やす時間(1PR=10分として500分/月)との費用対効果を考えると十分に安い。

実行頻度の制御

# 小さなコミットには走らせない
on:
  pull_request:
    paths:
      - 'src/**'
      - 'tests/**'
    # docs/のみの変更はスキップ

CLAUDE.md をCI環境向けに設定する

Claude Codeをそのままrunnerで動かす場合(非SDKアプローチ)は、プロジェクトの .claude/settings.json にCI専用の設定を入れる。

{
  "permissions": {
    "allow": [
      "Bash(git:*)",
      "Bash(npm:*)",
      "Bash(python:*)",
      "Read",
      "Write"
    ],
    "deny": [
      "Bash(curl:*)",
      "Bash(wget:*)"
    ]
  }
}

外部への通信を制限するのはセキュリティ上の基本。CI上ではネットワークアクセスが最小限になるように設計する。


実際に運用してわかった罠

罠1: 差分が大きいとLLMが混乱する

500行を超える差分をそのままClaudeに渡すと、後半の変更を無視するかコメントがふんわりになる。重要な変更だけに絞るか、ファイル単位で分割して呼ぶ方が精度が上がる。

罠2: .env ファイルをContextに混入させるな

git diff で差分を取る時、誤って .env の変更が含まれるケースがある。APIにシークレットが渡るリスクがあるので、差分からシークレット系ファイルを除外するフィルタを入れること。

# 差分のフィルタリング
EXCLUDE_PATTERNS = ['.env', 'secrets', 'credentials', '.pem']
lines = [l for l in diff.split('\n') 
         if not any(p in l for p in EXCLUDE_PATTERNS)]
罠3: Bot同士の無限コメントループ

AIのコメントにAIが返信する、みたいな状況になる可能性がある。ワークフローに if: github.actor != 'github-actions[bot]' を入れておく。


まとめ

Claude Code × GitHub Actions の組み合わせで現実的に使えるパターンは3つ:

  1. PRレビュー自動化 — Diffを渡してOpusにレビューさせる。週10PR以上あるなら費用対効果が出る
  2. テスト失敗分析 — Haikuで十分。落ちたログを食わせるだけ
  3. Lint自動修正 — 攻めた設定だが、機械的なミスを減らすには有効

コストは月$10〜15。人間がPRレビューにかける時間との比較でペイするかどうかは、チームの規模と開発ペースによる。

関連記事も参考に。

Claude Code HooksでGitワークフローを自動化する — commit前後の品質ゲートを実装する
Claude Code HooksでGitワークフローを自動化する — commit前後の品質ゲートを実装するClaude Code HooksをGitワークフローに組み込む実践ガイド。commitトリガーでのlint/test自動実行、危険コマンドのブロック、コミットメッセージの自動生成、git push前のセキュリティチェックなど、実際に動くコード例付きで解説。読む →
Claude Code × Batch APIで100件のタスクを並列処理する——コスト50%削減の実装パターン
Claude Code × Batch APIで100件のタスクを並列処理する——コスト50%削減の実装パターンAnthropic Batch APIをClaude Codeから活用する実践ガイド。単一リクエストと比べてコスト50%削減・処理時間1/10になるケースを実装コード付きで解説。エラーハンドリング・進捗監視・結果集約まで含めた本番向けパターン集。読む →
Claude Codeのサブエージェントと Agent Teams——どっちをいつ使うのか設計ロジックを整理した
Claude Codeのサブエージェントと Agent Teams——どっちをいつ使うのか設計ロジックを整理したClaude Codeのサブエージェント(Task tool)とAgent Teams(マルチセッション)の違いを設計レイヤで解説。タスクの粒度・コンテキスト分離・コスト効率で使い分ける判断基準。読む →
Claude Codeのパーミッションプロンプトを設計する——allowlistとsettings.jsonで確認頻度を最適化する
Claude Codeのパーミッションプロンプトを設計する——allowlistとsettings.jsonで確認頻度を最適化するClaude Codeのパーミッションプロンプトが頻繁に出て作業が止まる問題を解決する。settings.jsonのallowlist設定、プロジェクト別権限とグローバル権限の使い分け、危険コマンドは残しつつ安全なコマンドを通す設計パターンを実装例付きで解説。読む →
← 記事一覧に戻る