GitHub Actions の matrix strategy で複数環境テストを一気に回す
概要
ライブラリやアプリケーションを複数の言語バージョン・OS・依存バージョンの組み合わせで動かすとき、CI をバージョンごとにコピペしていると、ジョブ定義がすぐに肥大化して保守できなくなります。
GitHub Actions の strategy.matrix を使うと、1つのジョブ定義から組み合わせの数だけジョブを自動展開できます。この記事では matrix の基本から、組み合わせの追加・除外、失敗時の挙動制御、実務で効いてくるチューニングまでをハンズオン形式でまとめます。本記事は公開ドキュメントと一般的な構成に基づくもので、特定の顧客・案件の情報は含みません。
環境
- GitHub Actions(
ubuntu-latestなどの GitHub ホストランナー) - 例としてのランタイム: Node.js / Python(内容は他言語にも読み替え可能)
- ワークフローファイル:
.github/workflows/test.yml
発生した問題
複数バージョン対応のプロジェクトで、最初はこう書きがちです。
jobs:
test-node-18:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 18
- run: npm ci
- run: npm test
test-node-20:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm test
バージョンが増えるたびにジョブ全体をコピーすることになり、ステップを1つ直すと全ジョブを直す羽目になります。OS も掛け合わせたくなると破綻します。
原因
「同じ手順を、パラメータだけ変えて複数回実行したい」という構造を、ジョブの複製で表現してしまっているのが原因です。これはまさに matrix が解決する課題です。
対応方法
strategy.matrix に変数を定義すると、GitHub Actions はその直積(組み合わせ)ごとにジョブを並列展開します。変数は matrix.<変数名> で参照できます。
name: test
on:
push:
pull_request:
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
この定義だけで 3 OS × 3 バージョン = 9 ジョブ が自動展開され、並列実行されます。ジョブ名には test (ubuntu-latest, 20) のように組み合わせが表示されるので、どの組み合わせで落ちたか一目で分かります。
実装例
1. 特定の組み合わせだけ追加する(include)
「基本は 3×3 だが、最新 OS では実験的バージョンも1つ試したい」というとき、全体の直積を広げずにピンポイントで足せます。
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node-version: [18, 20]
include:
- os: ubuntu-latest
node-version: 23
experimental: true
include で追加した組み合わせだけ matrix.experimental を持つので、後続ステップで分岐できます。
2. 不要な組み合わせを除外する(exclude)
直積のうち、サポート外・動かないと分かっている組み合わせを落とせます。
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ["3.9", "3.10", "3.11", "3.12"]
exclude:
- os: macos-latest
python-version: "3.9"
3. 失敗したときに他を止めない(fail-fast)
デフォルトでは1つのジョブが失敗すると、残りの matrix ジョブがキャンセルされます(fail-fast: true)。「どの組み合わせで落ちるか全部知りたい」ときは無効化します。
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [18, 20, 22]
4. 同時実行数を絞る(max-parallel)
ランナーの消費を抑えたい、外部サービスへの同時アクセスを制限したい場合に使います。
strategy:
max-parallel: 2
matrix:
node-version: [18, 20, 22]
5. 実験的バージョンの失敗を許容する(continue-on-error)
最新・未リリースのバージョンは「落ちても CI 全体は赤くしたくない」ことがあります。matrix 変数とジョブレベルの continue-on-error を組み合わせます。
jobs:
test:
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.experimental == true }}
strategy:
fail-fast: false
matrix:
node-version: [18, 20]
include:
- node-version: 23
experimental: true
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
6. matrix を動的に生成する
テスト対象を別ジョブで算出し、fromJSON で matrix に流し込むパターンです。変更されたディレクトリだけテストする、といった使い方に発展できます。
jobs:
setup:
runs-on: ubuntu-latest
outputs:
versions: ${{ steps.set.outputs.versions }}
steps:
- id: set
run: echo 'versions=["18","20","22"]' >> "$GITHUB_OUTPUT"
test:
needs: setup
runs-on: ubuntu-latest
strategy:
matrix:
node-version: ${{ fromJSON(needs.setup.outputs.versions) }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
確認方法
- 上記
test.ymlを.github/workflows/に置いて push する - GitHub の Actions タブを開き、1つのワークフロー実行から複数ジョブが展開されていることを確認する
- 各ジョブ名に
(ubuntu-latest, 20)のように組み合わせが表示されているか見る fail-fast: falseを付けた場合、1つ落ちても他ジョブが最後まで走ることを確認するinclude/excludeでジョブ数が意図どおり増減しているかを数える
注意点
- 組み合わせ爆発に注意: 変数を掛け算で増やすと、気づくと数十ジョブになります。実行時間・課金(プライベートリポジトリの場合)に直結するので、
excludeで現実的な組み合わせに絞るのが実務のコツです。 fail-fastの使い分け: 開発中の原因切り分けはfalse、通常の PR チェックはtrue(早く失敗を返して無駄な実行を減らす)を基本にすると扱いやすいです。- キャッシュのキーに matrix 変数を含める:
actions/cacheのキーに OS やバージョンを含めないと、組み合わせ間でキャッシュが汚染されます。 - OS 依存のコマンド差:
windows-latestはシェルが異なるため、run:の書き方(パス区切り・環境変数)で差が出ます。クロスプラットフォームで回すならshell: bash指定が無難なことがあります。
まとめ
strategy.matrixは「同じ手順をパラメータ違いで並列実行する」ための仕組みincludeで局所追加、excludeで局所削除、直積を細かく調整できるfail-fast/max-parallel/continue-on-errorで失敗時の挙動と負荷を制御するfromJSONで matrix を動的生成すると、対象を絞ったテストに発展できる
まずは「OS × バージョン」の小さな matrix から始め、ジョブ数と実行時間を見ながら exclude で現実的な範囲に絞り込んでいくのがおすすめです。
