こんにちは、フロントエンドエンジニアのモアイと申します。
SmartHR では、SmartHR UI というプロダクト間共通の React コンポーネントライブラリを運用していますが、この記事では SmartHR UI のリリース作業を GitHub Actions で自動化した話をご紹介します。
ちなみに SmartHR UI そのものについては過去の記事で詳しく紹介されていますので、ご興味があればそちらも併せて御覧ください。
三行まとめ
- リリース作業が複雑化していたので GitHub Actions を使って自動化した
- リリース中に作業者の確認を挟むプロセスを Issue とラベル付けによって実現した
- 自動化最高!
これまでのリリース作業
SmartHR UI では Pull Request ベースで開発しており、様々な歴史的経緯からこのようなブランチ構成で運用しています。
また、リリース時に必要なバージョン番号の採番や変更履歴の生成などをまとめて行ってくれる standard-version というツールを使用しており、リリースを行う際には次のような作業が必要になります。
- リリースを行う状態のブランチを作成
- standard-version を dry run 実行して内容確認
- standard-version を実行
- npm publish
- 作成されたタグを GitHub に push
- リリースで発生した変更を master に取り込む Pull Request を作成
- GitHub 上でリリースノートを作成
これまではこれら作業をひとつずつ手動で行っていましたが、手順が複雑で作業コストが高い上にミスや作業漏れが発生する懸念があったため、作業を自動化することでこれら問題点を解消したい!となったわけです。
自動化の方法
自動化は GitHub Actions を用いて行い、ざっくり要件は以下のような感じです。
- 任意のタイミングでリリースを開始できる
- ボタンポチッで実行できる
- リリース内容を事前確認できる
- 作業は可能な限り自動化する
この内、「リリース内容を事前確認できる」ことを実現するために少し工夫が必要になります。やりたいこととしては、実際にリリース処理を行う前にリリースされる内容を確認して問題ないかどうかを判断できるようにしたいのですが、そのためにはリリース処理中に作業者の判断を待つプロセスを設ける必要があります。
今回はそのプロセスを、GitHub 上の Issue と、Issue に対するラベル付け によって表現することにしました(参考にした記事)。つまり、事前確認に必要な情報を本文に載せた Issue を作成し、作業者がその Issue に特定のラベルを付けることで「内容を確認し、問題ないと承認した」ことを表す、ということです。
実際の作業の流れとしては
- 作業者がリリース処理を開始すると、事前確認用の Issue が作成される
- 作業者はその Issue を確認し、内容に問題なければその Issue に特定のラベルを付ける
- ラベルが付けられたことを検知して、実際のリリース処理が走る
というように、処理は確認用 Issue を作成するまでの前半部分と実際のリリース処理を行う後半部分のふたつに分かれる形になり、その間に作業者による確認オペレーションを挟むことで、リリース内容の事前確認プロセスを実現します。
リリース開始から Issue 作成までのワークフロー
ここから、実際の GitHub Actions のワークフローを組んでいきます。
まずは前半部分の、リリース開始から確認用 Issue の作成までを行うワークフローを作成します。
リリース開始のトリガ
最初に、リリースを開始するためのトリガをワークフローに設定します。
on: workflow_dispatch: inputs: mode: description: 'Input release mode, "normal" or "prerelease".' required: true default: '' jobs: start_release: if: github.event.inputs.mode == 'normal' || github.event.inputs.mode == 'prerelease' runs-on: ubuntu-latest env: RESULT_PATH: /tmp/result.txt IS_PRERELEASE: ${{ github.event.inputs.mode == 'prerelease' }}
リリースは任意のタイミングで開始できるようにしたいため、手動でワークフローを開始できる workflow_dispatch
トリガを使います。また、開始時に通常リリースかプレリリースかを選べるようにするために inputs
を設定してテキストボックスを設置しています。このテキストボックスは normal
か prerelease
を入力することでリリースモードを選べるようにしていますが、それ以外の入力ではジョブが走らないように設定しているので、誤実行の防止も兼ねています。
これで、このワークフローは GitHub 上の UI から開始できるようになります。
ソースの準備
次にリリースを行うためのソースの準備を行う処理をワークフローの steps
に記述していきます。
steps: - uses: actions/checkout@v2 with: fetch-depth: 0 - uses: actions/setup-node@v2 with: node-version: 14 - name: prepare release run: | BASE_TAG=v$(npx -c 'echo "$npm_package_version"') git checkout $BASE_TAG git merge --no-edit ${{ github.ref }} - run: yarn install --frozen-lockfile - name: push branch run: | git checkout -b release-candidate git push origin release-candidate
- ソースをチェックアウト
- 必要なブランチ操作をなんやかんや行う
- yarn install
- この状態を
release-candidate
ブランチとして GitHub に push
確認用 Issue の作成
リリース内容の事前確認を行うための Issue を作成する処理を記述していきます。
- name: release dry run run: yarn standard-version --dry-run > ${{ env.RESULT_PATH }}} - name: wrap dry run log run: | echo "Dry Run Log: \`\`\` $(cat ${{ env.RESULT_PATH }}) \`\`\`" > ${{ env.RESULT_PATH }} - name: create issue uses: peter-evans/create-issue-from-file@v2 with: title: Release candidate content-filepath: ${{ env.RESULT_PATH }} labels: release candidate
standard-version を dry run 実行すると実際の処理を行わずにリリースの内容を確認することが出来るので、その内容をどこか適当な場所に出力しておきます。Issue 上で見やすくなるように出力した内容を少し修飾してから、create-issue-from-file アクションを使って出力ファイルから Issue を作成します。
これで、dry run の内容を本文に載せた Issue が作成されます。
実際にリリース処理を行うワークフロー
前半のワークフローでリリース内容確認用の Issue が作成できたので、今度は後半部分の実際にリリース処理を行うワークフローを作成します。
ラベル付けによる「承認」とトリガ
作業者は作成されたリリース確認用 Issue を確認して、内容に問題なければラベルを付けることによって「承認」します。今回は approve release
というラベルを付けると実際のリリースが開始されるようにしました。
このラベル付けによってワークフローが開始されるようにトリガを設定します。
on: issues: types: - labeled jobs: publish_release: if: contains(github.event.issue.labels.*.name, 'release candidate') && github.event.label.name == 'approve release'
このトリガ設定によって、このワークフローは「Issue に対してラベル付けを行ったとき」に開始されるようになります。ただ、それだけだと全ての Issue に対する全てのラベル付けに反応してしまうので、ジョブ側の実行条件で「リリース確認用 Issue に対して approve release
ラベルを付けたとき」に絞っています。
これで、リリース内容の承認時に開始されるワークフローが作成できたので、あとはこのワークフローに実際のリリース処理を記述していけばOKです。
実際のリリース処理
ソースの準備
steps: - uses: actions/checkout@v2 with: ref: release-candidate fetch-depth: 0 - uses: actions/setup-node@v2 with: node-version: 14 registry-url: 'https://registry.npmjs.org' - run: yarn install --frozen-lockfile
- 前半のワークフローで push しておいた
release-candidate
ブランチをチェックアウト - npm への公開のために
setup-node
でregistry-url
を設定 - yarn install
standard-version を実行
- run: yarn standard-version
- 変更履歴などを更新したコミットと、タグが作成されます
npm レジストリへ公開
- run: npm publish env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
GitHub へタグを push
- run: echo NEW_TAG=$(git describe) >> $GITHUB_ENV - run: git push origin $NEW_TAG
- 後々参照するため、タグ名を記録しています
GitHub 上にリリースノートを作成
- run: npx ts-node ./scripts/getLatestChangelog.ts > ${{ env.CHANGELOG_PATH }} - name: create release on GitHub uses: actions/create-release@v1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: tag_name: ${{ env.NEW_TAG }} release_name: ${{ env.NEW_TAG }} body_path: ${{ env.CHANGELOG_PATH }} commitish: master prerelease: ${{ env.IS_PRERELEASE }}
リリースノートにはこのリリースに含まれる変更履歴を載せたいのですが、 standard-version によって作成・更新される変更履歴は、ひとつのファイルの中に今までの全リリースの変更履歴を含んでいるため、自作したスクリプトによって全履歴の中から今回のリリース分だけを抽出し、ファイルに出力しています。 リリースノートの作成は create-release アクションで行い、本文に先程抽出した変更履歴ファイルを指定しています。
リリースの後処理
- name: close issue uses: peter-evans/close-issue@v1 - name: delete branch run: git push origin :release-candidate
- 確認用 Issue をクローズ
release-candidate
ブランチを削除
master への Pull Request を作成
- name: cherry-pick release commit run: | git checkout master git cherry-pick $NEW_TAG - name: craete pull request uses: peter-evans/create-pull-request@v3 with: title: 'chore(release): ${{ env.NEW_TAG }}' branch: 'merge-release-${{ env.NEW_TAG }}'
standard-version を実行すると変更履歴や package.json
内のバージョン番号を更新したコミットが作成されるので、その変更を master に取り込むための Pull Request を作成する必要があります。この Pull Request には standard-version によって行われた変更だけを含めたいので git cherry-pick
などでブランチをゴニョゴニョしてから、create-pull-request アクションを使って Pull Request を作成します。
なお場合によっては git cherry-pick
する際にコンフリクトが発生して Pull Request 作成に失敗してしまうことがありますが、かなりフォローが難しいので、そのときは諦めて Pull Request の作成だけ手動で行ってリカバリすることにしています。
これでリリース処理は完了となり、一連の作業を自動化することができました。
記事中では説明のため一部記述を簡略化しているところがありますが、実際に運用しているワークフローはこちらから確認できます。
自動化してどうだったか
- 良かった点
- リリースが簡単になった
- リリース作業のコストが下がったため、リリースサイクルを早められた
- 適切な権限さえあれば誰でもリリースできるようになった
- 一度 Issue を挟むので、リリース作業が可視化された
- 懸念
- ワークフローがエラーなどで失敗した場合、ワークフローの内容を把握していないとリカバリが難しい
現状では概ね好評といった感じです。 GitHub Actions に不慣れだったためかなり手探りで進めた部分もあり、まだまだ改善点はありますが、以前の手動で行っていた状態よりは確実に便利になったのではないかと思います。自動化最高!
最後に
SmartHR ではフロントエンドエンジニアを募集中です!