10 人が閲覧(直近 30 日) CI/CD

GitHub Actionsでビルド・自動テストを設定する方法|build.ymlの書き方と失敗の直し方

GitHub Actionsでビルド・自動テストを設定する方法|build.ymlの書き方と失敗の直し方

GitHub Actionsでビルドと自動テストを回すには、リポジトリの.github/workflows/にYAMLファイルを1つ置き、pushとプルリクエストをきっかけに「コードの取り出し→実行環境の準備→依存のインストール→テスト→ビルド」を並べます。GitHub自体がコンパイルするわけではなく、GitHubが用意する仮想マシン(ランナー)の上で、手元と同じコマンドを実行する仕組みです。

この記事では、2026年10月時点の現行版(actions/checkout v7、actions/setup-node v7、upload-artifact v7、download-artifact v8)とNode.js 24で書いたbuild.ymlの完成形を示し、Python・Java・Goの書き換え方、テストとビルドをジョブに分ける判断、ビルドが落ちたときの原因の切り分けまでを扱います。版と既定値は各アクションの公式リポジトリとGitHub Docsで確認したものです。

まとめ:ビルドと自動テストのワークフローで最初に決めること

  • 置き場所:.github/workflows/build.yml(ファイル名は任意、拡張子は.ymlか.yaml)。
  • きっかけ:pull_requestと、mainへのpushの2つで足ります。
  • 版:checkoutはv7、setup-nodeはv7、upload-artifactはv7、download-artifactはv8。アプリをビルドするNode.jsは24を基準にします(20は2026年4月30日にサポート終了)。
  • 依存のインストール:npm installではなくnpm ci。ロックファイルと食い違えば止まるので、CIで依存がずれません。
  • ジョブの分け方:小さなリポジトリは1ジョブで十分です。テストを複数版で回す、ビルド成果物を後段で使う、のどちらかが出てきたら分けます。

ビルドが落ちたら、最後の「Process completed with exit code 1」ではなく、その上に出ている最初のエラー行を読みます。この記事で取り上げる原因は、ロックファイルの不一致、大文字小文字の違うimport、CI=trueで変わるツールの挙動、権限、の4つです(切り分け表)。

build.ymlの完成形:checkoutからビルドまでの1ジョブ構成

Node.jsのプロジェクトを例に、そのまま置いて動く形を先に示します。前提は、Node.js 24で動くこと、package-lock.jsonがコミットされていること、npm testが監視モードに入らず終了すること、npm run buildがdist/に成果物を出力することの4つです。出力先が違う場合は、upload-artifactのpathを合わせてください。

# .github/workflows/build.yml
name: build

on:
  pull_request:
  push:
    branches: [main]

permissions:
  contents: read

concurrency:
  group: build-${{ github.ref }}
  cancel-in-progress: true

jobs:
  build:
    runs-on: ubuntu-24.04
    timeout-minutes: 15
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 24
          cache: npm
      - run: npm ci
      - run: npm test
      - run: npm run build
      - uses: actions/upload-artifact@v7
        with:
          name: dist
          path: dist/
          if-no-files-found: error
          retention-days: 7

コミットしてプッシュすると、リポジトリの「Actions」タブに実行が現れます。プルリクエストを作れば、画面下部のチェック欄にbuildジョブの結果が並びます。

build.ymlの主要キーの役割と既定値

キー この例の値 省略したとき
on pull_request・mainへのpush 省略不可
permissions contents: read リポジトリ設定の既定権限
concurrency 同じブランチの古い実行を中止 全実行が並行して走る
runs-on ubuntu-24.04 省略不可
timeout-minutes 15 360分
if-no-files-found error warn(成果物ゼロでも成功)
retention-days 7 リポジトリ設定に従う(初期値90日)

