GitHub ActionsでEnvironmentsを使わずにManual approvalを実装する

SREの今です。

CircleCIやGitHub Actions等のCI/CDツールでデプロイの自動化に取り組む際に、意図しない本番反映の防止策として承認アクション(Manual approval)を挟んでおくことがあります。
GitHub Actionsでは、Environmentsを利用することでワークフローの実行前にレビューを必須化することが出来ます1。しかし、この機能をプライベートリポジトリで利用するにはリポジトリの所有者がGitHub Enterpriseを契約している必要があります2
業務の都合上、Environmentsを利用できないリポジトリでManual approvalを利用したい機会があり、ワークフロー実行履歴を利用したManual approvalを実装したのでご紹介します。

本記事はSRE連載の12月号 + 面白法人グループ Advent Calendar 2023 の3日目のエントリーです。

方針

デプロイを実行するワークフローと別にデプロイ承認用のワークフローを用意し、デプロイ承認ワークフローの実行履歴を承認済みとしてデプロイ作業を続行させます。

概要

デプロイ承認ワークフローの実行履歴にはCommit Hashが紐づいているため、絞り込むことで対象の実行ワークフローを狙って承認することができます。

  • デプロイ実行ワークフローを実行し、デプロイを開始する
  • 実行ワークフローの途中で、Sleepを挟みつつGitHub CLI gh run list を実行し、承認ワークフローの実行完了を待機する
  • 実行ワークフローを実行したBranchを指定してデプロイ承認ワークフローを実行する
  • 承認ワークフローが成功すると、実行ワークフローが次のStepへ進みデプロイが進行する

GitHub Actinosの設定実例

デプロイ本処理を行うデプロイ実行ワークフローでは、手動承認待ちのタイミングでgh run list --workflow approve …を10秒間隔で実行します。
Stepのtimeout-minutes: 5で承認待ち時間を5分間に指定しています。

name: deploy
on:
  workflow_dispatch
jobs:
  deploy:
    name: deploy
    runs-on: ubuntu-latest
    timeout-minutes: 10
    steps:
    - name: Check out code
      uses: actions/checkout@v3

    - name: Wait manual approval
      run: |
        ghRunListCmd="gh run list --workflow approve --branch ${{ github.ref_name }} \
            --status success --json headSha --jq ".[].headSha" --limit 1"

        isApproved=false
        until ${isApproved}; do
          sleep 10
          result=$(${ghRunListCmd})

          # github.shaが一致すればOK
          if [ "${{ github.sha }}" = "${result}" ]; then
            isApproved=true
          fi
        done

    - name: Deploy
      run : |
        make deploy

手動承認の代わりとなる承認ワークフローは、必要に応じてデプロイを承認する条件を判定して、ワークフロー自体を成功/失敗で終了させます。
今回の例では、私自身のGitHubアカウントのユーザー名ToshihitoKon以外で実行された場合は承認しないよう失敗させています。

name: approve
on:
  workflow_dispatch
jobs:
  approve:
    name: approve
    runs-on: ubuntu-latest
    steps:
      - name: Check unauthorized users
        if: github.triggering_actor != 'ToshihitoKon'
        run: exit 1

deployワークフローを実行し、Wait manual approvalStepが実行されている間にapproveワークフローを実行すると、承認とみなしてdeployワークフローのWait manual approvalStepを終了し、次のStepへ進みます。

動作デモ

実行ワークフローと承認ワークフローのみを用意したデモです。
https://github.com/ToshihitoKon/actions-manual-approval-demo

デモのSlack通知

こちらは実際に業務で利用している様子です。
実行する内容、実行結果を逐次Slackに報告するようにしています。

実際のデプロイに利用しているSlack通知

今回の例ではワークフローの実行中にManual approvalを待機するStepを実行しているため、待機時間中もActionsの時間従量課金がかかり続けるのでご注意ください。

締め

若干無理やりではありますが、Environmentsを使わないManual approval実装の一例をお届けしました。
Environmentsとは違い承認時にコメントを残せない、GitHubにわかりやすいUIが出ないなど足りない箇所はありますが、知らぬ間に本番環境にデプロイされてしまった!のような事故の防止策の一つには出来ていると思います。
願わくば、ActionsにもCircleCIのように機能としてManual approvalを…。

以上、SREの今でした。
カヤックでは安心安全な自動化を構築する仲間を募集しています。