BitriseとGitHub APIを用いてGitHubリポジトリのリリースノートを自動作成する

この記事はLIFULL Advent Calendar 2022の24日目の記事として投稿しています。
普段はiOSアプリの開発に携わっており、その中で得た経験を今回記事としてアウトプットします。
※とはいえ、今回はSwiftとかiOS系の記事ではないです笑（若干関わりますが）

https://qiita.com/advent-calendar/2022/lifull

# 背景
普段関わるプロジェクトのリポジトリでは、アプリのリリースのたびにリリースタグを切ってリリースノートを作成しています。
アプリがリリースされて数日経って不具合がないことを確認できたら`stable`ブランチにマージを行い、そのタイミングでリリースノートを手動で作成すると言った運用フローです。

ところがstableマージを行った後に、リリーサーがリリースノートを作成することが漏れてしまうことがありました。
あと、手順としてはそこまで難しくないけど地味にめんどくさいなと感じていました。

じゃあこのフローを自動化しよう！ということで実現しました。
開発環境ではCI/CDサービスとして`Bitrise`を用いていたので、これを活用することにしました。

# 通常のリリースノート作成
まずはそもそものフローをおさらいしてみましょう。

GitHubのReleasesから、`Draft a new release`をクリックすると下記の画面になります。
その後、**リリースタグ名・ベースとするブランチ名・タイトル**を入力して画面中央部にそのリリースでの変更内容や説明を自由に記述します。
この本文は、`Generate release notes`をクリックすると前のリリースからの変更差分をコミット単位で箇条書き形式で、自動生成されます。
<img width="700" alt="release-note-img.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550560/2ac75ca2-68c9-0cc0-2226-84e6d7afe44d.png">

Generateして作成したリリースの一つの例はこのようなイメージになります（大半を割愛しててすみません笑）
<img width="600" alt="release-note-sample" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550560/3628db3a-2bb3-fadc-937c-ff81d754b459.png">

# Bitriseの設定

## Workflowの全体図
リリースノートの作成を自動化するWorkflowはこのような流れで設定しました。
<img width="250" alt="workflow" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550560/27daa4ef-0eb3-1160-db19-8a5a26a528f7.png">
SSH Keyの確認やリポジトリのクローン等のフローは一般的な設定になるのでここでは割愛して、リリースノートの作成に関連する部分を取り上げて紹介します。（画像赤枠部分です。）

## Sciptフロー
手順としては大きく3つあります
1. Xcodeからリリースバージョンを取得
1. リリースノートの本文用に前回バージョンの変更差分を取得
1. 取得した差分を見やすい形に直す

これらをそれぞれ分けて説明していきます。

### Xcodeからリリースバージョンを取得
まずはXcodeからリリースバージョン番号を管理している値がBuild Settingsにあるので、それを取得するようにします。

自分の環境ではリリースタグは単なる数値形式（`X.X.X`）ではなく、`release-verX.X.X`といった形式で作成しているのでそれに合わせます。
作成した文字列の値は、次のStepの`Github Release`で用いるので、`envman`コマンドで環境変数として追加します。

```shell
# XcodeのBuild Settingsから最新バージョンのナンバーを取得
version=$(sed -n '/MARKETING_VERSION/{s/MARKETING_VERSION = //;s/;//;s/^[[:space:]]*//;p;q;}' {プロジェクト名}.xcodeproj/project.pbxproj)

if [ -z "${version}" ]; then
  echo "[ERROR] failed to get MARKETING_VERSION"
  exit 1
fi

# リリースタグのバージョン文字列を生成（ここではGIT_TAGという名前で定義）
envman add --key GIT_TAG --value "release-ver${version}"
```

### リリースノートの本文用に前回バージョンの変更差分を取得
ここでは`GitHubのReleases API`を用いてGenerate release notesを押した時の内容に関するデータを取得します。

**今回リリースする予定のバージョン**と**現在リリースしているバージョン**の差分を知りたいので、`POST_BODY`では`tag_name`と`previous_tag_name`の２つのパラメータの指定が必要です。
また、`target_commitish`はリリースノートの作成をするベースのブランチを指定するのですが、デフォルトだと`master(main)`になっているようです。
自分の環境では運用ルールとして`stable`ブランチをベースにリリースノートを作成するのでここでは`stable`を指定します。

