GitHub Actionsで重複ビルドを排除する

最近のTravisCIの残念なムーブもあり、GitHub Actionsへの移行を積極的に行なっているのですが、個人的に1つ気になっているのがブランチへのプッシュとプルリクエストに対するアクションでビルドが重複してしまうケースがあることです。

まず、GitHub上で標準で用意されているテンプレートからGitHub Actionsの設定を作成すると以下のような感じになります。

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

この場合、masterにpushしたコミット、およびmasterに対するプルリクエストに対してビルドが実行されます。ただ、TravisCIやCircleCIではmasterブランチ以外のブランチに対するコミットやプルリクエストでもビルドが走っていたのでこれと同じ動作を実現するために以下のように設定を変更します。

on: [push, pull_request]

すると、このリポジトリに作成したブランチからプルリクエストを作成した場合に、以下のようにブランチのpushに対するビルドとプルリクエストに対するビルドで同じコミットに対して2回ビルドが走ってしまいます。

f:id:takezoe:20201204162745p:plain

まあ問題ないといえばないのですが、同じコミットに対して二回ビルドするのは無駄ですし、ジョブが多いとプルリクエストに大量のジョブ実行結果が二重に表示されてしまうので、できれば避けたいところです(ちなみにTravisCIでは同様に重複実行されるのですが、CircleCIでは重複実行されないようになっているようです)。調べてみたところ、以下のように設定されているオープンソースプロジェクトを見かけました。

on:
  push:
    branches: [ master ]
  pull_request:

この設定はコードの変更時は必ずプルリクエストを作成し、マージされたらmasterブランチをビルドするというワークフローであればうまく機能します。とはいうものの、個人のリポジトリでは毎回プルリクエストを作成するのも面倒ですし、逆に業務では複数のリリースブランチを運用したい(masterブランチ以外へのpushでもビルドを走らせたい)というケースも多いのではないかと思います。

どうやらGitHub Actionsで完全にTravisCIやCircleCIと同じ挙動にすることは難しそうな気配なので、次善の策として以下のSkip Duplicate Actionsというアクションを試してみました。

github.com

READMEにも書いてあるように、以下のような感じでpre_jobという重複をチェックするジョブを追加し、もしビルドが重複している場合はmain_jobでifを使ってジョブをスキップするようにします。

jobs:
  pre_job:
    runs-on: ubuntu-latest
    # Map a step output to a job output
    outputs:
      should_skip: ${{ steps.skip_check.outputs.should_skip }}
    steps:
      - id: skip_check
        uses: fkirc/skip-duplicate-actions@master
        with:
          github_token: ${{ github.token }}

  main_job:
    needs: pre_job
    if: ${{ needs.pre_job.outputs.should_skip != 'true' }}
    runs-on: ubuntu-latest
    steps:
      - run: echo "Running slow tests..." && sleep 30

確かにこのアクションによってmain_jobをスキップすることは可能でした。しかし結果的にスキップはされるものの、ビルドはトリガーされてしまうので、プルリクエストには以下のように複数のビルドが表示されてしまいます。

f:id:takezoe:20201204162808p:plain

というわけでGitHub ActionsでTravisCIやCircleCIと同じ動作をさせつつ重複ビルドを排除するという目的はいまだ達成できていないのでした。何か方法はあるのでしょうか…。