🚀

GitHub Actions、定期実行でプルリクエストを作成してマージする

2023/12/20に公開

とあるWebサイトの運営で毎日定時にコードの差分を検出して定期的にデプロイを実行しないといけない場面がありました。そこで毎日定時に develop ブランチと main ブランチの差分を検出してプルリクエストの作成とマージを自動で実施するGitHub Actionsを作成してみました。

事前準備

定期実行するワークフローの実行が終わったらデプロイを実行するGitHub Actionsを用意しておきます。

deploy.yaml
name: Deploy for production

on:
  # 別のワークフローから起動できるようにする
  workflow_call:
  # pushやpull_requestはGitHubボットが実行した
  # PRのマージには反応しないので以下は利用できない
  #push:
  #  branches: [ main ]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      # 開発しているアプリに合わせてビルドとデプロイのジョブを作っておく

Pull Requestを自動生成するActions

このジョブが毎日15時に実行されプルリクエストがマージされると、事前準備で設定したデプロイジョブが自動で実行されるようになっています。定期実行ジョブ -> デプロイジョブの順に実行されます。

daily-wf.yaml
name: Daily deploy for production

on:
  schedule:
    # 毎日15:00(UTC6:00)に実行する
    - cron:  '0 6 * * *'
  workflow_dispatch:
  
jobs:
  daily-deploy:
    runs-on: ubuntu-latest
    outputs:
      IS_DISPATCH: ${{ steps.merge_pr.outputs.IS_DISPATCH }}
    env:
      # GitHubコマンドを実行するために必要な設定
      GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
    steps:
      - name: Set current datetime as env variable
        env:
          TZ: 'Asia/Tokyo'
        run: echo "CURRENT_DATETIME=$(date +'%Y-%m-%d %H:%M:%S')" >> $GITHUB_ENV
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
	  # この設定がないと差分検出がうまくいかないので注意
          fetch-depth: 0
          ref: develop
      - name: Diff
        id: diff
        # diffがあるとfailureになる
        run: |
          git diff origin/main origin/develop --exit-code
	# failure時に次のジョブを実行できるようにする
        continue-on-error: true
      - name: 【定期実行】Create Pull Request
        id: create-pull-request
        # diffがある時のみPRを作成する
        if: steps.diff.outcome == 'failure'
        run: |
          PULL_REQUEST_URI=$(gh pr create -B main -t "【定期実行】本番リリース ${{ env.CURRENT_DATETIME }}" -l 'daily deploy' -b "")
          echo "PULL_REQUEST_URI=$PULL_REQUEST_URI" >> "$GITHUB_OUTPUT"
      - name: 【定期実行】Merge Pull Request
       id: merge_pr
        # diffがある時のみPRをマージする
        if: steps.diff.outcome == 'failure'
        run: |
          gh pr merge ${{steps.create-pull-request.outputs.PULL_REQUEST_URI}} --merge
	  echo "IS_DISPATCH=TRUE" >> "$GITHUB_OUTPUT"
  call-workflow:
    # merge_prのステップまで実行できたらdeploy.yamlを起動する
    if: needs.daily-deploy.outputs.IS_DISPATCH == 'TRUE'
    needs: [daily-deploy]
    uses: ./.github/workflows/deploy.yaml

定期実行

定期実行の設定は以下になります。基本は cron コマンドと同じです。

on:
  schedule:
    # 毎日15:00(UTC6:00)に実行する
    - cron:  '0 6 * * *'

差分の検出

コードの差分検出は git コマンドを使って行います。git diff コマンドは差分があると failure になるので continue-on-error を設定します。

- name: Diff
  id: diff
  # diffがあるとfailureになる
  run: |
    git diff origin/main origin/develop --exit-code
  # failure時に次のジョブを実行できるようにする
  continue-on-error: true

差分検出後のジョブは Diff が failure すなわち差分があるときだけ実行するようにします。

if: steps.diff.outcome == 'failure'

Pull Requestの作成

Pull Request の作成部分は以下になります。GitHubコマンドを使ってプルリクエストを作成します。次のマージジョブのためにプルリクのURLを PULL_REQUEST_URI 環境変数に設定しておくのがポイントです。

- name: 【定期実行】Create Pull Request
  id: create-pull-request
  # diffがある時のみPRを作成する
  if: steps.diff.outcome == 'failure'
  run: |
    PULL_REQUEST_URI=$(gh pr create -B main -t "【定期実行】本番リリース ${{ env.CURRENT_DATETIME }}" -l 'daily deploy' -b "")
    echo "PULL_REQUEST_URI=$PULL_REQUEST_URI" >> "$GITHUB_OUTPUT"

-l 'daily deploy' の部分はなくても問題ないです。あとで自動実行したプルリクを追えるようにラベルを設定しています。

Pull Requestのマージ

プルリクのURLを指定してGitHubコマンドでマージします。

- name: 【定期実行】Merge Pull Request
  # diffがある時のみPRをマージする
  if: steps.diff.outcome == 'failure'
  run: |
    gh pr merge ${{steps.create-pull-request.outputs.PULL_REQUEST_URI}} --merge

デプロイワークフローの呼び出し

merge_pr のステップまで実行できたらあらかじめ用意しておいた deploy.yaml のワークフローを起動します。

call-workflow:
  if: needs.daily-deploy.outputs.IS_DISPATCH == 'TRUE'
  needs: [daily-deploy]
  uses: ./.github/workflows/deploy.yaml

注意点

GitHub Actions のジョブ内で Pull Request をマージしても他のワークフローがトリガーされない罠があるのでご注意ください。回避策は GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} をやめて自前のデプロイトークンを利用する(GitHubボットに実行させない)か、workflow_run を使ってトリガーするか、または今回の例のように workflow_call を使うかの3択です。
https://www.memory-lovers.blog/entry/2022/11/20/180000

Discussion