Github Actionsでリリースノートを自動生成する
要件
結構シンプルでok
-
リリース毎にバージョン+1リリースの粒度に応じて、+1, +0.1などだし分けられればより🩵
イメージ
type | increment | reset |
---|---|---|
major | 1.0.0 | minor+patch |
minor | 0.1.0 | patch |
patch | 0.0.1 | - |
その他 | - | - |
- カレンダーバージョン
- リリースに該当するPRを表示
- 新機能, バグ修正などだし分けられればより🩵
まずコピペ
まずはこちらの神記事の完成形をコピペしてみる
よく分かっていないがとりあえず動いている雰囲気はある
set-output → $GITHUB_OUTPUTに書き換え
コピペしたコードにset-outputの記述があった
将来的にset-outputは非推奨になりworkflowでエラーとなるようなので書き換えを行う
この記事を参考に書き換えていく
下記のようにすれば良い
- name: Set output
run: echo "::set-output name={name}::{value}"
↓
- name: Set output
run: echo "{name}={value}" >> $GITHUB_OUTPUT
name: Create release tag and release note.
on:
push:
branches: [main]
jobs:
create-release-tag:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TZ: "Asia/Tokyo"
steps:
- uses: actions/checkout@v3
# 前回のリリースタグを取得する
- name: Get previous tag
id: pre_tag
run: |
- echo "::set-output name=pre_tag::$(curl -H 'Accept: application/vnd.github.v3+json' -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r .tag_name)"
+ echo "pre_tag=$(curl -H 'Accept: application/vnd.github.v3+json' -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r .tag_name)" >> $GITHUB_OUTPUT
# タグを生成する 「v{バージョン番号}」
- name: Generate tag
id: release_tag
run: |
today=$(date +'%Y.%m.%d')
pre_release_date=$(echo ${{ steps.pre_tag.outputs.pre_tag }} | awk -F'-' '{print $1}')
pre_release_count=$(echo ${{ steps.pre_tag.outputs.pre_tag }} | awk -F'-' '{print $2}')
if [[ ! $pre_release_date = $today ]]; then
echo "init count"
pre_release_count=0
fi
- echo "::set-output name=release_tag::$today-$(($pre_release_count + 1))"
+ echo "release_tag=$today-$(($pre_release_count + 1))" >> $GITHUB_OUTPUT
# 前回リリースからの差分をもとに、リリースノートの本文を生成する
- name: Generate release note
id: release_note
run: |
- echo "::set-output name=release_note::$(curl -X POST -H 'Accept: application/vnd.github.v3+json' -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/repos/${{ github.repository }}/releases/generate-notes -d '{"tag_name":"${{ steps.release_tag.outputs.release_tag }}", "previous_tag_name":"${{ steps.pre_tag.outputs.pre_tag }}"}' | jq .body | sed 's/"//g')"
+ echo "release_note=$(curl -X POST -H 'Accept: application/vnd.github.v3+json' -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/repos/${{ github.repository }}/releases/generate-notes -d '{"tag_name":"${{ steps.release_tag.outputs.release_tag }}", "previous_tag_name":"${{ steps.pre_tag.outputs.pre_tag }}"}' | jq .body | sed 's/"//g')" >> $GITHUB_OUTPUT
# タグを切り、リリースノートを作成する
- name: Create Release
run: |
curl -X POST \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-d "{ \"tag_name\": \"${{ steps.release_tag.outputs.release_tag }}\", \"name\": \"${{ steps.release_tag.outputs.release_tag }}\", \"body\": \"${{ steps.release_note.outputs.release_note }}\"}" \
https://api.github.com/repos/${{ github.repository }}/releases
リリースタグから日付を削除
シンプルに前回のリリースカウントから+1とする
みなくて良い
name: Create release tag and release note.
on:
push:
branches: [main]
jobs:
create-release-tag:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TZ: "Asia/Tokyo"
steps:
- uses: actions/checkout@v3
# 前回のタグを取得
- name: Get previous tag
id: pre_tag
run: |
echo "pre_tag=$(curl -H 'Accept: application/vnd.github.v3+json' -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r .tag_name)" >> $GITHUB_OUTPUT
# タグを生成「v{バージョン番号}」
- name: Generate tag
id: release_tag
run: |
- today=$(date +'%Y.%m.%d')
- pre_release_date=$(echo ${{ steps.pre_tag.outputs.pre_tag }} | awk -F'-' '{print $1}')
pre_release_count=$(echo ${{ steps.pre_tag.outputs.pre_tag }} | awk -F'-' '{print $2}')
- if [[ ! $pre_release_date = $today ]]; then
- echo "init count"
- pre_release_count=0
- fi
- echo "release_tag=$today-$(($pre_release_count + 1))" >> $GITHUB_OUTPUT
+ echo "release_tag=$(($pre_release_count + 1))" >> $GITHUB_OUTPUT
# 前回リリースからの差分をもとに、リリースノートの本文を生成する
- name: Generate release note
id: release_note
run: |
echo "release_note=$(curl -X POST -H 'Accept: application/vnd.github.v3+json' -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/repos/${{ github.repository }}/releases/generate-notes -d '{"tag_name":"${{ steps.release_tag.outputs.release_tag }}", "previous_tag_name":"${{ steps.pre_tag.outputs.pre_tag }}"}' | jq .body | sed 's/"//g')" >> $GITHUB_OUTPUT
# タグを切り、リリースノートを作成する
- name: Create Release
run: |
curl -X POST \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-d "{ \"tag_name\": \"${{ steps.release_tag.outputs.release_tag }}\", \"name\": \"${{ steps.release_tag.outputs.release_tag }}\", \"body\": \"${{ steps.release_note.outputs.release_note }}\"}" \
https://api.github.com/repos/${{ github.repository }}/releases
一周してセマンティックバージョンで行う場合人の手が及びそうな気がしたのでやめます😰
ざっと懸念は下記
- qa → mainのPRのタイトルにv.1.0.1 のように書いてgithub actionに伝搬させる
- PRにmajor,minor,patchなどのラベルをつける
- github actions側でmajorの場合+1などを定義
- Conventional Commitsなどでコミットからバージョンを特定する(+1か+0.1のような)
- 現状frontend,backendともにコミットにprefix(feat: ~など)ついてないので厳しそう
気を取り直して上記の変更からセマンティックにしようとしてたところを元に戻す
name: Create release tag and release note.
on:
push:
branches: [main]
jobs:
create-release-tag:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TZ: "Asia/Tokyo"
steps:
- uses: actions/checkout@v3
# 前回のリリースタグを取得する
- name: Get previous tag
id: pre_tag
run: |
echo "pre_tag=$(curl -H 'Accept: application/vnd.github.v3+json' -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r .tag_name)" >> $GITHUB_OUTPUT
# タグを生成する 「{YYYY.MM.DD}-{当日リリース回数}」
- name: Generate tag
id: release_tag
run: |
today=$(date +'%Y.%m.%d')
pre_release_date=$(echo ${{ steps.pre_tag.outputs.pre_tag }} | awk -F'-' '{print $1}')
pre_release_count=$(echo ${{ steps.pre_tag.outputs.pre_tag }} | awk -F'-' '{print $2}')
if [[ ! $pre_release_date = $today ]]; then
echo "init count"
pre_release_count=0
fi
echo "release_tag=$today-$(($pre_release_count + 1))" >> $GITHUB_OUTPUT
# 前回リリースからの差分をもとに、リリースノートの本文を生成する
- name: Generate release note
id: release_note
run: |
echo "release_note=$(curl -X POST -H 'Accept: application/vnd.github.v3+json' -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/repos/${{ github.repository }}/releases/generate-notes -d '{"tag_name":"${{ steps.release_tag.outputs.release_tag }}", "previous_tag_name":"${{ steps.pre_tag.outputs.pre_tag }}"}' | jq .body | sed 's/"//g')" >> $GITHUB_OUTPUT
# タグを切り、リリースノートを作成する
- name: Create Release
run: |
curl -X POST \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-d "{ \"tag_name\": \"${{ steps.release_tag.outputs.release_tag }}\", \"name\": \"${{ steps.release_tag.outputs.release_tag }}\", \"body\": \"${{ steps.release_note.outputs.release_note }}\"}" \
https://api.github.com/repos/${{ github.repository }}/releases
現状のリリースノートはこんな感じ。
PRを通さずに直接mainにpushしてしまっているからPR名が表示されていないが、mainにマージされるPRが全て一覧で表示される
PRのラベルに応じてリリースノートの変更をカテゴライズする
下記のようにすることでリリースノートがいい感じになる
# PRのラベルに応じてリリースノートのカテゴリを分ける
changelog:
exclude:
authors:
- github-actions
categories:
- title: New Features
labels:
- "feature"
- title: Bug Fixes
labels:
- "bug"
- title: Hot Fixes
labels:
- "hotfix"
- title: Other Changes
labels:
- "*"
作成されたリリースノート
紫にはPR作成者のidが入ってます
ブランチに応じてPRのラベルを自動的に付与する
手動でPRにラベルをつけることで絶対ミスが起きる、ここで次のGithub Actionsを定義した
流れとしては
- feature/~, fix/~, hotfix/~で開発
- PRをopen
- ブランチに応じてlabelをつける
name: Automatically labeling pull request.
on:
pull_request_target:
types: [closed]
branches:
- main
jobs:
auto-labeling-pr:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@v3
# ラベル名を取得する
- name: Get label name
id: label_name
run: |
branch_type=$(echo ${{ github.head_ref }} | cut -d "/" -f1)
if [ $branch_type == 'feature' ]; then
label_name="feature"
elif [ $branch_type == 'fix' ]; then
label_name="bug"
elif [ $branch_type == 'hotfix' ]; then
label_name="hotfix"
else
label_name=""
fi
echo "label_name=$label_name" >> $GITHUB_OUTPUT
# PRにラベルを付与する
- name: Auto labeling
if: ${{ steps.label_name.outputs.label_name != '' }}
run: |
number=$(echo ${{ github.event.pull_request.number }})
gh pr edit $number --add-label ${{ steps.label_name.outputs.label_name }}
PRのSummaryを任意でつけたい
現状ではPRの一覧をWhat's Changedに表示しているが、リリースの規模が大きかったりでPRの数が多いとどうしてもノイズとなるPRが出てくると思った。
なので、リリースの際にqa環境 → main環境へのマージを行うPRのdescriptionでSummaryを書き、それをリリースにノートに含める運用にしようと思います
完成イメージ↓
Summary
- 新機能1
- 新機能2
What's Changed
New Features
- featureブランチのPRタイトルが入る
Bug Fixes
- fixブランチのPRのタイトル
Hot Fixes
- HotfixブランチのPR
Other Changes
- qa → main
Full Changelog
リリースノートにmainへのPRのdescriptionを入れる流れ
- PRの番号を取得する
- get-a-pull-requestをリクエストし、descriptionを取得
- 2で取得したdescriptionを整形する(null判定)
- release note のbodyにSummaryを入れる
# タグを切り、リリースノートを作成する
- name: Create Release
run: |
curl -X POST \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-d "{
\"tag_name\": \"${{ steps.release_tag.outputs.release_tag }}\",
\"name\": \"${{ steps.release_tag.outputs.release_tag }}\",
\"body\": \"${{ここにdescriptionを入れる?}}/${{ steps.release_note.outputs.release_note }}\"}" \
https://api.github.com/repos/${{ github.repository }}/releases
PRのclose時にworkflowを走らせる
- 現状はmainへのpushをトリガーにworkflowを走らせているが、これだと取得できないっぽい。
- mainをターゲットにしたPRがcloseされた時にworkflowを走らせるように変える。
on:
- push:
- branches: [main]
+ pull_request:
+ types: [closed]
+ branches:
+ - main
参考
mainへのマージを確認後にworkflowを走らせる
- 現状はmainへのマージに関係なく、PRが閉じたタイミングで発火するようになっている
- diffを取得し、mainのマージをトリガー条件に含めていく
diffを取得するとか必要なかったっぽい
name: Create release tag and release note.
on:
pull_request:
types: [closed]
branches:
- main
jobs:
create-release-tag:
+ if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
PRの番号を取得する
${{github.event.pull_request.number}}
で可能
# PRの番号を取得する
- name: Get pr number
id: pr_number
run: |
echo "pr_number=$(${{github.event.pull_request.number}})" >> $GITHUB_OUTPUT
できてるぽい
get-a-pull-requestをリクエストし、descriptionを取得
# PRのDescriptionを取得しマークダウン形式に変換する
- name: Get pr description
id: pr_description
run: |
echo "pr_description=$(curl -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' 'https://api.github.com/repos/${{ github.repository }}/pulls/${{github.event.pull_request.number}}' | jq .body | sed 's/"//g')" >> $GITHUB_OUTPUT
参考
release noteのbodyにdescriptionを追加する
# タグを切り、リリースノートを作成する
name: Create Release
run: |
curl -X POST \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
- -d "{ \"tag_name\": \"${{ steps.release_tag.outputs.release_tag }}\", \"name\": \"${{ steps.release_tag.outputs.release_tag }}\", \"body\": \"${{ steps.release_note.outputs.release_note }}\"}" \
+ -d "{ \"tag_name\": \"${{ steps.release_tag.outputs.release_tag }}\", \"name\": \"${{ steps.release_tag.outputs.release_tag }}\", \"body\": \"${{steps.pr_description.outputs.pr_description}} ${{ steps.release_note.outputs.release_note }}\"}" \
https://api.github.com/repos/${{ github.repository }}/releases
完成 してなかった
name: Create release tag and release note.
on:
pull_request:
types: [closed]
branches:
- main
jobs:
create-release-tag:
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TZ: "Asia/Tokyo"
steps:
- uses: actions/checkout@v3
# 前回のリリースタグを取得する
- name: Get previous tag
id: pre_tag
run: |
echo "pre_tag=$(curl -H 'Accept: application/vnd.github.v3+json' -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r .tag_name)" >> $GITHUB_OUTPUT
# タグを生成する 「{YYYY.MM.DD}-{当日リリース回数}」
- name: Generate tag
id: release_tag
run: |
today=$(date +'%Y.%m.%d')
pre_release_date=$(echo ${{ steps.pre_tag.outputs.pre_tag }} | awk -F'-' '{print $1}')
pre_release_count=$(echo ${{ steps.pre_tag.outputs.pre_tag }} | awk -F'-' '{print $2}')
if [[ ! $pre_release_date = $today ]]; then
echo "init count"
pre_release_count=0
fi
echo "release_tag=$today-$(($pre_release_count + 1))" >> $GITHUB_OUTPUT
# PRのDescriptionを取得しマークダウン形式に変換する
- name: Get pr description
id: pr_description
run: |
echo "pr_description=$(curl -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' 'https://api.github.com/repos/${{ github.repository }}/pulls/${{github.event.pull_request.number}}' | jq .body // "" | sed 's/"//g')" >> $GITHUB_OUTPUT
# 前回リリースからの差分をもとに、リリースノートの本文を生成する
- name: Generate release note changes
id: release_note
run: |
echo "release_note=$(curl -X POST -H 'Accept: application/vnd.github.v3+json' -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/repos/${{ github.repository }}/releases/generate-notes -d '{"tag_name":"${{ steps.release_tag.outputs.release_tag }}", "previous_tag_name":"${{ steps.pre_tag.outputs.pre_tag }}"}' | jq .body | sed 's/"//g')" >> $GITHUB_OUTPUT
# タグを切り、リリースノートを作成する
- name: Create Release
run: |
curl -X POST \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-d "{ \"tag_name\": \"${{ steps.release_tag.outputs.release_tag }}\", \"name\": \"${{ steps.release_tag.outputs.release_tag }}\", \"body\": \"${{ steps.release_note.outputs.release_note }}\"}" \
https://api.github.com/repos/${{ github.repository }}/releases
これはテストですからWhat's Changedの前までがPRで入力した内容です
マークダウンがformatされずにエラーをはく
返されたbodyがnullの時は空文字を返すようにした対応でマークダウンが動かなくなった。
echo "pr_description=$(curl -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' 'https://api.github.com/repos/${{ github.repository }}/pulls/${{github.event.pull_request.number}}' | jq -r '.body // ""')" >> $GITHUB_OUTPUT
のjq -r '.body // ""'
が怪しい
エラーメッセージは下記
Error: Unable to process file command 'output' successfully.
Error: Invalid format '- a
'
おそらく複数行に対応していないことが原因
正確な原因はわからないが、以下で解決した
# PRのDescriptionを取得しマークダウン形式に変換する
- name: Get pr description
id: pr_description
run: |
echo "pr_description=$(curl -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' 'https://api.github.com/repos/${{ github.repository }}/pulls/${{github.event.pull_request.number}}' | jq .body | sed 's/"//g' | awk '{if ($0 == "null") print ""; else print}')" >> $GITHUB_OUTPUT
追加したのはこれ
| awk '{if ($0 == "null") print ""; else print}'
'jq .body
の時(nullをそのまま文字列で表示)に、マークダウンが正常に表示できていたので、nullをそのままもらってawk ~~
の箇所で文字列がnullの時に空文字を返すようにした
jq .body // empty
など試したができなかったため上記で対応した
完成
name: Create release tag and release note.
on:
pull_request:
types: [closed]
branches:
- main
jobs:
create-release-tag:
# PRがマージされたときのみ実行
if: github.event.pull_request.merged == true
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TZ: "Asia/Tokyo"
steps:
- uses: actions/checkout@v3
# 前回のリリースタグを取得する
- name: Get previous tag
id: pre_tag
run: |
echo "pre_tag=$(curl -H 'Accept: application/vnd.github.v3+json' -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r .tag_name)" >> $GITHUB_OUTPUT
# タグを生成する 「{YYYY.MM.DD}-{当日リリース回数}」
- name: Generate tag
id: release_tag
run: |
today=$(date +'%Y.%m.%d')
pre_release_date=$(echo ${{ steps.pre_tag.outputs.pre_tag }} | awk -F'-' '{print $1}')
pre_release_count=$(echo ${{ steps.pre_tag.outputs.pre_tag }} | awk -F'-' '{print $2}')
if [[ ! $pre_release_date = $today ]]; then
pre_release_count=0
fi
echo "release_tag=$today-$(($pre_release_count + 1))" >> $GITHUB_OUTPUT
# PRのDescriptionを取得しマークダウン形式に変換する
- name: Get pr description
id: pr_description
run: |
echo "pr_description=$(curl -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' 'https://api.github.com/repos/${{ github.repository }}/pulls/${{github.event.pull_request.number}}' | jq .body | sed 's/"//g' | awk '{if ($0 == "null") print ""; else print}')" >> $GITHUB_OUTPUT
# 前回リリースからの差分をもとに、リリースノートの本文を生成する
- name: Generate release note changes
id: release_note
run: |
echo "release_note=$(curl -X POST -H 'Accept: application/vnd.github.v3+json' -H 'Authorization: token ${{ secrets.GITHUB_TOKEN }}' https://api.github.com/repos/${{ github.repository }}/releases/generate-notes -d '{"tag_name":"${{ steps.release_tag.outputs.release_tag }}", "previous_tag_name":"${{ steps.pre_tag.outputs.pre_tag }}"}' | jq .body | sed 's/"//g')" >> $GITHUB_OUTPUT
# タグを切り、リリースノートを作成する
- name: Create Release
run: |
curl -X POST \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-d "{ \"tag_name\": \"${{ steps.release_tag.outputs.release_tag }}\", \"name\": \"${{ steps.release_tag.outputs.release_tag }}\", \"body\": \"${{steps.pr_description.outputs.pr_description}} ${{ steps.release_note.outputs.release_note }}\"}" \
https://api.github.com/repos/${{ github.repository }}/releases
# PRのラベルに応じてリリースノートのカテゴリを分ける
changelog:
exclude:
authors:
- github-actions
categories:
- title: New Features
labels:
- "feature"
- title: Bug Fixes
labels:
- "bug"
- title: Hot Fixes
labels:
- "hotfix"
- title: Other Changes
labels:
- "*"
name: Automatically labeling pull request.
on:
pull_request:
types: [opened]
jobs:
auto-labeling-pr:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- name: Checkout code
uses: actions/checkout@v3
# ラベル名を取得する
- name: Get label name
id: label_name
run: |
branch_type=$(echo ${{ github.head_ref }} | cut -d "/" -f1)
if [ $branch_type == 'feature' ]; then
label_name="feature"
elif [ $branch_type == 'fix' ]; then
label_name="bug"
elif [ $branch_type == 'hotfix' ]; then
label_name="hotfix"
else
label_name=""
fi
echo "label_name=$label_name" >> $GITHUB_OUTPUT
# PRにラベルを付与する
- name: Auto labeling
if: ${{ steps.label_name.outputs.label_name != '' }}
run: |
number=$(echo ${{ github.event.pull_request.number }})
gh pr edit $number --add-label ${{ steps.label_name.outputs.label_name }}