Claude CodeでactをGitHub Actionsのpush前検証に使う手順
actでworkflowをローカル実行し、終了コードとログをClaude Codeに返して直させる手順です。Stop hookの組み方と、actで再現できないOIDCやpermissionsの見分け方も扱います。
GitHub Actionsのworkflowは、pushしないと動かしてみられません。1行直してはpushし、赤くなっては直す。この往復を手元に引き寄せるのが、workflowをローカルのDockerコンテナで実行するツールのactです。
Claude Codeにworkflowを直させるなら、actの終了コードとログがそのまま検証ループになります。ただしactは、GitHub上の実行を完全には再現しません。この記事では、Stop hookでactを回して結果をClaude Codeへ返す構成と、act上の成功を信用してよい範囲を手順にします。
actで試せること、試せないこと
actは .github/workflows/ を読み、必要なDockerイメージを用意して、依存関係の順にジョブをコンテナで走らせます。環境変数とファイルシステムはGitHubに合わせて構成されます。コミットとpushを挟まずに、workflowの変更を確かめるための道具です。
手元で回す価値が高いのは、次のような失敗です。
run:のシェルスクリプトが途中で落ちる- ステップの順序や
needs:の取り違え - matrixの組み合わせごとの挙動
${{ }}式が空文字になって、コマンドが壊れる
逆に、actのドキュメントが「未対応」と明記している機能があります。ここを知らずに使うと、ローカルでは緑なのにGitHubで赤くなります。
| 機能 | actでの扱い |
|---|---|
permissions:(ジョブ単位) | actでの扱い無視される |
| OpenID Connect(OIDC)の取得先 | actでの扱いURLが定義されない |
concurrency: / run-name: | actでの扱い無視される |
environment: とデプロイ環境ごとのsecrets | actでの扱い無視される・未対応 |
GITHUB_STEP_SUMMARY への書き込み | actでの扱い破棄される |
timeout-minutes / continue-on-error(ジョブ単位) | actでの扱い無視される |
以降の手順は、この表を前提に組みます。
準備:Docker、act、.actrc
actはDocker Engine APIに依存します。macOSならDocker Desktop、LinuxならDocker Engineが要ります。podmanなどの別バックエンドは、公式が「動くかもしれないが保証しない」としています。
インストールはHomebrewが手軽です。
brew install act
act -lact -l は、実行対象のworkflowとジョブを一覧します。
既定のランナーイメージは、GitHubのホステッドランナーと同じ中身ではありません。ドキュメントも「意図的に不完全」と書いています。ubuntu-latest には、小さいほうから順にmicro・medium・largeの3段階のイメージが用意されています。GitHubのランナーに近い中身が要るときは、-P でフル版に差し替えます。
act -P ubuntu-latest=catthehacker/ubuntu:full-latest毎回の引数は、リポジトリ直下の .actrc に1行1引数で書けます。Claude Codeに長いコマンドを組ませずに済みます。
--pull=false
--action-offline-mode
-e .github/act/event.json--pull=false は、取得済みのイメージを使い回します。--action-offline-mode は、一度実行したactionをキャッシュから使います。どちらも、イメージやactionの取得にかかる待ちを減らすための設定です。
ローカル専用の目印をworkflowに入れる
ローカルで回したくないステップがあります。Claude Codeを呼ぶステップ、Slack通知、リリースのタグ付けなどです。actは実行中に環境変数 ACT を立てるので、ステップ単位なら次のように外せます。
- name: Claude Codeによるレビュー
if: ${{ !env.ACT }}
uses: anthropics/claude-code-action@v1ジョブ単位の if: では env コンテキストが使えません。その場合は、イベントファイルに独自のプロパティを持たせます。.github/act/event.json を作ります。
{ "act": true }ジョブ側は if: ${{ !github.event.act }} と書きます。イベントファイルを渡さない本番の実行では、この条件は常に真です。
claude-code-actionは、既定のGitHub App認証で id-token: write を要求します。OIDCの取得先がactでは定義されないので、このステップは外しておくのが前提です。ワークフロー全体の作り方はGitHub Actionsのワークフロー作成をClaude Codeで進める手順、Claude Code自身をworkflowに載せる側はClaude CodeをGitHub Actionsに組み込むにあります。
Stop hookでactの結果をClaude Codeへ返す
Claude Codeのhookは、コマンドの終了コードで振る舞いを変えます。Stop は「Claudeが応答を終えるとき」に発火し、終了コード2で抜けると標準エラーの内容がフィードバックとして渡され、Claudeは作業を続けます。
PostToolUseでファイルを保存するたびにactを走らせると、コンテナの起動が編集ごとに挟まります。actはイメージの用意からジョブの実行までを通すため、応答を終える直前に1回だけ回すStop hookが合います。静的検査のactionlintを編集ごとに回す構成はClaude Codeのactionlint活用にあり、両者は役割が違います。actionlintが書き間違いを落とし、actが実行時の失敗を落とします。
.claude/hooks/act-check.sh として保存します。
#!/bin/bash
INPUT=$(cat)
[ "$(echo "$INPUT" | jq -r '.stop_hook_active // false')" = "true" ] && exit 0
cd "$CLAUDE_PROJECT_DIR" || exit 0
# workflowに変更がなければ何もしない
git status --porcelain -- .github/workflows | grep -q . || exit 0
LOG=$(mktemp)
act push -W .github/workflows/ci.yml -j lint > "$LOG" 2>&1
CODE=$?
if [ "$CODE" -ne 0 ]; then
echo "act が終了コード $CODE で失敗しました。ログ末尾:" >&2
tail -n 40 "$LOG" >&2
exit 2
fiポイントは4つです。
stop_hook_activeがtrueなら即exit 0。前回のhookを受けて続行中のターンで、同じ検査を延々と繰り返さない- workflowが未変更なら何もしない。ふだんの会話をactで遅くしない
- ログ全体ではなく末尾40行だけを返す。Claude Codeのコンテキストを使い切らない
- 対象は
-Wと-jで1つのジョブに絞る。全ジョブをローカルで回すと時間もディスクも嵩む
Stop hookは、連続で8回ブロックすると上書きされます。公式のトラブルシュートにある上限で、収束しないループは8回で打ち切られます。登録は次のとおりです。
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/act-check.sh",
"timeout": 300
}
]
}
]
}
}timeout は秒単位で、commandタイプの既定は10分です。ジョブの実測に合わせて短めに置くと、固まったコンテナを待ち続けずに済みます。実行権限も忘れずに付けます。
chmod +x .claude/hooks/act-check.shテストの自動実行にStopを使う考え方はClaude Code hooksでテストを自動実行すると共通です。
シークレットを渡すとき、Claude Codeに見せない
workflowが secrets.* や GITHUB_TOKEN を使うなら、actに渡す必要があります。GITHUB_TOKEN はGitHubが自動で用意するものなので、ローカルでは自前で渡します。GitHub CLIが入っていれば、次の形が公式に示されています。
act -s GITHUB_TOKEN="$(gh auth token)"この書き方は、シェルの履歴にトークンが残ります。引数に値を書かず -s MY_SECRET とだけ書くと、同名の環境変数を使い、無ければ入力を促されます。ファイルで渡す --secret-file my.secrets は、.env と同じ書式です。
ファイルで渡す場合は、Claude Codeからの読み取りを permissions で拒否しておきます。actの実行だけは許可します。
{
"permissions": {
"allow": ["Bash(act *)"],
"deny": ["Read(./.secrets)", "Read(./.env)"]
}
}.secrets も .gitignore に入れておきます。Stop hookの中でシークレットを組み立てるときも、ログに値を出さない書き方にします。
CLAUDE.mdで直し方の作法を縛る
hookが返すのはログです。ログを受けて何をするかは、CLAUDE.mdで決めます。
## GitHub Actions
- workflowを編集したら、応答を終える前にactの結果が返る。失敗したらworkflow側を直す
- `if: ${{ !env.ACT }}` を足して失敗するステップを外さない。外してよいのは、人が承認した外部送信のステップだけ
- actが対応していない機能(OIDC、permissions、concurrency、environment)の挙動は、actの成功を根拠にしない。変更点を人に伝える
- 失敗の原因がactの環境差だと判断したら、直さずに理由を報告する2行目が肝です。赤を消す最短の手は、失敗するステップを !env.ACT で外すことです。これを禁じておかないと、検証ループは常に緑になります。3行目は、次の節の話につながります。
失敗がactの環境差かを見分ける
actが赤いとき、直すべきなのはworkflowなのか、actの環境なのか。ログを受けたClaude Codeにこの切り分けを任せる前に、人が基準を持っておくと迷いません。
| ログの症状 | 疑う場所 | 対応 |
|---|---|---|
command not found(jq やビルドツール) | 疑う場所既定イメージに入っていない | 対応-P でフル版イメージに替えるか、workflowにインストール手順を足す |
| トークンやsecretsが空で認証エラー | 疑う場所-s を渡していない | 対応act -s で渡す |
upload-artifact が動かない | 疑う場所アーティファクトサーバー未起動 | 対応--artifact-server-path $PWD/.artifacts を付ける |
${{ github.event.pull_request... }} が空 | 疑う場所イベントのペイロードが無い | 対応-e でイベントファイルを渡す |
| matrixの全組み合わせが走って遅い | 疑う場所絞り込み未指定 | 対応--matrix node:8 のように絞る |
| OIDCトークンの取得で失敗 | 疑う場所act未対応 | 対応ローカルでは外し、GitHub上で確かめる |
run: の途中で落ちる | 疑う場所workflowの誤り | 対応Claude Codeにその行を直させる |
最後の行が、act本来の使いどころです。run: のスクリプトが落ちる失敗は、環境差を排したあとに残る失敗として、直す価値が高いものです。
actが緑でも、pushで赤くなるケース
actの成功は「run: が動いた」ことの保証であり、「workflowがGitHub上で動く」ことの保証ではありません。とくに次の3つは、act側で通ってしまう側に倒れます。
permissionsの不足。ジョブ単位の permissions: は無視されるため、pull-requests: write の付け忘れはactでは検出されません。GitHub上では403になります。claude-code-actionのように id-token: write を要求するactionでは、取りこぼしが致命的です。
OIDCを使うデプロイ。クラウドへのキーレス認証は、取得先のURLがactに無いので再現できません。仕組みの側はWIFをGitHub Actionsと連携するで扱っています。
環境ごとのsecretsとconcurrency。 environment: に紐づくsecretsや承認ルールは、actでは意味を持ちません。同時実行の制御を決める concurrency: も無視されます。
この3つは、Claude Codeが「actは緑でした」と報告しても、読む側が割り引くところです。CLAUDE.mdの3行目に書いた「根拠にしない」は、この意味です。
手順の全体像
push前の検証ループ
- 1
workflowを編集する
Claude Codeが
.github/workflows/を直します。 - 2
Stop hookがactを回す
変更があるときだけ、対象の1ジョブを実行します。
- 3
失敗ログの末尾を返す
終了コード2で、ログの最後の40行がClaude Codeに渡ります。
- 4
環境差か、workflowの誤りかを切り分ける
環境差は報告、誤りは修正して再実行します。
- 5
pushしてGitHub上で残りを確かめる
permissions、OIDC、environmentはここで初めて検証されます。
まとめ
actで先に落とせるのは、スクリプトとデータの流れの誤りです。権限と認証の誤りは、pushするまで残ります。Stop hookに組み込むなら、対象を1ジョブに絞り、!env.ACT で失敗を隠すことを禁じるところまでをセットにすると、緑の意味が崩れません。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。
関連する記事
Claude Code をもっと見る →Claude Codeとは — できること・料金・始め方と使い方の全体像
Claude Codeでreusable workflowとcomposite actionに重複手順を切り出す
Claude Codeのactionlint活用 — workflowを編集のたびに検証する設定
Claude CodeでChromaticのビジュアルテストを回す — PRの差分検知とHooks連携
npm trusted publishingをClaude Codeで組む — トークン不要でprovenance付き
JenkinsfileをGitHub Actionsに移行 — Claude Codeで並走検証する手順