こんにちは。サーバーサイドエンジニアの岡田です
みなさんはどんなデプロイフローを採用していますでしょうか?今回は自分が取り組んでいたデプロイフローのちょっとした改善を皆さんに紹介したいと思います!
きっかけ
現状コネヒトではgdpを使ったデプロイ方法を採用しており、手元のコマンドからデプロイするのが標準になっています。
元々自分はgit-pr-releaseを使ったワンクリックデプロイを以前の現場で使っており、手元でコマンドを打たなければいけないのが地味にめんどうだなあと感じていました。 そこでワンクリックでもっとラクしてデプロイしたい!と思ったのが今回のデプロイフローを考案したきっかけになります!
それ以外にも手元のコマンドからデプロイする事に感じていた課題感
- 手元でコマンド打つ場合、打ち間違いやコマンド履歴等から意図せずデプロイしてしまう可能性
- devデプロイ〜本番リリースの間(ビルドの7〜8分)地味にブランチを切り替え辛い(リリース前にmainブランチにcheckoutが必要なため)
- 新しい人が入社してきた場合にgdp&hubのインストールと設定が必要になる
- 本番デプロイ後にリリースノートを公開(gdp publish)するのを忘れてしまう
そしてこのデプロイフローが生まれた背景
当初はgit-pr-releaseを導入すれば簡単にワンクリックデプロイ出来るようになる!と考えていました。しかしいざgit-pr-releaseを導入しようと調べてみると、難しい背景がいくつか見つかりました。
ちなみにgit-pr-releaseとは
git-pr-releaseとは、本番環境向けの対象ブランチへのリリースPRを作成&リリース内容を一覧にして作成してくれるgemライブラリです。導入も簡単なので使える環境であればぜひ使ってみて欲しいです
こちらの記事が非常に参考になります!
まずgit-pr-releaseが想定しているブランチモデルは
- 本番用のmainブランチと開発用のdevelopブランチが並走する形で、developブランチからfeatureブランチを切ってmainブランチへマージしていく、いわゆるgit flowの簡易版の様なフローが前提
- mainブランチへのマージが本番デプロイへのトリガーとして想定されている
- 環境構成:本番環境(main)、dev環境(develop)
対してコネヒトでは
- GitHub Flowを踏襲しており、本番用のmainブランチからfeatureブランチを切ってmainブランチへマージしていくフローになっている
- 最新のmainブランチから切ったtagのpushが本番デプロイへのトリガーになっている
- 環境構成:本番環境(最新のtag)、dev環境(featureブランチ)
なのでもしgit-pr-releaseを採用する場合はブランチ戦略自体を見直す必要があり、流石にそれは影響範囲が大きすぎるかつ現実的ではありませんでした。そこで今あるブランチ戦略を変えずにもっと簡単にデプロイを実現する方法として考えたのがこの「Draft Releaseデプロイフロー」です。
そして実際にできたデプロイフローがこちら
いつものようにPRをマージします
Slackにdev環境へのデプロイ完了とドラフトリリースの作成が通知されます
作成されたドラフトリリースを
Publishすると
tagが切られ本番デプロイが走ると言った感じです
ビフォーアフター
既存のgdpを使ったデプロイフロー
- mainブランチからfeatureブランチを切る
- feature PRをmainブランチへマージ(dev環境へデプロイ)
- dev環境へのデプロイが完了したら、手元でgdpコマンドからタグを切ってpush
- 手元のmainを最新化(git checkout main && git pull origin main && git pull --tags)
- リリース内容確認(gdp deploy -d)
- タグを切って本番デプロイ(gdp deploy)
- 本番環境へのデプロイが完了したら、手元でgdpを使ってリリースノートを公開
- リリースノート内容確認(gdp publish -d)
- リリースノート公開(gdp publish)
今回作成したDraft Releaseデプロイフロー
- mainブランチからfeatureブランチを切る
- feature PRをmainブランチへマージ(dev環境へデプロイ)
- dev環境へのデプロイが完了したら、GitHub上から下書きリリースノートの内容を確認し、問題なければ公開(合わせてタグも切られるのでそのまま本番デプロイ)
改善ポイント
- 手元でコマンド打つ場合、打ち間違いやコマンド履歴等から意図せずデプロイしてしまう可能性
- GUI上から確認できるので、意図せずデプロイしてしまうといったことを防げる
- devデプロイ〜本番リリースの間(ビルドの7〜8分)地味にブランチを切り替え辛い(リリース前にmainブランチにcheckoutが必要なため)
- devデプロイ後は別のブランチに切り替えてすぐ作業の続きができるように
- 新しい人が入社してきた場合にgdp&hubのインストールと設定が必要になる
- 今回の対応で不要に
- 本番デプロイ後にリリースノートを公開(gdp publish)するのを忘れてしまう
- 必ずリリースノートが作成されるフローに
実際のコード
呼び出し元
ドラフトリリース作成ワークフロー
下記コードを各PJのリポジトリに展開するだけでドラフトリリースが作成されます!
on: push: branches: - main permissions: contents: write jobs: deploy: # デプロイ処理 ... create_draft_release: if: ${{ success() }} needs: [ deploy ] uses: {Reusable Workflow用リポジトリ}/reusable-workflow/.github/workflows/create_draft_release.yml@main secrets: slack-webhook-url: ${{ secrets.SLACK_WEBHOOK_URL }}
呼び出される側
ドラフトリリース作成ワークフロー
ドラフトリリース作成actionの実行と、Slack通知を行っています。
name: Create Draft Release on: workflow_call: secrets: slack-webhook-url: required: true jobs: start: runs-on: ubuntu-latest steps: - name: Action create-draft-release id: create-draft-release uses: {Reusable Workflow用リポジトリ}/reusable-workflow/.github/actions/create-draft-release@main - name: Notify slack of create draft release uses: 8398a7/action-slack@v3 with: status: ${{ job.status }} fields: repo,workflow author_name: ${{ github.actor }} text: | ドラフトリリースが作成されました。<${{ steps.create-draft-release.outputs.draft-release-link }}|${{ steps.create-draft-release.outputs.draft-release-tag }}> <${{ github.server_url }}/${{ github.repository }}/releases|リリース一覧> env: SLACK_WEBHOOK_URL: ${{ secrets.slack-webhook-url }}
実装時のポイント
Reusable Workflowとはいわゆる再利用可能なワークフローの事で、各リポジトリで共通のワークフローを実行することができます!
作成方法はシンプルでon.workflow_call
を定義したymlファイルを作成するだけです。
on: workflow_call:
今回Reusable Workflowとして実装した理由ですが、Reusable Workflowと後述のaction.ymlの大きな違いとしては実行環境に左右されるかどうかだと考えています。
Reusable Workflowの場合はワークフロー(1つ以上のジョブ)でしか実行できないので前後の処理や実行環境に左右されません。逆にaction.ymlではジョブの一ステップとして実行されるので、必要であれば actions/checkout@v2
や actions/setup-node@v2
など使って実行環境を整える必要があります。
そして今回のDraft Release作成処理は完全に処理が独立しているので、リポジトリ毎の実行環境に左右されずに横展開しやすいReusable Workflowで実装しました
Reusable Workflow使用時の注意点としては実行されるコードはあくまで呼び出し元で実行されるので
actions/checkout
を使用する場合は呼び出し元のブランチにチェックアウトされる- Reusable Workflowのリポジトリ内にあるファイルは参照できない
と言った点に注意が必要です!上記点を解決するために次のaction.ymlへとつながります
詳しくは公式ドキュメントを確認してください!
ドラフトリリース作成処理本体
こちらがメインのドラフトリリース作成処理です。実行している処理はシンプルにGitHub CLIを使ったリリースノートの作成と削除を行なっています。(削除はリリースノートの自動生成を使いたいため一度削除→再作成しています)
name: Create Draft Release Action description: Create draft release with calver tag outputs: draft-release-link: description: Create draft release link. value: ${{ steps.create-draft-release.outputs.draft-release-link }} draft-release-tag: description: Create draft release tag. value: ${{ steps.create-draft-release.outputs.draft-release-tag }} runs: using: "composite" steps: - name: Create Draft Release id: create-draft-release env: TZ: "Asia/Tokyo" GH_TOKEN: ${{ github.token }} shell: bash run: | # 最新のタグ取得 latest_tag=$(gh release list --repo '${{ github.repository }}' | awk -F'\\t' '$2=="Latest" { print $3 }') echo "latest_tag:${latest_tag}" # calver形式での次バージョンのタグ取得 next_tag=$(/bin/bash ${{ github.action_path }}/calver.sh $latest_tag) echo "next_tag:${next_tag}" # draft release取得 draft_tag=$(gh release list --repo '${{ github.repository }}' | awk -F'\\t' '$2=="Draft" { print $3 }' | head -n 1) echo "draft_tag:${draft_tag}" # draft release が既に存在していれば、削除してから再作成(リリースノートの自動生成を使いたいため) if [ "$draft_tag" != "" ]; then gh release delete $draft_tag --repo '${{ github.repository }}' -y fi draft_release_link=$( gh release create $next_tag \\ --repo '${{ github.repository }}' \\ --title "Release ${next_tag}" \\ --latest --draft --generate-notes ) echo "draft-release-link=${draft_release_link}" >> "${GITHUB_OUTPUT}" echo "draft-release-tag=${next_tag}" >> "${GITHUB_OUTPUT}" echo $draft_release_link
実装時のポイント
action.ymlとはいわゆるメタデータ構文と言われているもので、ワークフロー内の処理を切り出したり共通化することができます!
普段よく使っているようなactions/checkout
のようなアクションを作成できるイメージです。作成方法はこちらもシンプルでaction.yml
またはaction.yaml
のファイル名で作成するだけです
action.ymlではパブリックリポジトリにあるアクションはもちろん、同一リポジトリ内のアクションからプライベートリポジトリのアクションも実行可能です。
# パブリックのaction uses: actions/create-draft-release@main # 同一リポジトリ内のaction uses: ./.github/actions/create-draft-release # プライベートリポジトリのaction uses: {owner}/{repo}/.github/actions/create-draft-release@main
詳しくは公式ドキュメントと
こちらの記事が参考になりました!
calver形式でのタグ作成シェルスクリプト
このシェルスクリプトを作成した背景ですが、前提としてコネヒトではcalver形式でのバージョン管理を採用しています。
タグ作成にあたってできるだけ既存と同じようなcalver形式のタグを作成する必要があったのですが、現状これと同じようなタグを作成するツールが見つかりませんでした。そこで元々のgdpコマンドで行なっていたタグ作成ロジックをそのままコピーしてきて、このシェルスクリプトを作成することにしました。(GitHub Actionsとの相性や実行の手軽さも考えてシェルスクリプトにしました)
#!/bin/bash set -eu ############################## ## Get next calver ## @ref <https://github.com/Connehito/gdp/blob/main/format.go> ############################## today=$(date +'%Y%m%d') regexp="(.*)([0-9]{8})\\.(.+)" version=${1:-$today} if [[ $version =~ $regexp ]]; then if [[ $today = ${BASH_REMATCH[2]} ]]; then minor=${BASH_REMATCH[3]} next=$(($minor + 1)) echo "${BASH_REMATCH[1]}${today}.${next}" exit 0 fi echo "${BASH_REMATCH[1]}${today}.1" exit 0 fi echo "${today}.1" exit 0
おわりに
同じような環境でデプロイフローに悩みを感じでいる方の参考になれば嬉しいです