runs-onにubuntu-latestと書いても動きますが、今は版を固定する時期です。GitHubの2026年9月17日の告知によると、ubuntu-latestは2026年10月19日から11月19日にかけて、Ubuntu 24.04から26.04へ段階的に切り替わっています。26.04では一部のツールや版が更新・削除されており、プリインストールのソフトに頼るビルドは壊れる可能性があると告知自体が注意しています。例のようにubuntu-24.04で固定し、別のブランチでubuntu-26.04を試してから上げる順序が安全です。ラベルの記法と切り替えの備えはGitHub Actionsのruns-on|ラベル指定の記法と-latest更新・arm64への備えで扱っています。

timeout-minutesの既定はワークフロー構文の公式リファレンスで360分です。テストが固まると6時間ランナーを占有し、プライベートリポジトリではその分の実行時間が無料枠から引かれます。普段の所要時間の2〜3倍を上限に置いてください。permissionsを書かない場合の既定と最小権限の考え方はGitHub Actionsのpermissions|GITHUB_TOKENの既定権限と最小権限の設計にまとめています。

npm ciを使う理由とテストをビルドより前に置く理由

npm ciの公式説明には、package-lock.jsonが必須であること、ロックファイルとpackage.jsonの依存が一致しなければ更新せずエラーで終了すること、どちらのファイルも書き換えないことが書かれています。npm installはロックファイルを黙って更新するため、手元と別の依存でビルドが通ってしまうことがあります。CIで検出したいのはまさにこのずれなので、npm ci一択です。

この例では、ビルド成果物を必要としない単体テストを先に実行し、失敗した場合のビルドを省きます。生成済みファイルを使うテストやビルド後のアプリを対象にするテストでは、ビルドを先に置いてください。ビルドに数分かかるプロジェクトでは、型エラーやテストの失敗をビルドの前に知れるだけで待ち時間が縮みます。lintも同じ理由でテストの前に置けます。ワークフローの静的検査そのもの(YAMLの書き間違いや未定義の式)は、actionlintとは?GitHub Actionsワークフローの静的解析ツールの使い方・インストール・設定で、プッシュ前に手元で拾えます。

2026年10月時点のアクションの版とNode.js 24への移行

検索で見つかる解説の多くはactions/checkout@v2〜@v4のまま止まっています。2026年10月26日時点の各アクションの最新リリースは次のとおりです。

アクション 現行メジャー 最新リリース 押さえる変更
actions/checkout v7 v7.0.1(2026-07-20) pull_request_target・workflow_runでフォークのPRコード取得を既定で拒否
actions/setup-node v7 v7.1.0(2026-10-08) 自動キャッシュはnpmのみ
actions/setup-python v7 v7.1.0(2026-10-22) cache: pip等を指定
actions/setup-java v6 v6.1.0(2026-10-14) distributionの指定が必要
actions/setup-go v7 v7.0.0(2026-07-16) cacheの既定がtrue
actions/upload-artifact v7 v7.0.2(2026-10-07) archive: falseで単一ファイルをZIP化せず転送
actions/download-artifact v8 v8.0.2(2026-10-07) ハッシュ不一致で既定エラー
actions/cache v6 v6.1.0(2026-06-26) ―

upload-artifactとdownload-artifactは、同じ日に出た組でもメジャー番号が1つずれています。揃えようとしてdownload-artifact@v7に下げる必要はありません。

アクション自体が動くNode.js 24とランナーv2.327.1

JavaScriptで書かれたアクションは、ランナーに同梱されたNode.jsで動きます。checkoutはv5.0.0(2025-08-11)、setup-nodeはv5.0.0(2025-09-04)、upload-artifactはv6.0.0、download-artifactはv7.0.0(どちらも2025-12-12)で、この実行環境がnode20からnode24へ上がりました。各リリースノートはランナーv2.327.1以上を要件に挙げています。

そのうえで、2026年9月23日の告知でNode 20はGitHub Actionsのランナーから外れ、JavaScriptアクションはNode 24で実行されるようになりました。一時的に古い版へ戻すACTIONS_ALLOW_USE_UNSECURE_NODE_VERSIONも使えません。node20向けに作られた古いメジャー版を使い続けるより、表の現行版へ上げておくのが筋です。

