セマンティックバージョニング(または略してSemVer)は、<メジャー>.<マイナー>.<パッチ>という形式の3つの部分からなるバージョン番号を使用するソフトウェアバージョニングスキームで、例えば1.0.2のような形式で、-<プリアorelease>という形式のオプションのプリアoreleaseサフィックスを持ち、例えば1.0.2-betaです。

SemVerはおそらく現在最受欢迎のバージョニングスキームです。例えば、Nugetnpmは推奨し、サポートしており、VS Codeも使用しています

GitHubのリリース機能を使用してリリースを公開している大多数のリポジトリでは、最新リリースのバッジにSemVerバージョン番号が表示され、以下のスクリーンショットで確認できます:

私はしばしば、ASP.NET Core APIプロジェクトをビルドする際にSemVerバージョン番号を設定し、その後ランタイムで読み取ったり報告したりする必要があります。

例えば、バージョンを1.0.2-betaに設定されている最小のAPIを構築した場合、Hoppscotch(これはPostmanのようなツールで、ブラウザを実行するという利便性があります)の下のスクリーンショットに示されているように、これはAPIによって公開された/versionエンドポイントによって報告されるでしょう:

WebアプリやAPIなどのデプロイされたサービスから報告されたバージョンが正しいことをチェックすることは、私のCDパイプラインの重要な部分であり、デプロイが成功したかどうかを判断するために使用するスモークテストの1つです。

SemVerのバージョン番号を.NETアセンブリに設定する際に少し複雑なことがあります。.NETアセンブリでSemVerバージョン番号を設定するときに少し複雑なのは、.NETはもともと1.0.3.212のような4つの部分バージョン番号を使用しており、アセンブリはまだこれらを持っているということです(アセンブリは.NET用語で、.NETバイトコードにコンパイルされたコードのユニットを表し、最も典型的なものはdllとexeです)。

もう1つは、.NETは1つではなく、同じアセンブリに存在する多くの、わずかに異なるバージョン番号を持っているということです。

この記事では、これらの癖を回避し、ビルド中に.NETアセンブリにSemVerバージョン番号をスタンプする方法を紹介します。

目次

セマンティック バージョン番号の構造

例えば、1.0.21.0.2-betaのようなセマンティック バージョン番号を考えます。それには以下の形式があります。<メジャー>.<マイナー>.<パッチ>< prerelease>

これらの各コンポーネントが何を意味するか如下です:

バージョンナンバーの<major>コンポーネントは、新しいリリースが既存の(最新の)リリースを破壊する場合にのみインクリメントされます。

UIアプリの場合、クライアントは人間のクライアントを指す可能性があります。したがって、新しいリリースがユーザーの既存のアセット(ワークフローの定義など)を破壊する場合、メジャーバージョンナンバーをインクリメントする必要があります。この場合、前回のリリースが1.0.2の場合、新しいリリースは2.0.0(バージョンナンバーのすべての下位コンポーネントはリセットされます).

ライブラリの場合、NugetやNPMなどのライブラリパッケージの場合、クライアントは他のコードです。したがって、新しいリリースが既存のクライアントコードを破壊する場合、つまり自身の前回のバージョンと後方互換性がない場合、再び<major>コンポーネントがインクリメントされます。

<minor>は、新しい機能が追加されたが新しいバージョンがまだ後方互換性を持つ場合にインクリメントされます。したがって、1.0.2から1.1.0に進みます。

<patch>は、新しいリリースが必要だが破壊的な変更がなく、新しい機能が追加されていない場合にインクリメントされます。これは、例えばバグフィックスがリリースされる場合などに起こる可能性があります。

-<prerelease>サフィックスはオプションです。通常、ソフトウェアがアルファやベータなどの事前リリーステストフェーズで利用可能にする必要がある場合に、3つの部分からなるバージョンナンバーに付きます。例えば、ソフトウェアのバージョン1.0.2を一般にリリースする前に、ベータテスターに1.0.2-betaとして提供することができます。

<prerelease>コンポーネントは、あなたの選択する任意の文字列であり、唯一の要求は、それがアルファベット数字の識別子であること、例えばbetaまたは12またはalpha2(数字またはアルファベットの文字以外の文字は許可されません)もしくは、ドット(.)で区切られた複数のアルファベット数字の識別子、例えばdevelopment.versionなどです。

.NETアセンブリの多くのバージョン番号

