この記事は、SmartHR Advent Calendar 2024 シリーズ1の5日目の記事です。
こんにちは!最近はSmartHRでスキル管理というプロダクトを開発しているs.miyoshiデス。
今回は日々の運用がちょっとだけ楽になるかもしれないスクリプトをご紹介したいと思います。
はじめに
スキル管理プロダクトでは通常以下のようなステップで開発を進めています。
- それぞれのブランチで開発
- PRを出して、レビューokならstagingブランチにマージ
- stagingブランチにマージされると、自動でstaging環境にデプロイ
- staging環境でのチェックが問題なければ、PRを作成してmainブランチにマージ
- 自動で本番環境にデプロイ
いわゆるGitHub Flowにstagingブランチが追加されている感じです。
今回はこの手順4でstagingブランチからmainブランチへマージするためのPRを自動作成する試みです。
成果物
このようなPRを自動で作成するようにしました。
[release] 2024-12-05
というPRが自動で作成され、そこにリリースされる内容が一覧で見えるようになっています。
リリースされる内容が表示されていると、いつどんな内容がリリースされたのか一目でわかるので、後から振り返りやすかったり不具合発生時の原因特定がやりやすかったりと色々メリットがあります。
では、実際にどのようなコードなのかを解説していきたいと思います。
コード全体像
name: Create a pull request for release.
on:
push:
branches: [staging]
jobs:
create-release-pr:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_TITLE: "[release]"
BASE_BRANCH: main
steps:
- uses: actions/checkout@v4
# リリース用PRが既に存在するかどうかをチェック
- name: Check if pr exists
id: check_pr
run: |
echo "count=$(gh pr list -S ${PR_TITLE}' in:title' -B $BASE_BRANCH | wc -l)" >> $GITHUB_OUTPUT
# リリース用PRを作成
- name: Create release pr
if: ${{ steps.check_pr.outputs.count == 0 }}
run: |
today=`TZ=JST date +"%Y-%m-%d"`
gh pr create -B ${BASE_BRANCH} -t "${PR_TITLE} ${today}" -b "" -d
- name: Set description
run: |
today=`TZ=JST date +"%Y-%m-%d"`
pr_number=`gh pr list -S ${PR_TITLE}' in:title' -B $BASE_BRANCH --json number --jq '.[0].number'`
pr=`gh api /repos/kufu/sample-product/pulls/${pr_number}/commits --paginate | jq '.[] | select(.commit.message | contains("Merge pull request")) | .commit.message' | awk '{print "- "$4}'`
tmpfile=$(mktemp)
cat << EOF > $tmpfile
## 関連リンク
$pr
EOF
gh pr edit staging --title "${PR_TITLE} ${today}"
gh pr edit staging --body-file $tmpfile
rm -f $tmpfile
コードの解説
このコードがどのように動くかもうちょっと詳しく解説していきたいと思います。
まず、Github Actionはstagingブランチにpushされた時に発火するようにします。
つまりstagingブランチにpushされるごとにcreate-release-pr jobが走るようになっています。
on:
push:
branches: [staging]
create-release-pr jobでは最終的に[release] 2024-12-05
みたいなPRを作成します。
既存の同タイトルのPRがあるかをチェックして、あればその内容を編集、なければ作るといった作戦です。
各ステップの解説
Check if pr existsステップでまずはすでにPRがあるかをチェックしています。
echo "count=$(gh pr list -S ${PR_TITLE}' in:title' -B $BASE_BRANCH | wc -l)" >> $GITHUB_OUTPUT
ghコマンドでPRを見ていって[release]
がタイトルに含まれているPRが存在するかをチェックします。
最後に書き出しているのは次のstepのif分岐させるためですね。
Create release prのstepは単にghコマンドでPRを作成しているだけです
-dでdraft PRにし、-b ""で空のPRを作成してます。
このあたりはgh pr create --help
で調べると詳しくチェックできます。(ghコマンドはヘルプが充実しててほんと便利)
そしてその後Set description stepで中身を埋めていきます。
descriptionにはこのPRに含まれるすべてのPRの内容を表示するようにしたいのでその取得部分がこれです。
gh api /repos/kufu/sample-product/pulls/${pr_number}/commits --paginate | jq '.[] | select(.commit.message | contains("Merge pull request")) | .commit.message' | awk '{print "- "$4}'
GithubでPRをマージすると以下のようなマージコミットが作成されます。
Merge pull request #88 from sh-miyoshi/split-damage-type
つまりリリースPRに含まれる全てのcommitを確認して、そのコミットメッセージにMerge pull request
が含まれるPRを取得していけばいいのです。
実はcommitを全て取得するにはgh pr list --json commits
とやれば取得できます。
しかし、このコマンドの場合commitsが100件を超えているとページネーションの関係で取得できないという問題がありました。
そこでgh api /repos/kufu/sample-product/pulls/${pr_number}/commits --paginate
コマンドによりページネーションを考慮して取得するようにしています。
(基本的にcommitsが100件を超えることはないのですが、たまに大きい機能のリリースやライブラリのバージョンアップなどが重なると超えたことがありました)
取得したcommitsはjqコマンドによってMerge pull request
がコミットメッセージに含まれるcommitだけにフィルタリングし、awkコマンドでPR番号を取得しています。
ちなみにGithubはPR番号を記載すると成果物のスクショのようにPRのタイトルを表示してくれるので便利ですね。
おわりに
自動化は正義!
あと何かゴニョゴニョやりたいときにjqコマンドは便利すぎる・・・