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が通る状態を先に作り、そのコマンドをワークフローに書く順序で進めます。