GitHubがホストするランナーは自動で更新されるので気にする必要はありません。影響を受けるのは、自前のサーバーで動かすセルフホストランナーと、GitHub Enterprise Serverです。古いランナーのまま新しいメジャー版へ上げると、ジョブが開始直後に失敗します。同じ告知で、Node 24が動かないmacOS 13.4以前とARM32のセルフホストランナーはサポート対象外になりました。セルフホストでは、ランナーとOSの対応状況を確認してからアクションを更新してください。ただし、GitHub Enterprise Serverではupload-artifact v7・download-artifact v8は未対応のため、掲載例をそのまま適用せず、各アクションのGHES対応版を選んでください(GitHubセルフホストランナーとは?導入手順とhosted版の違い【2026年版】)。

アプリをビルドするNode.jsの版の選び方

上のnode24は「アクションが動く環境」の話で、setup-nodeのnode-versionで指定する「自分のアプリをビルドする版」とは別物です。混同すると、アクションの版を上げたのにアプリのNode.jsは古いまま、ということが起きます。

Node.jsのリリーススケジュールでは、20は2026年4月30日にサポートが終わりました。24のサポート終了は2028年4月30日、22は2027年4月30日です。26は2026年10月28日にLTSへ入る予定になっています。GitHub DocsのNode.jsのビルドとテストの例は今も18.xと20.xのマトリクスで書かれているため、そのまま写すとサポートの終わった版でテストすることになります。

