Actions上でDeployments APIのリクエストをテンプレート化を試みながらやってみた

GitHub Actions上でのDeployment APIへのリクエストを、ある程度使いまわせそうな形に調整しながらやってみました。
2020.08.05

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

Deployments APIへのリクエストを行うActionを使ってworkflowを組み立てていましたが、Deploymentsの動作把握を兼ねてActionを使わずにAPIを直接呼び出す形で作り直してみました。

DeploymentsのAPI呼び出し

Deploymentsは特定のブランチを対象にしたデプロイ操作を記録するAPIで、最初に対象とするブランチを記録し、その後デプロイ処理毎に記録していきます。

  1. Create Deployments
  2. Create Deployments Status

最初のDeployments APIへリクエストすることでDeploymentsのIDが発行されます。このIDを元にしてStatusを記録していくことになります。

尚、デプロイ処理毎に記録というのは、

  1. 環境Aにデプロイ
  2. 環境Bにデプロイ

のように一度のデプロイ操作で複数の環境に反映される場合に、各環境毎にStatusを記録するということです(例:開発環境、検証環境)。デプロイ開始時とデプロイ終了時等の状況変化にも合わせて通知をしておくと把握の負担が大きく下がります。

リクエストの作成

stepの作成順

以下の順で作成してみます。

  1. Create Deployments
  2. Create Deployments Status(start)
  3. Deploy work
  4. Create Deployments Status(end)

実際のDeploy処理後のStatusは失敗した時と成功した時の双方を用意する必要があります。

共通値の設定

Deployments APIへのリクエストURL共通部分を事前にenvへ指定しておき、ついでにDeploymentsDeployments Statusそれぞれのヘッダも指定します。今回は開発環境のみのデプロイを想定してdevelop決め打ちにしておきます。

env:
  DEPLOYMENT_API_BASE: https://api.github.com/repos/${GITHUB_REPOSITORY}/deployments
  ENVIRONMENT: develop
  HEAD: "Accept: application/vnd.github.ant-man-preview+json"
  STATUS_HEAD: "Accept: application/vnd.github.flash-preview+json, application/vnd.github.ant-man-preview+json"

指定が必須のブランチ名は$GITHUB_REFを加工しての取得としました。また、Deploymentsのログからworkflowをたどりやすくするため、workflowのURLをlog_urlとします。

- name: Extract environment
  run: |
    echo "::set-env name=branch::$(echo ${GITHUB_REF#refs/heads/})"
    echo "::set-env name=log_url::$(echo https://github.com/${GITHUB_REPOSITORY}/commit/${{ github.sha }}/checks)"

APIへのリクエスト

まずDeploymentsへのリクエストを作成し、その後Deployments Statusへのリクエストを作成します。

Deployments StatusにはDeploymentsのレスポンス内にあるidが必要になるため、jqを用いて取得しておきます。

- name: Create deployment
  run: |
    create_deployment_request_url=${{ env.DEPLOYMENT_API_BASE }}
    deployment_data=$(curl -X POST -v \
      -d '{
            "ref": "${{ env.branch }}",
            "production_environment": false,
            "environment": "${{ env.ENVIRONMENT }}"
          }' \
      -H "${{ env.HEAD }}" -H "Content-Type: application/json" -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
      $create_deployment_request_url)
    create_deployment_status_request_url=${{ env.DEPLOYMENT_API_BASE }}/$(echo $deployment_data | jq '.id')/statuses
    deployment_status_data=$(curl -X POST -v \
      -d '{
            "log_url": "${{ env.log_url }}",
            "environment": "${{ env.ENVIRONMENT }}",
            "state": "in_progress"
          }' \
      -H "${{ env.DEPLOYMENT_STATUS_HEAD }}" -H "Content-Type: application/json" -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
      $create_deployment_status_request_url)
    echo "::set-env name=deployment_id::$(echo $deployment_data | jq '.id')"

最後にset-envをしているのは、Deploy後のStatus記録時に使うためです。

Deployments Statusstateには既定値を指定しますが、内容によってはヘッダにメディアタイプの指定が追加で必要になります。

state extend mediatype
error
failure
pending
success
inactive application/vnd.github.ant-man-preview+json
in_progress application/vnd.github.flash-preview+json
queued application/vnd.github.flash-preview+json

なお、inactiveを指定した場合、GitHub上ではdestroyedとして表示されます。

個人的に一番嵌ったのはToken指定です。Personal Access Tokenを使うとアカウントに依存してしまうためにGITHUB_TOKENを使うべきですが、

'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}'

と指定します。BearerTokenになっていたり、secretsつけではないGITHUB_TOKEN呼び出しは該当する環境変数が存在しないため動作しません。

Deploy判定による分岐

