こんにちは。会員システムグループの渡邊です。
皆さんはGitHubのリリースノートを使っていますか?
リリースノートはGitHubリポジトリでプロダクトのリリースを管理し、ユーザーに新しいバージョンや変更点を提供するための機能です。
私が所属するチームではスクラムを採用しており、細かいリリースが1スプリントで多く行わるので、このリリースノートをスプリントで行ったリリースの実績として活用しています。
ただし、私たちは複数のリポジトリにまたがった開発を行っており、各リポジトリごとに手動でリリースノートを作成するのは非常に時間がかかる作業でした。そこで、リリースノートの自動化を検討し、以下のような条件を付け加えて実装を行いました。
- 2週間で行われるスプリントの最終日に今回のスプリントで行ったリリース内容がリリースノートに公開される
- どのスプリントかをわかりやすくするために、リリースノートのタグとタイトルにスプリント名(sprint1,sprint2)をつける
前提
今回はRelease DrafterというGitHub Actionsのワークフローを使って実装していきます。
簡単にRelease Drafterの説明をします。
Release DrafterはPRを出したタイミングで変更したファイルの種類を検出して、自動的にラベルを付与します。
そのラベルをもとにmasterにpushなどの特定のトリガーを基準にリリースノートのドラフトを自動で作成するワークフローです。
加えて、GitHub公式が提供しているgithub-scriptというGitHub Actionを使うことで、ワークフロー内でJavaScriptを実行し、ワークフローをカスタマイズしています。
実装
configファイル作成
.github/release-drafter.yml というファイルを作成します。
これはconfigファイルになっていて、リリースノートに記載する内容の制御やPR時に付与するラベルの条件を設定することができます。
ラベルを付与する条件は、ディレクトリや特定の拡張子を指定することが可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
categories: - title: '🎉 Release' labels: - 'release' - title: '🚀 Features' labels: - 'feature' - 'enhancement' - title: '🐛 Bug Fixes' labels: - 'fix' - 'bugfix' - 'bug' - title: '🧰 Maintenance' label: 'chore' - title: '🔧 Refactoring' label: 'refactor' - title: '📖 Documentation' label: 'documentation' - title: '⛓️ Dependency update' label: 'dependencies' change-template: '- $TITLE @$AUTHOR (#$NUMBER)' change-title-escapes: '\<*_&' # You can add # and @ to disable mentions, and add ` to disable code blocks. template: | $CHANGES autolabeler: - label: feature branch: - '/^feat(ure)?[/-].+/' - label: bug branch: - '/^fix[/-].+/' - label: chore branch: - '/^chore[/-].+/' - label: refactor branch: - '/(refactor|refactoring)[/-].+/' - label: documentation branch: - '/doc(umentation)[/-].+/' files: - '*.md' - label: release branch: - '/release[/-].+/' - label: enhancement branch: - '/(enhancement|improve)[/-].+/' - label: nextjs files: - 'src/**/*' - 'package.json' - label: github files: - '.github/**/*' |
実行ワークフロー作成
次に.github/workflow/release-drafter.yml というワークフローを作成します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
name: Release Drafter on: schedule: - cron: '0 8 * * 2' #JST 17:00 on Tuesday pull_request: types: [opened, reopened, synchronize] jobs: update_release_draft: runs-on: ubuntu-latest timeout-minutes: 15 steps: - name: checkout uses: actions/checkout@v3 - name: Get latest release tag id: get_tag uses: actions/github-script@v6 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const repo = context.repo; const { data } = await github.rest.repos.getLatestRelease({ owner: repo.owner, repo: repo.repo, }); let tag = data.tag_name; console.log(data) const regex = /sprint(\d+)/; const match = tag.match(regex); if (match) { const sprintNumber = parseInt(match[1]); tag = sprintNumber + 1; } return tag; - name: Check release date id: check_date uses: actions/github-script@v6 with: github-token: ${{ secrets.GITHUB_TOKEN }} script: | const repo = context.repo; const { data } = await github.rest.repos.getLatestRelease({ owner: repo.owner, repo: repo.repo, }); const releaseDate = new Date(data.published_at); const currentDate = new Date(); const diffTime = Math.abs(currentDate - releaseDate); const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); return diffDays >= 10; # リリース日から10日以上経過している場合はリリースを作成する - name: create release if: ${{ steps.check_date.outputs.result == 'true' }} uses: release-drafter/release-drafter@v5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: publish: true name: 'sprint${{ steps.get_tag.outputs.result }}' tag: 'sprint${{ steps.get_tag.outputs.result }}' # リリース日から2週間以上前の場合はリリースを作成しない - name: add label if: ${{ steps.check_date.outputs.result == 'false' }} uses: release-drafter/release-drafter@v5 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: disable-releaser: true |
処理内容について簡単に説明します。
- スケジュールおよびプルリクエストトリガーの設定
on
セクションで、このワークフローが2つのトリガーで起動するように設定されています。schedule
トリガーは、毎週火曜日のJST 17:00(UTC 8:00)に設定されています。pull_request
トリガーは、プルリクエストが開かれた、再オープンされた、または同期された場合に発生します。
- 最新リリースタグの取得
get_tag
ステップは、github-scriptを使用して最新のリリースノートのタグを取得します。- タグ名は
sprintX
の形式で、X は現在のスプリント番号です。 - 取得したタグを次のスプリントのタグに更新します。
- リリース日のチェック
check_date
ステップは、最新のリリースノートの公開日と現在の日付を比較して、その間の日数を計算します。- 日数が10日以上経過している場合、後続のリリースノートを作成するための
if
条件が満たされます。 - リリースノートを作成した日付を基準に動くため、2週間スプリントなので14日と設定すると1日でもリリースノート作成がずれると正しく動かないので、余裕を持って10日と設定しています。
- リリースノートの作成
create
release
ステップは、release-drafter
アクションを使用して、日数が10日以上経過している場合はリリースノートを作成し、公開します。- タグとリリースノート名は、前のステップで取得した次のスプリント番号を使用します。
- ラベルの追加
add
label
ステップは、日数が10日未満の場合に実行され、最新のリリースノートを自動で作成することを制御します。
リリースノートの作成タイミングを変更する場合
- scheduleのcronを実行したい日付に変更
- GitHub ActionsのタイムゾーンはUTCなので、日本時間に合わせるため9時間プラスします。
check_date
ステップのdiffDays >= 10を変更- 1週間のスプリントならcronだけでいいので、
check_date
とadd
label
ステップを削除します。
- 1週間のスプリントならcronだけでいいので、
動作確認
今回は火曜日の17:00に実行するようにcronを設定しています。
GItHubとslackを連携しているので、17時頃に実行者がgithub-actions[bot]でリリースノートとタグが自動的に作成されました。
まとめ
今回はRelase Drafterをチームの開発手法に合わせてカスタマイズしました。
github-scriptを組み合わせることによって、JavaScriptでGitHub APIが叩けるようになり、求めていた動作を実現することができました。