GitHub Actions 講座 #4 マトリックス・キャッシュ・アーティファクト — 組み合わせ実行と時間・コスト削減

読了 5分

テストが Python 3.12 では通るのに 3.13 で壊れるなら、両方のバージョンで回さないと分かりません。ジョブ定義をコピーしてバージョンだけ変えれば 2 つ作れますが、OS まで組み合わせると 6 つ、9 つと膨らんでメンテナンスが崩壊します。今回の最初のテーマであるマトリックスは、この問題をジョブ定義 1 つで解決します。続いて逆方向、つまり増えた実行の時間とコストを減らすキャッシュ、そして #2 で先送りにした「ジョブ間のファイル受け渡し」の答えであるアーティファクトまで扱います。

マトリックス — ジョブ 1 つを組み合わせに増やす #

strategy.matrix に軸を宣言すると、ジョブが組み合わせの数だけ並列に実行されます。

.github/workflows/test.yml
jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest]
        python: ["3.12", "3.13"]
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python }}
      - run: pip install -r requirements.txt
      - run: pytest

2×2 で 4 つのジョブが作られ、各ジョブの中では matrix.os と matrix.python で自分の組み合わせの値を使います。組み合わせを調整するオプションが 3 つあります。

  • exclude — 特定の組み合わせを除外します。「macOS では 3.12 をスキップする」といった調整です。
  • include — 組み合わせを追加したり、特定の組み合わせだけに変数を載せたりします。「Ubuntu の 3.13 の組み合わせだけカバレッジ計測を有効にする」といった例外処理です。
  • fail-fast — デフォルトの true は、1 つの組み合わせが失敗すると残りを即座にキャンセルします。どの組み合わせが失敗するのか全部見たいなら false に変えます。失敗の原因を組み合わせごとに比較したいテストなら false が便利で、コスト優先なら true を維持します。

マトリックスはランナーの使用量を組み合わせの数だけ掛け算にする、という点も覚えておきます。#1 で見たとおり macOS ランナーは課金の倍率が高いので、プライベートリポジトリなら macOS の軸が本当に必要かを見極めることがコスト管理の第一歩です。

キャッシュ — 毎回ダウンロードし直さない #

ランナーは毎回まっさらなマシンなので、依存関係のインストールが毎回ゼロから始まります。actions/cache は指定したディレクトリを実行の間で保存し、この無駄を減らします。

.github/workflows/test.yml
      - uses: actions/cache@v4
        with:
          path: ~/.cache/pip
          key: pip-${{ runner.os }}-${{ hashFiles('requirements.txt') }}
          restore-keys: |
            pip-${{ runner.os }}-

核心は key の設計です。hashFiles('requirements.txt') が依存関係ファイルのハッシュをキーに入れるため、依存関係が変わるとキーが変わり、キャッシュが自然に無効化されます。restore-keys は完全一致するキーがないときに前方一致で探すフォールバックで、依存関係が変わった直後でも大半のパッケージは以前のキャッシュから復元され、変わった部分だけ新しくダウンロードされます。

実務ではこれよりもっと簡単な方法、setup 系アクションの内蔵キャッシュが先です。

.github/workflows/test.yml
      - uses: actions/setup-python@v5
        with:
          python-version: "3.13"
          cache: "pip"

cache: "pip" の 1 行が、上の cache アクションの構成と同じ仕事をします。setup-node や setup-go なども同じオプションを提供しているので、言語の依存関係キャッシュは内蔵オプションをデフォルトにし、内蔵がカバーしないパス(ビルドの中間生成物など)にだけ actions/cache を直接使います。キャッシュはリポジトリごとに総量の上限があり、しばらく使われないと自動削除される一時ストレージです。つまりなくても動くべきで、速くするだけの存在であるべきという原則さえ守れば、副作用はありません。

アーティファクト — ジョブ間のファイル受け渡しと成果物の保管 #

#2 で、ジョブ間で文字列は outputs で渡せてもファイルは渡らないと言いました。ファイルの通り道がアーティファクトです。

.github/workflows/build-deploy.yml
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: ./build.sh -o dist/
      - uses: actions/upload-artifact@v4
        with:
          name: app-dist
          path: dist/

  deploy:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: app-dist
          path: dist/
      - run: ./deploy.sh dist/

ビルドジョブがアップロードした成果物をデプロイジョブがダウンロードする、「一度ビルドしてそのままデプロイ」の基本骨格です。アーティファクトは実行画面から人がダウンロードすることもできるので、テストレポートやログの束の保管場所としても使われます。retention-days で保存期間を指定でき、デフォルトの保存期間は長めに設定されていてストレージ課金の原因になることもあるため、ビルド成果物のように寿命が短いものは短く明示するのがコストの習慣です。キャッシュとの区別は明確です。キャッシュは「なくてもよい加速装置」、アーティファクトは「実行が作った成果物」です。

まとめ #

今回扱った内容です。

  • マトリックスはジョブ定義 1 つを組み合わせの数だけ並列実行に増やします。exclude と include で組み合わせを調整し、fail-fast はコストと情報量のトレードオフです
  • マトリックスはランナーの使用量を掛け算で増やします。macOS の軸は倍率が高く、コスト見直しの最優先候補です
  • キャッシュのキーに hashFiles を入れて依存関係の変更時に自然に無効化されるようにし、restore-keys で部分復元を確保します
  • 言語の依存関係は setup アクションの内蔵 cache オプションが先で、actions/cache は内蔵がないパスに使います
  • ファイルの受け渡しと成果物の保管はアーティファクトです。キャッシュは加速装置、アーティファクトは成果物で、retention-days の明示がコストの習慣です

次回(#5 再利用)では、複数のリポジトリとワークフローに繰り返されるステップの塊を部品にする方法、composite action と reusable workflow を扱います。

X