Claude Media
Claude CodeでCIのテスト失敗を自動修正 — claude -pで修正PRを作る

Claude CodeでCIのテスト失敗を自動修正 — claude -pで修正PRを作る

CIで落ちたテストのログ末尾をclaude -pに渡し、修正をPRとして出すGitHub Actionsの構成です。dontAskとallowedToolsの権限、再実行で直ったかを確かめる関門まで書きます。

CIでテストが落ちたら、失敗ログの末尾だけを claude -p に渡して原因を直させ、修正は新しいブランチのPRとして出す。この流れはGitHub Actionsのワークフロー1本で組めます。要になるのは3点です。Claudeに渡す権限を絞ること、ワークフロー自身がテストを再実行して直ったかを判定すること、main へは直接pushしないことです。

Claude CodeをGitHub Actionsに組み込むではaction v1の導入を扱っています。ここでは「テストが落ちたときだけ動く修正ジョブ」の構成に絞ります。

失敗から修正PRまでの流れ

手順

テスト失敗から修正PRまで

  1. 1

    テストを実行してログを残す

    npm test の出力を test.log に書き出し、失敗してもジョブを止めません。

  2. 2

    失敗時だけログ末尾を渡す

    tail -n 150 test.log を claude -p の標準入力に流します。

  3. 3

    許可したツールだけで修正させる

    読み取り、src/ 配下の編集、テスト再実行の3つだけを許可します。

  4. 4

    ワークフローがテストを再実行する

    Claudeの「直りました」は信用せず、同じテストコマンドを自分で走らせます。

  5. 5

    通ったときだけ修正PRを出す

    新しいブランチへpushし、main 向けのPRを作ります。

claude -p は非対話で動くモードで、成功すれば終了コード0、失敗すれば非0で終わります。標準入力も読むので、ビルドログをパイプで渡す使い方は公式のheadless解説にもあります。

ワークフローの全体像

次のYAMLは、main へのpushでテストを回し、失敗したときだけ修正ジョブ相当のステップが動く構成の一例です。パスやコマンドはプロジェクトに合わせて読み替えます。

name: test-and-autofix
on:
  push:
    branches: [main]
  workflow_dispatch:
 
permissions:
  contents: write
  pull-requests: write
 
jobs:
  test:
    runs-on: ubuntu-latest
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v6
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
 
      - name: テスト
        id: test
        continue-on-error: true
        run: |
          set -o pipefail
          npm test 2>&1 | tee test.log
 
      - name: Claudeに修正させる
        if: steps.test.outcome == 'failure'
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        run: |
          npm install -g @anthropic-ai/claude-code
          tail -n 150 test.log | claude --bare -p \
            "標準入力は失敗したテストのログ末尾です。原因をsrc配下で直し、npm testで確認してください。" \
            --permission-mode dontAsk \
            --allowedTools "Read" "Edit(src/**)" "Bash(npm test)" "Bash(npm test *)" \
            --max-turns 15
 
      - name: 直ったかを再実行で確かめる
        id: verify
        if: steps.test.outcome == 'failure'
        continue-on-error: true
        run: npm test
 
      - name: 修正PRを作る
        if: steps.verify.outcome == 'success'
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          git config user.name "autofix-bot"
          git config user.email "autofix-bot@users.noreply.github.com"
          BRANCH="autofix/${GITHUB_RUN_ID}"
          git switch -c "$BRANCH"
          git add -A -- src
          git commit -m "fix: CIで失敗したテストの自動修正"
          git push origin "$BRANCH"
          gh pr create --base main --head "$BRANCH" \
            --title "fix: テスト失敗の自動修正 (run ${GITHUB_RUN_ID})" \
            --body "失敗ログ末尾をClaudeに渡して修正し、再実行で通過を確認しました。レビューしてからマージします。"
 
      - name: 元のテスト失敗を赤のまま残す
        if: steps.test.outcome == 'failure'
        run: exit 1

最後のステップは、修正PRができても元のコミットのCIを赤のまま残すためのものです。直したのはPRの中身であって、main のコミットは失敗したままだからです。

ログは末尾だけ渡す

標準入力には上限があり、公式は10MBとしています。超えると非0で終了するので、巨大なログを丸ごと渡す構成は避けます。末尾150行程度なら上限に当たる心配はなく、トークンも節約できます。

末尾を切るときは、テストランナーが失敗の要約を最後にまとめて出すかどうかを確認します。JestやPytestは最後に失敗一覧を出しますが、並列実行の出力が混ざるランナーでは、要約より前に原因が流れている場合があります。そのときは tail の行数を増やすか、失敗したテスト名で grep -A 30 して渡します。

渡すのは「ログ」と「やってほしいこと」の2つだけにします。ファイル名を推測させる指示は要りません。Claude自身が Read でソースを開けます。

