GitHub ActionsでCHANGELOG駆動Release
目次
概要
GitHub Actionsというサービスがある。 サービス自体の説明についてはここでは触れない。 本ポストのテーマはタイトルの通り、CHANGELOGを用いたリリースおよびタグの管理だ。
タグがpushされたタイミングで、それまでのcommitを自動で読み取りCHANGELOG.mdおよびReleaseを発行してくれるという 至れり尽くせりなGitHub Actionsの例は各所で見受けられるが、 正直自分としてはcommitログそのままReleaseに書かれるとか嫌だし、 tagをpushするよりもReleaseの履歴がわかりやすくあってほしい。 要するに、tagが発行されたタイミングではなくCHANGELOGを修正したタイミングで、 CHANGELOGに応じた諸々のリリースフローを行ってほしいわけである。
まとめると
- CHANGELOG.mdが修正されたcommitがmasterにpushされたタイミングでActionが起動
- CHANGELOG.mdに記載されているVersionのtagを発行
- CHANGELOG.mdに記載されている内容のReleaseを発行
というGitHub Actionsを作るぞという話。
手法
CHANGELOG
下記のようなCHANGELOG.mdを想定している。
## Version 0.1.0
* akanechan kawaii yatta-
* add choco mint ices
## Version 0.0.2
* fix release flow
## Version 0.0.1
* initial application version
Action定義ファイル
上記のCHANGELOG.mdに対してのAction定義ファイル。
Extract CHANGELOG
stepを適当にいじれば大体のCHANGELOG.mdの書き方に対応できると思われる。
name: Release
on:
push:
branches: [ master ]
paths: ['CHANGELOG.md']
jobs:
build:
name: Release
runs-on: ubuntu-latest
steps:
- name: Check out
uses: actions/checkout@v2
- name: Extract CHANGELOG
id: versioning
run: |
VERSION=$(head -1 CHANGELOG.md | sed -e 's/^.*Version //g')
git fetch --prune --unshallow
PRETAG=$(git describe --tags --abbrev=0)
git diff $PRETAG..${{ github.sha }} -- CHANGELOG.md | grep -E '^\+' | grep -v '+++' | sed -e 's/^\+//g' > diff-changelog.txt
echo ::set-output name=version::$VERSION
- name: Tag
id: tag_version
uses: mathieudutour/github-tag-action@v5.2
with:
custom_tag: ${{ steps.versioning.outputs.version }}
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Release
uses: softprops/action-gh-release@v1
with:
files: |
CHANGELOG.md
LICENSE
tag_name: ${{ steps.tag_version.outputs.new_tag }}
body_path: diff-changelog.txt
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
ポイント
基本戦略
- 1行目の文字列から
Version
以降の文字列を抽出しversionとする - 前回のtagとの差分をReleaseのdescriptionとする
git fetch --unshallow
GitHub Actionsを使用していれば大概の場合使うであろう actions/checkout
actionだが、
こちらでcloneしているリポジトリは shallow repository である。
履歴情報がないので、そのままでは git diff
やら git describe --tags
をしても空振りに終わってしまう。
git fetch --unshallow
をしてあげることで過去の変更履歴を取得して、前回のtagからの変更履歴が参照できるようにする。
outputs と *
echo ::set-output name=version::$VERSION
という構文を用いれば、stepから情報を出力できる。 基本的にはこの手法を用いてstep間で情報の授受をすれば問題ないと思われる。
しかし、CHAGELOGの情報をoutputsに宣言した際、 *
がファイル名に展開されてしまっていた。
いまいち原因は調べきれていないので、詳しい方いたら理由を教えていただけると幸いである。
対応策として、CHANGELOGの差分情報はファイルに書き出して、ファイル経由で情報の伝播を行うようにした。
課題
validation等は一切していないので、1行目が前と同じversionの状態でmasterへマージしてしまったりすると、 tagの上書きが発生してしまうと思われるのでCHANGELOGの修正には注意が必要。(一敗)
まとめ
CHANGELOGの修正をトリガーとし、CHANGELOGの内容に従ったtagとReleaseを発行することができた。 tagやReleaseの発行は外部Actionを使用しており、正直どこまで外部Actionに頼っていいかは疑問だし、不安の種でもある。 まあ、GitHub自体が破壊的なAPIの変更等を加えない限り同様のバージョンを使い続けていれば不慮の事故は起こらないと信じたい。