🤖

GitHub Actions によるデプロイの手動起動で開発体験を改善する

に公開

オンライン家庭教師マナリンク 開発の Technote です。

今回は GitHub Actions を利用した開発体験改善の取り組みの一部を紹介します。

背景

もともと develop ブランチにマージでステージングに、master ブランチにマージで本番にそれぞれデプロイする workflow が組まれていました。

本番でも使用している Docker 環境を使用しているので、各自ローカルで本番に近い環境で開発できていますが、Expo のプッシュ通知や外部からの Webhook の動作などは確認が難しい、もしくはできない場合があります。

費用的、開発規模的な面からしばらくはサーバーはステージングと本番のみで運用したい、でも develop にマージする前に動作確認もしたい...

一応 develop ブランチだけではなく特定の名前(例: for-actions/deploy)がついたブランチにプッシュすることでステージングにデプロイするようにもなっていましたが、以下のような問題がありました。

  1. 確認後にステージングを develop ブランチの状態に戻すのが面倒
  2. 特定の名前のブランチにプッシュはしたいけどデプロイはしたくない場合はアクションをキャンセルする必要があり面倒

この状況を少しでも改善するために GitHub Actions でいつでも簡単にデプロイできるようにしました。

やったこと

PR にデプロイ用ラベルを付与でデプロイ

プルリクを作成している場合、そのプルリクに特定のラベルを追加することで workflow が起動するようにしました。

  1. デプロイ開始時に「デプロイ用」ラベルを剥がして「デプロイ中」ラベルを付与
  2. デプロイ処理
  3. 「デプロイ中」ラベルを剥がす

label-deploy

以下がその workflow です。

on:
  pull_request:
    types: [ labeled ]

name: Manual Deploy

jobs:
  # デプロイを起動するかどうかチェック
  # 後で追加する起動条件のためにチェックとデプロイを分けておく
  check:
    name: Whether to run
    runs-on: ubuntu-latest
    timeout-minutes: 1 # ほぼラベルのチェックだけなのでタイムアウトを短く設定
    outputs:
      run: ${{ env.RUN_FLAG }} # 後続のjobでチェックの結果を使用
    steps:
        # 特定のラベル (例: Deploy) の場合にフラグを立てる
      - run: echo "RUN_FLAG=1" >> $GITHUB_ENV
        if: github.event.label.name == 'Deploy'

        # actions/github-script は Octokit も利用できる JavaScript を実行できちゃうすごいやつ
        # ここでは Deploy ラベルを剥がして Deploying ラベルを付ける
      - uses: actions/github-script@v4
        name: Remove label
        if: github.event.label.name == 'Deploy Staging'
        with:
          script: |
            github.issues.removeLabel({
              ...context.repo,
              issue_number: context.issue.number,
              name: 'Deploy'
            })
            github.issues.addLabels({
              ...context.repo,
              issue_number: context.issue.number,
              labels: ['Deploying']
            })

  deploy:
    name: Deploy
    runs-on: ubuntu-latest
    timeout-minutes: 10 # タイムアウトは必ず設定しましょう
    needs: check
    if: needs.check.outputs.run # check ジョブの結果を使用
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      # 任意のデプロイステップ
      # ...

  # 付与した Deploying ラベルを剥がす
  remove-label:
    name: Remove Label
    runs-on: ubuntu-latest
    timeout-minutes: 1 # ラベルを剥がすだけなのでタイムアウトを短く設定
    needs: deploy
    # デプロイジョブが失敗していてもラベルを剥がす
    # failure の場合は Slack に通知などの処理を入れてもいいかも
    if: always() && needs.check.outputs.run
    steps:
      - uses: actions/github-script@v4
        name: Remove label
        with:
          script: |
            github.issues.removeLabel({
              ...context.repo,
              issue_number: context.issue.number,
              name: 'Deploying'
            })

ラベルの付与のようなイベントでも起動できるのは本当に使い勝手がいいです。

他にも起動させられるイベントが色々あって眺めるだけでも楽しいです。

https://docs.github.com/ja/actions/reference/events-that-trigger-workflows

手動で任意のブランチをデプロイ

develop ブランチの状態に戻したり、プルリクを作成していないブランチのデプロイにも対応できるようにブランチを指定して起動できるようにしました。
とは言っても起動できるようにするには以下の1行を追加するだけです‥!

 on:
   pull_request:
     types: [ labeled ]
+  workflow_dispatch:

これだけでアクションの画面からブランチを選択して起動できるようになります。

branch-deploy

ただ今回はプルリクへのラベル追加の時にも起動するので、workflow 内の if を少し書き換える必要があります。
前述の workflow を少し書き換えて最終的には以下のような workflow になりました。

 on:
   pull_request:
     types: [ labeled ]
+  workflow_dispatch:

 name: Manual Deploy

 jobs:
   check:
     name: Whether to run
     runs-on: ubuntu-latest
     timeout-minutes: 1
     outputs:
       run: ${{ env.RUN_FLAG }}
     steps:
       - run: echo "RUN_FLAG=1" >> $GITHUB_ENV
-        if: github.event.label.name == 'Deploy'
+        if: github.event_name == 'pull_request' && github.event.label.name == 'Deploy'
+      - run: echo "RUN_FLAG=1" >> $GITHUB_ENV
+        if: github.event_name == 'workflow_dispatch'

       - uses: actions/github-script@v4
         name: Remove label
-        if: github.event.label.name == 'Deploy Staging'
+        if: github.event_name == 'pull_request' && github.event.label.name == 'Deploy'
         with:
           script: |
             github.issues.removeLabel({
               ...context.repo,
               issue_number: context.issue.number,
               name: 'Deploy'
             })
             github.issues.addLabels({
               ...context.repo,
               issue_number: context.issue.number,
               labels: ['Deploying']
             })

   deploy:
     name: Deploy
     runs-on: ubuntu-latest
     timeout-minutes: 10
     needs: check
     if: needs.check.outputs.run
     steps:
       - name: Checkout
         uses: actions/checkout@v2
       # 任意のデプロイステップ
       # ...

   remove-label:
     name: Remove Label
     runs-on: ubuntu-latest
     timeout-minutes: 1
     needs: deploy
-    if: always() && needs.check.outputs.run
+    if: always() && needs.check.outputs.run && github.event_name == 'pull_request'
     steps:
       - uses: actions/github-script@v4
         name: Remove label
         with:
           script: |
             github.issues.removeLabel({
               ...context.repo,
               issue_number: context.issue.number,
               name: 'Deploying'
             })

以上の設定により、当初の課題であったステージングサーバー上での動作確認への心理的障壁を下げることができました。

また、Firebase などの PaaS でも同じように workflow を用意すれば動作確認がより手軽に行うことができます。

マナリンク Tech Blog

Discussion