Agentive
自動化ラボ

GitHub Actions × AI:CI/CDパイプラインにAIを組み込む実践ガイド

約9分で読めます

PRを出すたびに手動でレビューし、テストを書き、READMEを更新する。個人開発ではこの繰り返しが地味にきつい。GitHub ActionsにAIを組み込めば、この三つを自動化できる。本記事では実際に動くワークフローYAMLを示しながら、導入手順を解説する。

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

コードレビュー、テスト生成、ドキュメント更新はどれも「コードを読んで判断・生成する」タスクだ。LLMが最も得意とする領域と一致する。

GitHub Actionsに組み込む利点は三つある。

  1. PR単位で自動実行: 人間がトリガーを意識しなくていい
  2. 既存パイプラインに追加するだけ: インフラを新たに立てる必要がない
  3. コスト管理がしやすい: APIコールはPRイベント発生時のみなので青天井にならない

使うAPIはAnthropicのClaude APIだが、OpenAI APIでもロジックはほぼ同じだ。

ユースケース1:PRへのAIコードレビュー

何をやるか

PRがオープン・更新されたとき、差分をAIに送り、改善点・バグリスク・セキュリティ懸念をコメントとして返す。

ワークフローYAML

# .github/workflows/ai-code-review.yml
name: AI Code Review

