概要
Release Drafter は main ブランチへの push 等をトリガーに GitHub の Release を自動で作成してくれる便利な GitHub Workflow です1。この Release Drafter は Release を作成する際に自動でタグ名も生成してくれるのですが、タグ名の形式は x.y.z
という SemVer 形式にしか対応していません。
リポジトリによっては 2022-02-14_1
のような日付ベースのタグ (CalVer) を付与したい場合があります。直接は CalVer に対応していない Release Drafter ですが、一工夫することで CalVer を付与することも可能なようです。そこでこの記事では、Release Drafter を利用して CalVer なタグ名を自動で付与する具体的な方法を紹介しています。
背景
Release Drafter のリポジトリには、日付ベースのタグ (CalVer)を利用したいという issue (PR) が存在しています。
- https://github.com/release-drafter/release-drafter/issues/315
- https://github.com/release-drafter/release-drafter/pull/807
現時点では直接サポートされることはなさそうな気配ですが、代わりに各自の工夫で実現する方法が紹介されています。
具体的には、下記のように前段の step を追加して date
コマンドでタグ名を生成する方法です。
# 説明に直接関係する部分のみを抜粋
- name: Generate CalVer version
id: calver
run: |
export VERSION=$(date "+%Y.%m.%d")
echo ::set-output name=version::${VERSION}
- uses: release-drafter/release-drafter@v5
with:
tag: ${{ steps.calver.outputs.version }}
ここで問題となるのが、生成されるタグ名が 2022.02.14
という形式のため、同じ日に2つ以上の Release を生成する場合にタグ名が重複するという点です。この問題を回避するためには、例えば 2022.02.14_1
のように、末尾に連番を付与する等の工夫が必要となります。
この問題点についても先ほどの issue で言及されているのですが、既存のタグ名を gh release view
コマンドで取得できる・・・という部分のみで具体的にどのように実現すれば良いのか? までは書かれていなかったので、本記事はその部分を紹介するものとなります。
内容
実装
背景で紹介した issue の内容を参考にすると、概ね下記の方針により 2022-02-14_1
のような日付+連番のタグ名を付与することが可能そうです。
- Release Drafter の前段 step で date コマンドにより
2022-02-14
のような文字列を生成 -
gh release view
コマンドにより最新 Release のタグ名を取得、同じ日の Release が既にある場合は連番をインクリメント
これらの方針に従って、実直に GitHub Workflow を実装してみた例が下記となります。
name: Release Drafter
on:
push:
branches:
- main
jobs:
update_release_draft:
runs-on: ubuntu-latest
steps:
- name: Generate CalVer version
id: calver
run: |
TODAY=$(date "+%Y-%m-%d")
RELEASE_TAG=$(gh release --repo ${{github.repository}} view --json tagName --jq .tagName)
if [ "${RELEASE_TAG}" = "" ]; then
RELEASE_TAG="${TODAY}_0"
echo "There is no release, use ${RELEASE_TAG}"
fi
TAGS=(${RELEASE_TAG//_/ })
MAJOR_VERSION=${TAGS[0]}
PATCH_VERSION=${TAGS[1]}
PREFIX="${VERSION_PREFIX}${TODAY}"
if [ "${MAJOR_VERSION}" = "${PREFIX}" ]; then
PATCH_VERSION=$(expr ${PATCH_VERSION} + 1)
echo "There is already a release for the same date, increment patch_version: ${PATCH_VERSION}"
else
PATCH_VERSION=1
fi
VERSION="${PREFIX}_${PATCH_VERSION}"
echo ::set-output name=version::${VERSION}
echo "Version set to ${VERSION}"
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION_PREFIX: "v"
TZ: "Asia/Tokyo"
- uses: release-drafter/release-drafter@v5
with:
tag: ${{ steps.calver.outputs.version }}
name: ${{ steps.calver.outputs.version }}
version: ${{ steps.calver.outputs.version }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
この GitHub Workflow は下記のような仕様となります。
-
main
ブランチに push された際に動作(PRをmergeした場合もpushが発生するので動作します) - 生成されるタグ名は
v2022-02-14_1
という形式 - 日付は日本時間(JST)を基準
Release Drafter を動作させるには、この GitHub Workflow ファイル (.github/workflows/release-drafter.yml
) に加えて、設定ファイル (.github/release-drafter.yml
) もリポジトリに設置する必要があります。
設定ファイルの詳細は本記事の主題ではないため、動作に必要な最小限の設定のみを下記に示しています。
今回の記事に直接関連するのは tag-template
の部分です。公式のサンプルでは v$RESOLVED_VERSION
という形で先頭に v
がついているのですが、ここでは後述する理由により v
なしで指定しています。
name-template: '$RESOLVED_VERSION '
tag-template: '$RESOLVED_VERSION'
template: |
## Changes
$CHANGES
とりあえず動かしてみたいという場合は、以上の内容をリポジトリに設定すれば動作するはずです。
またサンプルで示した Workflow は概ね単なるシェルスクリプトなので、ファイルを見れば自明な部分が多いと思うのですが、実際にやってみると細かなポイントがいくつかありました。そこで以降では、各部分の詳細について説明していきます。
詳細
on:
push:
branches:
- main
Workflow の trigger 指定で main ブランチへの push 時に動作するように指定しています。
公式のサンプルでは Pull Request もトリガーに指定されていますが、そちらは Autolabeler という機能を使う場合に必要となるもので、今回の説明には不要だったので削除しています。
jobs:
update_release_draft:
runs-on: ubuntu-latest
steps:
- name: Generate CalVer version
id: calver
run: |
TODAY=$(date "+%Y-%m-%d")
...
ここで定義している step が今回の Workflow の主要な部分となります。
最初に date コマンドでタグの日付部分を生成しています。もしタグ形式を 20220214
や 2022.02.14
のような別の形式に変更したい場合は、この部分を変更することで実現できます。
RELEASE_TAG=$(gh release --repo ${{github.repository}} view --json tagName --jq .tagName)
if [ "${RELEASE_TAG}" = "" ]; then
RELEASE_TAG="${TODAY}_0"
echo "There is no release, use ${RELEASE_TAG}"
fi
次に gh release view
コマンドで最新リリースのタグ名を取得しています。
ここでのポイントは下記の2点です。
-
--jq
オプションでタグ名のみを取得する -
--repo
オプションでリポジトリ名を指定する
gh release view
コマンドには json 形式で特定の内容だけを取得する機能があります。またこの json に対して jq 形式のクエリーを実行できるようになっています。結果としてこのような指定をすることで、タグ名の文字列だけ2を取り出すことが可能となります。
またこの workflow では checkout を実行していないため、そのままでは gh
コマンドがリポジトリ名を特定できずにエラーとなってしまいます。そのため --repo
オプションでリポジトリ名を指定する必要があります。
TAGS=(${RELEASE_TAG//_/ })
MAJOR_VERSION=${TAGS[0]}
PATCH_VERSION=${TAGS[1]}
前段で取得したタグ名を _
で split して、日付部分と連番部分をそれぞれ取り出しています。
PREFIX="${VERSION_PREFIX}${TODAY}"
if [ "${MAJOR_VERSION}" = "${PREFIX}" ]; then
PATCH_VERSION=$(expr ${PATCH_VERSION} + 1)
echo "There is already a release for the same date, increment patch_version: ${PATCH_VERSION}"
else
PATCH_VERSION=1
fi
最新Releaseのタグ名の日付部分が今日の日付と一致するか(つまり同じ日のReleaseが既にあるか)を判定しています。
もし同じ日のReleaseがある場合は、連番部分をインクリメントして利用します。
日付をタグ名に利用する場合 v2022-02-01
のように先頭に prefix を付ける場合や 2022-02-01
のように prefix を付けない場合の両方が考えられます。本来の Release Draft のバージョンとしては数字部分だけを扱うようなのですが、それらの両方に対応するため v
の部分についてもスクリプトで付与するようにしています。
(設定ファイルの tag-template
で v
なしの指定をしていたのは、スクリプト内で v
も含めたタグ名を生成するためです)
VERSION="${PREFIX}_${PATCH_VERSION}"
echo ::set-output name=version::${VERSION}
echo "Version set to ${VERSION}"
日付と連番を組み合わせてバージョン番号の文字列(v2022-02-14_1
等)を生成します。
生成した文字列を set-output
で保存しておき Release Drafter の実行時に利用します。
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VERSION_PREFIX: "v"
TZ="Asia/Tokyo"
環境変数の指定でポイントになるのは下記です。
-
gh
コマンド用にGITHUB_TOKEN
を指定する - TZ で Timezone を指定する(
date
コマンドでの日付を制御するため)
TZ を指定しない場合は UTC が使われます。多くの場合、特定のタイムゾーンでの日付を基準にしたいはずなので、適切な Timezone を指定しておく必要があります。
- uses: release-drafter/release-drafter@v5
with:
tag: ${{ steps.calver.outputs.version }}
name: ${{ steps.calver.outputs.version }}
version: ${{ steps.calver.outputs.version }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Release Drafter を実行する step です。
前段の step で output に設定された 日付+連番 のタグ名を渡すことで、それを利用してくれるようになります。
その他(細かな動作など)
複数日に渡って実行した場合
例えば下記のような例を考えてみます。
- 2022-02-14: main ブランチに最初のPRをmerge →
v2022-02-14_1
のタグ名で Release Draft が生成される - 2022-02-15: main ブランチに次のPRをmerge → ???
通常のバージョン番号であれば、翌日になってもタグ名は変わらないので簡単なのですが、日付ベースのタグ名の場合はどうなるのか? と考えると下記のようにいくつかの選択肢がある気がしてきます。
-
v2022-02-14_1
のまま内容だけが更新される -
v2022-02-15_1
のタグ名で新規に Release Draft が生成される(v2022-02-14_1
も残る) -
v2022-02-15_1
のタグ名で既存の Release Draft が更新される
この点を実際に試してみたところ、一番最後の動作(既存 Draft の更新)となりました。
実装を確認していないので観測した範囲での理解となるのですが、Release Drafter は実行毎に最新の Release Draft 3 を更新するという動作をしているようです。そのためタグ名がどのような形式かというのは関係なく、常に最新の Draft が設定に従って書き換わるという結果になるようです。