🚥

GitHub Actions で差分有無に応じた細かい制御を行う

2022/05/15に公開

GitHub Actions では、指定したフォルダ (ファイル) の差分の有無によって workflow を実行するかどうかを制御することできますが[1]、差分がない場合にワークフロー全体をスキップする代わりに一部だけスキップできれば便利な状況があり、git diff の結果に応じて分岐するフローを作りました。

最終形

name: CI
on: pull_request
jobs:
  ci:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: git fetch origin ${{ github.base_ref }} --depth=1
      - id: diff
        run: echo "::set-output name=changed::$(git diff --name-only origin/${{ github.base_ref }} HEAD --relative ./フォルダ名/ | wc -l)"
      - if: ${{ steps.diff.outputs.changed != '0' }}
        run: npm run test  # テストコマンドを実行

以下は解説です。

マージ先のブランチ名

PRのマージ先のブランチ名は${{ github.base_ref }}で取得できます (Context)。なおワークフローがプルリクエストによってトリガーされたのでない場合 (例: デフォルトブランチに直接 push した場合) ここが空文字列になるため、この記事のワークフローは動作しなくなります。

マージ先をフェッチする

git fetch で PR のターゲットブランチをフェッチします。

      - run: git fetch origin ${{ github.base_ref }} --depth=1

origin は PR のターゲットのリポジトリを指します (fork から本家に PR を投げた場合、origin = 本家)。

--depth=1 で過去のコミットを遡ってフェッチするのを防ぎます。

差分の有無を検知する

注目しているフォルダ内で変更があったファイルの一覧を git diff で取得します。--name-only とすることで変更があったファイルのパスが一行一ファイルで列挙されます。

      - id: diff
        run: git diff --name-only origin/${{ github.base_ref }} HEAD --relative ./フォルダ名/

出力結果を wc -l して行数を数えます。一行一ファイルなので、行数 = 変更があったファイルの個数です。

      - id: diff
        run: git diff --name-only origin/${{ github.base_ref }} HEAD --relative ./フォルダ名/ | wc -l

この情報を後続のステップに渡すために ::set-output を使います。

      - id: diff
        run: echo "::set-output name=changed::$(git diff --name-only origin/${{ github.base_ref }} HEAD --relative ./フォルダ名/ | wc -l)"

後続のステップでこれを jobs.<job_id>.steps[*].if で読み取れば差分の有無に応じた分岐ができます。

      - id: diff
        run: echo "::set-output name=changed::$(git diff --name-only origin/${{ github.base_ref }} HEAD --relative ./フォルダ名/ | wc -l)"
      - if: ${{ steps.diff.outputs.changed != '0' }}
        run: npm run test  # テストコマンドを実行

参考にしたもの

https://zenn.dev/mizchi/articles/gha-run-test-only-changed

脚注
  1. on.push.paths などを使う ↩︎

Discussion