on:
  pull_request:
    types: [opened, synchronize]

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

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

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

      - name: Run AI review
        if: steps.diff.outputs.diff_size != '0'
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR_NUMBER: ${{ github.event.pull_request.number }}
          REPO: ${{ github.repository }}
        run: |
          DIFF=$(cat /tmp/diff.txt | head -c 12000)

          # 注意: DIFFに含まれるダブルクォートや改行等の特殊文字がJSONを破壊する可能性がある。
          # 本番運用ではjqやpython等でJSONを安全に組み立てること。
          # 例: jq -n --arg diff "$DIFF" '{model:"claude-3-5-haiku-20241022",...}'
          RESPONSE=$(curl -s https://api.anthropic.com/v1/messages \
            -H "x-api-key: $ANTHROPIC_API_KEY" \
            -H "anthropic-version: 2023-06-01" \
            -H "content-type: application/json" \
            -d "{
              \"model\": \"claude-3-5-haiku-20241022\",
              \"max_tokens\": 1024,
              \"messages\": [{
                \"role\": \"user\",
                \"content\": \"以下のgit diffをレビューしてください。バグリスク、セキュリティ懸念、改善点を箇条書きで日本語で返してください。問題がなければ '特に問題なし' とだけ返してください。\n\n${DIFF}\"
              }]
            }")

          COMMENT=$(echo "$RESPONSE" | python3 -c "
          import json, sys
          data = json.load(sys.stdin)
          print(data['content'][0]['text'])
          ")

          gh pr comment $PR_NUMBER --repo $REPO --body "## AI コードレビュー

          ${COMMENT}

          *このコメントはAIによって自動生成されました*"

AIレビューの精度とコストを両立させる設計

差分サイズの制限: head -c 12000 で入力トークンを抑える。大きなPRは自動でスキップされるが、それで十分だ。小さなPRほどAIレビューが刺さる。

モデル選択: レビューには claude-3-5-haiku-20241022 を使っている。速くて安い。OpusやSonnetは精度は高いが、毎PRで呼ぶには高コストすぎる。

permissions: pull-requests: write: PRへのコメント書き込みに必要。忘れると403エラーになる。

プロンプト設計: 「問題がなければ ‘特に問題なし’ とだけ返す」と明示しておくことで、過剰なコメントを防げる。

ユースケース2:AIによるテスト自動生成

何をやるか

PRで変更されたソースファイルを検出し、そのファイルに対するテストコードのドラフトをPRコメントとして出力する。そのままペーストして使えるレベルを目標にする。

ワークフローYAML

# .github/workflows/ai-test-gen.yml
name: AI Test Generation

on:
  pull_request:
    types: [opened]
    paths:
      - 'src/**/*.ts'
      - 'src/**/*.js'
      - 'lib/**/*.py'

jobs:
  generate-tests:
    runs-on: ubuntu-latest
    permissions:
      pull-requests: write
      contents: read

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

      - name: Get changed source files
        id: files
        run: |
          CHANGED=$(git diff --name-only origin/${{ github.base_ref }}...HEAD \
            | grep -E '\.(ts|js|py)$' \
            | grep -v '\.test\.' \
            | grep -v '\.spec\.' \
            | head -3)
          echo "files<<EOF" >> $GITHUB_OUTPUT
          echo "$CHANGED" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

      - name: Generate tests
        if: steps.files.outputs.files != ''
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PR_NUMBER: ${{ github.event.pull_request.number }}
          REPO: ${{ github.repository }}
        run: |
          COMMENT_BODY="## AI テスト生成ドラフト\n\n"

          while IFS= read -r FILE; do
            [ -z "$FILE" ] && continue
            [ ! -f "$FILE" ] && continue

            CONTENT=$(cat "$FILE" | head -c 8000)
            LANG=$(echo "$FILE" | sed 's/.*\.//')

            RESPONSE=$(curl -s https://api.anthropic.com/v1/messages \
              -H "x-api-key: $ANTHROPIC_API_KEY" \
              -H "anthropic-version: 2023-06-01" \
              -H "content-type: application/json" \
              -d "{
                \"model\": \"claude-3-5-haiku-20241022\",
                \"max_tokens\": 2048,
                \"messages\": [{
                  \"role\": \"user\",
                  \"content\": \"以下の${LANG}コードに対するユニットテストを生成してください。正常系・異常系・境界値を含めてください。テストコードのみ返してください。\n\n\`\`\`${LANG}\n${CONTENT}\n\`\`\`\"
                }]
              }")

            TEST_CODE=$(echo "$RESPONSE" | python3 -c "
          import json, sys
          data = json.load(sys.stdin)
          print(data['content'][0]['text'])
          ")

            COMMENT_BODY="${COMMENT_BODY}### \`${FILE}\`\n\n${TEST_CODE}\n\n---\n\n"
          done <<< "${{ steps.files.outputs.files }}"

          printf "%b" "$COMMENT_BODY" > /tmp/comment.txt

          gh pr comment $PR_NUMBER --repo $REPO --body-file /tmp/comment.txt

テスト生成の対象選定とコスト制御

対象ファイルの絞り込み: .test..spec. を含むファイルを除外している。テストファイル自体のテストを生成しても意味がない。head -3 で最大3ファイルに限定してコストを抑える。

paths フィルタ: ソースファイルが変更されていないPR(ドキュメントのみの変更など)ではジョブ自体を起動しない。無駄なAPI呼び出しを防ぐ。

生成物の扱い: あくまでドラフトとしてコメントに出力し、自動コミットはしない。AIが生成したテストをレビューなしでリポジトリに入れるのはリスクが高い。確認してから手動でペーストする運用が現実的だ。

言語検出: 拡張子から言語を判定してプロンプトに渡す。TypeScriptとPythonでは慣習的なテストの書き方が異なるため、これだけで生成品質が上がる。

ユースケース3:AIによるドキュメント自動更新

何をやるか

mainブランチへのマージ後、変更されたソースファイルを元にREADMEの該当セクションを更新するPRを自動作成する。

ワークフローYAML

# .github/workflows/ai-doc-update.yml
name: AI Doc Update

on:
  push:
    branches: [main]
    paths:
      - 'src/**'
      - 'lib/**'

jobs:
  update-docs:
    runs-on: ubuntu-latest
    permissions:
      contents: write
      pull-requests: write

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 2

      - name: Get changed files
        id: changed
        run: |
          CHANGED=$(git diff HEAD~1 --name-only | grep -E '\.(ts|js|py)$' | head -5)
          echo "files<<EOF" >> $GITHUB_OUTPUT
          echo "$CHANGED" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

      - name: Generate doc update
        if: steps.changed.outputs.files != ''
        id: docgen
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          CHANGED_FILES="${{ steps.changed.outputs.files }}"
          README=$(cat README.md | head -c 4000)
          DIFF=$(git diff HEAD~1 HEAD -- $CHANGED_FILES | head -c 8000)

          RESPONSE=$(curl -s https://api.anthropic.com/v1/messages \
            -H "x-api-key: $ANTHROPIC_API_KEY" \
            -H "anthropic-version: 2023-06-01" \
            -H "content-type: application/json" \
            -d "{
              \"model\": \"claude-3-5-haiku-20241022\",
              \"max_tokens\": 2048,
              \"messages\": [{
                \"role\": \"user\",
                \"content\": \"以下のREADMEとコード変更差分を見て、READMEで更新が必要な箇所を特定し、更新後のMarkdownテキストを返してください。README全体を返す必要はなく、変更が必要なセクションのみ返してください。変更が不要なら '更新不要' とだけ返してください。\n\n## 現在のREADME\n${README}\n\n## コード変更差分\n${DIFF}\"
              }]
            }")

          DOC_UPDATE=$(echo "$RESPONSE" | python3 -c "
          import json, sys
          data = json.load(sys.stdin)
          print(data['content'][0]['text'])
          ")

          echo "$DOC_UPDATE" > /tmp/doc_update.txt
          echo "update_needed=$([ "$DOC_UPDATE" != '更新不要' ] && echo 'true' || echo 'false')" >> $GITHUB_OUTPUT

      - name: Create PR with doc update
        if: steps.docgen.outputs.update_needed == 'true'
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          BRANCH="ai-doc-update-$(date +%Y%m%d-%H%M%S)"
          git config user.name "github-actions[bot]"
          git config user.email "github-actions[bot]@users.noreply.github.com"
          git checkout -b $BRANCH

          # AIが提案した更新内容をREADMEに追記(末尾に提案として追加)
          echo "" >> README.md
          echo "<!-- AI Doc Update Suggestion -->" >> README.md
          cat /tmp/doc_update.txt >> README.md

          git add README.md
          git commit -m "docs: AI suggested README update"
          git push origin $BRANCH

          gh pr create \
            --title "docs: AI によるREADME更新提案" \
            --body "$(cat <<'PRBODY'
          mainへのマージ後にAIが生成したドキュメント更新提案です。

          内容を確認・編集してからマージしてください。不要なら閉じて構いません。

          *このPRはAIによって自動生成されました*
          PRBODY
          )" \
            --base main \
            --head $BRANCH

