Pull RequestトリガーのGitHub ActionsでSkip CIを実現する (Deprecated)

5 min read読了の目安(約5200字

この記事で説明している手法は不要になりました

GitHub Actionsは、組み込みでSkip CIをサポートするようになりました (GitHub Actions: Skip pull request and push workflows with [skip ci] - GitHub Changelog)。コミットメッセージに以下のキーワードが含まれていると、Workflowの実行が自動的にスキップされます。

  • [skip ci]
  • [ci skip]
  • [no ci]
  • [skip actions]
  • [actions skip]

そのため、この記事で紹介しているSkip CIの実現方法は不要になりました。記事を残しておきますが、以下で説明するJobをWorkflowに追加する必要はありません。

ここから不要になった方法の紹介です

Pull Requestトリガーで実行されるGitHub Actions Workflowで、コミットメッセージに [skip ci] などの特定のキーワードが含まれていたら、ビルドの実行をキャンセルする方法を紹介します。

PushトリガーのWorkflowの場合

PushトリガーのWorkflowでは、以下の記事で紹介されている方法で、Skip CIの仕組みを実現できます。

WorkflowがPushイベントで起動した場合、githubコンテキストgithub.event.head_commit.message プロパティにコミットメッセージが設定されています。contains 組み込み関数でコミットメッセージがキーワードを含んでいるかどうかを調べ、その結果を反転してJobの if に設定すれば、Jobの実行有無を制御することができます。

on: push
jobs:
  accept_commit:
    runs-on: ubuntu-20.04
    if: "! contains(github.event.head_commit.message, '[skip ci]')"
    steps:
    - run: echo "build is NOT skipped"
  build:
    runs-on: ubuntu-20.04
    needs: accept_commit
    steps:

Pull RequestトリガーのWorkflowの場合

Pull RequestトリガーのWorkflowでは、githubコンテキストのプロパティにコミットメッセージが入ってこないので、上記の方法は使うことができません。その代わり、コミットのハッシュが github.event.pull_request.head.sha プロパティでわかるため、git log コマンドでコミットメッセージを取得し、grep コマンドでキーワードの有無を調べることができます。以下に例を示します。

on: pull_request
jobs:
  check_commit:
    runs-on: ubuntu-20.04
    env:
      SKIP_CI_KEYWORD: "[skip ci]"
    outputs:
      skip_ci: ${{ steps.check_message.outputs.skip_ci }}
    steps:
    - uses: actions/checkout@v2
      with:
        fetch-depth: 2
    - name: Check commit message
      id: check_message
      run: |
        message=$(git log --format=%B -n 1 ${{ github.event.pull_request.head.sha }})
        if echo "$message" | grep -q -F "${{ env.SKIP_CI_KEYWORD }}"; then
          echo "::set-output name=skip_ci::true"
        else
          echo "::set-output name=skip_ci::false"
        fi
  build:
    runs-on: ubuntu-20.04
    needs: check_commit
    if: ${{ needs.check_commit.outputs.skip_ci != 'true' }}
    steps:

check_commit ジョブで、以下のことを実行しています。

  1. リポジトリをチェックアウトする
  2. コミットハッシュ github.event.pull_request.head.sha のコミットメッセージを取得して、[skip ci] キーワードが含まれているかどうかを調べる
  3. 調べた結果を、check_commit ジョブのOutput skip_citrue or false で設定する

後続のジョブは、needscheck_commit ジョブに依存することを宣言し、needs.check_commit.outputs.skip_cicheck_commit ジョブの skip_ci Outputを参照することができます。あとは、ジョブの if 条件文で、needs.check_commit.outputs.skip_citrue でない場合にジョブを実行する (= needs.check_commit.outputs.skip_citrue だったらジョブの実行をキャンセルする)、と記述することで、Skip CIの仕組みを実現できます。

ここで、check_commit ジョブにおいて、GITHUB_SHA をコミットのハッシュとして利用することはできないので、注意が必要です。Pull RequestトリガーのWorkflowでは、GITHUB_REF はGitHubが自動的に生成するのPRマージブランチ refs/pull/:prNumber/merge を指しており、GITHUB_SHA はこのPRマージブランチの最新コミットを指しています (Events that trigger workflows - GitHub Docs)。PRマージブランチは、GitHubが自動的に生成するブランチで、Pull Requestの作業ブランチとベースブランチをマージした状態を追跡しています。Pull Requestの作業ブランチとベースブランチのマージコミットが常に自動的に作成されており、Pull RequestトリガーのWorkflowでは GITHUB_SHA はそのマージコミットを指しています。今回、コミットメッセージを調べたいコミットは、Pull Request作業ブランチの最新コミット = マージコミット (GITHUB_SHA) の1つ前のコミットです。actions/checkout@v2 アクションは、デフォルトでは GITHUB_SHA のコミットだけしかチェックアウトしないので、fetch-depth: 2 パラメーターを指定してその1つ前のコミットまでチェックアウトし、GITHUB_SHA ではなく github.event.pull_request.head.sha のコミットを調べる必要があります。

GitHub Actionsのサンプル

上で紹介した方法のサンプルは、kafumi/github-actions-skip-ci-samples に配置してあります。