マガジン

AIが書いたコードがローカルでは通るのにGitHub Actionsで落ちるときの調べ方

AIを使いこなす

Claudeで執筆し、GPTで自動校閲・修正しています。

ノートと鉛筆を使って、ローカル環境とCIの違いを確認する準備をした机
記事の内容をもとに制作したAI生成イメージです。実際の製品画面や検証結果の写真ではありません。

結論から言うと、原因の多くは「コードの誤り」ではなく「実行条件の違い」です。まず失敗した最初のステップのログを読み、次にローカルとCIで違う条件(OS、Nodeなどのバージョン、依存関係のインストール方法、環境変数、ファイルの有無)を表で洗い出します。この順番で進めると、AIに「直して」と丸投げするより、短い手数で原因を絞れます。 この記事の前提は、GitHub Actions上でテストが落ち、手元では通るケースです。ログの読み方と差分の洗い出しに絞ります。表と手順は一般的な切り分けの考え方をまとめた編集上の整理で、特定のリポジトリで動作確認したものではありません。

この記事の目次
  1. 1. まず「CIは毎回まっさらな環境」だと押さえる
  2. 2. ログを読む順序のチェックリスト
  3. 3. ローカルとCIの条件の差分表
  4. 4. 手元でCIに近づけて再現する
  5. 5. AIに渡す依頼文の例
  6. 6. 原因別の判断の目安

1. まず「CIは毎回まっさらな環境」だと押さえる

GitHub-hostedランナーは、多くの場合ジョブごとに新しい仮想マシンです。公式ドキュメントでは、GitHubホストランナーは新しいVMとして用意されると説明されています。また ubuntu-latest は「GitHubが提供する最新の安定イメージ」を指し、OSベンダーの最新版とは限りません。

つまり、手元にだけ存在する次のものはCIにありません。

  • 以前インストールしたままのパッケージや node_modules
  • .gitignore で除外した設定ファイル(.env など)
  • ローカルDBやキャッシュに残っていたデータ

AIが書いたコードは「動いた環境」を前提にしがちで、こうした暗黙の依存を含みやすい点に注意します。

2. ログを読む順序のチェックリスト

ログは Actionsタブから対象の実行→ジョブを開くと、失敗したステップが自動で展開されます。次の順に確認してください。

  1. 最初に赤くなったステップを特定する。 テストではなく、チェックアウト、セットアップ、インストール、ビルドの段階で落ちていないか見ます。後続の失敗は連鎖であることが多いです。
  2. そのステップの最初のエラー行を探す。 末尾の「Process completed with exit code 1」は結果にすぎません。最初に出たエラーが手掛かりです。
  3. 実行環境の表示を確認する。 OS、ランタイムのバージョン、実行したコマンドを、手元と見比べます。
  4. インストールの警告を見る。 依存関係の不整合や取得失敗がないか確認します。
  5. 失敗したテスト名と期待値・実際値を控える。 毎回同じ箇所か、実行のたびに変わるかも記録します。
  6. ここまでで分からなければ詳細ログを有効にする。 再実行時のデバッグログ有効化、または ACTIONS_STEP_DEBUG を true にする方法があります。詳しくは公式のデバッグログの説明を参照してください。

ログ内の検索は、展開されているステップだけが対象です。見つからないときは、関係しそうなステップを開いてから検索します。コマンドラインでは gh run view 実行ID --log-failed で失敗したステップのログだけを表示できます。

3. ローカルとCIの条件の差分表

ログから見当が付かないときは、下の表を埋めて比較します。右端は、見つかった場合の主な確認先です。

  • OS、ローカルで起きがちなこと:macOS/Windowsで開発、CIでの違い:多くはLinux。ファイル名の大文字小文字を区別する、確認先:import文のパスと実際のファイル名
  • ランタイム、ローカルで起きがちなこと:古いNodeなどが入っている、CIでの違い:ワークフローで指定したバージョン、確認先:ワークフローのセットアップ設定、手元の node -v
  • 依存関係、ローカルで起きがちなこと:既存の node_modules を使用、CIでの違い:毎回新規インストール、確認先:ロックファイルがコミットされているか
  • インストール方法、ローカルで起きがちなこと:npm install、CIでの違い:npm ci を使うことが多い、確認先:下記参照
  • 環境変数、ローカルで起きがちなこと:.env やシェル設定がある、CIでの違い:CI=true などが設定され、秘密情報は未設定のことがある、確認先:ワークフローの env、Secrets
  • ファイル、ローカルで起きがちなこと:未コミットのファイルがある、CIでの違い:コミット済みのものだけ、確認先:git status、.gitignore
  • 時刻・順序、ローカルで起きがちなこと:タイムゾーンや実行順が固定的、CIでの違い:異なる場合がある、確認先:日付を使うテスト、テスト間の依存
  • 性能、ローカルで起きがちなこと:高性能な手元の機械、CIでの違い:リソースが異なり、遅いとタイムアウトしうる、確認先:タイムアウト設定

