GitHub Actionsのmatrixで並列処理を試してみる

CI/CD

GitHub Actionsのworkflowを作成する際、共通している処理を実行し、処理に時間がかかる問題がありました。こんな時に有効な機能がmatrixです。matrixは共通している処理を並列実行し、実行時間を短縮することができます。 この記事では、簡単な例を使ってmatrix機能を触り、その実用性を見ていきます。
公式ドキュメント:Running variations of jobs in a workflow


基本的な機能の確認

まずは、基本的な機能の例を見ていきます。

name: Fruit Matrix Example

on:
  workflow_dispatch:

jobs:
  process-fruits:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        fruit: [apple, banana, cherry]
    steps:
      - name: Print fruit name
        run: 'echo "Processing fruit: ${{ matrix.fruit }}"'

このworkflowは、「apple」「banana」「cherry」の3つの名前を一つずつ処理します。
処理の結果はこんな感じになります


一歩進んだ例

次は、workflow内のjobでリストを作成しそれをmatrixにで並列処理する例を見ていきます。

name: Fruit Matrix Example

on:
  workflow_dispatch:

jobs:
  create-fruit-list:
    runs-on: ubuntu-latest
    outputs:
      fruit-list: ${{ steps.set-fruits.outputs.fruits }}
    steps:
      - name: Create fruit list
        id: set-fruits
        run: |
          fruits='["apple", "banana", "cherry"]'
          echo "fruits=$fruits" >> $GITHUB_OUTPUT

  process-fruits:
    needs: create-fruit-list
    runs-on: ubuntu-latest
    strategy:
      matrix:
        fruit: ${{ fromJson(needs.create-fruit-list.outputs.fruit-list) }}
    steps:
      - name: Print fruit name
        run: 'echo "Processing fruit: ${{ matrix.fruit }}"'

このworkflowは、最初のジョブでリストを作成しその値をmatrixに派生しています。
処理の結果はこんな感じになります

この方法を活用することで、共通している処理を切り離して並列実行し、処理に時間がかかる問題を回避することができそうです。


エラーが出た時どうするか

エラーハンドリングの種類は2つあります。
fail-fast:matrix全体に適用される。matrix内のいずれかのジョブが失敗した際、matrix内のすべての進行中のjobとQueueに入れられたjobをキャンセルする。デフォルトはtrue。
continue-on-error:単一のジョブに適用される。ジョブが失敗してもそのmatrix内の後続の処理を実行する。デフォルトはfalse。
continue-on-errorはmatrix以外でも使うので、matrix固有のものはfail-fastだけ。

以下はfail-fastcontinue-on-errorを使用した例です。

name: Fruit Matrix Example

on:
  workflow_dispatch:

jobs:
  process-fruits:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        fruit: [apple, banana, cherry]
      fail-fast: false
    steps:
      - name: Print fruit name
        run: 'echo "Processing fruit: ${{ matrix.fruit }}"'

      - name: Simulate processing
        continue-on-error: true
        run: |
          if [ "${{ matrix.fruit }}" == "banana" ]; then
            exit 1
          fi
          echo "Successfully processed ${{ matrix.fruit }}"

fail-fast: falseでmatrix内のいずれかのジョブが失敗した場合でも他のmatrixをキャンセルしないようにしています。また、最後のstepにおいて、bananaのmatrix内でexit 1でエラーをあえて出しています。continue-on-error: trueにしているため、bananaのmatrix内の処理がキャンセルされず、後続の処理も実行されます。
処理の結果はこんな感じになります

bananaでエラーが出ていますが、banana自体の処理も成功していますし、その他のmatrixもキャンセルされていません。
それぞれの影響範囲は以下の画像の通りです


終わりに

今回はmatrix機能を試してみました。共通している処理を並列で処理してくれる便利な機能でした。
使用するシーンに合わせてエラーハンドリングも調整してみるといいと思います。