Magicode logo
Magicode
5
6 min read

[GitHub Actions] ビルドマトリクスを動的に生成してジョブを並列実行する

https://cdn.magicode.io/media/notebox/fb53ca98-27e2-4b3a-b7c5-d98070017277.jpeg

概要

  • GitHub Actionsのビルドマトリクスは動的に生成することが出来る
  • 動的に生成したビルドマトリクスを使ってジョブを並列実行する

ビルドマトリクスとは

公式より
マトリックス戦略を使うと、単一のジョブ定義中で変数を使って、変数の組み合わせに基づく複数のジョブの実行を自動的に生成できます。 たとえばマトリックス戦略を使って、コードを言語の複数のバージョンや、複数のオペレーティングシステムでテストできます。

本題

GitHub Actionsのコード

name: 'Dynamic Build Matrix Actions'

on:
  workflow_dispatch:
    inputs:
      zeus:
        description: 'zeus'
        type: boolean
        required: true
        default: false
      athena:
        description: 'athena'
        type: boolean
        required: true
        default: false
      hades:
        description: 'hades'
        type: boolean
        required: true
        default: false

jobs:
  setup:
    runs-on: ubuntu-latest
    timeout-minutes: 3
    outputs:
      targets: ${{ steps.build-matrix.outputs.targets }}
    steps:
      - name: Setup Dynamic Build Matrix
        id: build-matrix
        run: |
          TARGETS=()

          if [ "${{ github.event.inputs.zeus }}" = "true" ]; then
            TARGETS+=("zeus")
          fi
          if [ "${{ github.event.inputs.athena }}" = "true" ]; then
            TARGETS+=("athena")
          fi
          if [ "${{ github.event.inputs.hades }}" = "true" ]; then
            TARGETS+=("hades")
          fi

          targets=$(printf '%s\n' "${TARGETS[@]}" | jq -R . | jq -s -c .)
          echo "::set-output name=targets::$targets"

  execute:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    needs:
      - setup
    strategy:
      fail-fast: true
      max-parallel: 3
      matrix:
        targets: ${{ fromJSON(needs.setup.outputs.targets) }}
    steps:
      - uses: actions/checkout@v3
      - name: Install Figlet
        run: sudo apt-get install -y figlet
      - name: Execute Script
        working-directory: scripts
        run: ./${{ matrix.targets }}.sh

解説

ポイントは3つ。
  • workflow_dispatch なワークフローを定義し、入力値として実行するスクリプトのon / offを選択させる
  • setup ジョブでワークフローの入力値からビルドマトリクスに必要な値を動的に生成し、ジョブの outputs に出力する
  • 並列実行する後続のジョブでGitHub Actionsの組み込み関数である fromJSON() を使い、ビルドマトリクスとして定義する

workflow_dispatch なワークフローの定義

type: boolean とすることでチェックボックスの入力項目が定義出来る。
入力値はチェックボックスがONであれば true 、OFFであれば false となる。

入力値の解析とビルドマトリクスの必要な値の動的な生成

ワークフローの入力値からBashの配列を生成し、jqコマンドを組み合わせて文字列のJSON配列を生成する。
生成したJSON配列を echo "::setoutput name=xxx::$yyy" でステップの結果として出力、さらにジョブの outputs に伝搬させることでジョブ間での値の共有を図る。
setup:
  runs-on: ubuntu-latest
  timeout-minutes: 3
  outputs:
    targets: ${{ steps.build-matrix.outputs.targets }}
  steps:
    - name: Setup Dynamic Build Matrix
      id: build-matrix
      run: |
        TARGETS=()

        if [ "${{ github.event.inputs.zeus }}" = "true" ]; then
          TARGETS+=("zeus")
        fi
        if [ "${{ github.event.inputs.athena }}" = "true" ]; then
          TARGETS+=("athena")
        fi
        if [ "${{ github.event.inputs.hades }}" = "true" ]; then
          TARGETS+=("hades")
        fi

        targets=$(printf '%s\n' "${TARGETS[@]}" | jq -R . | jq -s -c .)
        echo "::set-output name=targets::$targets"

動的なビルドマトリクスを設定してジョブを並列実行する

needs でジョブの依存関係を定義し、さらに先行ジョブである setup の出力値をGitHub Actionsの組み込み関数である fromJSON() に食わせることでJSON配列の出力値から動的にビルドマトリクスが設定される。
あとは ${{ matrix.targets }} を必要な箇所で使えばジョブが並列実行される。
ビルドマトリクスが動的生成されるのも面白いが、このビルドマトリクス自体がただの文字列でしかないので、これを使ってさらにコマンドを組み立てるようなことも可能なのが面白い。
今回の例ではあらかじめ実行するスクリプトを用意しておき、そのスクリプトとビルドマトリクスの文字列を一致させるようにすることで複数のスクリプトを並列実行させている。
execute:
  runs-on: ubuntu-latest
  timeout-minutes: 5
  needs:
    - setup
  strategy:
    fail-fast: true
    max-parallel: 3
    matrix:
      targets: ${{ fromJSON(needs.setup.outputs.targets) }}
  steps:
    - uses: actions/checkout@v3
    - name: Install Figlet
      run: sudo apt-get install -y figlet
    - name: Execute Script
      working-directory: scripts
      run: ./${{ matrix.targets }}.sh

実行結果

athena / hades / zeus の全てをチェックしてワークフローを実行した場合。
それぞれ別のスクリプトが実行されていることが分かる。

おわりに

今回のデモコードは以下。

Discussion

コメントにはログインが必要です。