ドキュメント更新PRの安全な運用設計

更新をPRとして出す: ドキュメントを直接mainに書き込まない。AIの提案は必ず人間がレビューするフローを維持する。

fetch-depth: 2: HEAD~1 との差分を取るために必要。デフォルトの fetch-depth: 1 では直前のコミットが取得できずエラーになる。

コスト管理のための入力制限: READMEは4000文字、差分は8000文字に絞っている。それ以上は無視するが、実用上は十分だ。大規模なモノレポには向かないが、個人プロジェクト規模なら問題ない。

コスト試算

個人開発での実際のコストを示す。モデルは claude-3-5-haiku-20241022 を前提とする。

ユースケース入力トークン目安出力トークン目安1回あたりコスト
AIコードレビュー~3,000~500~$0.003
テスト生成(3ファイル)~6,000~1,500~$0.008
ドキュメント更新~4,000~1,000~$0.005

月30PRを出すプロジェクトで全機能を使ったとしても、月$0.5未満に収まる計算だ。

コストを抑える主なコントロールポイントは二つだ。

モデルの使い分け: Haikuで十分な精度が出ることがほとんどだ。コードが複雑で精度を上げたい場合だけSonnetに切り替える。

paths フィルタの活用: 関係ないファイルが変更されたときにジョブを起動しない。設定ファイルやドキュメントのみの変更でAI系ジョブを走らせる必要はない。

Secrets管理

ANTHROPIC_API_KEY はリポジトリのSettings > Secrets and variables > ActionsにRepository secretsとして登録する。

プライベートリポジトリなら問題ないが、パブリックリポジトリでは注意が必要だ。forkされたリポジトリからのPRでは、デフォルトでSecretsが渡されない(pull_request_target を使えば渡せるが、セキュリティリスクを伴う)。パブリックリポジトリでAIレビューを使いたい場合は、pull_request_target の挙動を十分理解した上で設定すること。

まとめと導入順序

三つのユースケースを紹介したが、いきなり全部入れる必要はない。導入の難易度と効果を考えると、以下の順序が現実的だ。

  1. まずAIコードレビュー: 最もシンプルで効果がわかりやすい。差分を渡してコメントを返すだけで、すぐ動く。
  2. 次にテスト生成: テストが薄いプロジェクトで即効性がある。出力はドラフトとして使い、品質は自分で判断する。
  3. 最後にドキュメント更新: PRを自動作成するため、設定が少し複雑になる。リポジトリの運用が安定してから導入する。

CI/CDパイプラインにAIを組み込むことで、レビュー・テスト・ドキュメントの三点セットが自動化できる。ただし、AIの出力を無条件に信頼しないことが前提だ。コメントやPRとして提案し、人間が最終判断する設計を維持することで、品質とスピードを両立できる。

関連記事

A

Agentive 編集部

AIエージェントを実際に使い倒す個人開発者。サイト制作の自動化を実践しながら、その知見を発信しています。