GitHub Actionsで大量差分時にワークフローが起動しない問題と解決策
GitHub Actionsは大量差分をトリガーしない
GitHub Actionsでは push
や pull_request
などのイベントを指定し、その発生時にワークフローを実行できます。
さらに paths
を設定すると、特定のファイルやディレクトリが変更された場合のみワークフローを動作させることも可能です。
しかし、稀にこのトリガーが意図通りに動作しないケースがあります。
これはGitHub Actionsの仕様として、検知できるファイルの変更数が最大300ファイルまでに制限されているためです。
(参考: GitHub Discussions)
検証
例えば、以下のように dir-a
用のデプロイ用ワークフローを作成します。
name: Directory A Continuous Deployment Workflow
on:
push:
paths:
- 'dir-a/**'
- '.github/workflows/dir-a-cd.yml'
branches:
- 'develop'
- 'staging'
- 'production'
jobs:
hello-world:
runs-on: ubuntu-latest
steps:
- name: Say Hello
run: echo "Hello, world!"
これを dir-a
〜 dir-e
まで計5つ用意し、各ディレクトリで100個の空ファイルを作成します。
touch {1..100}.txt
合計500ファイルの変更を加えて、develop
ブランチへ変更を反映すると、全てのワークフローの実行が期待されますが、3つのワークフローしか実行されないことが確認できます。
回避方法
この制限を回避する方法のひとつが dorny/paths-filter
の利用です。
※ このアクションは公式提供ではありません。利用は自己判断でお願いします。
仕組みとしては、まず「変更検知専用のワークフロー」を1つ用意し、そこで paths-filter
で変更有無を判定します。
その結果に応じて、必要なワークフローを workflow_call
で呼び出します。
変更検知専用のワークフローの例
name: Multi Directory Continuous Deployment Workflow
on:
push:
paths:
- 'dir-a/**'
- '.github/workflows/dir-a-cd.yml'
- 'dir-b/**'
- '.github/workflows/dir-b-cd.yml'
- 'dir-c/**'
- '.github/workflows/dir-c-cd.yml'
- 'dir-d/**'
- '.github/workflows/dir-d-cd.yml'
- 'dir-e/**'
- '.github/workflows/dir-e-cd.yml'
branches:
- "develop"
- "staging"
- "production"
jobs:
changes:
runs-on: ubuntu-latest
outputs:
dir-a: ${{ steps.filter.outputs.dir-a }}
dir-b: ${{ steps.filter.outputs.dir-b }}
dir-c: ${{ steps.filter.outputs.dir-c }}
dir-d: ${{ steps.filter.outputs.dir-d }}
dir-e: ${{ steps.filter.outputs.dir-e }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
base: ${{ github.ref }}
filters: |
dir-a:
- 'dir-a/**'
- '.github/workflows/dir-a-cd.yml'
dir-b:
- 'dir-b/**'
- '.github/workflows/dir-b-cd.yml'
dir-c:
- 'dir-c/**'
- '.github/workflows/dir-c-cd.yml'
dir-d:
- 'dir-d/**'
- '.github/workflows/dir-d-cd.yml'
dir-e:
- 'dir-e/**'
- '.github/workflows/dir-e-cd.yml'
call-dir-a-workflow:
needs:
- changes
if: ${{ needs.changes.outputs.dir-a == 'true'}}
uses: ./.github/workflows/dir-a-cd.yml
secrets: inherit
call-dir-b-workflow:
needs:
- changes
if: ${{ needs.changes.outputs.dir-b == 'true'}}
uses: ./.github/workflows/dir-b-cd.yml
secrets: inherit
call-dir-c-workflow:
needs:
- changes
if: ${{ needs.changes.outputs.dir-c == 'true'}}
uses: ./.github/workflows/dir-c-cd.yml
secrets: inherit
call-dir-d-workflow:
needs:
- changes
if: ${{ needs.changes.outputs.dir-d == 'true'}}
uses: ./.github/workflows/dir-d-cd.yml
secrets: inherit
call-dir-e-workflow:
needs:
- changes
if: ${{ needs.changes.outputs.dir-e == 'true'}}
uses: ./.github/workflows/dir-e-cd.yml
secrets: inherit
これにより、中央の検知ワークフローが実行され、変更ファイル数に関係なく必要なワークフローだけが動くようになります。
注意点
理想は大量のファイル変更が発生しないよう運用を工夫すべきですが、どうしても避けられないケースでは今回紹介した方法が有効です。
ただし、通常時は paths
による実行制御のみで十分であり、実行時間やコストの面でも有利です。
むやみに集約ワークフローを導入すると、逆に全体の実行時間が延びる可能性があるため注意しましょう。
Discussion