if:を使って成功時と失敗時それぞれの結果を送信します。

- name: Failed notice
  if: failure()
  run: |
    request_url=${{ env.DEPLOYMENT_API_BASE }}/${{ env.deployment_id }}/statuses
    data=$(curl -X POST -v \
      -d '{
            "log_url": "${{ env.log_url }}",
            "environment": "${{ env.ENVIRONMENT }}",
            "state": "failure" 
          }' \
      -H "${{ env.DEPLOYMENT_STATUS_HEAD }}" -H "Content-Type: application/json" -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
      $request_url)

- name: Update deployment status
  if: success()
  run: |
    request_url=${{ env.DEPLOYMENT_API_BASE }}/${{ env.deployment_id }}/statuses
    data=$(curl -X POST -v \
      -d '{
            "log_url": "${{ env.log_url }}",
            "environment": "${{ env.ENVIRONMENT }}", 
            "state": "success" 
          }' \
      -H "${{ env.DEPLOYMENT_STATUS_HEAD }}" -H "Content-Type: application/json" -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
      $request_url)

workflow全体

最初のon等は外してありますが、通してのフローは以下のようになります。

env:
    DEPLOYMENT_API_BASE: https://api.github.com/repos/${GITHUB_REPOSITORY}/deployments
    ENVIRONMENT: develop
    HEAD: "Accept: application/vnd.github.ant-man-preview+json"
    STATUS_HEAD: "Accept: application/vnd.github.flash-preview+json, application/vnd.github.ant-man-preview+json"
jobs:
    deploy:
      name: Deploy
      runs-on: ubuntu-latest
      steps:
      - name: Checkout
        uses: actions/checkout@v2

      - name: Extract environment
        run: |
          echo "::set-env name=branch::$(echo ${GITHUB_REF#refs/heads/})"
          echo "::set-env name=log_url::$(echo https://github.com/${GITHUB_REPOSITORY}/commit/${{ github.sha }}/checks)"

      - name: Create deployment
        run: |
          create_deployment_request_url=${{ env.DEPLOYMENT_API_BASE }}
          deployment_data=$(curl -X POST -v \
            -d '{
                  "ref": "${{ env.branch }}",
                  "production_environment": false,
                  "environment": "${{ env.ENVIRONMENT }}"
                }' \
            -H "${{ env.DEPLOYMENT_HEAD }}" -H "Content-Type: application/json" -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
            $create_deployment_request_url)

          create_deployment_status_request_url=${{ env.DEPLOYMENT_API_BASE }}/$(echo $deployment_data | jq '.id')/statuses
          deployment_status_data=$(curl -X POST -v \
            -d '{
                  "log_url": "${{ env.log_url }}",
                  "environment": "${{ env.ENVIRONMENT }}",
                  "state": "in_progress"
                }' \
            -H "${{ env.DEPLOYMENT_STATUS_HEAD }}" -H "Content-Type: application/json" -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
            $create_deployment_status_request_url)
          echo "::set-env name=deployment_id::$(echo $deployment_data | jq '.id')"

      - name: Deploy
        run: .....
      
      - name: Failed notice
        if: failure()
        run: |
          request_url=${{ env.DEPLOYMENT_API_BASE }}/${{ env.deployment_id }}/statuses
          data=$(curl -X POST -v \
            -d '{
                  "log_url": "${{ env.log_url }}",
                  "environment": "${{ env.ENVIRONMENT }}",
                  "state": "failure" 
                }' \
            -H "${{ env.DEPLOYMENT_STATUS_HEAD }}" -H "Content-Type: application/json" -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
            $request_url)

      - name: Update deployment status
        if: success()
        run: |
          request_url=${{ env.DEPLOYMENT_API_BASE }}/${{ env.deployment_id }}/statuses
          data=$(curl -X POST -v \
            -d '{
                  "log_url": "${{ env.log_url }}",
                  "environment": "${{ env.ENVIRONMENT }}", 
                  "state": "success" 
                }' \
            -H "${{ env.DEPLOYMENT_STATUS_HEAD }}" -H "Content-Type: application/json" -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
            $request_url)

あとがき

今回は一つの環境のみへのデプロイという前提でやってみました。

APIへのリクエストが正常に行われていない場合はWorkflowのログを辿って調べる必要がありますが、大体はステータスコードが確認できる程度になると思います。その場合は、まずは以下の4点を確認しましょう。

  • 必須パラメータの指定を忘れていないか
  • URLに埋め込んだ変数がnullになっていないか
  • 既定値を間違えていないか
  • パラメータの型を間違えていないか(String? Boolean?)

大体はこれらのどれかに当てはまると思われます。焦っても無駄にコミットが増えるだけなので、落ち着いて対処しましょう。

参考リンク