🙌

GitHub Actions の依存関係は後続のジョブに波及する

2024/08/02に公開

以下のようなワークフローを作っていて、いざ実行させてみたら自分の認識とは違っていたのでメモを残しておく。

  1. main ブランチに push されたタイミングでワークフローを起動
  2. DB マイグレーション用のファイルが更新されたか確認する
  3. 2で変更があった場合、DB マイグレーション用のアプリをビルドする
  4. 3のビルドが実行された場合、DB マイグレーション用のアプリをデプロイする
  5. 4でデプロイが実行された場合、DB マイグレーションを実行する
  6. 5に依存させるかたちで、アプリをビルドする (DB マイグレーション関連のジョブはスキップされる可能性があるのでalways()をつける)
  7. 6に依存させるかたちで、アプリをデプロイする

YAML の抜粋は以下。

jobs:
  # DB マイグレーション用のファイルが更新されたか確認
  changes:
    runs-on: ubuntu-latest
    outputs:
      migrations: ${{ steps.filter.outputs.migrations }}
    steps:
      - name: Checkout
        uses: actions/checkout@v4
      - uses: dorny/paths-filter@v3
        id: filter
        with:
          base: 'refs/heads/${{ github.ref_name }}'
          filters: |
            migrations:
              - 'db/migrations/**/*.sql'

  # DB マイグレーション用のアプリをビルド
  build:
    needs: changes
    if: ${{ needs.changes.outputs.migrations == 'true' }}
    name: Build
    runs-on: ubuntu-latest
    # ... 省略 ...

  # DB マイグレーション用のアプリをデプロイ
  deploy:
    needs: build
    name: Deploy
    runs-on: ubuntu-latest
    # ... 省略 ...

  # DB マイグレーションの実行
  migrate:
    needs: deploy
    name: DB Migration
    runs-on: ubuntu-latest
    # ... 省略 ...

  # アプリのビルド
  build-app:
    name: Build
    runs-on: ubuntu-latest
    needs: migrate
    if: always()
    # ... 省略 ...

  # アプリのデプロイ
  deploy-app:
    needs: build-app
    name: Deploy
    runs-on: ubuntu-latest
    # ... 省略 ...

# ... 省略 ...

作った時点での想定では、build-app ジョブに always() を付けているので、以降の deploy-app ジョブは問題なく実行される想定だったがスキップされてしまった。

https://docs.github.com/ja/actions/writing-workflows/choosing-what-your-workflow-does/using-jobs-in-a-workflow

互いに必要とする一連のジョブが実行に含まれている場合、失敗またはスキップの時点から、依存関係チェーン内のすべてのジョブに失敗またはスキップが適用されます。 依存しているジョブが成功しなかった場合でもジョブを実行する場合は、jobs.<job_id>.if のalways() 条件式を使用します。

ここにバッチリ書かれていた。失敗またはスキップされた時点から後続の依存関係のあるジョブに波及するということらしい。
私が知らなかっただけ、かつ今までこういった場面に遭遇していなかったので勉強になった。

回避策は以下の通り依存関係のあるジョブに条件式を付けてあげるだけ。
if: ${{ always() && needs.build-app.result == 'success' }}

jobs:
  # ... 省略 ...

  # アプリのビルド
  build-app:
    name: Build
    runs-on: ubuntu-latest
    needs: migrate
    if: always()
    # ... 省略 ...

  # アプリのデプロイ
  deploy-app:
    needs: build-app
    if: ${{ always() && needs.build-app.result == 'success' }}
    name: Deploy
    runs-on: ubuntu-latest
    # ... 省略 ...

# ... 省略 ...

Discussion