Nerdbank.GitVersioningでNuGetパッケージのバージョン管理をしよう
NuGetパッケージをリリースする際、パッケージのバージョン番号を管理するのは煩雑で、本質的ではない作業です。そんなバージョン番号管理を助けてくれるツールとして、Nerdbank.GitVersioningを紹介いたします。
Nerdbank.GitVersioningのGitHubページはこちら。
Nerdbank.GitVersioningを学びたいシナリオについても少し説明しますが、とにかく早くNerdbank.GitVersioningの使い方を知りたい方は絵文字🚀のある場所を読んでみて、前提などが良く分からない時に他の部分を読んでみるとよいかもしれません。
セマンティックバージョニング
NuGetパッケージのバージョン番号を決めるとき、Microsoftによって推奨されている番号の付け方としてセマンティックバージョニング 2.0.0を使うのが安定した選択となるでしょう。
セマンティックバージョニングの考え方では、バージョンは「メジャーバージョン」「マイナーバージョン」「パッチバージョン」「プレリリースバージョン」から成ります。
プレリリース版を頻繁に出したい!
ここから先の節を通じて、NuGetパッケージのバージョンを管理するためにNerdbank.GitVersioningを利用する方法を解説します。今回は使い方の一例として、ぼくが実際に直面したシナリオを紹介しようと思います。
くわしい背景
ここ最近、ぼくは"Imfact"という静的なDIコンテナを開発していたのですが、開発が進んでくるとNuGet上にプレリリース版のパッケージを頻繁にアップロードしたいと思うようになりました。
しかしNuGetパッケージを作成してアップロードするという手順は、何十回も繰り返すには煩雑すぎますので、PowerShellなどのスクリプトを利用して自動化を進めていました。そんな中、自動化することが特に面倒なタスクはバージョン番号を管理することでした。
バージョン番号を自動的に進めるには、最新のバージョン番号が書かれたファイルを用意し、それを読み取り、バージョンを上げて、作業の終わった後はファイルに書き込む……といったIOのからむ複雑なスクリプトを書く必要があります。
ぼく自身でこういった作業を自動化するアプリを作ることもできますが、すでにあるものを利用できないか?と思い、調べているうちにNerdbank.GitVersioningというツールを見つけました。
nbgvのつかいかた🚀
ここから先は、Nerdbank.GitVersioningのことをnbgv
と呼ぶことにします。
それから、以下のことを前提とします。
-
dotnet
コマンドをインストールし、すでに使えるようになっていること - 管理したいプロジェクトを含むgitのリポジトリがすでにあること
-
main
という名前のブランチ上で作業していること- ブランチ名が
master
の方向けにはこの後補足があります。 - ついでに、
main
ブランチという呼び方に馴染みのない方はこちらの記事が参考になるかもしれません。
- ブランチ名が
nbgvをインストールする
まずは、nbgvをPCにインストールします。以下のコマンドを実行しましょう。
dotnet tool install -g nbgv
version.jsonを生成する
nbgvのインストールが済んだら、version.json
というファイルのひな形を生成してみます。このファイルは、nbgvが現在のバージョンを認識するための情報などが書かれた設定ファイルです。
以下のコマンドを、対象のgitリポジトリのルートで実行してください。
nbgv install
以下のようなファイルが生成されます。
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
"version": "1.0-beta",
"publicReleaseRefSpec": [
"^refs/heads/master$",
"^refs/heads/v\\d+(?:\\.\\d+)?$"
],
"cloudBuild": {
"buildNumber": {
"enabled": true
}
}
}
この時点で、このリポジトリ内のプロジェクトのバージョンは1.0-beta
であることになります。このversion.json
ファイルをgitにコミットしたら、次のステップに進みます。
get-versionサブコマンドを試してみよう
version.json
があるフォルダで、以下のコマンドを実行してみてください。
nbgv get-version
Version: 1.0.1.3707
AssemblyVersion: 1.0.0.0
AssemblyInformationalVersion: 1.0.1-beta+7b0efb11e9
NuGetPackageVersion: 1.0.1-beta-g7b0efb11e9
NpmPackageVersion: 1.0.1-beta.g7b0efb11e9
get-version
サブコマンドを使うことで、現在のバージョンを取得することができます。また、このコマンドにはバージョンの書式を指定するオプションを渡すことができます。
nbgv get-version -v NuGetPackageVersion
1.0.1-beta-g7b0efb11e9
-v
オプションと、その値としてNuGetPackageVersion
を指定することで、オプション無しで実行したバージョンのリストの中からひとつを選び出して表示することができました。こうして出力できたバージョン番号は、シェルスクリプトなどでNuGetのパッケージバージョンとして使用できることでしょう。
とはいえ、バージョンの末尾にg7b0efb11e9
などという謎のハッシュがついていて、このままパッケージのバージョン番号として使いたい感じではないですね。このハッシュの取り除き方についてはこの後説明します。
謎のハッシュを取り除く
get-version
したときについてくる謎のハッシュ文字列は、get-version
サブコマンドを実行した時点でのgitのコミットIDと対応しています。ただし、先頭にg
が追加されています。
この状態は、パッケージのバージョン番号をコミットIDと対応させたい場面では役に立ちそうですが、ユーザーに提供するプレリリース版としては適切ではないかもしれません。
このハッシュ文字列を取り除くためには、version.json
を少し書き換えます。publicReleaseRefSpec
という配列内にある、master
という文字列をmain
に書き換えましょう。
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
"version": "1.0-beta",
"publicReleaseRefSpec": [
- "^refs/heads/master$",
+ "^refs/heads/main$",
"^refs/heads/v\\d+(?:\\.\\d+)?$"
],
"cloudBuild": {
"buildNumber": {
"enabled": true
}
}
}
この状態で、nbgv get-version
コマンドを実行してみてください。
Version: 1.0.2.30851
AssemblyVersion: 1.0.0.0
AssemblyInformationalVersion: 1.0.2-beta+8378bd9a46
NuGetPackageVersion: 1.0.2-beta
NpmPackageVersion: 1.0.2-beta
これで、ハッシュ文字列が追記されないようになりました。
このハッシュ文字列は、publicReleaseRefSpec
配列の中に正規表現で書かれたブランチ名が、現在作業中のブランチ名とマッチしていない場合に末尾に追記されるものです。バージョン番号にハッシュがついて困っているときは、publicReleaseRefSpec
を確認してみてください。
バージョンを上げるには
バージョンを上げる2つの方法
おさらいですが、セマンティックバージョン(以下Semver)の文字列はドットやハイフンで区切られ、各部分は互いに異なる場面でインクリメントされる、あるいは0にリセットされます。
nbgvを使うとき、バージョンを上げる際の手順は主に2つあります。
リリースによってバージョンを上げる
nbgvのprepare-release
というサブコマンドによって、現在のバージョン番号に対応した新しいブランチを作成し、同時にmain
ブランチでは事前に指定しておいたSemverのいずれかの桁をインクリメントさせることができます。
こちらの手順では主にメジャー、マイナー、そして時折はパッチバージョンがインクリメントされます。
この手順について、この後の節「prepare-release のつかいかた」で解説します。
コミットによってバージョンを上げる
プレリリースバージョンを頻繁にリリースしたい場面では、プレリリースを出すたびに新たなブランチが作られるのは不都合かもしれません。nbgvでは、Semverの文字列中にgit heightと呼ばれる数値を埋め込むよう設定することができます。
// ここ、文1と文2の繋がりが不明瞭
git heightは、例えば1.0.1-beta
というSemverにおいてパッチバージョンの部分に埋め込んであるとすると、そのリポジトリに新たなコミットが追加されたときにバージョンがインクリメントされ、1.0.2-beta
となります。
git heightの値はversion.json
ファイルに保存されることはなく、gitリポジトリの状態に基づいて決定されます。
この手順について、この後の節「git height のつかいかた」で解説します。
prepare-release のつかいかた🚀
いま、nbgv get-version
コマンドを実行したときの状態が以下の通りだとします。
$ nbgv get-version
Version: 1.0.2.30851
AssemblyVersion: 1.0.0.0
AssemblyInformationalVersion: 1.0.2-beta+8378bd9a46
NuGetPackageVersion: 1.0.2-beta
NpmPackageVersion: 1.0.2-beta
ここでいま、パッケージのユーザー向けに安定版をリリースしたい状況になったと想像してみましょう。そんなとき、今の設定には問題があります。
バージョン番号を見てみると、-beta
というプレリリースバージョンが付いています。安定版のパッケージには通常付けないものなので、外す必要があります。とはいえ、これを外してリリースを終えたなら、また-beta
を付けなおして開発を再開したいはずです。
こうしたリリース作業をする際には、nbgv prepare-release
コマンドを使うと簡単で正確です。さっそく実行してみましょう。
$ nbgv prepare-release
v1.0 branch now tracks v1.0 stabilization and release.
main branch now tracks v1.1-alpha development.
実行した後にgitのブランチを確認してみると、v1.0
という名のブランチが作られていることが分かると思います。このブランチをチェックアウトしてから、nbgv get-version
コマンドの結果を確認してみてください。
$ git checkout v1.0
Switched to branch 'v1.0'
$ nbgv get-version
Version: 1.0.3.41834
AssemblyVersion: 1.0.0.0
AssemblyInformationalVersion: 1.0.3+6aa3f9883a
NuGetPackageVersion: 1.0.3
NpmPackageVersion: 1.0.3
-beta
の取り除かれたバージョンが取得できることが分かります。パッケージ番号にはこの値が使えそうですね。
mainブランチはどうなった?
次に、main
ブランチに戻ってバージョンを確認してみましょう。
$ git checkout main
Switched to branch 'main'
$ nbgv get-version
Version: 1.1.2.64794
AssemblyVersion: 1.1.0.0
AssemblyInformationalVersion: 1.1.2-alpha+1afd5eaa24
NuGetPackageVersion: 1.1.2-alpha
NpmPackageVersion: 1.1.2-alpha
マイナーバージョンが上がっていることが分かります。また、main
ブランチではプレリリースバージョンはついたままであることが分かります。
……とはいえ、さっきまでbetaだった部分がalphaになってしまっていますね。この挙動を変えるにはversion.json
を少し書き換えるか、prepare-release
サブコマンドにオプションを与える必要がありますが、詳しくは公式ドキュメントを読んでみてください。
git height の使い方🚀
いま、nbgv get-version
コマンドを実行したときの状態が以下の通りだとします。
$ nbgv get-version
Version: 1.1.2.64794
AssemblyVersion: 1.1.0.0
AssemblyInformationalVersion: 1.1.2-alpha+1afd5eaa24
NuGetPackageVersion: 1.1.2-alpha
NpmPackageVersion: 1.1.2-alpha
ここで、リポジトリのルートに適当な空のファイルを作って、コミットしてみてください。その後同様にバージョンを取得すると、異なる結果が得られます。
$ nbgv get-version
Version: 1.1.3.27754
AssemblyVersion: 1.1.0.0
AssemblyInformationalVersion: 1.1.3-alpha+6a6c9dcf1a
NuGetPackageVersion: 1.1.3-alpha
NpmPackageVersion: 1.1.3-alpha
パッチバージョンの部分がインクリメントされていることが分かります。このようにnbgvでは、コミットが追加されるたびにインクリメントされる値をバージョン番号に埋め込むことができるのです。この値をgit heightといいます。
これはプレリリースバージョンを頻繁にリリースしたい場合に楽できる方法です。バージョンを上げるコマンドを実行する必要はありませんし、新しいブランチが作られることもありません。
プレリリースバージョンを上げたい
ところでSemverでは、1.0-beta.1
というふうにプレリリースバージョンの部分に数値を埋め込むことも許されています。実際ぼくの開発時も、パッチバージョンよりも優先度の低い数値として、コミットごとに上がる数値をプレリリースバージョンに使いたい状況でした。
git heightの値を埋め込む位置をプレリリースバージョンの位置にできたなら便利そうです。
git heightの位置はversion.json
上で決めることができます。設定ファイルを以下のように書き換えてみましょう。
-
{height}
という文字列をプレリリースバージョンの任意の位置に埋め込みます。 -
nuGetPackageVersion/semVer
の設定値を追加します。これが必要なのは、-beta.1
のようにプレリリースバージョン内で更にドットで区切れるようにするためです。
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
- "version": "1.1-alpha",
+ "version": "1.1-alpha.{height}",
+ "nuGetPackageVersion": {
+ "semVer": 2.0
+ },
"publicReleaseRefSpec": [
"^refs/heads/main$",
"^refs/heads/v\\d+(?:\\.\\d+)?$"
],
"cloudBuild": {
"buildNumber": {
"enabled": true
}
}
}
version.json
をリポジトリにコミットすると、バージョンは以下のようになります。
$ nbgv get-version
Version: 1.1
AssemblyVersion: 1.1.0.0
AssemblyInformationalVersion: 1.1.0-alpha.1+2f9f4433f2
NuGetPackageVersion: 1.1.0-alpha.1
NpmPackageVersion: 1.1.0-alpha.1
設定ファイルで{height}
を書いた部分が、現在のgit heightに置き換えられていますね!リポジトリに何らかの変更をコミットして、実際にこの値がインクリメントされるかも確かめてみてください。
これにて、パッチバージョンより優先度の低いプレリリースバージョンに、git heightの値を埋め込むことができました。バージョンのリスト上でも、パッチバージョンより低い優先順位で、かつ昇順に並んでくれるはずです。
パッチバージョンをリリースしたい場合🚀
ここまでで、リリースによってバージョンを上げる方法と、コミットによってバージョンを上げる方法を紹介しました。しかし、前者の方法ではリリース時に上がるのはマイナーバージョンですし、後者の方法ではパッチバージョンをコミットごとに上げることしかできません。
パッチバージョンをリリースごとに上げる方法があると便利そうです。以下のように設定ファイルを書き換えてみてください。
-
version
の値にパッチバージョンも書く -
release/versionIncrement
をbuildに設定する
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
"version": "1.1.0-alpha.{height}",
"nuGetPackageVersion": {
"semVer": 2.0
},
"publicReleaseRefSpec": [
"^refs/heads/main$",
"^refs/heads/v\\d+(?:\\.\\d+)?$"
],
"release": {
"versionIncrement": "build"
},
"cloudBuild": {
"buildNumber": {
"enabled": true
}
}
}
設定ファイルをコミットして、現在のバージョンを確認します。
$ nbgv get-version -v NuGetPackageVersion
1.1.0-alpha.1
そして、nbgv prepare-release
を実行してから、改めてバージョンを確認します。
$ nbgv prepare-release
v1.1.0 branch now tracks v1.1.0 stabilization and release.
main branch now tracks v1.1.1-alpha.{height} development.
$ nbgv get-version -v NuGetPackageVersion
1.1.1-alpha.2
1.1.0-alpha.1
が 1.1.1-alpha.2
になりましたね!確かにパッチバージョンがインクリメントされました。release/versionIncrement
の使い方について詳しくは、公式ドキュメントを見てみてください。
おわりに
Nerdbank.GitVersioningでバージョン番号を管理する方法をご紹介しました。get-version
コマンドで標準出力へバージョン番号を出力できるので、そのまま他のコマンドに流し込んだり、変数に代入したりできるはずです。
バージョン番号の管理ツールとして好みは分かれそうですが、ぼくはかなり気に入りました!現状英語での解説しか見つけられなくて、仕様が把握しづらい部分もあって苦労したので、日本語の記事が誰かの助けになれば……と思い本記事を書いてみました。ぜひ使ってみてください!
ちなみに、GitHub Action版もあります。
余談ですが、Nerdbank.GitVersioningはdotnetチームで開発しているプロダクトのようです。Microsoftがバックに居るならそれなりに長くサポートされるかも?と思ったのも、ぼくが目を付けた理由のひとつです。
Discussion