```shell
# 前回バージョン（現在リリースされている最新バージョン）の取得
previous_version=$(curl \
  -H "Accept: application/vnd.github+json" \
  -H "Authorization: Bearer $GITHUB_ACCESS_TOKEN" \
  https://api.github.com/repos/$GITHUB_ORGANIZATION_NAME/$GITHUB_REPOSITORY_NAME/releases/latest | jq -r .tag_name)

# POSTする際に必要なパラメータを整形
POST_BODY="{\"tag_name\":\"release-ver${version}\", \"previous_tag_name\":\"${previous_version}\", \"target_commitish\":\"stable\"}"

# Generate release noteを押した時の中身を取得
body=$(curl \
  -X POST \
  -H "Accept: application/vnd.github+json" \
  -H "Authorization: Bearer $GITHUB_ACCESS_TOKEN" \
  https://api.github.com/repos/$GITHUB_ORGANIZATION_NAME/$GITHUB_REPOSITORY_NAME/releases/generate-notes \
  -d "$POST_BODY" | jq .body | sed 's/"//g')
```

### 取得した差分を見やすい形に直す
上記の結果をそのまま渡して作成すると、リリースノート自体は作成できるものの、本文はコミットの箇条書きが項目ごとに改行されずに1行の長い文章として表示されてしまっていました。
よって、項目ごとに改行して見やすい形に直してあげます。

今回は愚直に一時的なtxtファイルを作成して、その中に改行し直した文章を出力。その後その中身をBodyとして渡してあげるという流れにしました。
不要になったファイルも最後に削除しています。

```shell
# Generate Release Noteの結果の改行部分を変換する
echo -e "${body}" > tmp.txt
sed 's/\n//g' tmp.txt
body=$(cat tmp.txt)

# Release Note用のBodyを作成
envman add --key RELEASE_NOTE_BODY --value "${body}"

# 不要になったtxtファイルを削除
rm tmp.txt
```

## GitHub Releaseフロー
このフローでは、下記画像のように必要な項目に値を埋め込んでいく作業になります。
<img width="400" alt="github_release_example.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550560/5c7d00b5-b1c6-1f86-ced0-e989b83aab71.png">

実際に設定した値は下記のようなイメージです。
`$GIT_TAG`と`$RELEASE_NOTE_BODY`は前のScriptフローで定義した環境変数になります。
他の値に関しては、Secretsで定義した環境変数等を使うようにすると良いかと思います。
|項目  |値  |
|---|---|
|Personal API token|[自分のGithubのPersonal Access Token]|
|Username|[自分のGithubのユーザ名]|
|Repository URL|[対象のGithubのリポジトリURL]|
|Tag|$GIT_TAG|
|Commit|stable|
|Release name|$GIT_TAG|
|Release body|$RELEASE_NOTE_BODY|

## トリガーの設定
Bitriseの`Workflow Editor > Triggers`から、`PUSH`を選択して必要なトリガーを設定します。
今回はStableにリリースのPRがマージされたタイミングでリリースノートを作成したかったので、そのタイミングで設定しました。
<img width="900" alt="github_release_example.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550560/d5c3177d-b286-48e1-c97f-4fd24be65bc7.png">

# 結果
Stableマージがされたタイミングでリリースノート作成フローが開始し、無事Slackに成功した通知が送られてきました。
筆者もリアクションで喜んでいるのがわかりますね。
<img width="500" alt="release-note-sample" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/550560/5cf05c35-d08e-87fc-646e-d9aaf105c3cc.png">
GitHubのReleasesのページを確認しても、最新版のリリースノートが作成されていました！

# おわりに
今回は主にBitriseとGitHubのAPIを用いてリリースノート作成を自動化させてみました。

Bitriseは`ﾁｮｯﾄｻﾜｯﾀｺﾄｱﾙ`程度で、GitHubのAPIは`ﾅﾆｿﾚｵｲｼｲﾉ?`レベル、shellも`大学の授業でなら...`って具合でしたが、調べながら独力で解決までできました。
リリースタグを作ること自体はもっと簡単にできますが、今回はGenerate release notesを押した時の本文を取得して表示させるところまでを実現している記事は見つからなかったので色々試行錯誤しました。
この記事がどなたかの参考になると幸いです。

自分の未開拓な領域の挑戦をさせてくれた職場やチームに感謝です。
できることがまたひとつ増えたので、この知見を活かして別の改善もできるようになりたいですね。

それではみなさん、メリークリスマス！

# 参照
- [CIでXcode11の$(MARKETING_VERSION)からリリースバージョン番号を取得する](https://qiita.com/hirothings/items/8b417b2efe2e1c1bc0ac)
- [Generate release notes content for a release](https://docs.github.com/ja/rest/releases/releases?apiVersion=2022-11-28#generate-release-notes-content-for-a-release)