GitHub ActionsのReleaseイベントを使ってWorkflowを起動する

publish

GitHub ActionsのReleaseイベントを使ってWorkflowを起動する

このブログのビルドはGitHub Actionsを使ってビルドしたものをCloudflare Pagesにデプロイしている。

近頃は、GitHubでコードを管理しているプロダクトが利用するCI/CDの有力な候補対象になるGitHub Actionsだが、普段使っているなかでも、GitHubで完結できる上にとても柔軟な対応ができて非常に便利だなと感心している。

ブログのデプロイフローを少し変えたのだが、そのときにGitHub Actionsの仕組みについて調べたうえで結構勉強になったので、今回のユースケースと対応方法を書いておく。

これまでのデプロイフロー

これまではリポジトリ上に、 main (本番環境) 、 develop (開発環境) ブランチを準備して、それぞれのブランチにマージしたらデプロイされるようにしていた。

下記のような感じで、pushイベントをトリガーに起動するWorkflowを作成していた。デプロイのWorkflowファイルは1つで、デプロイ先の振分けや環境ごとに違う環境変数の適用は、GitHubリポジトリのSettingsから構成する環境 (Environments) を作成して、そこに設定した環境変数を利用するようにしていた。

on:
  push:
    branches:
      - main
      - develop

jobs:
  deploy:
    ...
    environments:
      name: ${{ github.ref_name == 'main' && 'production' || 'development' }}
      url: ${{ vars.SITE_URL }}

GitHub上でリポジトリのデフォルトブランチを develop に設定して、本番へのリリースはある程度溜まったコミットを main ブランチにマージしていた。

main ブランチがデフォルトにならないことにより develop からマージされないコード・コミットが増えて乖離が起こることが少し気持ち悪いなと思った。

あと、せっかくRelease機能があるんだから、ちゃんとタグを打ってリリースした履歴がわかりやすく見れるようになるので、この機能を有効活用したいと思ったってのもあって、下記のように変更をした。

改良したデプロイフロー

やったことは前述のとおりだが、 develop ブランチを廃止して main ブランチをデフォルトに戻して、featureブランチは main にどんどんマージしていくように変更し、本番環境へのデプロイはリポジトリのRelease機能のイベントをトリガーにデプロイWorkflowを起動するようにして管理することにした。

Workflow起動のトリガーに push イベントを使っていたところに、 release イベントを追加する。releaseイベントには作成したリリースの作成から公開までの流れにいくつかアクティビティ1があって、適切なアクティビティを選択して、トリガーしたいタイミングを調整する。

リリースの状態とタグ

リリースには大きくDraftとPublishedの状態がある。

Publishedにも種類があって、 pre-releaserelease の状態に区分できる。 公開時に pre-release を選択する(WEB画面からはチェックボックスをつけ、CLIでは --prerelease オプションをつける)と、そのリリースが pre-release 状態になる。

このリリースを編集して pre-release を解除することで正式なリリース released の状態に変わる。

この場では余談だがDraftにも触れておくと、リリース作成時に指定するタグはDraftのときはまだ打たれず、リリースを公開 (Published) するときにタグが打たれるため、まだタグを打つコミットを決定したくないときにDraft状態にしておく。

利用するアクティビティ

今回既存の development 環境と production 環境に加えて、作成したリリースの状態を正式公開前の pre-released リリースを確認する staging 環境を作成することにした。

main ブランチへのマージ ( push イベント ) で development 環境にデプロイする。

そのほか、 productionstaging の振り分けするために、Workflowのトリガーとして published アクティビティを利用することにした。

そのほか、 release イベントのアクティビティに prereleasedreleased があるがこれらは下記の理由2で適していない。

  • prereleased はこのアクティビティはDraftリリースからPre-Releaseで公開した場合に、Workflowがトリガーされない
  • released はPre-Releaseのときはトリガーされないのと、 published と一緒に使った場合、Pre-Releaseを経ずに直接リリースしたときに二重でWorkflowが起動してしまう

上記二つのアクティビティ状態も、 published では拾うことができるため1つ使うだけでよくて、「Pre-Releaseでの公開」と「正式なリリース」の2つの場合は、Job内で参照する内部的なコンテキストに必要な情報が提供されているので判別にはそれを利用する。

Workflow YAMLの変更

環境数を増やしたため、 environments に記載する条件文が長く複雑になったため、環境を選別して変数に格納する別のステップを追加して、その値を利用するようにした。

on:
  push:
    branches:
      - main
  release:
    types:
      - published
      - released

jobs:
  select-environment:
    ...
    outputs:
      environment: ${{ steps.select-environment.outputs.environment }}
    steps:
      - name: Select environment
        id: select-environment
        run: |
          if [[ "${{ github.event_name }}" == "release" ]]; then
            if [[ "${{ github.event.action }}" == "published" ]]; then
              if [[ "${{ github.event.release.prerelease }}" == "true" ]]; then
                echo "environment=staging" >> $GITHUB_OUTPUT
              else
                echo "environment=production" >> $GITHUB_OUTPUT
              fi
            fi
          else
            echo "environment=development" >> $GITHUB_OUTPUT
          fi

  deploy:
    ...
    needs: select-environment
    environments:
      name: ${{ needs.select-environment.outputs.environment }}
      url: ${{ vars.SITE_URL }}
    ...

トリガーされたActionの状態を確認していくのに利用した項目は下記の通り

項目説明
github.event_nameトリガーされたイベントの名前
github.event.actionトリガーされたイベントのアクション (アクティビティ)
github.event.release.prereleaseリリースがPre-Releaseかどうか

event.actionpublished のときに、

  • event.release.prereleasetrue の場合は staging 環境にデプロイする
  • event.release.prereleasefalse の場合は production 環境にデプロイする

ハマったこと

jobs.<job_id>.environments.name の設定を、これまでどおり条件分岐で頑張ろうとしたところ、このJobが正常に起動しなくなってしまった。

jobs:
  deploy:
    ...
    environments:
      name: |
        ${{
          github.event.release.type == 'published' && 'production'
        || github.event.release.type == 'prereleased' && 'staging'
        || 'development'
    }}
    ...

同じWorkflowの他のJobはうまく実行できるのに、上記が書かれたJobが動作が始まると The job could not retrieve the repository token. というエラーとなってしまった。

このエラーについて検索してみたりしたが、一般的には GITHUB_TOKEN の権限が不足していることを示すことが主なようで、 permissions の設定を見直すことを進めることが書かれていたが、 permissions は新旧で変えていないので、これが原因とは考えにくかった。

environments は参照できるコンテキストが限られていたり(envとか無理)、環境変数を司っていたりするので、この辺を複雑にしているのが怪しいと思って、上記の方法にしたところエラーが出なくなった。

まとめ

GitHubのRelease機能を使って本番デプロイができるようにWorkflowを変更した。

Release機能によるWorkflow起動と、アクティビティの特性をうまく利用して、デプロイ先をコントローする方法を実践した。

これで、ブランチと本番のリリースの作成から公開までの流れをちゃんと管理できるようになったと思う!

参考

Footnotes

  1. ワークフローをトリガーするイベント

  2. releaseイベントのアクティビティ説明 の Note に記載がある。