12 |
13 | ## Overview
14 |
15 | Inspired by [MinVer](https://github.com/adamralph/minver), Build Versioning is a different attempt at the same problem - to make versioning simple.
16 | The simplicity comes from how the version strings are generated and the built-in integrations.
17 |
18 | ## 🤝 Licensing and Support
19 |
20 | Build Versioning is licensed under the MIT license. It is free to use in personal and commercial projects.
21 |
22 | There are [support plans](https://turnersoftware.com.au/support-plans) available that cover all active [Turner Software OSS projects](https://github.com/TurnerSoftware).
23 | Support plans provide private email support, expert usage advice for our projects, priority bug fixes and more.
24 | These support plans help fund our OSS commitments to provide better software for everyone.
25 |
26 | ## 📖 Table of Contents
27 | - [Requirements](#requirements)
28 | - [Getting Started](#getting-started)
29 | - [Example Versions](#example-versions)
30 | - [CI Versioning Integrations](#integrations)
31 | - [Customizing Version Strings](#customizing-version-strings)
32 | - [Formatting Tags](#formatting-tags)
33 | - [Version Strings](#version-strings)
34 | - [Additional Settings](#additional-settings)
35 |
36 | ## 📋 Requirements
37 |
38 | - Your project must be using a modern SDK-style project file
39 | - One of the following .NET runtimes must be installed:
40 | - .NET 6
41 | - .NET 8
42 |
43 | The runtime requirement is so that Build Versioning itself can run.
44 | Your project though can target whatever version of .NET you want (Framework/Standard/Core etc).
45 |
46 | ## ⭐ Getting Started
47 |
48 | 1. [Install Build Versioning](https://www.nuget.org/packages/TurnerSoftware.BuildVersioning/)
49 | ```powershell
50 | PM> Install-Package TurnerSoftware.BuildVersioning
51 | ```
52 | 2. There is no second step - *you're done!*
53 |
54 | The version information is extracted from the current state of the Git repository.
55 | From a tag that is [SemVer v2.0](https://semver.org/spec/v2.0.0.html) compliant, it can extract the major, minor, patch, pre-release and build metadata information.
56 | This information is then fed through a formatting system to generate specific [version strings](#Version-Strings).
57 |
58 | Additional information is provided from Git directly including the commit height (number of commits since the last tag) and the commit hash itself.
59 |
60 | ### Example Versions
61 |
62 | These examples use the default configuration after installing Build Versioning.
63 |
64 | |Example|Git Tag|Commit Height|Full Version|File Version|Assembly Version|
65 | |-|:-:|:-:|:-:|:-:|:-:|
66 | |New Release |1.2.4 |0|1.2.4+a4f31ea |1.2.4.0|1.0.0.0|
67 | |New Pre-Release |1.2.4-alpha|0|1.2.4-alpha+a4f31ea |1.2.4.0|1.0.0.0|
68 | |Main Branch / Active Development |1.2.4 |4|1.2.4-dev.4+a4f31ea |1.2.4.0|1.0.0.0|
69 | |Non-PR Commit via GitHub Actions |1.2.4 |4|1.2.4-dev.4+a4f31ea-github.432515|1.2.4.0|1.0.0.0|
70 | |PR Commit via GitHub Actions |1.2.4 |4|1.2.4-pr.17+a4f31ea-github.432515|1.2.4.0|1.0.0.0|
71 |
72 | ## 🛠 CI Versioning Integrations
73 |
74 | By default, Build Versioning provides rich pre-release and build metadata from the current CI environment.
75 | For pull requests, this will automatically have a pre-release defined which will include the PR number (eg. `1.2.4-pr.17`).
76 | For all commits, the build metadata will include the CI environment and a relevant build identifier (eg. `1.2.4+a4f31ea-github.432515`).
77 |
78 | ### Default Integrations
79 |
80 | |Integration|Configuration Tag|Notes|
81 | |-|-|-|
82 | |[GitHub Actions](https://github.com/features/actions)|``|Will perform a `git fetch` for tags that are missing by default for GitHub Actions. This specific behaviour can be disabled by setting `` to false.|
83 | |[Azure DevOps](https://azure.microsoft.com/en-us/services/devops/pipelines/)|``||
84 | |[AppVeyor](https://www.appveyor.com/)|``|Will update the AppVeyor build name to match the build version. This specific behaviour can be disabled by setting `` to false.|
85 |
86 | ### Disabling an Integration
87 |
88 | Each integration can be individually disabled through configuration. For example, include the following in your project file to disable the GitHub Actions integration:
89 |
90 | ```xml
91 | false
92 | ```
93 |
94 | ## ✏ Customizing Version Strings
95 |
96 | ### Formatting Tags
97 |
98 | These are formatting tags available for you to use for customizing your version strings.
99 |
100 | |Tag|Notes|
101 | |-|-|
102 | |`{Major}`|The major version retrieved from the Git tag. If there are no tags available, defaults to `0`.|
103 | |`{Major++}`|The major version retrieved from the Git tag incremented by 1. If this is a tagged release, the value will return the major version without increment.|
104 | |`{Minor}`|The minor version retrieved from the Git tag. If there are no tags available, defaults to `0`.|
105 | |`{Minor++}`|The minor version retrieved from the Git tag incremented by 1. If this is a tagged release, the value will return the minor version without increment.|
106 | |`{Patch}`|The patch version retrieved from the Git tag. If there are no tags available, defaults to `0`.|
107 | |`{Patch++}`|The patch version retrieved from the Git tag incremented by 1. If this is a tagged release, the value will return the patch version without increment.|
108 | |`{CommitHeight}`|The number of commits since the last tag. If there are no tags available, defaults to `0`.|
109 | |`{CommitHash}`|The first 7 characters of the most recent commit hash.|
110 |
111 | Additionally, the full version string supports two additional formatting tags.
112 |
113 | |Tag|Default Value|Configuration Tag|Description|
114 | |-|-|-|-|
115 | |`{PreRelease}`|`dev.{CommitHeight}`|``|The pre-release portion of the version. This will include the leading dash (`-`) if a pre-release is defined, otherwise blank. The value is overridden by the Git tag if this is a tagged release.|
116 | |`{BuildMetadata}`|`{CommitHash}`|``|The build metadata portion of the version. This will include the leading plus (`+`) if build metadata is defined, otherwise blank. The value is overridden by the Git tag if this is a tagged release and is defined in the tag.|
117 |
118 |
119 | ### Version Strings
120 |
121 | |Name|Configuration Tag|Default Value|
122 | |-|-|-|
123 | |📦 **Full Version** aka. the "package" or "product" version, it is used for versioning the package itself and displayed in NuGet.|``|`{Major}.{Minor}.{Patch}{PreRelease}{BuildMetadata}`|
124 | |📄 **File Version** A superficial version number, displayed by the OS. This is not used by the .NET runtime.|``|`{Major}.{Minor}.{Patch}.0`|
125 | |⚙ **Assembly Version** Used by .NET for referencing the assembly when strong-named signing is enabled. Updating this by major version is advised.|``|`{Major}.0.0.0`|
126 |
127 | For more information on file version vs assembly version, [see the MSDN docs](https://docs.microsoft.com/en-us/troubleshoot/visualstudio/general/assembly-version-assembly-file-version).
128 |
129 | ## 🎛 Additonal Settings
130 |
131 | ### Disabling Build Versioning
132 |
133 | You can disable build versioning by setting `` in your project file to `true`.
134 |
135 | ### Enable Output Logging
136 |
137 | You can enable output logging for Build Versioning by specifying `` as `normal` (for basic logging) or `high` (for detailed logging).
--------------------------------------------------------------------------------
/TurnerSoftware.BuildVersioning.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31025.194
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TurnerSoftware.BuildVersioning", "src\TurnerSoftware.BuildVersioning\TurnerSoftware.BuildVersioning.csproj", "{F7049F6B-9985-41A7-A911-EAE54EB8A708}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TurnerSoftware.BuildVersioning.Tool", "src\TurnerSoftware.BuildVersioning.Tool\TurnerSoftware.BuildVersioning.Tool.csproj", "{D796E0D8-9315-47F1-86BD-EA8958E5A7FA}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{1EFB8EBA-39B4-472E-9DE3-2B25E902EE2C}"
11 | ProjectSection(SolutionItems) = preProject
12 | src\Directory.Build.props = src\Directory.Build.props
13 | EndProjectSection
14 | EndProject
15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{790239FE-AB99-45EE-B764-23F4F351C61E}"
16 | ProjectSection(SolutionItems) = preProject
17 | .appveyor.yml = .appveyor.yml
18 | .codecov.yml = .codecov.yml
19 | .editorconfig = .editorconfig
20 | .gitignore = .gitignore
21 | azure-pipelines.yml = azure-pipelines.yml
22 | CodeCoverage.runsettings = CodeCoverage.runsettings
23 | License.txt = License.txt
24 | README.md = README.md
25 | EndProjectSection
26 | EndProject
27 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{EC91FA7D-31F4-4BB8-ABD1-2C142491D34C}"
28 | ProjectSection(SolutionItems) = preProject
29 | tests\Directory.Build.props = tests\Directory.Build.props
30 | EndProjectSection
31 | EndProject
32 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TurnerSoftware.BuildVersioning.Tests", "tests\TurnerSoftware.BuildVersioning.Tests\TurnerSoftware.BuildVersioning.Tests.csproj", "{F97BCD71-3D0E-4B36-8BE5-B401A05E0829}"
33 | EndProject
34 | Global
35 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
36 | Debug|Any CPU = Debug|Any CPU
37 | Release|Any CPU = Release|Any CPU
38 | EndGlobalSection
39 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
40 | {F7049F6B-9985-41A7-A911-EAE54EB8A708}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
41 | {F7049F6B-9985-41A7-A911-EAE54EB8A708}.Debug|Any CPU.Build.0 = Debug|Any CPU
42 | {F7049F6B-9985-41A7-A911-EAE54EB8A708}.Release|Any CPU.ActiveCfg = Release|Any CPU
43 | {F7049F6B-9985-41A7-A911-EAE54EB8A708}.Release|Any CPU.Build.0 = Release|Any CPU
44 | {D796E0D8-9315-47F1-86BD-EA8958E5A7FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45 | {D796E0D8-9315-47F1-86BD-EA8958E5A7FA}.Debug|Any CPU.Build.0 = Debug|Any CPU
46 | {D796E0D8-9315-47F1-86BD-EA8958E5A7FA}.Release|Any CPU.ActiveCfg = Release|Any CPU
47 | {D796E0D8-9315-47F1-86BD-EA8958E5A7FA}.Release|Any CPU.Build.0 = Release|Any CPU
48 | {F97BCD71-3D0E-4B36-8BE5-B401A05E0829}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49 | {F97BCD71-3D0E-4B36-8BE5-B401A05E0829}.Debug|Any CPU.Build.0 = Debug|Any CPU
50 | {F97BCD71-3D0E-4B36-8BE5-B401A05E0829}.Release|Any CPU.ActiveCfg = Release|Any CPU
51 | {F97BCD71-3D0E-4B36-8BE5-B401A05E0829}.Release|Any CPU.Build.0 = Release|Any CPU
52 | EndGlobalSection
53 | GlobalSection(SolutionProperties) = preSolution
54 | HideSolutionNode = FALSE
55 | EndGlobalSection
56 | GlobalSection(NestedProjects) = preSolution
57 | {F7049F6B-9985-41A7-A911-EAE54EB8A708} = {1EFB8EBA-39B4-472E-9DE3-2B25E902EE2C}
58 | {D796E0D8-9315-47F1-86BD-EA8958E5A7FA} = {1EFB8EBA-39B4-472E-9DE3-2B25E902EE2C}
59 | {F97BCD71-3D0E-4B36-8BE5-B401A05E0829} = {EC91FA7D-31F4-4BB8-ABD1-2C142491D34C}
60 | EndGlobalSection
61 | GlobalSection(ExtensibilityGlobals) = postSolution
62 | SolutionGuid = {17BD2449-BDDF-40FB-A123-E2B7B927BF9C}
63 | EndGlobalSection
64 | EndGlobal
65 |
--------------------------------------------------------------------------------
/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | trigger:
2 | - main
3 |
4 | jobs:
5 | - job: BuildApplication
6 | pool:
7 | vmImage: ubuntu-latest
8 |
9 | variables:
10 | BUILD_ARTIFACT_PATH: $(Build.ArtifactStagingDirectory)
11 |
12 | steps:
13 | - task: UseDotNet@2
14 | displayName: Install .NET 6 SDK
15 | inputs:
16 | version: 6.0.x
17 | - task: UseDotNet@2
18 | displayName: Install .NET 8 SDK
19 | inputs:
20 | version: 8.0.x
21 |
22 | - script: dotnet --info
23 | displayName: .NET info
24 |
25 | - script: dotnet restore
26 | displayName: Install dependencies
27 |
28 | - script: dotnet build --no-restore -c Release /p:ContinuousIntegrationBuild=true -bl:$(BUILD_ARTIFACT_PATH)/msbuild-build.binlog
29 | displayName: Build
30 |
31 | - script: dotnet test --no-restore /p:SkipBuildVersioning=true
32 | displayName: Test
33 |
34 | - script: dotnet pack --no-build -c Release /p:PackageOutputPath=$(BUILD_ARTIFACT_PATH) /p:ContinuousIntegrationBuild=true -bl:$(BUILD_ARTIFACT_PATH)/msbuild-pack.binlog
35 | displayName: Pack
36 |
37 | - task: PublishBuildArtifacts@1
38 | displayName: Publish artifacts
39 | inputs:
40 | ArtifactName: BuildVersioning
41 | pathToPublish: $(BUILD_ARTIFACT_PATH)
42 |
--------------------------------------------------------------------------------
/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TurnerSoftware/BuildVersioning/095a5224de5a65ed141112321ecab32a6e7475d8/images/icon.png
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "github>TurnerSoftware/.github:renovate-shared"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/src/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | TurnerSoftware.BuildVersioning
5 |
6 | Turner Software
7 |
8 | $(AssemblyName)
9 | true
10 | MIT
11 | icon.png
12 | readme.md
13 | https://github.com/TurnerSoftware/BuildVersioning
14 | semver;semantic;versioning;git
15 |
16 |
17 | true
18 | true
19 | true
20 | snupkg
21 |
22 | Latest
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/TurnerSoftware.BuildVersioning.Tool/AssemblyInternals.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [assembly: InternalsVisibleTo("TurnerSoftware.BuildVersioning.Tests")]
--------------------------------------------------------------------------------
/src/TurnerSoftware.BuildVersioning.Tool/BuildVersion.cs:
--------------------------------------------------------------------------------
1 | namespace TurnerSoftware.BuildVersioning.Tool;
2 |
3 | public record BuildVersion
4 | {
5 | public string FullVersion { get; init; }
6 | public string FileVersion { get; init; }
7 | public string AssemblyVersion { get; init; }
8 | }
9 |
--------------------------------------------------------------------------------
/src/TurnerSoftware.BuildVersioning.Tool/BuildVersioner.cs:
--------------------------------------------------------------------------------
1 | namespace TurnerSoftware.BuildVersioning.Tool;
2 |
3 | internal class BuildVersioner(IVersionDetailsProvider versionDetailsProvider)
4 | {
5 | public BuildVersion GetBuildVersion(BuildVersioningOptions options)
6 | {
7 | var versionDetails = versionDetailsProvider.GetVersionDetails();
8 | if (versionDetails is null)
9 | {
10 | return null;
11 | }
12 |
13 | if (!versionDetails.IsTaggedRelease && versionDetails.PreRelease is null && options.PreReleaseFormat?.Length > 0)
14 | {
15 | versionDetails = versionDetails with
16 | {
17 | PreRelease = options.PreReleaseFormat
18 | .Replace("{CommitHeight}", versionDetails.CommitHeight.ToString())
19 | };
20 | }
21 |
22 | if (options.BuildMetadataFormat?.Length > 0)
23 | {
24 | versionDetails = versionDetails with
25 | {
26 | BuildMetadata = options.BuildMetadataFormat
27 | .Replace("{CommitHash}", versionDetails.CommitHash)
28 | .Replace("{CommitHeight}", versionDetails.CommitHeight.ToString())
29 | };
30 | }
31 |
32 | var fullVersion = FormatFullVersion(options.FullVersionFormat, versionDetails);
33 | var fileVersion = FormatVersion(options.FileVersionFormat, versionDetails);
34 | var assemblyVersion = FormatVersion(options.AssemblyVersionFormat, versionDetails);
35 |
36 | return new BuildVersion
37 | {
38 | FullVersion = fullVersion,
39 | FileVersion = fileVersion,
40 | AssemblyVersion = assemblyVersion
41 | };
42 | }
43 |
44 | private static string FormatFullVersion(string format, VersionDetails versionDetails)
45 | {
46 | if (string.IsNullOrEmpty(format))
47 | {
48 | return format;
49 | }
50 |
51 | return FormatVersion(format, versionDetails)
52 | .Replace("{PreRelease}", versionDetails.PreRelease is null ? default : $"-{versionDetails.PreRelease}")
53 | .Replace("{BuildMetadata}", versionDetails.BuildMetadata is null ? default : $"+{versionDetails.BuildMetadata}");
54 | }
55 |
56 | private static string FormatVersion(string format, VersionDetails versionDetails)
57 | {
58 | if (string.IsNullOrEmpty(format))
59 | {
60 | return format;
61 | }
62 |
63 | var autoIncrement = versionDetails.IsTaggedRelease ? 0 : 1;
64 | return format
65 | .Replace("{Major}", versionDetails.MajorVersion.ToString())
66 | .Replace("{Major++}", (versionDetails.MajorVersion + autoIncrement).ToString())
67 | .Replace("{Minor}", versionDetails.MinorVersion.ToString())
68 | .Replace("{Minor++}", (versionDetails.MinorVersion + autoIncrement).ToString())
69 | .Replace("{Patch}", versionDetails.PatchVersion.ToString())
70 | .Replace("{Patch++}", (versionDetails.PatchVersion + autoIncrement).ToString())
71 | .Replace("{CommitHeight}", versionDetails.CommitHeight.ToString())
72 | .Replace("{CommitHash}", versionDetails.CommitHash ?? "NOCANDO");
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/TurnerSoftware.BuildVersioning.Tool/BuildVersioningOptions.cs:
--------------------------------------------------------------------------------
1 | namespace TurnerSoftware.BuildVersioning.Tool;
2 |
3 | public record BuildVersioningOptions
4 | {
5 | public string FullVersionFormat { get; init; }
6 | public string FileVersionFormat { get; init; }
7 | public string AssemblyVersionFormat { get; init; }
8 | public string PreReleaseFormat { get; init; }
9 | public string BuildMetadataFormat { get; init; }
10 | }
11 |
--------------------------------------------------------------------------------
/src/TurnerSoftware.BuildVersioning.Tool/GitCommandRunner.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Threading.Tasks;
4 |
5 | namespace TurnerSoftware.BuildVersioning.Tool;
6 |
7 | internal class GitCommandRunner : IGitCommandRunner
8 | {
9 | private static string RunCommand(string command)
10 | {
11 | using var process = new Process();
12 | process.StartInfo = new ProcessStartInfo("git", command)
13 | {
14 | RedirectStandardOutput = true
15 | };
16 |
17 | var waitOnExit = new TaskCompletionSource