インストール方法の違いは特に見落としやすい

npm ciはロックファイルが必須で、package.jsonとロックファイルが食い違うとエラーになり、node_modulesを削除してからインストールします。手元の npm install はロックファイルを更新しうるため、AIにパッケージを追加させたのにロックファイルをコミットし忘れると、CIだけ落ちることがあります。

CI環境かどうかで挙動を分けるコードに注意

GitHub Actionsでは、CI と GITHUB_ACTIONS が常に true に設定されます。テストランナーや生成コードが CI の有無で挙動(監視モード、色、再試行など)を変えている場合があります。手元で CI=true を付けて同じコマンドを実行すると、再現することがあります。

4. 手元でCIに近づけて再現する

差分の見当が付いたら、手元をCIに近づけます。手順は次のとおりです。

  1. 変更を全てコミットし、git status が空であることを確認する。
  2. 別の場所にクローンし直す(git clone したてのフォルダ)。
  3. ワークフローと同じバージョンのランタイムに切り替える。
  4. ワークフローと同じ順のコマンドを、CI=true を付けて実行する(Node系なら npm ci → テスト)。

これで落ちれば、クリーンな環境でも再現することが分かります。依存関係やコミット漏れだけでなく、コードやテスト自体の問題も含めて調べます。落ちなければ、OSやリソース、Secretsなどのランナー固有の条件を疑います。

CIの環境をその場で確認したいときは、ワークフローの失敗前に診断用ステップを足す手もあります。次は説明用の例で、実行確認はしていません。

yaml
- name: 環境の診断(説明用の例)
  run: |
    echo "OS: $RUNNER_OS"
    node -v
    npm -v
    echo "CI=$CI"
    pwd
    ls -la
    git status --short

出力にはパスなどが含まれるため、Secretsや認証情報は表示しないでください。原因が分かったら、このステップは削除します。

5. AIに渡す依頼文の例

ログをそのまま貼って「直して」と頼むと、AIは推測で本番コードを書き換えがちです。条件の差分を先に渡し、原因の仮説を挙げさせるようにします。以下は説明用の例です。

GitHub Actionsでだけテストが失敗します。ローカルでは成功します。
原因を断定せず、確認すべき順に仮説を3つまで挙げてください。
コードの修正案は、仮説ごとに確認方法を示してからにしてください。

【失敗したステップ】テスト実行(npm test)
【最初のエラー行】(ここに貼る。トークンや社内URLは伏せ字にする)
【ローカル】OS: macOS / Node: v○○ / 実行: npm install → npm test
【CI】OS: ubuntu-latest / Node: ワークフロー指定の値 / 実行: npm ci → npm test
【最近の変更】AIが追加した関数とテスト(差分を貼る)
【未コミットのファイル】(git statusの結果)

人が確認する点は3つです。仮説がログの該当行に基づいているか、修正が「テストを通すためだけ」の変更(テストの削除、期待値の書き換え、タイムアウトの無制限化)になっていないか、秘密情報が含まれていないか、です。

6. 原因別の判断の目安

  • インストール段階で失敗、まず疑うこと:ロックファイル未コミット、不整合、次の一手:ローカルで npm ci を試す
  • Cannot find module、まず疑うこと:大文字小文字、コミット漏れ、次の一手:git ls-files で追跡対象を確認
  • 環境変数が undefined、まず疑うこと:CIに設定がない、次の一手:必要な変数名を洗い出し、Secretsや env に設定
  • 毎回同じテストだけ失敗、まず疑うこと:環境差か実際のバグ、次の一手:CI=true 付きでローカル再現
  • 実行ごとに結果が変わる、まず疑うこと:順序依存、時間依存、非同期の待ち不足、次の一手:該当テストを単独で繰り返し実行
  • タイムアウト、まず疑うこと:CIの処理速度、外部通信、次の一手:どの処理が遅いかをログの時刻で確認

どの行にも当てはまらないときは、デバッグログを有効にして再実行し、追加された出力から手順2に戻ります。

出典・参考資料