手元とCIの版を1か所で管理するなら、node-version: 24と書く代わりにnode-version-file: '.nvmrc'を指定します。setup-nodeの入力一覧と版指定の書き分けはactions/setup-nodeとは|v7の入力・キャッシュ設定とv5以降の既定変更、LTSの移行時期はNode.js LTSとは?現行版の確認とlts/*の固定・移行時期を実装目線で解説で詳しく扱っています。

Python・Java・Goのビルドとテストのステップ

言語が変わっても骨格は同じで、差し替えるのは「実行環境を入れるアクション」と「依存・テスト・ビルドのコマンド」だけです。on・permissions・checkoutは上の完成形のまま使えます。

Python:setup-python v7とpytest

    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-python@v7
        with:
          python-version: '3.13'
          cache: pip
      - run: pip install -r requirements.txt
      - run: pytest
      - run: python -m build

python-versionは文字列で書きます。3.10を引用符なしで書くとYAMLが数値の3.1として読むためです。setup-pythonはcacheの既定値を持たないので、キャッシュを効かせるにはpip・pipenv・poetryのどれかを明示します。python -m buildを使う場合は、依存にbuildパッケージを含めておきます。

Java:setup-java v6とGradle・Maven

    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-java@v6
        with:
          distribution: temurin
          java-version: '21'
          cache: gradle
      - run: ./gradlew build

setup-javaはdistribution(Temurin、Zulu、Correttoなど)の指定が実質必須です。action.ymlの説明では、.sdkmanrcで配布元を判別できる場合を除き必須とされています。cacheはmaven・gradle・sbtの3つから選びます。Gradleのbuildタスクはテストを含むので、テストとビルドを1行で回せます。Mavenならcache: mavenとmvn -B verifyに置き換えます。

Go:setup-go v7とgo test

    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-go@v7
        with:
          go-version-file: go.mod
      - run: go test ./...
      - run: go build -o bin/app ./cmd/app

setup-goはcacheの既定がtrueで、何も書かなくてもモジュールとビルドキャッシュが保存されます。go-version-fileでgo.modを読ませると、toolchainディレクティブがあればその版を、なければgoディレクティブの版を使います。手元との一致には、ローカルでも同じツールチェーンを使用する必要があります。

テストとビルドを別ジョブに分ける判断とneedsでの受け渡し

掲載例のGitHubホストランナーでは、ジョブごとに新しい環境が割り当てられ、作業ディレクトリは空から始まります。前のジョブのnode_modulesもdist/も残っていません。分けるたびに依存のインストールが走り、実行時間はジョブ単位で1分未満が切り上げられるので、何も考えずに分けると遅く高くなります。

分けて得をするのは次の3つの場合です。

  • テストを複数のNode.jsの版やOSで回したいが、ビルドは1回でよい
  • lintとテストを並列に走らせて、待ち時間を縮めたい
  • ビルド成果物をデプロイや別のジョブで使う

どれにも当たらないなら1ジョブのままにします。分ける場合はneedsで順序を作り、成果物はアーティファクトで渡します。

jobs:
  test:
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 24
          cache: npm
      - run: npm ci
      - run: npm test

  build:
    needs: test
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: 24
          cache: npm
      - run: npm ci
      - run: npm run build
      - uses: actions/upload-artifact@v7
        with:
          name: dist
          path: dist/
          if-no-files-found: error

  deploy:
    needs: build
    runs-on: ubuntu-24.04
    steps:
      - uses: actions/download-artifact@v8
        with:
          name: dist
          path: dist/
      - run: ls -R dist

受け渡しで詰まりやすい点が3つあります。upload-artifactはv4以降、同じ名前で2回アップロードすると2回目が失敗します。マトリクスで回すジョブから上げるなら、名前に${{ matrix.node-version }}のような識別子を付けます。保持期間の既定は90日で、retention-daysで短くできます。download-artifactはv8から、ダウンロードしたファイルのハッシュが合わないとき既定でジョブを失敗させるようになりました(digest-mismatchで変更可)。受け渡しの設計はGitHub Actionsのアーティファクト|受け渡しの設計と同名不可・保持期間の決め方で詳しく扱っています。

複数の版・OSで自動テストを回すmatrixの書き方

テストだけを複数の版で回すなら、strategy.matrixで展開します。

  test:
    runs-on: ${{ matrix.os }}
    strategy:
      fail-fast: false
      matrix:
        os: [ubuntu-24.04, windows-2025]
        node-version: [22, 24]
    steps:
      - uses: actions/checkout@v7
      - uses: actions/setup-node@v7
        with:
          node-version: ${{ matrix.node-version }}
          cache: npm
      - run: npm ci
      - run: npm test

この例は2×2で4ジョブになります。GitHub Actionsの上限一覧では、1回のワークフロー実行でmatrixが生成できるジョブは256までです。fail-fastの既定はtrueで、1つ落ちると残りが取り消されます。どの組み合わせで落ちるかを全部見たいテストでは、例のようにfalseにしておくと原因の切り分けが早くなります。includeとexcludeでの調整はGitHub Actionsのマトリックスビルド|組み合わせ展開とinclude・excludeの調整を参照してください。

Windowsのランナーは、GitHub Actionsの課金に関する公式ドキュメントで1分あたりの単価がLinuxより高く設定されています。Windowsを動作環境に含めるライブラリや製品なら、不具合が出る前からWindowsのテストを組み込みます。Linuxのサーバーでしか動かさないアプリなら、Windowsは足さずに実行時間を抑えます。

依存キャッシュでビルドを速くする設定

完成形のcache: npmは、npmのキャッシュディレクトリをpackage-lock.jsonのハッシュをキーに保存し、次回のnpm ciで再利用する指定です。setup-nodeは、キャッシュを保存するpost処理にsuccess()の条件が付いているため、途中で失敗したジョブではキャッシュが保存されません。

版によって既定が変わっている点に注意します。setup-nodeはv5.0.0で、package.jsonにpackageManagerの記載があればcacheを書かなくてもキャッシュが自動で有効になり、v6.0.0でこの自動キャッシュがnpmに限定されました。yarnやpnpmはcache: yarn・cache: pnpmを明示します。npmへの公開や本番デプロイのような権限の強いジョブでは、キャッシュの汚染を避けるためにpackage-manager-cache: falseで自動キャッシュを無効にし、完成形にあるcache: npmなどの明示指定も削除してください。

setup系アクションのキャッシュで足りない場合(ビルドツール自体の中間生成物など)はactions/cache@v6でキーを自分で設計します。キーの作り方と復元されない原因の調べ方はGitHub Actionsのキャッシュ|actions/cacheのkey設計と復元されない原因の切り分けにまとめています。

ビルドが失敗したときの原因の切り分け

ジョブのログの最後に出る「Process completed with exit code 1」は、直前のコマンドが失敗したという結果だけを表します。原因は、そのステップを開いて最初に出ているERR!やError:の行に書かれています。手元では通るのにCIで落ちるときは、次の表の順に疑うと早く当たります。

ログの症状 原因 直し方
npm ciがlockの不一致で終了 package.jsonだけ更新した npm installしてlockもコミット
Cannot find module(手元は成功) importの大文字小文字の違い ファイル名と表記を一致させる
スナップショットのテストだけ失敗 CI=trueでJestが新規保存しない 手元で更新してコミット
フォークからのPRだけ失敗 secretsが渡らない secretsの要らないテストに分ける
Resource not accessible by integration GITHUB_TOKENの権限不足 permissionsで必要な権限を足す
ubuntu-latestのジョブだけ急に失敗 Ubuntu 26.04への切り替え ubuntu-24.04に固定して検証
Waiting for a runnerのまま runs-onのラベル違い ラベル名とランナーの状態を確認
6時間で強制終了 処理の停止・長時間化で既定360分に到達 停止箇所を直しtimeout-minutesも設定

手元のmacOS・Windowsでは通るのにLinuxで落ちる理由

macOSとWindowsの既定のファイルシステムは大文字小文字を区別しませんが、Linuxは区別します。import Header from './header'と書いて実ファイルがHeader.tsxだと、手元では通り、ubuntuのランナーでは「Cannot find module」で落ちます。Gitもファイル名の大文字小文字だけの変更を検知しないことがあるため、名前を変えたときはgit mvで変更を記録します。

CI=trueで挙動が変わるツール

GitHub Actionsは既定の環境変数としてCIを常にtrueに設定します。これを見て挙動を変えるツールがあります。Jestは設定のciの既定値をこの判定で決めており、CI上では–ciオプションの説明どおり、新しいスナップショットを自動保存せずにテストを失敗させます。スナップショットは手元で--updateSnapshotを付けて更新し、コミットしてから流します。

フォークからのプルリクエストで秘密情報が空になる理由

公式ドキュメントには、フォークしたリポジトリからワークフローが起動された場合、GITHUB_TOKENを除いてsecretsはランナーに渡されないと書かれています。パブリックリポジトリでは、外部APIのキーをsecretsから読む結合テストはフォークからのPRで認証に失敗します(プライベートリポジトリには、フォークのPRへsecretsを渡すかどうかを選ぶ管理設定があります)。キーの要らない単体テストとジョブを分け、結合テストはif: github.event.pull_request.head.repo.full_name == github.repositoryのような条件で同じリポジトリからのPRに限ります。これを避けようとしてpull_request_targetに切り替えるのは危険です。checkout v7がこのトリガーでのフォークのコード取得を既定で拒否するようになったのは、そこから権限を乗っ取る攻撃が実際にあるためです。

ワークフローそのものを手元で動かして確かめたい場合は、GitHub Actionsをローカル実行するact|導入とイメージ選択・再現できる範囲が使えます。ただしGitHubのランナーと同じイメージではないため、上の表のうちOSやsecretsに由来する失敗は再現できないことがあります。

ビルドの成功をプルリクエストのマージ条件にする設定

ワークフローを置いただけでは、ビルドが落ちていてもマージボタンは押せます。マージを止めるには、ブランチの保護ルール(またはルールセット)で「Require status checks to pass」を有効にし、必須のチェックとしてジョブ名(この記事の例ならbuild)を選びます。必須チェックに選ぶには、そのチェックが対象リポジトリで過去7日以内に正常に完了している必要があるため、先にワークフローを実行して成功させておきます。

matrixを使うと、チェック名がtest (ubuntu-24.04, 24)のように組み合わせごとに分かれます。組み合わせを変えるたびに必須チェックの指定がずれて、PRが「Expected — Waiting for status to be reported」のまま止まる原因になります。設定手順と止まったときの直し方はRequire status checks to passとは?GitHubの必須ステータスチェックの設定とPendingで止まる原因で扱っています。

テストの範囲をデータベースまで広げる場合はGitHub Actionsのサービスコンテナ(services)|DB・Redisを立てる結合テストと起動待ちの設計、Dockerイメージを作る場合はGitHub ActionsでDockerイメージをビルドする|push先とキャッシュ・マルチアーキの設計へ進んでください。ビルドからテスト、デプロイまでのパイプライン全体を自社に合わせて組み直したい場合は、DevOps・CI/CD導入支援でワークフローの設計から運用まで支援しています。

よくある質問

ワークフローのYAMLファイルはどこに置き、何という名前にしますか?

リポジトリ直下の.github/workflows/に置きます。拡張子は.ymlか.yamlで、ファイル名は自由です。build.ymlやci.ymlがよく使われます。1つのディレクトリに複数置けば、それぞれが別のワークフローとして動きます。

GitHubだけでビルドやコンパイルはできますか?

できます。ただし実際に動くのは、GitHub Actionsが用意するランナー上で実行されるnpm run buildやgo buildなどのコマンドです。GitHubのリポジトリ画面にビルドボタンがあるわけではありません。生成物はupload-artifactで保存すると、実行結果のページからダウンロードできます。

GitHub Actionsのビルドは無料で使えますか?

パブリックリポジトリなら、標準のランナーは無料です。プライベートリポジトリは、Freeプランで月2,000分、Teamで月3,000分の無料枠があり、超えた分が1分単位で課金されます。実行時間はジョブごとに1分未満が切り上げられます。単価と削減の手順はGitHub Actionsの料金|2026年改定後の分単価・無料枠とコスト削減の判断基準で確認できます。

npm run buildの成果物はどこからダウンロードできますか?

ワークフローの最後にactions/upload-artifact@v7を置くと、実行結果ページの下部に成果物が表示され、zipでダウンロードできます。既定の保持期間は90日です。タグを打ったときに配布物として残したい場合は、GitHub Releasesの使い方|タグとの違い・リリースノート自動生成・Latestの決まり方のとおりReleasesに添付します。

GitHubでのテスト自動化は、GitHub Actionsだけで完結しますか?

テストを実行する仕組みとしては完結します。ただしテストを書くのはJestやpytest、JUnitなどのテストフレームワークで、GitHub Actionsはそれを決まったタイミングで動かす役割です。手元でnpm testが通る状態を先に作り、そのコマンドをワークフローに書く順序で進めます。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

ほか 43 件の記事からもリンクされています。

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.27 テックブログ Brotli 脆弱性:CVE-2026-111497の影響確認とNode.js・Python・.NETの更新手順
  2. 2026.10.28 テックブログ リンツ不正アクセス:314万件の対象確認とパスワード強制リセット後の対処
  3. 2026.10.29 テックブログ So-net情報漏洩:258万件の対象確認とメールのパスワード再設定、窓口の番号
  4. 2026.10.27 テックブログ Joshin(ジョーシン)不正アクセス:最大600万件の対象とサブスク初期パスワードの対処
  5. 2026.10.29 テックブログ ソニー生命の不正アクセス:38.5万件の対象確認と窓口、停止中の手続き

RELATED POSTS 関連記事

目次