3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

GitHub Actionsのjobはmatrixがexcludeにより空になった場合も実行されてしまう

Last updated at Posted at 2023-02-13

TL;DR;

GitHub Actionsのjobはmatrixを使うことで、複数の入力条件で実行できる。
また、条件として列挙されたうちの一部をexcludeで除外することができる。

しかし、excludeによってmatrixの要素数が0になると、matrixの値が空の状態で実行されてしまう。
多くの場合、これは意図しない動作になるだろう。

jobの実行は防げないので、jobの中のstepでmatrixの値をチェックして個別の処理をスキップする必要がある。

背景

複数のリソースを管理しているリポジトリで

  • diffのあったリソースのみデプロイする
  • ただし、特定のリソースは2つある環境(stageとproduction)のうち一方にだけデプロイする

というワークフローを書いていた。
例えば、こんな構成になっていて、

resources/
    a/ # stageとproduction両方
    b/ # stageとproduction両方
    c/ # stageのみ
  • aに変更があったときは、aをstageとproductionにリリースする
  • cに変更があったときは、cをstageにリリースする

といった具合である。
これを実現するために、

  1. 変更のあったディレクトリを検知
  2. 変更したディレクトリのリストをmatrixの要素として受け取りstageのデプロイを実行
  3. 変更したディレクトリのリストをmatrixの要素として受け取りproductionのデプロイを実行
    • ただし、excludeでcを指定

というワークフローを書いた。
そしてcに変更があった場合、期待する動きは

  1. 変更のあったディレクトリとしてcが検知
  2. 変更したディレクトリのリストとして ["c"] をmatrixとして受け取り、stageにcをデプロイ
  3. 変更したディレクトリのリストとして ["c"] をmatrixとして受け取るが、cはexcludeで指定されてmatrixは空なので何も実行せず終了

だった。
しかし、実際には

  1. 変更のあったディレクトリとしてcが検知
  2. 変更したディレクトリのリストとして ["c"] をmatrixとして受け取り、stageにcをデプロイ
  3. 変更したディレクトリのリストとして ["c"] をmatrixとして受け取るが、cはexcludeで指定されてmatrixは空なのだが、要素として空の値(null)が指定されて1回実行される

という挙動となった。
その結果、本来、a,b,cのいずれかを期待しているjobはエラーを起こし、failedで完了することとなった。

コードイメージ

そのコードを直接載せるのは差し支えがあるし、そもそも見づらいので、同様の問題が発生するシンプルなコードを掲載する。

deploy.yaml
name: Empty matrix deploy

on:
  pull_request:
    branches:
      - main

jobs:
  # 対象のリソースを取得する(ここでは固定でcを取得しているが、本来は動的に決まるもの)
  get_target_resources:
    runs-on: ubuntu-latest
    outputs:
      resources: ${{ steps.get_resources.outputs.resources }}
    steps:
      - name: get resources
        id: get_resources
        run: |
          echo 'resources=["c"]' >> $GITHUB_OUTPUT

  # stageのデプロイ(すべてのリソースをデプロイする)
  deploy_stage:
    runs-on: ubuntu-latest
    needs: get_target_resources
    strategy:
      matrix:
        resource: ${{ fromJson(needs.get_target_resources.outputs.resources) }}
    steps:
      # matrix.resourcが"c"で実行されるので、 "deploy resource: [c]" と出力される
      - name: deploy resource
        run: |
          echo "deploy resource: [${{ matrix.resource }}]"

  # productionのデプロイ(cはデプロイしないので、本来は実行されてほしくないのだが、resourceの値がnullで実行されてしまう)
  deploy_production:
    runs-on: ubuntu-latest
    needs: get_target_resources
    strategy:
      matrix:
        resource: ${{ fromJson(needs.get_target_resources.outputs.resources) }}
        exclude:
          - resource: "c"
    steps:
      # matrix.resourcがnullで実行されるので、 "deploy resource: []" と出力されてしまう
      - name: deploy resource
        run: |
          echo "deploy resource: [${{ matrix.resource }}]"

とりあえずの解決方法

調べた限り、このケースにjobの実行を防ぐすべは見当たらなかった(知っている人がいたらぜひ教えてほしい)。
matrixの値にnullが入ることを利用して、以下のようにstepの中でmatrixの値をチェックし、nullでない場合のみ実行するようにすれば、意図しない(つまりmatrixの値がnullでの)実行は防ぐことができる。

  deploy_production_workaround:
    runs-on: ubuntu-latest
    needs: get_target_resources
    strategy:
      matrix:
        resource: ${{ fromJson(needs.get_target_resources.outputs.resources) }}
        exclude:
          - resource: "c"
    steps:
      # nullでない場合のみ実行する
      - name: deploy resource
        if: ${{ matrix.resource != null }}
        run: |
          echo "deploy resource: [${{ matrix.resource }}]"

補足: matrixとして受け取る値が空の場合

excludeによって空になるのではなく、そもそもmatrixに入る配列が空になるようなケースの対応はこちらで紹介されている。
前のjobで作った配列(になるJSON文字列)をチェックし、空と見なされるようなケースはifで除くというやり方である。

抜粋
test:
  needs: changes
  if: ${{ needs.changes.outputs.services != '[]' && needs.changes.outputs.services != '' }}
  strategy:
    matrix:
      service: ${{ fromJson(needs.changes.outputs.services) }}

一応補足しておくと、この方法はexcludeで空になるようなケースでは使えない。

test:
  needs: changes
  if: ${{ matrix.service != null }}
  strategy:
    matrix:
      service: ${{ fromJson(needs.changes.outputs.services) }}

のような書き方をすると、このスコープではmatrixが使えないため、
Unrecognized named-value: 'matrix'
というエラーが出て実行できない。

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?