ChangeLogからGitHub用のリリースノートを作ってGoReleaserでリリースする

# Motivation

最近、[GoReleaser](https://goreleaser.com/)を使ってGolangの自作ツールをGitHubにリリースすることが多いです。
GoReleaserの標準設定だと、コミットログからリリースノートを生成することは容易にできますが、第三者向けのリリースノートとしてそれほど見やすい形式ではないと思います。

私の場合、ChangeLogを毎回真面目に書いているので、このChangeLogの該当バージョンの記述をそのままリリースノートに設定したいと思いました。

検索すると、コミットログからChangeLogを生成する方法はたくさん見つかるのですが、ChangeLogからリリースノートを生成するような方法は見つからなかったので、簡単なスクリプトを書いて実現しました。

本記事ではこの方法と、GitHub Actionsでの設定例を紹介します。

# 動作確認環境

- Ubuntu 18.04
- mawk 1.3.3-17ubuntu3

# リリースノート生成スクリプトの作成
## AWKでChangeLogの特定バージョンの記述を抽出する

私はいつも、下のようなMarkdownのフォーマットでChangeLogを記述しています（内容は適当なサンプルです）:

```Markdown:CHANGELOG.md
## 0.2.0 (2020-07-16)

Feature:

- (CLI) Add `new` command

Improve:

- (client) Detect running environment

## 0.1.0 (2020-07-15)

Initial release.
```

このような形式のテキストから特定のバージョンの記述を抜き出すタスクは、下のAWKスクリプトで実現できます:

```awk:extract-release.awk
BEGIN { current = 0 } # 初期値の設定
{
  # 最初に見出し行にマッチしたときにcurrent変数を1に設定
  if (current == 0 && match($0, "^## " version)) {
    current = 1
    print $0 # ヘッダとして出力

  # current == 1のときに次の見出し行にマッチしたら出力処理を終了
  } else if (current == 1 && /^##/) {
    current = -1

  # current == 1の間、行の内容を出力し続ける
  } else if (current == 1) {
    print $0
  }
}
```

ちなみに、上で `version` 変数が未指定だと最新バージョンの記述を取得できます。

このAWKスクリプトを利用して、先ほどのCHANGELOG.mdから特定のバージョンの記述を抜き出すには、下のようにシェル上でコマンドを実行すればよいです:

```sh
awk -v version=$VERSION -f extract-release.awk CHANGELOG.md
```

## シェルスクリプトの作成

AWKスクリプトのままでもその後の設定は可能ですが、CIの設定をシンプルにするために上の処理をシェルスクリプトにまとめておきます。

AWKスクリプトは短いので、シェルスクリプト内ではワンライナーで記述しました。

```sh:gen-release-note.sh
#!/usr/bin/env bash

set -euo pipefail

VERSION=${VERSION:-}

if [[ -z "$VERSION" ]]; then
  # versionの取得方法は適宜変えて下さい
  VERSION="$(go run ./cmd/main.go --version | awk '{print $2}')"
fi

awk -v version="$VERSION" '
  BEGIN { current = 0 }
  {
    if (current == 0 && match($0, "^## " version)) {
      current = 1
      print $0
    } else if (current == 1 && /^##/) {
      current = -1
    } else if (current == 1) {
      print $0
    }
  }
' CHANGELOG.md
```

# GoReleaser x GitHub Actionsによる自動リリース設定
## GoReleaserでリリースノート生成コマンドを指定する方法

https://goreleaser.com/customization/release/#custom-release-notes

上のドキュメントページに記載されているように、GoReleaserで任意のリリースノートを設定する場合、 `goreleaser` コマンドの `--release-notes` オプションで指定します。

```sh
# テキストファイル指定
goreleaser --release-notes=path/to/release-note.txt

# シェルのプロセス置換を使う
goreleaser --release-notes <(command-to-generate-release-note)
```

## GitHub Actionsのワークフロー設定

下のワークフロー設定によって、上記のシェルスクリプトによるカスタムリリースノート付きのリリースを作成することが可能になりました:

```YAML:.github/workflows/goreleaser.yml
name: goreleaser

on:
  push:
    tags:
      - '*'

jobs:
  release:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-go@v2
        with:
          go-version: 1.14

      # リリースノートを一時ファイルに書き出しておく
      - run: ./gen-release-note.sh > /tmp/release-note.md

      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v2
        with:
          version: latest
          # 書き出したリリースノートファイルを--release-notesオプションで指定する
          args: release --rm-dist --release-notes=/tmp/release-note.md
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
```

作成されたリリースの例はこちらです: https://github.com/binqry/binq/releases/tag/v0.8.0

### 上手く行かなかった設定

初め、シェルのプロセス置換を使って次のように設定しようとしました:

```YAML:.github/workflows/goreleaser.yml
      :
      - name: Run GoReleaser
        uses: goreleaser/goreleaser-action@v2
        with:
          version: latest
          args: release --rm-dist --release-notes <(./gen-release-note.sh)
```

…が、これはGitHub Actions上でエラーになってしまいました。

```
   • generating changelog
   ⨯ release failed after 2.35s error=open <(./gen-release-note.sh): no such file or directory
##[error]The process '/opt/hostedtoolcache/goreleaser-action/0.140.0/x64/goreleaser' failed with exit code 1
```

単にパスの指定が悪いのかなと思い、その後何度かデバッグを試みましたが、上手く行かなかったので諦めました。

（shで起動していてプロセス置換が効いていないとかなのかなと思いつつ、ちゃんとした確認はしていません。）

# まとめ

ChangeLogの特定のバージョンの記述を抜き出すAWKスクリプトと、それを利用してGoReleaser + GitHub Actionsで、GitHub上でカスタムリリースノート付きのリリースを作成する方法を紹介しました。

参考になれば幸いです。