GitHub ActionsでファイルAが更新されているがファイルBが更新されていない時だけ実行する、を実現する

GitHub Actionsの on: pushon: pull_rerequest のトリガーには paths/paths_ignore という機能があり、特定のファイルパスが変更された時だけワークフローを実行できます。

# 例えばiOSとAndroidのmonorepoで、ios以下が変更された時だけiOSのテストを実行したい
on:
  push:
    paths:
      - 'ios/**'

paths/paths_ignoreには配列で複数のパス指定・globパターン指定ができ、そのうちのどれか1つにでもマッチするとワークフローが実行されます。しかしこの機能には1つ弱点があり、pathspaths_ignore を同時に使うことができません。つまり、ファイルAが変更され・かつ・ファイルBが変更されていない、という条件が記述できません(と認識しています)。

You can exclude paths using two types of filters. You cannot use both of these filters for the same event in a workflow.

今回はこれを乗り越える方法の紹介です。trilom/file-changes-actionというアクションを使用して、ジョブ内で自分でファイル差分をチェックしてみましょう。ここではiOSアプリのリポジトリで、Podfileが更新されているがPodfile.lockが更新されていない時、ということにしてみます。

name: Update Podfile.lock

on:
  pull_request:
    paths:
      - Podfile

jobs:
  precheck:
    runs-on: ubuntu-latest
    outputs:
      files_changed: ${{ steps.file_changes.outputs.files }}
    steps:
      - uses: actions/checkout@v2
      - id: file_changes
        uses: trilom/file-changes-action@v1.2.3

  update:
    runs-on: macos-latest
    needs: precheck
    if: contains(fromJson(needs.precheck.outputs.files_changed), 'Podfile.lock') == false
    steps:
      - uses: actions/checkout@v2
      - run: pod install
      - uses: stefanzweifel/git-auto-commit-action@v4.2.0
        with:
          commit_message: Update Podfile.lock
          file_pattern: Podfile.lock

まずは前提条件としてpathsPodfileを指定して、「Podfileが更新されている」ことをワークフローレベルでフィルタリングします。

ここからが本題です。precheckジョブで、trilom/file-changes-actionを使ってファイル差分を取得します。そしてそのステップのoutputジョブのoutputに保存して、後続のジョブからアクセスできるようにします。ここでジョブを分割しているのは

  • 後続の処理が複数ステップに渡る場合、それぞれのステップにifを書くのが面倒・メンテナンスしにくい
  • 今回の例では後続の処理にmacOSが必要だが、macOSは課金が高額(Ubuntuの10倍!)なので、ファイル差分のチェックはUbuntuで行う

ためです。

updateジョブはneedsを使ってprecheckジョブの終了後に直列で実行されるようにします(デフォルトでは複数のジョブは並列で実行されます)。次にジョブレベルのifで、Podfile.lockが変更されていない、という条件を書きます。needs contextを使うと、前段のジョブの実行結果やoutputsにアクセスできます。ここではprecheckジョブでoutputsに保存したtrilom/file-changes-actionの出力結果を使用し、contains関数で変更されたファイル名の配列にPodfile.lockが含まれるかを確認します。

後は実処理として pod installしてPodfile.lockを更新してから、stefanzweifel/git-auto-commit-actionを使ってプルリクエストを作成しています。

こうして「Podfileが更新されているがPodfile.lockが更新されていない時」という条件を実現できました。めでたしめでたし。色んなアクションを組み合わせて柔軟なジョブ制御を実現していきましょう。