権限は「許可した分だけ」にする

非対話の claude -p は、権限モードを指定しないとManualモードで始まります(Claude Code v2.1.302以降)。承認が必要な呼び出しは、許可ルールやフックが無ければ拒否されます。無人のCIでは都合のよい既定ですが、何も許可しなければ何も直せません。

CIで使える設定は主に3つあります。

設定動きこのジョブでの扱い
--permission-mode dontAsk + --allowedTools動き許可リストにある呼び出しだけ実行し、残りは拒否このジョブでの扱い採用。許可の範囲が一目で分かる
--permission-mode auto動き分類器が各操作を審査するこのジョブでの扱い許可リストを書かずに済むが、通る操作が読みにくい
--dangerously-skip-permissions動き確認を全て省くこのジョブでの扱いコンテナなど隔離環境が前提。この用途では避ける

dontAsk は、承認が必要な呼び出しをすべて拒否するモードです。読み取り専用のコマンドなど、承認不要のものは動きます。--allowedTools に挙げた分も動くので、許可リストがそのまま権限の全体になります。

許可リストの書き方で効く点が3つあります。

  • Edit(src/**) で編集範囲を絞る: パスを付けた Edit ルールは、その配下だけに効きます。テストファイルや .github/ を許可しなければ、Claudeがテスト側を書き換えて通す近道を塞げます
  • Bash は裸で許可しない: Bash(npm test) のようにコマンド単位で書きます。末尾に空白と * を付けた Bash(npm test *) は前方一致で、npm test の後に引数が付く呼び出しを通します
  • git は許可しない: コミットとpushは後続のステップに任せ、Claudeの権限には含めません

--max-turns でターン数の上限も付けます。上限に達すると非0で終了し、後続の再実行ステップが走らないのでPRは出ません。

「直ったか」はワークフロー側で確かめる

Claudeが npm test を実行して通ったと報告しても、その結果は次のどちらかです。本当に直ったか、Claudeが見た環境でたまたま通ったかです。そこで、claude のステップとは別に、同じコマンドをワークフローが再実行します。

上のYAMLでは、再実行ステップの結果(steps.verify.outcome)が success のときだけPR作成に進みます。失敗すればPRは出ず、ログに失敗が残るだけです。

もう1つの関門が、変更の範囲です。git add -A -- src で src/ だけをステージするので、Claudeが許可外のファイルを何かの経路で変えても、コミットには含まれません。差分が空だと git commit が失敗してジョブが止まります。「直す対象が無かった」ことが赤いステップとして見えます。

mainに入れず、PRで人に渡す

修正はブランチ autofix/<run id> にpushし、main 向けのPRとして出します。マージは人間が決めるので、Claudeの修正が誤っていても main は汚れません。ワークフローに必要なのは contents: write と pull-requests: write で、公式のGitHub Actionsの例も同じ権限を使っています。

落とし穴

GITHUB_TOKENで作ったPRではCIが走らない

GitHubは、既定の GITHUB_TOKEN で行ったコミットからはワークフローを起動しません。公式のトラブルシューティングにも同じ注意があります。この構成で出した修正PRには、テストのワークフローが自動では走らない点に注意します。PRにもCIを走らせたいときは、GitHub Appのトークンなど別の認証を使います。このジョブはPR作成の前に自前で再実行しているので、最低限の確認は済んでいます。

自動修正が自動修正を呼ぶループにも注意が要ります。修正PRのマージ後にまた main で失敗すると、次の修正PRが出ます。最初は workflow_dispatch で手動起動だけにして、挙動を見てから push トリガーを足すと安全です。

action v1で組むときの違い

同じ流れは anthropics/claude-code-action@v1 でも組めます。prompt を渡すと自動実行モードになり、コメントのメンションを待たず動きます。権限は claude_args に --allowedTools として書きます。

ただし、自動実行モードのアクションは、起動したユーザーが人間かどうかも検査します。ボットが起動した実行は、allowed_bots に載せない限り拒否されます。schedule 起動の実行も、GitHubが最後に cron を編集したユーザーの実行として扱うため、その人がボットなら対象です。

観点claude -p を直接呼ぶclaude-code-action@v1
認証claude -p を直接呼ぶANTHROPIC_API_KEY を環境変数で渡すclaude-code-action@v1anthropic_api_key かOAuthトークン、GitHub App
権限の指定claude -p を直接呼ぶCLI引数claude-code-action@v1claude_args 内のCLI引数
PR作成claude -p を直接呼ぶワークフローが gh で行うclaude-code-action@v1Claudeに任せる設計(Claude側にGitHubの書き込み手段が要る)
ボット起動claude -p を直接呼ぶ制限なしclaude-code-action@v1allowed_bots で許可が必要

細かい違いはGitHub Actionsのパラメータとclaude_argsの渡し方にまとめています。この記事のように「渡す権限をワークフローの外側で固定したい」ジョブでは、直接呼ぶ構成のほうが何が起きるか読み取りやすくなります。

プロジェクトの規約は--bareでは読まれない

公式はCIでは --bare を勧めています。ユーザーごとの ~/.claude や .mcp.json に左右されず、どのマシンでも同じ結果になるからです。ただし --bare はCLAUDE.mdも自動では読みません。「テストは変えない」「依存を足さない」といったリポジトリの規約をCLAUDE.mdに書いていても、このジョブには届かない点に注意します。

規約を渡すには、--append-system-prompt-file でファイルを指定します。CI専用の短い規約ファイルを用意するのが扱いやすい形です。

# .github/autofix-rules.md
- 修正は src/ 配下だけで行う
- テストの期待値を変えて通さない。実装側を直す
- 依存パッケージを追加しない
- 原因が分からないときは変更せず、その旨だけ書いて終了する
          tail -n 150 test.log | claude --bare -p \
            "標準入力は失敗したテストのログ末尾です。直してnpm testで確認してください。" \
            --append-system-prompt-file .github/autofix-rules.md \
            --permission-mode dontAsk \
            --allowedTools "Read" "Edit(src/**)" "Bash(npm test)" "Bash(npm test *)"

規約の最後の行は、直せないときの出口です。出口が無いと、通すために無関係な箇所を触りに行く方向へ流れやすくなります。許可リストで塞ぐ壁と、規約で示す方針の両方を置く形です。

この構成に向く失敗と向かない失敗

どんな失敗でも自動修正に向くわけではありません。ログ末尾を渡す設計は、原因がログに写っている失敗に合います。

失敗の種類向き不向き理由
型エラー、未定義の変数、import漏れ向き不向き向く理由スタックトレースに場所が出る
直前のコミットで壊れた単体テスト向き不向き向く理由差分が小さく、原因が src/ 内に収まる
実行のたびに結果が変わる不安定なテスト向き不向き向かない理由再実行で通るだけの「修正」がPRに混ざる
外部サービスや環境変数に依存する失敗向き不向き向かない理由直す場所が src/ の外にあり、編集範囲の許可外になる

不安定なテストは、ワークフロー側の再実行ステップがかえって誤判定を招きます。直していないのに2回目で通れば、PRが出てしまうからです。再実行を2回続けて通すことを条件にするか、不安定なテストは別の対処に回すのが現実的です。

よくあるつまずき

切り分け

動かないときの確認先

  • 認証エラーになる

    --bare ではOAuthの資格情報やキーチェーンを読みません。APIキーを使うなら環境変数 ANTHROPIC_API_KEY に入れ、ステップの env: で渡します。

  • 何も直らず終わる

    許可リストが足りず、Edit や npm test が拒否されていることが多いです。--output-format stream-json を付けると、拒否された呼び出しが permission_denied として出ます。

  • forkのPRで動かない

    公開リポジトリでは、fork元のPRにはsecretsが渡りません。この構成を pull_request で回すなら、同一リポジトリのブランチのPRに限る前提になります。

  • 費用が読めない

    --max-turns と timeout-minutes で上限を付けます。--output-format json にすると、応答に total_cost_usd が入ります。

autofix-prとの使い分け

Claude Code自身にも、PRのCI失敗を監視して自動修正する組込みの機能があります。仕組みと動かない原因はClaude Code autofix-prとはに分けて書いています。

観点組込みのauto-fixこの記事のワークフロー
起動組込みのauto-fixPRごとに有効化するこの記事のワークフローワークフローのトリガーで決める
対象組込みのauto-fixPRのCI失敗とレビューコメントこの記事のワークフロー自分が決めたテストコマンドの失敗
権限と手順組込みのauto-fix製品側が決めるこの記事のワークフローYAMLで全て固定できる
向く場面組込みのauto-fixPRの面倒を任せたいこの記事のワークフロー手順と権限を監査可能にしたい

テストを書く側の進め方はClaude Codeでテスト駆動開発を回す手順にあります。

まとめ

失敗したテストの自動修正は、Claudeに任せる範囲を「ログ末尾を読み、src/ を直し、テストを回す」までに限ると組みやすくなります。コミット、push、PR作成、そして直ったかの最終判定はワークフローの側に置きます。こうすると、Claudeの判断が誤っても、損なわれるのは新しいブランチのPRだけです。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/github-actions
  2. code.claude.com/docs/en/headless
  3. code.claude.com/docs/en/permission-modes
  4. code.claude.com/docs/en/permissions
この記事を共有:XはてブLinkedIn