Andrew Lockの.NETバージョニングに関する記事で説明されているように、.NETアセンブリには一つの而不是る複数の異なるバージョン番号があります:

  • アセンブリバージョン:これは4つの部分からなるバージョン番号で、例えば1.0.2.0です。ランタイムがリンクされたアセンブリを読み込む際に使用されます。
  • ファイルバージョン:これは、アセンブリを右クリックしてプロパティを選択したときに、Windowsファイルエクスプローラーで報告される.dllファイルのバージョン番号です。
  • 情報バージョン: 更に別のバージョン番号で、FileVersionと同様に、Windowsでアセンブリを右クリックして「プロパティ」を選択すると表示されるプロパティダイアログで確認できます。このバージョンには、AssemblyVersionやFileVersionに制約されている整数とドットだけでなく、文字列を含めることができます。

  • パッケージバージョン: プロジェクトがNuGetパッケージの場合、これはアセンブリが含まれるパッケージのバージョン番号です。

これらのバージョン番号は、コンパイル時にアセンブリのメタデータとして発行されます。 JetBrains dotPeek(無料)やRed gate Reflector(有料でない)などのツールでアセンブリを検査するとこれらを見ることができます。

FileVersionと情報バージョンは、Windowsエクスプローラーでアセンブリファイルを右クリックして「プロパティ」を選択すると表示されるプロパティダイアログの詳細タブでも確認できます。

上記のスクリーンショットでは、「Product version」はInformationalVersionのキャプションで、「File version」はFileVersionのキャプションです。

上記で説明した4つのバージョン番号のうち、最初の3つだけがすべてのアセンブリに適用されます(アセンブリがNugetパッケージの一部であるかどうかに関係ありません)。

そのうちの3つであるAssemblyVersionは、3桁のSemVerバージョン(オプションのprereleaseサフィックスを除く)を設定しようとすると、4番目の位置に0を追加します。例えば、ビルド中にSemVerバージョンを1.0.2-betaに設定し、その後アセンブリ内で実行時に関数のAssemblyVersion値を読み取ると、それは1.0.2.0になります。

FileVersionも同様に、上記のスクリーンショットに示されるように行います。

InformationalVersionだけがビルド中に設定したサーバーバージョンに正確に設定されるバージョン番号であり、上記のスクリーンショットが示しています。

したがって、InformationalVersionは実行時にアセンブリのSemVerバージョンを取得するために読むべきバージョンです。

SemVerバージョン番号の設定方法

ビルド中にアセンブリにSemVerバージョン番号を設定するには、2つのことを行う必要があります。

まず、プロジェクトのcsprojファイルの<PropertyGroup>要素内に、要素<IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion>を追加します:

<PropertyGroup>
 ...
 <IncludeSourceRevisionInInformationalVersion>false</IncludeSourceRevisionInInformationalVersion> 
</PropertyGroup>

この問題所述,这确保了InformationalVersion被精确设置为指定的SemVer版本号,并且在末尾不会附加+<hash code>

第二,将版本号作为传递给dotnet build命令的Version属性的值,例如:

dotnet build --configuration Release -p Version=1.0.2-beta

这将把编译后的程序集(.exe或.dll文件)中的InformationalVersion设置为1.0.2-beta

顺便说一下,它还会设置AssemblyVersion和FileVersion(在1.0.2的末尾添加一个额外的0),但我们不关心这些。

请注意,除了在命令行上传递Version参数外,还可以在csproj文件的<PropertyGroup>元素中设置MS Build属性<Version>1.0.2-beta</Version>。但是,将Version参数的值传递给dotnet build更简单,因为每次版本号增加时不需要修改csproj文件。这在CD管道中很有用。此外,默认情况下,csproj文件中没有与版本控制相关的属性。

如何在运行时读取程序集的SemVer版本

读取运行时InfromationalVersion的代码如下:

string? version = Assembly.GetEntryAssembly()?.
  GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.
  InformationalVersion;

私のミニマルAPIでは、上記の導入セクションに示したように、/versionエンドポイントを追加するために、上記のスニペットをProgram.csに配置し、そのすぐ後に以下のスニペットを追加します。ことに注意してくださいbuilder.Build() が呼び出される前に全部が表示される必要があります:

//この匿名型のオブジェクトは
//ハンドラから返されたときに
//JSONとしてレスポンスボディにシリアライズされます
var objVersion = new { Version = version ?? "" };

//OTHER CODE
//var app = builder.Build()

builder.Build()が呼び出された後、/versionエンドポイントのハンドラを作成します:

app.MapGet("/version", () => objVersion);

今、APIプロジェクトを実行し、/versionエンドポイントを呼び出すと、HTTPレスポンスボディのJSONオブジェクトとしてバージョン番号が返されます:

{
  "version": "1.0.2-beta"
}

これは導入で示したHoppscotchのスクリーンショットです:

結論

この記事では、.NETアセンブリ、ライブラリ、またはアプリケーションにSemVerバージョン番号を設定する方法を説明しました:

また、実行時にバージョン番号を読み取る方法も説明しました。