🚀

【Elixir】Mix projectにrelease drafterを導入してversion管理を快適に回す

2022/06/17に公開

GitHubのリリースノートとタグ管理をするツールにrelease-drafterというものがあります。

https://github.com/release-drafter/release-drafter

内容については以下の記事が分かりやすいです。

https://zenn.dev/kounoike/articles/20220211-easy-generate-release-notes

この記事ではrelease-drafterをElixirのMixプロジェクトに導入してイイ感じにリリースの管理をする流れを解説してみます。

Goal

画像のように v<major>:<minor>:<patch> の命名で、FeatureやBugfixでカテゴライズされたリリースノートが生成できるようになるのがゴールです。

image

以下に検証したリポジトリを置いておきます。何らか参考になれば幸いです。

https://github.com/koga1020/elixir-release-drafter-sample/

構築手順

リポジトリを作成済みの状態を前提とします。

labelの作成

  • バージョニングに使うラベル(major, minor, patch)
  • PRをカテゴライズするラベル(e.g. feature, bugfix, chore... etc)

を用意します。名前や色は自由に設定可能です。GitHub cliを使うとサクッとラベルを作成できます。

$ gh label create major
$ gh label create minor
$ gh label create patch
$ gh label create feature
$ gh label create bugfix

VERSIONファイルの作成・参照

projectのrootにversion番号だけ記載したVERSIONというファイルを作成し、 mix.exs ではそのファイルを読み込むようにします。外部ファイルに切り出すことで他のシェルスクリプトから読み出すなど扱いやすくなります。

ciの内容をアレンジして、mix.exs を直で書き換えるというのでも良いと思います。

$ echo -n "0.0.1" > VERSION
VERSION
0.0.1
mix.exs
-     version: "0.1.0",
+     version: File.read!("VERSION"),

release-drafterの設定ファイルの追加

.github/release-drafter.yml に作成する作成するリリースの設定を記述します。この辺はREADMEを参照してプロジェクトごとにアレンジしましょう。

例.

  • tag名とリリース名は v<major><minor><patch> とする
    • e.g. v1.0.0
  • 「Feature」と「Bug Fixes」で分類する
  • major, minor, patchというラベルを元に次versionを決定する
.github/release-drafter.yml
name-template: 'v$RESOLVED_VERSION'
tag-template: 'v$RESOLVED_VERSION'
template: |
  ## Changes
  $CHANGES
categories:
  - title: '🚀 Features'
    labels:
      - 'feature'
  - title: '🐛 Bug Fixes'
    labels:
      - 'bugfix'
version-resolver:
  major:
    labels:
      - 'major'
  minor:
    labels:
      - 'minor'
  patch:
    labels:
      - 'patch'
  default: patch

release-drafter workflowファイルの追加

draftリリースを作成するworkflowを用意します。PR経由でmainブランチにpushされた際に動作するようにします。

.github/workflows/release-drafter.yml
name: Release Drafter

on:
  push:
    branches:
      - main

permissions:
  contents: read

jobs:
  update_release_draft:
    permissions:
      contents: write  
      pull-requests: write
    runs-on: ubuntu-latest
    steps:
      - uses: release-drafter/release-drafter@v5
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

VERSIONファイルを修正後、tagを作り直すworkflowの追加

release-drafterで次バージョンの決定とリリースの作成までは行えるのですが、VERSIONファイルの更新は行えないため、VERSIONファイルの書き換えは別で行う必要があります。

リリースを参照して手動で書き換えるのは手間なので、リリースをdraftから公開したタイミングで以下の作業を行うworkflowを追加します。

  • 公開されたリリースのtagを元にVERSIONファイルを更新
  • botによって自動commit
  • 最新リリースバージョンのタグが新たに作成したcommitを向くようにtagを削除、再作成
  • 再作成したタグを紐づけてリリースを公開
.github/workflows/bump-version.yml
name: Bump Version
on:
  release:
    types: [released]
jobs:
  dispatch:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2

      - name: get latest release
        uses: actions/github-script@v6
        id: latest-release
        with: 
          script: |
            const release = await github.rest.repos.getLatestRelease({
              owner: context.repo.owner,
              repo: context.repo.repo
            });

            return release.data;

      - name: delete tag
        run: |
            TAG_NAME=${{ fromJson(steps.latest-release.outputs.result).tag_name}}
            git tag -d $TAG_NAME
            git push --delete origin $TAG_NAME

      - name: create bump version commit
        run: |
          set -ex
          TAG_NAME=${{ fromJson(steps.latest-release.outputs.result).tag_name}}
        
          echo -n $TAG_NAME | sed -e "s/v//" > VERSION
          git config user.name github-actions[bot]
          git config user.email 41898282+github-actions[bot]@users.noreply.github.com
          git add VERSION
          git commit -m "bump version"
          git push origin HEAD:main
          git tag $TAG_NAME
          git push origin $TAG_NAME 

      - name: update release
        uses: actions/github-script@v6
        env:
          RELEASE_ID: ${{ fromJson(steps.latest-release.outputs.result).id}}
          TAG_NAME: ${{ fromJson(steps.latest-release.outputs.result).tag_name}}
        with:
          script: |
            const { RELEASE_ID, TAG_NAME } = process.env
            await github.rest.repos.updateRelease({
              owner: context.repo.owner,
              repo: context.repo.repo,
              release_id: RELEASE_ID,
              draft: false,
              tag_name: TAG_NAME
            });

これでworkflowの設定回りは完了です。

実際に動かしてみる

リポジトリにpushしてworkflowを動せるようになったら、いくつかPRを作ってみます。

変更例.

  • minorをつけたPRをmergeする → minorバージョンの上がったリリースのdraftが作成される
  • feature ラベルをつけたPRをmergeする → 🚀 Features の配下にPRの内容が記述される

一通り変更が終わったら、リリース画面から「Publish Release」を実行するとbotによってVERSIONファイルを更新したcommitが作られ、そのcommitを指定したtagが作られていればOKです🎉

image

まとめ

Mix projectにrelease-drafterを導入する流れを解説しました。これをベースに

  • Conventional Commitsに基づいてmajor, minor, patchのラベルの付与を自動化するworkflowを用意する
  • カテゴリーをさらに細かくする(chore, dependencies, performance, ...)

といったアレンジもプロジェクトに合わせて可能だと思います。

Hexに公開するものであればmix hex.publishを行う処理も追加してあげれば良い具合にバージョン管理ができると思います。

release-drafterを活用して良きリリースライフを🚀

Discussion