💨

モノレポでの無駄な GitHub Actions の実行を避ける

2022/04/20に公開

最近では多くの開発において、モノレポが採用されているかと思います。

その時、GitHub Actions はトップレベルでしか用意できないため、フロントエンドしか更新していなくてもバックエンドのテストが走ってしまいますが、あまり意味がありません。
むしろ、Organization の quota をなるべく消費したくないはずです。

そこで、関係無い変更が行われたときに、その CI を無視するように設定する方法について、こちらの記事で紹介させていただきます。

paths-filter を使用する

https://github.com/dorny/paths-filter

paths-filter を使用すると、特定のファイル変更に対して、GitHub Actions の if で使用するための output が提供されます。

こちらをモノレポで使っていくには、以下のように設定していきます。

.github/filters.yml を用意します。
こちらは、YAML の anchor と alias を使用できるため、以下のように書けます。

.github/filters.yml
backend_ci: &backend_ci
  - '.github/workflows/backend*.yml'

backend:
  - *backend_ci
  - 'backend/**'


frontend_ci: &frontend_ci
  - '.github/workflows/frontend*.yml'

frontend:
  - *frontend_ci
  - 'frontend/**'

そうすると、以下のように、フロントエンドの変更があった時だけ Lint を実行する GitHub Actions が書けます。

.github/frontend-lint.yml
- uses: actions/checkout@v3.0.0

- name: Check for file changes
  uses: dorny/paths-filter@v2
  id: changes
  with:
    token: ${{ github.token }}
    filters: .github/filters.yml

- if: steps.changes.outputs.frontend == 'true'
  run: npm run lint

Branch Protection Rules を通すには、成功したように見せかける必要があります。

そのため、job 単位では実行する必要があり、step 単位ではスキップしていくという流れになります。

上記の例では、ファイルの変更をチェックした後、一度のみ step を実行していますが、その後も step がある場合は、全てに if を付けていく必要があります。
そうすることで、frontend/** に対して変更が無い場合は、ほぼ一瞬で GitHub Actions が成功するようになります。

paths-filter の設定ファイルを読む必要があるため、job に対して permissions を明示的に書きたい場合は、以下のようにする必要があります。

jobs:
  lint:
    permissions:
      contents: 'read'
      pull-requests: 'read'

Branch Protection Rules に指定されていない job

main ブランチにマージされたら、開発環境のデプロイを行う Action の場合は、Branch Protection Rules に入っていないはずです。

そういったものは、単純に以下のように job ごとスキップするように実装すると、step 毎に if を書く必要が無く、綺麗です。

jobs:
  changes:
    permissions:
      contents: 'read'
      pull-requests: 'read'

    runs-on: ubuntu-latest

    outputs:
      backend: ${{ steps.filter.outputs.backend }}
    steps:
      - uses: actions/checkout@v3.0.0
      - uses: dorny/paths-filter@v2
        id: filter
        with:
          token: ${{ github.token }}
          filters: .github/filters.yml

  deploy:
    permissions:
      contents: 'read'

    needs: changes
    if: needs.changes.outputs.backend == 'true'

    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3.0.0
        # if: は不要

        ...

最後に

どうしても、step 毎に if を書くと汚く見えてしまいます。
また、他の条件も組み合わせたい場合は、&& で繋げていく必要があります。

最初はあまり納得がいかず、色々と調べてみましたが、現状、Branch Protection Rules と組み合わせたい場合はこの方法しか無いみたいです。

こちらの記事が参考になれば幸いです。

GitHubで編集を提案

Discussion