やりたいこと
GitHub Actionsで次の要件を実現できるデプロイのワークフローを作りたい。
- 次のトリガーでデプロイできる
- リポジトリへのAPI呼び出し
- 手動
- プルリクエストのマージ
- デプロイするブランチと環境(主にstagingもしくはproduction)を選択できる
Web APIからだと次のようにデプロイできる。
# デプロイをリポジトリへのAPI呼び出しとして実行 $ curl -X POST \ -H "Accept: application/vnd.github+json" \ -H "Authorization: Bearer <token> \ -H "X-GitHub-Api-Version: 2022-11-28" \ https://api.github.com/repos/<owner>/<repo>/dispatches \ --data '{ "event_type": "deploy", "client_payload": { "branch": "main", "environment": "production" }}'
Web UIだと次のような画面を通じてデプロイできる。
このようなワークフローを作るメリットとして次の点がある。
- ユースケースに応じて異なるデプロイ方法を選択できる
- ふだんの開発フローでは自動デプロイしたstagingで動作確認する
- 他システムやSlackと連携するときはAPI呼び出しする
- 緊急時などにブラウザから手動でデプロイできる
- 複数環境のデプロイを同じワークフローで管理しつつ、環境ごとに異なる設定を利用できる
deployment environmentの作成
あらかじめリポジトリのdeployment environment*1としてstagingとproductionの2つの環境を作っておく。ワークフローからはこのenvironmentを使うと次のようなメリットがある。
- 環境ごとのデプロイ履歴が残る
- デプロイを許可するブランチを設定できる
Web UI上でenvironmentを作成すると次のようになる。
API呼び出しと手動のどちらでも実行できるワークフロー
まず、API呼び出しか手動でワークフローに対して入力を渡し、その入力に基づいてデプロイを実行するようなワークフローを作る。つまり、1枚のワークフローファイルに
- API呼び出しか手動で渡した入力を処理
- 入力に基づくデプロイ
の両方を書く。
name: Deploy on: # API呼び出しで実行 repository_dispatch: types: [deploy] # 手動で実行 workflow_dispatch: inputs: environment: default: staging options: - staging - production required: true type: choice jobs: setup: runs-on: # some runner # トリガーするイベントに応じてinputをoutputに変換 steps: - id: setup-from-inputs if: github.event_name != 'repository_dispatch' run: | echo environment=${{ inputs.environment }} >> $GITHUB_OUTPUT echo branch=${{ github.ref_name) }} >> $GITHUB_OUTPUT - id: setup-from-payload if: github.event_name == 'repository_dispatch' run: | echo environment=${{ github.event.client_payload.environment }} >> $GITHUB_OUTPUT echo branch=${{ github.event.client_payload.branch) }} >> $GITHUB_OUTPUT # ジョブのoutputとしてenvironmentとbranchを設定 outputs: environment: ${{ steps.setup-from-inputs.outputs.environment || steps.setup-from-payload.outputs.environment }} branch: ${{ steps.setup-from-inputs.outputs.branch || steps.setup-from-payload.outputs.branch }} # setup.outputs.environmentがproduction以外のときにstagingへのデプロイを実行 staging: needs: setup if: needs.setup.outputs.environment != 'production' runs-on: # some runner # deployment environmentに関する設定 environment: name: staging url: https://staging.example.com concurrency: staging steps: - uses: actions/checkout@v3 - name: deploy run: # ... # setup.outputs.environmentがproductionのときにproductionへのデプロイを実行 production: needs: setup if: needs.setup.outputs.environment == 'production' runs-on: # some runner # deployment environmentに関する設定 environment: name: production url: https://example.com concurrency: production steps: - uses: actions/checkout@v3 - name: deploy run: # ...
repository_dispatch
の場合はdeploy
イベントでenvironment
とbranch
をパラメータとして渡すことを想定している。
workflow_dispatch
の場合はWeb UIまたはGitHub CLIから手動で実行する*2。デプロイ先の環境を入力として受け取る。type: choice
にすると、Web UIのセレクトボックスからoption
で渡した選択肢を選択できる。
setup
ジョブではどのトリガーでワークフローが実行されたかを判別して、environment
とbranch
をジョブのoutputとして設定している。staging
ジョブとproduction
ジョブはsetup
ジョブに依存しており、setup
のoutputに基づいてstaging
かproduction
のどちらか1つのジョブだけを実行する。
environment
のセクションでname
で作成済みのenvironmentの名前を指定し、さらにデプロイ先をURLとして指定する。このURLはdeploymentに関するUIで利用される。2023年3月の時点ではname
には式を渡せない*3ので、ここでは文字列を直接渡している。
また、concurrency
にenvironmentの名前を指定することで、あるenvironmentに対して同時に複数のデプロイジョブが実行されることを防ぐ。
プルリクエストをマージすると自動でデプロイするワークフロー
上のワークフローを使って、自動デプロイワークフローを作る。上のワークフローにトリガーとしてworkflow_call
を追加する。
name: Deploy on: repository_dispatch: types: [deploy] workflow_call: # 追加 inputs: environment: default: staging type: string workflow_dispatch: # ...
workflow_call
をトリガーとして追加すると、reusable workflowとして他のワークフローから呼び出せるようになる。
これで次のように自動でデプロイするワークフローを作れる。
name: Auto deploy on: push: branches: - master jobs: staging: uses: ./.github/workflows/deploy.yml with: environment: staging secrets: inherit
uses
にdeploy.ymlへのパスを、入力としてenvironment: staging
を指定してデプロイを実行する。reusable workflowを使うときはstep
ではなくjob
直下にuses
を書くことに注意。また、リポジトリのシークレットをreusable workflowで使いたいときは、secrets: inherit
と書くとreusable workflowにそのままシークレットを渡せる。第三者のreusable workflowを使うときはsecrets: inherit
は使わず、必要なシークレットだけを明示的に渡すほうがよい。
*1:https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment
*2:https://docs.github.com/en/actions/managing-workflow-runs/manually-running-a-workflow
*3:https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idenvironment