📆

Github Actionsでpushされた時に特定ブランチとタグでトリガーする

2022/12/12に公開

はじめに

Github Actionsしてますか?

CI/CDフローを作成していると特定のブランチにタグを打ったタイミングにだけ処理を走らせたくなりますよね。
mainブランチのコミットにバージョンタグ(ie. v1.0.0)が付けられた時だけ本番環境にデプロイしたい etc.

そんな時、Github Actionsでどう実現するかを考えてみました。

結論

こうなりました。

mainブラインチ,v*タグの例
name: Example

on:
  push:
    tags:
      - 'v*'

jobs:
  prod_deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout main branch
        uses: actions/checkout@v3
        with:
          ref: main
          fetch-depth: 0
      - name: Check tagged branch
        run: |
          BRANCHS=$(git branch --contains ${{ github.ref_name }})
          set -- $BRANCHS
          for BRANCH in $BRANCHS ; do
            if [[ "$BRANCH" == "main" ]]; then
              exit 0
            fi
          done
          exit 1
      - name: Next step...
        run: |

詳細をみていきます。

トリガー Trigger

まずはトリガーについて確認します。
今回のシナリオではpushイベントに対してジョブを実行したいので、

on:
  push:

を使います。

pushイベントに対しては、pushされたブランチやタグの名前でフィルターを記述することができます。
それにより特定のブランチやタグでのみジョブを実行することが可能です。

このフィルターに特定のブランチへの指定のタグ付与という条件を記述できれば解決します。
しかし、Using filters to target specific branches or tags for push eventsの項を読むとbranchestagsを単純に併記した場合には、どちらかを満たせばワークフローが実行されるとわかります。

つまり、

on:
  push:
    branches:
      - 'main'
    tags:
      - 'v*'

としても、

  1. mainブランチにpushされた時
  2. (どのブランチへのコミットかに関わらず)v*タグがpushされた時

のどちらの時にもワークフローが開始されます。

そのため目的の挙動を実現するには、ブランチへのpushで開始されたワークフローの中でタグの有無を確認する、あるいは、タグのpushで開始されたワークフローの中で該当コミットがブランチに含まれるかを確認することが必要です。

今回は後者の方法を深掘りします。

コンテキスト Context

コンテキストを参照すると、トリガーとなったイベントの周辺情報を確認できます。

公式ドキュメントにある次の例のように、特定のジョブについてはpushされたのがmainブランチの時のみ実行する、という分岐を書けるようになります。

name: CI
on: push
jobs:
  prod-check:
    if: ${{ github.ref == 'refs/heads/main' }}
    runs-on: ubuntu-latest
    steps:
      - run: echo "Deploying to production server on branch $GITHUB_REF"

とはいえ、タグでトリガーされた時にブランチ名を直接参照するコンテキストは存在しません。そのため、上の例と同じようにif構文を使う方法ではうまくいきません。コンテキストをgitコマンドと組み合わせて使い、コマンドラインで取得する必要があります。

タグのつけられたコミットが含まれるブランチは次のgitコマンドで取得できます。

git branch --contains ${TAG}
$ git branch --contains v1.0.0
* main
  test_branch

このコマンドを使えばpushされたタグについて、特定のブランチにも含まれるのかを確認できそうです。
pushされたタグはコンテキストgithub.ref_nameを使ってタグ名を取得できます。

こねくりまわす

ここまでをまとめると、

  1. 特定のタグがpushされた時に起動する
  2. そのタグがついたコミットが含まれるブランチ一覧を取得する
  3. ブランチ一覧に特定のブランチが含まれる時正常終了、含まれない時異常終了する

とすれば実現できそうです。

それぞれ、次の通り記述できます。

  1. 特定のタグがpushされた時に起動する
on:
  push:
    tags:
      - 'v*'
  1. そのタグがついたコミットが含まれるブランチ一覧を取得する
    steps:
      - name: Checkout main branch
        uses: actions/checkout@v3
        with:
          ref: main
          fetch-depth: 0
      - name: Check tagged branch
        run: |
          BRANCHS=$(git branch --contains ${{ github.ref_name }})
  1. ブランチ一覧に特定のブランチが含まれる時正常終了、含まれない時異常終了する
          set -- $BRANCHS
          for BRANCH in $BRANCHS ; do
            if [[ "$BRANCH" == "main" ]]; then
              exit 0
            fi
          done
          exit 1

組み合わせたものが結論になっています。

おわりに

Github Actionsでpushされた時に特定ブランチとタグでトリガーする方法について記述しました。

今回のやり方ではmainブランチに含まれるコミットに対する命名規則を満たしたタグ付け全てにトリガーされます。
変更を遡ることに柔軟である一方、厳格さに欠ける面もあるかもしれません。
よりミスを減らすにはタグ付け自体をワークフローで実行するなど工夫できると良さそうです。
オペレーション自体を見直せるのであればワークフローを複雑化せずに済みなお良いでしょう。

シンプルで最良なフローの実装を頑張りたいですね。

Discussion