Versioning with NerdBank.GitVersion
I formerly tested the Versioning tool MinVer but was not satisfied for all my CI/CD needs. Therefore I tried the tool NerdBank.GitVersion and succeeded with implementing a minimalistic, configurable versioning with DevOps integration and access to the version numbers in the Buildpipe and the Releasepipe as well.
Installation
Run the following commands with your dotnet CLI to trigger the installation of the tool:
1
dotnet tool install -g nbgv
Then install the package in your repo with:
1
nbgv install
.NET Assemblies
The package integrates in the build process and stamps the version number into the assemblies. It works with Semantic Versioning and uses the Major and Minor numbers for the assemblyVersion, a git height calculated Patch number and can use a git hash or a calculated integer from the git hash as Revision number. The tool would work with prerelease version suffixes, too.
1
2
3
[assembly: System.Reflection.AssemblyVersion("1.0")]
[assembly: System.Reflection.AssemblyFileVersion("1.0.24.15136")]
[assembly: System.Reflection.AssemblyInformationalVersion("1.0.24-alpha+g9a7eb6c819")]
What is the ‘git height’?
Git ‘height’ is the number of commits in the longest path from HEAD (the code you’re building) to some origin point, inclusive. In this case the origin is the commit that set the major.minor version number to the values found in the HEAD.
For example, if the version specified at HEAD is 1.1 and the longest path in git history from HEAD to where the version file was changed to 1.1 includes 15 commits, then the git height is “15” and teh Version would look something like this: 1.1.15
.
Additional Information
A very nice addition is that the tool injects a class called ThisAssembly
which then holds the following infos to be accessed as internal const strings:
1
2
3
4
5
6
7
8
9
10
11
internal sealed partial class ThisAssembly {
internal const string AssemblyVersion = "1.0";
internal const string AssemblyFileVersion = "1.0.24.15136";
internal const string AssemblyInformationalVersion = "1.0.24-alpha+g9a7eb6c819";
internal const string AssemblyName = "Microsoft.VisualStudio.Validation";
internal const string PublicKey = @"0024000004800000940000...reallylongkey..2342394234982734928";
internal const string PublicKeyToken = "b03f5f7f11d50a3a";
internal const string AssemblyTitle = "Microsoft.VisualStudio.Validation";
internal const string AssemblyConfiguration = "Debug";
internal const string RootNamespace = "Microsoft";
}
version.json
The package needs a single version.json
in the root directory, or multiple project based version.json
files with the configuration of the NBGV. This could be set in root besides the Directory.Build.props
file with further project details like company info, authors and more (read more about props files here).
A simple example could look like this:
1
2
3
4
5
6
7
8
{
"$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json",
"version": "1.0",
"gitCommitIdShortFixedLength": 6,
"assemblyVersion": {
"precision": "revision"
}
}
This is the place where you manually set the Major and Minor version numbers. The schema field is optional and should help some editors to use autocompletion for editing the file.
See here to read about all configurable settings: version.json
DevOps
Use a full clone for your pipeline to avoid shallow clones with:
1
2
3
steps:
- checkout: self
fetchDepth: 0
NBGV needs the .git directory to work.
The package has some CI variables by default:
Build variable | property | Sample value |
---|---|---|
GitAssemblyInformationalVersion | AssemblyInformationalVersion | 1.3.1+g15e1898f47 |
GitBuildVersion | BuildVersion | 1.3.1.57621 |
GitBuildVersionSimple | BuildVersionSimple | 1.3.1 |
You could even activate more variables with (cloudbuild doc):
1
2
3
4
5
6
7
{
"version": "1.0",
"cloudBuild": {
"setVersionVariables": true,
"setAllVariables": true
}
}
Coding example
Now you can access the version infos and output them in runtime.
1
2
3
4
5
6
7
8
9
10
11
12
Console.WriteLine($"Data to access with the NBGV class:{Environment.NewLine}" +
$"AssemblyName: {ThisAssembly.AssemblyName} {Environment.NewLine}" +
$"AssemblyTitle: {ThisAssembly.AssemblyTitle} {Environment.NewLine}" +
$"AssemblyVersion: {ThisAssembly.AssemblyVersion} {Environment.NewLine}" +
$"AssemblyFileVersion: {ThisAssembly.AssemblyFileVersion} {Environment.NewLine}" +
$"AssemblyInformationalVersion: {ThisAssembly.AssemblyInformationalVersion} {Environment.NewLine}" +
$"{Environment.NewLine}");
Console.WriteLine($"Finished program. {Environment.NewLine}" +
$"Company: {FileVersionInfoProcessor.GetFileVersionInfo().CompanyName}, {Environment.NewLine}" +
$"File Version: {FileVersionInfoProcessor.GetFileVersionInfo().FileVersion}, {Environment.NewLine}" +
$"Product Version: {FileVersionInfoProcessor.GetFileVersionInfo().ProductVersion}, {Environment.NewLine}Bye bye all!!");
Pipeline Example using library vars
Here’s an example of a pipeline that I use in my projects combined with the library variables:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
trigger:
branches:
include:
- 'master'
pool:
vmImage: 'windows-latest'
variables:
- group: YOUR_GROUP_NAME
- name: SolutionName
value: YOUR_SOLUTION_NAME
- name: Vars_GroupId
value: 'YOUR_GROUP_ID'
- name: NBGV_Version
value: 'NBGV_$(SolutionName)'
- name: solution
value: '**/*.sln'
- name: buildPlatform
value: 'Any CPU'
- name: buildConfiguration
value: 'Release'
steps:
- checkout: self
submodules: true
persistCredentials: true
fetchDepth: 0
- task: NuGetToolInstaller@1
displayName: 'Install NuGet Tools'
inputs:
checkLatest: true
- task: NuGetCommand@2
displayName: 'Restore NuGet Packages'
inputs:
restoreSolution: '$(solution)'
- task: VSBuild@1
displayName: 'Build Solution'
inputs:
solution: '$(solution)'
platform: '$(buildPlatform)'
configuration: '$(buildConfiguration)'
- task: CopyFiles@2
displayName: 'Copy build artifacts to staging folder'
inputs:
SourceFolder: '$(System.DefaultWorkingDirectory)'
Contents: '$(SolutionName)/bin/$(buildConfiguration)/**'
TargetFolder: '$(Build.ArtifactStagingDirectory)'
- task: PublishPipelineArtifact@1
displayName: 'Publish Pipeline Artifacts'
inputs:
targetPath: '$(Build.ArtifactStagingDirectory)'
ArtifactName: 'drop'
publishLocation: 'pipeline'
- powershell: |
dotnet tool install --tool-path . nbgv
displayName: Install NBGV CLI Tools
# Service-TFSBuild user needs the azure CLI with DevOps extension. Install with 'az extension add --name azure-devops'
- powershell: |
Write-Host "Update library variable '$(NBGV_Version)' with NBGV var GitBuildVersion $(GitBuildVersion) ..."
az pipelines variable-group variable update --group-id $(Vars_GroupId) --name $(NBGV_Version) --value "$(GitBuildVersion)"
displayName: Update library NBGV Version Variable
env:
AZURE_DEVOPS_EXT_PAT: $(System.AccessToken)