はじめに
今までチームのリリース作業そのものは github actions を利用して自動化ができていましたが、
それに伴う付帯作業は手作業で行っていました。
例えば、
- リリース時に Slack でのリリース開始報告
- リリースノートの手動管理
- リリースバージョンの手動管理
- リリース完了後の Slack でのリリース完了報告
などです。
これらについて地味に面倒臭い作業だったので、github acitons で全部自動化しちゃおうというのが今回の内容です。
概要(完成形)
pipeline の実行ボタンをポチッと押せばデプロイ〜リリースノート管理〜リリース連絡まで自動でやってくれるパイプラインです。
リリース(正常)
以下の点を満たします。
- リリースバージョンを自動でカウントアップして設定したい
- マイナーリリースバージョンだけでなく、メジャーリリースバージョンも指定したい
- 任意のバージョン指定もできればやりたい
- リリース時/リリース完了時に Slack で連絡したい
- リリース完了時に Slack でリリースした内容を連絡したい
- Slack の開発チーム用のチャンネルにもリリース状況を報告したい
- デプロイ完了後、リリースノートを作成したい
- リリースノートの内容に前回リリース時からの PR 差分内容を記載したい
リリース(異常発生時)
以下の点を満たします。
- Slack の開発チーム用のチャンネルにもリリース Failed を報告したい
actions.yml 全体
カウントアップ周りのロジックが入れ子構造になって大分冗長な感じになっている気がしますが、動きはします。
name: release input test
on:
workflow_dispatch:
inputs:
major_release:
description: メジャーバージョンリリース カウントアップ(optional)
type: boolean
default: false
required: false
release_version:
description: リリースバージョンの明示的な指定(optional) 入力した場合はmajor_releaseオプションは無視される。
type: string
required: false
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}
jobs:
release:
name: release
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: 現在の最新タグを取得
id: cur_tag_var
run: |
git fetch --prune --unshallow
echo "$(git tag --sort=creatordate | tail -n 1)" > CUR_TAG
echo ::set-output name=cur_tag::$(cat CUR_TAG)
- name: リリースタグを設定
id: set_release_tag
run: |
echo "${{ steps.cur_tag_var.outputs.cur_tag }}"
if [[ "${{ github.event.inputs.release_version }}" != '' ]]; then
echo 'リリースバージョンが指定されました'
echo "${{ github.event.inputs.release_version }}" > TAG_NAME
else
if [[ "${{ steps.cur_tag_var.outputs.cur_tag }}" =~ ^[0-9].+$ ]]; then
echo 'カウントアップ可能なリリースタグです'
CUR_VERSION=${{ steps.cur_tag_var.outputs.cur_tag }}
VERSION=( ${CUR_VERSION//./ } )
if ${{ github.event.inputs.major_release }}; then
echo 'メジャーバージョン リリース'
((VERSION[-2]+=1))
VERSION[-1]=0
else
echo 'マイナーバージョン リリース'
((VERSION[-1]+=1))
fi
echo $NEW_VERSION
NEW_VERSION=$(IFS="."; echo "${VERSION[*]}")
echo $NEW_VERSION > TAG_NAME
else
if [[ "${{ github.event.inputs.release_version }}" != '' ]]; then
echo 'カウントアップできないリリースタグのため、指定されたリリースバージョンでリリースします'
echo "${{ github.event.inputs.release_version }}" > TAG_NAME
else
echo 'リリースタグが不正です。リリースバージョンを指定してリリースしてください。'
exit 1
fi
fi
fi
echo ::set-output name=tag_name::$(cat TAG_NAME)
- name: call release slack message creator
id: call-release-slack-message-creator
run: |
SLACK_MESSAGE="
これからフロントリリースを始めます。
バージョン: ${{ steps.cur_tag_var.outputs.cur_tag }} -> ${{ steps.set_release_tag.outputs.tag_name }}"
echo "::set-output name=slack-message::${SLACK_MESSAGE//$'\n'/'%0A'}"
- name: Slack Release Notification on Success
uses: rtCamp/action-slack-notify@v2.0.2
env:
SLACK_USERNAME: フロントリリース
SLACK_CHANNEL: [YOUR_CHANNEL_NAME]
SLACK_LINK_NAMES: true
SLACK_TITLE: フロントリリース
SLACK_COLOR: "#36C5F0"
SLACK_MESSAGE: "${{ steps.call-release-slack-message-creator.outputs.slack-message }}"
- name: relase job
run: |
echo "リリース中..."
echo "hogehoge" > release_target.txt
- name: リリースタグをgitにpush
id: set_git_tag
run: |
echo "${{ steps.set_release_tag.outputs.tag_name }}" > TARGET_TAG_NAME
cat TARGET_TAG_NAME
git tag $(cat TARGET_TAG_NAME)
git push origin $(cat TARGET_TAG_NAME)
echo ::set-output name=target_tag_name::$(cat TARGET_TAG_NAME)
- name: get Tag ver
id: get_tag_ver
run: |
echo "$(git tag --sort=creatordate | tail -n 1)" > CUR_TAG
echo ::set-output name=cur_tag::$(cat CUR_TAG)
echo "最新タグ:$(cat CUR_TAG)"
echo "$(git tag --sort=creatordate | tail -n 2 | head -n1)" > PRE_TAG
echo ::set-output name=pre_tag::$(cat PRE_TAG)
echo "一つ前のタグ:$(cat PRE_TAG)"
- name: release diff get
id: commit_diff
run: |
echo -e ":clock10: release date: $(TZ=Asia/Tokyo date +%Y-%m-%d-%H%M%S)\n" > DIFF_COMMIT
echo "$(git log --merges --first-parent --reverse --pretty=format:"* %b" ${{ steps.get_tag_ver.outputs.pre_tag }}..${{ steps.get_tag_ver.outputs.cur_tag }})" >> DIFF_COMMIT
echo "差分:$(cat DIFF_COMMIT)"
echo ::set-output name=diffCommit::$(cat DIFF_COMMIT)
- name: Create release
id: create_release
uses: actions/create-release@v1.1.4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.get_tag_ver.outputs.cur_tag }}
release_name: Release ${{ steps.get_tag_ver.outputs.cur_tag }}
body_path: ./DIFF_COMMIT
draft: false
prerelease: false
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./release_target.txt
asset_name: release_target.txt
asset_content_type: text/plain
# テスト成功時はこちらのステップが実行される
- name: Slack Notification on Success
if: success()
uses: rtCamp/action-slack-notify@v2.0.2
env:
SLACK_USERNAME: 商用環境デプロイ通知
SLACK_CHANNEL: [YOUR_DEV_CHANNEL_NAME]
SLACK_TITLE: 商用環境へのデプロイが完了しました。
SLACK_COLOR: good
- name: release success slack message creator
if: success()
id: release-success-slack-message-creator
run: |
SLACK_MESSAGE="
:rocket: フロントリリースが完了しました。
:white_check_mark: バージョン: ${{ steps.cur_tag_var.outputs.cur_tag }} -> ${{ steps.set_release_tag.outputs.tag_name }}
:notebook_with_decorative_cover: リリースノート: <https://github.com/kansukeito/actions-test/releases>
リリース内容は以下のとおりです。
$(cat DIFF_COMMIT)"
echo ${SLACK_MESSAGE}
echo "$(cat DIFF_COMMIT)"
echo "::set-output name=slack-message::${SLACK_MESSAGE//$'\n'/'%0A'}"
- name: Slack Release Notification on Success
if: success()
uses: rtCamp/action-slack-notify@v2.0.2
env:
SLACK_USERNAME: フロントリリース
SLACK_CHANNEL: [YOUR_CHANNEL_NAME]
SLACK_TITLE: フロントリリース
SLACK_COLOR: "#36C5F0"
SLACK_MESSAGE: "${{ steps.release-success-slack-message-creator.outputs.slack-message }}"
# テスト失敗時はこちらのステップが実行される
- name: Slack Notification on Failure
uses: rtCamp/action-slack-notify@v2.0.2
if: failure()
env:
SLACK_USERNAME: 商用環境デプロイ通知
SLACK_CHANNEL: [YOUR_DEV_CHANNEL_NAME]
SLACK_TITLE: 商用環境へのデプロイに失敗しました。
SLACK_COLOR: danger
設定必要な secret 情報
項目 | 何を設定するのか |
---|---|
SLACK_WEBHOOK_URL | Slack incomming Webhook url |
各 step の説明
リリース時のバージョン指定
on:
workflow_dispatch:
inputs:
major_release:
description: メジャーバージョンリリース カウントアップ(optional)
type: boolean
default: false
required: false
release_version:
description: リリースバージョンの明示的な指定(optional) 入力した場合はmajor_releaseオプションは無視される。
type: string
required: false
inputs を記述すると pipeline の手動実行時に引数を設定できるようになります。
ここで設定している項目は2つです。
どちらもオプション設定で、特に何もいじらない場合はマイナーリリースのバージョンが自動でカウントアップされる想定です。
項目 | タイプ | 設定した場合 | 設定しない場合 |
---|---|---|---|
メジャーリリース | boolean | メジャーリリースバージョンがカウントアップされる | マイナーリリースバージョンがカウントアップされる |
バージョン手動指定 | string | 任意のバージョン文字列でリリースを行う。 メジャーリリースにチェックが入っていても無視される |
- |
最新タグを取得
- name: 現在の最新タグを取得
id: cur_tag_var
run: |
git fetch --prune --unshallow
echo "$(git tag --sort=creatordate | tail -n 1)" > CUR_TAG
echo ::set-output name=cur_tag::$(cat CUR_TAG)
現在リリースされている最新のタグを取得します。
--sort=creatordate
のオプションを使うことでタグの文字列に関係なくタグを打った順で並べて表示してくれるので、最新の 1 件のみパイプで抽出しています。
リリース用のタグ文字列を決定
- name: リリースタグを設定
id: set_release_tag
run: |
echo "${{ steps.cur_tag_var.outputs.cur_tag }}"
if [[ "${{ github.event.inputs.release_version }}" != '' ]]; then
echo 'リリースバージョンが指定されました'
echo "${{ github.event.inputs.release_version }}" > TAG_NAME
else
if [[ "${{ steps.cur_tag_var.outputs.cur_tag }}" =~ ^[0-9].+$ ]]; then
echo 'カウントアップ可能なリリースタグです'
CUR_VERSION=${{ steps.cur_tag_var.outputs.cur_tag }}
VERSION=( ${CUR_VERSION//./ } )
if ${{ github.event.inputs.major_release }}; then
echo 'メジャーバージョン リリース'
((VERSION[-2]+=1))
VERSION[-1]=0
else
echo 'マイナーバージョン リリース'
((VERSION[-1]+=1))
fi
echo $NEW_VERSION
NEW_VERSION=$(IFS="."; echo "${VERSION[*]}")
echo $NEW_VERSION > TAG_NAME
else
if [[ "${{ github.event.inputs.release_version }}" != '' ]]; then
echo 'カウントアップできないリリースタグのため、指定されたリリースバージョンでリリースします'
echo "${{ github.event.inputs.release_version }}" > TAG_NAME
else
echo 'リリースタグが不正です。リリースバージョンを指定してリリースしてください。'
exit 1
fi
fi
fi
echo ::set-output name=tag_name::$(cat TAG_NAME)
input
で指定された引数を考慮してリリース用のタグを決定します。
メジャーバージョンかマイナーバージョンか判断し、カウントアップ可能であればカウントアップを行います。
現在の最新リリースタグがカウントアップができないようなタグ(日付で管理されたタグなど)で、バージョン手動指定されていない場合はわざとパイプラインを落とします。
リリース前事前通知
- name: call release slack message creator
id: call-release-slack-message-creator
run: |
SLACK_MESSAGE="
これからフロントリリースを始めます。
バージョン: ${{ steps.cur_tag_var.outputs.cur_tag }} -> ${{ steps.set_release_tag.outputs.tag_name }}"
echo "::set-output name=slack-message::${SLACK_MESSAGE//$'\n'/'%0A'}"
- name: Slack Release Notification on Success
uses: rtCamp/action-slack-notify@v2.0.2
env:
SLACK_USERNAME: フロントリリース
SLACK_CHANNEL: [YOUR_CHANNEL_NAME]
SLACK_LINK_NAMES: true
SLACK_TITLE: フロントリリース
SLACK_COLOR: "#36C5F0"
SLACK_MESSAGE: "${{ steps.call-release-slack-message-creator.outputs.slack-message }}"
rtCamp/action-slack-notify
という actions 拡張を使って Slack へ通知しています。
メッセージの内容に改行などを使いたかったので、直前の step でメッセージを作ってから Slack に通知しています。
リリース作業中
- name: relase job
run: |
echo "リリース中..."
echo "hogehoge" > release_target.txt
このタイミングでリリース作業を行います(test, デプロイとか・・・)
今回はサンプルとしてリリースノートに保存する資材のシミュレーションとして text ファイルを吐くだけにしてます。
リリース完了後、タグを更新
- name: リリースタグをgitにpush
id: set_git_tag
run: |
echo "${{ steps.set_release_tag.outputs.tag_name }}" > TARGET_TAG_NAME
cat TARGET_TAG_NAME
git tag $(cat TARGET_TAG_NAME)
git push origin $(cat TARGET_TAG_NAME)
echo ::set-output name=target_tag_name::$(cat TARGET_TAG_NAME)
今回のリリース用に設定したリリースバージョンタグを github に反映しています。
最新のリリースバージョンタグと前回のリリースバージョンタグの間の PR の差分を取得する
- name: get Tag ver
id: get_tag_ver
run: |
echo "$(git tag --sort=creatordate | tail -n 1)" > CUR_TAG
echo ::set-output name=cur_tag::$(cat CUR_TAG)
echo "最新タグ:$(cat CUR_TAG)"
echo "$(git tag --sort=creatordate | tail -n 2 | head -n1)" > PRE_TAG
echo ::set-output name=pre_tag::$(cat PRE_TAG)
echo "一つ前のタグ:$(cat PRE_TAG)"
- name: release diff get
id: commit_diff
run: |
echo -e ":clock10: release date: $(TZ=Asia/Tokyo date +%Y-%m-%d-%H%M%S)\n" > DIFF_COMMIT
echo "$(git log --merges --first-parent --reverse --pretty=format:"* %b" ${{ steps.get_tag_ver.outputs.pre_tag }}..${{ steps.get_tag_ver.outputs.cur_tag }})" >> DIFF_COMMIT
echo "差分:$(cat DIFF_COMMIT)"
echo ::set-output name=diffCommit::$(cat DIFF_COMMIT)
最新のバージョンでリリースした内容を取得します。
この例では PR のタイトルの一覧を取得しています。
内容は DIFF_COMMIT という一時ファイルに書き込まれています。
リリースノートの作成とリリースした資材の格納
- name: Create release
id: create_release
uses: actions/create-release@v1.1.4
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
tag_name: ${{ steps.get_tag_ver.outputs.cur_tag }}
release_name: Release ${{ steps.get_tag_ver.outputs.cur_tag }}
body_path: ./DIFF_COMMIT
draft: false
prerelease: false
- name: Upload Release Asset
id: upload-release-asset
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }}
asset_path: ./release_target.txt
asset_name: release_target.txt
asset_content_type: text/plain
-
actions/create-release
という actions 拡張でリリースノートを自動作成します。 -
body_path
に内容が記載されたDIFF_COMMIT
ファイルを指定することでリリースノートの内容を指定します。 -
actions/upload-release-asset
という actions 拡張でリリースノートに資材を配置します。 - リリースノートを作成した step で upload_url が生成されているので、それを指定します。
資材を複数格納したい場合は、配置する数分actions/upload-release-asset
を呼び出してあげれば格納することができます。
リリース完了通知(開発チーム向け)
- name: Slack Notification on Success
if: success()
uses: rtCamp/action-slack-notify@v2.0.2
env:
SLACK_USERNAME: 商用環境デプロイ通知
SLACK_CHANNEL: [YOUR_DEV_CHANNEL_NAME]
SLACK_TITLE: 商用環境へのデプロイが完了しました。
SLACK_COLOR: good
if: success()
によって、パイプラインが成功した時のみ動作する step です。
SLACK_CHANNEL
に開発チーム向けのチャンネル名を指定しています。
リリース前通知の時と同様の方法で Slack に通知を行います。
例としてこんな感じの通知が Slack に流れます。
リリース完了通知
- name: release success slack message creator
if: success()
id: release-success-slack-message-creator
run: |
SLACK_MESSAGE="
:rocket: フロントリリースが完了しました。
:white_check_mark: バージョン: ${{ steps.cur_tag_var.outputs.cur_tag }} -> ${{ steps.set_release_tag.outputs.tag_name }}
:notebook_with_decorative_cover: リリースノート: <https://github.com/kansukeito/actions-test/releases>
リリース内容は以下のとおりです。
$(cat DIFF_COMMIT)"
echo ${SLACK_MESSAGE}
echo "$(cat DIFF_COMMIT)"
echo "::set-output name=slack-message::${SLACK_MESSAGE//$'\n'/'%0A'}"
- name: Slack Release Notification on Success
if: success()
uses: rtCamp/action-slack-notify@v2.0.2
env:
SLACK_USERNAME: フロントリリース
SLACK_CHANNEL: [YOUR_CHANNEL_NAME]
SLACK_TITLE: フロントリリース
SLACK_COLOR: "#36C5F0"
SLACK_MESSAGE: "${{ steps.release-success-slack-message-creator.outputs.slack-message }}"
if: success()
によって、パイプラインが成功した時のみ動作する step です。
リリース前通知の時と同様の方法で Slack に通知を行います。
例としてこんな感じの通知が Slack に流れます。
リリース失敗通知(開発チーム向け)
- name: Slack Notification on Failure
uses: rtCamp/action-slack-notify@v2.0.2
if: failure()
env:
SLACK_USERNAME: 商用環境デプロイ通知
SLACK_CHANNEL: [YOUR_DEV_CHANNEL_NAME]
SLACK_TITLE: 商用環境へのデプロイに失敗しました。
SLACK_COLOR: danger
if: failure()
によって、パイプラインが失敗した時のみ動作する step です。
SLACK_CHANNEL
に開発チーム向けのチャンネル名を指定しています。
リリース前通知の時と同様の方法で Slack に通知を行います。
例としてこんな感じの通知が Slack に流れます。
まとめ
- このパイプラインによって、リリースの際にかかる地味な手作業が無くなってかなり快適になりました。
- リリースノートへの記載あたりはもうちょっとどうにかできそうな気がします(release.yml を使うみたいに bugfix や feature などそれぞれで分けて記載できるようにするとか・・・)
- github actions自由度が高くて素晴らしいですね。