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

確認方法

  1. 上記 test.yml を .github/workflows/ に置いて push する
  2. GitHub の Actions タブを開き、1つのワークフロー実行から複数ジョブが展開されていることを確認する
  3. 各ジョブ名に (ubuntu-latest, 20) のように組み合わせが表示されているか見る
  4. fail-fast: false を付けた場合、1つ落ちても他ジョブが最後まで走ることを確認する
  5. 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 で現実的な範囲に絞り込んでいくのがおすすめです。

\ 最新情報をチェック /

コメントを残す

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください。