おそらくはそれさえも平凡な日々

リリース用のpull requestを自動作成し、マージされたら自動でタグを打つtagpr

常々GitHubにtag requestが欲しいと言ってきましたが、それを実現するツールを作りました。OSSなど、バージョニングとリリースが伴うソフトウェア開発のリリースエンジニアリングをとにかく楽にしたいという動機です。既に自分が管理している幾つかのOSSでは導入して便利に利用しています。

https://github.com/Songmu/tagpr

アイデア

基本の発想は以下のようにシンプルです。

  • リリース用のpull requestがGitHub Actionsで自動で作られる
    • バージョン番号が書かれたファイルやCHANGELOG.mdを自動更新
  • そのpull requestをマージするとマージコミットに自動でバージョンtagが打たれる
    • semver前提

リリース用のpull requestを自動で作りマージボタンを以てリリースと為す、というのは、みんな(僕が)大好き git-pr-release と同じ発想です。git-pr-releaseではブランチをdevelopとmainに分けることでマージをリリースとしていましたが、tagpr ではmainブランチ一本で、マージコミットにタグを打つ点が異なります。

タグが打たれるので、それをトリガーにして成果物の生成などを別途行うと良いでしょう。

CHANGELOG.mdについては、GitHubのリリースノート生成機能を利用しているため、.github/release.ymlで表示が調整できます。

ref. https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes

導入

リリース時にsemver形式のtagを打つフローのリポジトリであれば、今すぐ導入できます。GitHub Actionsでの実行を前提としており、以下のワークフローを配置するだけで導入完了です。

# .github/workflows/tagpr.yml
name: tagpr
on:
  push:
    branches: ["main"]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: Songmu/tagpr@main
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

tagが打たれた後に、更に別のAcitonを動かしたいときには GITHUB_TOKEN の扱いについて気をつけるべき点があり、それは後で説明します。

実際のフロー

tagpr の実際のフローをもう少し詳細に説明すると以下のようになります。

  1. (自動) mainブランチが進むとリリース用のpull requestが作られる
    • mainブランチの先頭からブランチングされる
    • mainブランチが更に進むと自動で追随する
  2. 1で出来たブランチに必要なら更新を加える
  3. pull requestを好きなタイミングでマージする
  4. (自動) タグが打たれ、GitHub Releasesも作られる

作られるpull requestは以下のようなものです。

pull requestの内容 https://github.com/Songmu/tagpr/pull/69

作った動機

個人のOSS開発上の課題意識から。OSS開発のリリース作業は、定形作業ではあるのですが、細々した作業が発生し、案外漏れが出たり、属人的にもなりやすい。例えば以下のような作業です。

  • 次のバージョン番号を決める
    • major/minor/patch
  • リリースに必要な変更作業をする
    • バージョン番号が書かれたファイルを変更する (gemspecやsetup.cfg等)
    • リリースノートやChangelogなどを書く
    • 依存ライブラリ定義等の更新
  • 上記の変更をまとめてcommitを作りgit tagを打つ
  • CPANやgemやコンテナレジストリなどに成果物をアップロードする

特に、リリースに必要なファイル変更は、機能開発とは関係ないちょっとした変更なのでリリース担当者がサッと済ませてしまうことも多く、案外暗黙知となります。それで、他の人が見様見真似でやると必要なファイル更新が漏れる事故などが起こりがち。

これは、ちゃんと引き継げばいいと言うよりかは、引き継がなくても恙無くリリースできる状況を作るのが理想です。

なので、これもpull requestにしてレビュー可能な状況を作るとともに、pull request及びその変更も可能な限り自動で作られ、それがマージされたらリリースされるようにしたい。それを実現したのがこのtagprというわけです。

リポジトリに訪れた人も、そのpull requestを見れば、未リリース項目が把握でき、マージすれば新バージョンがリリースされることがわかるという、見える化・透明性の向上効果もあります。

リリース作業時にリポジトリ上で追加で変更したいファイルがある点や、tagを以てリリース完了としたいという点で git-pr-release とユースケースが異なります。

pull requestのタイトルと本文

上記のスクショを見てもらうとわかるように、pull requestの本文に、"What's Changed"で始まるリリース項目が自動で一覧されるのがお役立ちです。これはGitHub標準のリリースノート生成機能を利用しています。

このタイトルと本文は、設定ファイル.tagprtagpr.template キーにGo形式のテンプレートを指定することでカスタマイズ可能です。

pull requestのcommit内容

tagprはpull request生成時に標準で以下のファイル更新を行います。

  • semver記述ファイルのバージョンをインクリメントする
    • gemspecやsetup.cfg, version.go等 (versionfileと呼ぶ)
    • 初回実行時に自動検出され設定ファイル .tagpr に書き込まれます
      • tagpr.versionfileキー
      • 誤検出時は適宜書き換えてください
      • versionfileを空に設定すると、タグのみのリリース管理となります
  • CHANGELOG.mdの自動更新
    • GitHubの標準機能及び.github/release.yml に頼る
    • これはopinionatedな機能なので、オフにするオプションは要望次第で作るかも

また、初回実行時に以下のファイルが存在しない場合、最小限設定済の雛形を生成します。

  • .tagpr (設定ファイル)
  • .github/release.yml

リリース作業時に他にも自動更新させたいプロジェクト固有の項目がある場合、tagpr.command 設定にコマンドを指定し、commit前に任意のコマンド実行ができます。

また、リリース前の変更作業を手動でおこないたい場合、pull requestのブランチを直接編集し、commitを重ねてしまって構いません。

後はこのpull requestをマージすれば、マージコミットに自動でtagが打たれます。merge commitを作る形式でもsquash mergeでも構いません。

tagと同時にGitHub Releaseも作るのですが、これをオフにするオプションも設けるかもしれません。

次バージョン指定方法

tagprのバージョンインクリメント挙動はデフォルトではpull request上で単にpatchバージョンをインクリメントするだけです。しかし、major, minorバージョンを上げたいことも当然あるでしょう。これには二つの対応方法があります。

  • versionfileのブランチ上での直接書き換え
  • tagprが作ったpull requestへの tagpr:major, tagpr:minor ラベルの付与

後者は、versionfileを利用しない運用の際に採用すると良いでしょう。また、リリース対象項目のpull requestに付与されたラベル等を見て、major,minor,patchのどれをインクリメントするかを推測する機能を将来的に追加したい。

secrets.GITHUB_TOKEN が次のActionsをキックしない問題

tagprは自動でgit tagを打つところまでやってくれるため、そこから別のGitHub Actionsのワークフローをトリガーしたいこともあるでしょう。成果物の生成やアップロード等。

しかし、tagprの動作にGitHub Actionsが自動で提供してくれる secrets.GITHUB_TOKEN を利用すると、後続のワークフローは動きません。これは、ワークフローの意図しない再帰的な連続実行を防ぐための仕様です。

ref. https://docs.github.com/en/actions/security-guides/automatic-token-authentication#using-the-github_token-in-a-workflow

このため、後続のワークフローをトリガーしたい場合、tagprを別の権限で実行する必要があります。以下は、secrets.GH_PAT にpersonal access tokenを指定した例です。git push --tags も別権限で行いたいので、actions/checkoutにもこのtokenを指定しています。secretsはリポジトリ設定から別途指定してください。

name: tagpr
on:
  push:
    branches:
    - main
jobs:
  tagpr:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v3
      with:
        token: ${{ secrets.GH_PAT }}
    - uses: Songmu/tagpr@main
      env:
        GITHUB_TOKEN: ${{ secrets.GH_PAT }}

簡単のためにpersonal access tokenの例を書きましたが、GitHub Apps等を用いてtokenを都度生成するような手法を使ったほうが安全でしょう。

まとめ

他の細かい設定等についてはドキュメントをご確認ください。長々と書きましたが導入は簡単で便利です。実際個人的に便利に使っていますし、結構頑張って作ったので使ってもらえると大変喜びます。

とはいえ主張が強いツールなので、色々適用しづらい場合もあるかとは思います。そのあたりはフィードバックがあると大変嬉しいです。

ちなみに、今回のtagprを含め、これまでOSSのリリースエンジニアリングに関わるOSSを幾つか自作したり開発に携わったりしてきたので、どうやら私はこの分野が好きなようです。

そのあたりの話を来月のGo Conference mini 2022 Autumn IN SENDAIでお話する予定です。「OSSの最適なリリースエンジニアリングを求める旅路」というタイトルです。

私は仙台に行く予定ですが、現地枠もオンライン参加枠もまだあるようなので是非お申し込みください。仙台で交流できることも楽しみにしています。

https://sendaigo.connpass.com/event/256463/

created at
last modified at
comments powered by Disqus