A minimalist .NET package for versioning .NET SDK-style projects using Git tags.
Platform support: all platforms supported by .NET SDK-style projects.
Also available as a command-line tool for use in any Git repository.
- Install MinVer.
- Build your project.
Your project will be versioned according to the latest tag found in the commit history.
To build with GitHub Actions, set the fetch depth appropriately.
When you want to release a version of your software, whether it's a pre-release, RTM, patch, or anything else, simply create a tag on the commit you want to release with a name which is a valid SemVer 2.x version. For example:
git tag 1.2.3
git push --tags
When you build your software from the commit with that tag, MinVer will apply the version to the assemblies and packages. (If you like to prefix your tag names, see the FAQ.)
Bear in mind that MinVer is unaware of the branches which contain the commit, nor your release process, so it is compatible with any branching or release strategy you choose.
NOTE: The MinVer package reference should normally include PrivateAssets="All"
. See NuGet docs for more info. If you install MinVer using an IDE or tool, this should be done for you automatically.
- If the current commit has a version tag:
- The version is used as-is.
- If the current commit does not have a version tag:
- The commit history is searched for the latest commit with a version tag.
- If a commit with a version tag is found:
- If the version is a pre-release:
- The version is used as-is, with height added.
- If the version is RTM (not pre-release):
- The patch number is incremented, but this can be customised.
- Default pre-release identifiers are added. The default identifiers are
alpha.0
, but this can be customised. - For example, if the latest version tag is
1.0.0
, the current version is1.0.1-alpha.0
. - Height is added.
- If the version is a pre-release:
- If no commit with a version tag is found:
- The default version
0.0.0-alpha.0
is used, with height added.
- The default version
- If a commit with a version tag is found:
- The commit history is searched for the latest commit with a version tag.
If the current commit does not have a version tag, another number is added to the pre-release identifiers. This is the number of commits since the latest commit with a version tag or, if no commits have a version tag, since the root commit. This is known as "height". For example, if the latest version tag found is 1.0.0-beta.1
, at a height of 42 commits, the calculated version is 1.0.0-beta.1.42
.
This behaviour can be disabled.
MinVer sets the following custom properties:
MinVerVersion
MinVerMajor
MinVerMinor
MinVerPatch
MinVerPreRelease
MinVerBuildMetadata
Those properties are used to set the following .NET SDK properties, satisfying the official open-source library guidance for version numbers:
Property | Value |
---|---|
AssemblyVersion |
{MinVerMajor}.0.0.0 |
FileVersion |
{MinVerMajor}.{MinVerMinor}.{MinVerPatch}.0 |
InformationalVersion |
{MinVerVersion} |
PackageVersion |
{MinVerMajor}.{MinVerMinor}.{MinVerPatch} (or {MinVerMajor}.{MinVerMinor}.{MinVerPatch}-{MinVerPreRelease} ) |
Version |
{MinVerMajor}.{MinVerMinor}.{MinVerPatch} (or {MinVerMajor}.{MinVerMinor}.{MinVerPatch}-{MinVerPreRelease} ) |
This behaviour can be customised.
Options may be specified as either MSBuild properties (for the MinVer package), command-line options (for the minver-cli package), or environment variables (for both the MinVer and minver-cli packages).
MSBuild property or environment variable | Command-line option |
---|---|
MinVerAutoIncrement |
-a|--auto-increment |
MinVerBuildMetadata |
-b|--build-metadata |
MinVerDefaultPreReleaseIdentifiers |
-p|--default-pre-release-identifiers |
MinVerIgnoreHeight |
-i|--ignore-height |
MinVerMinimumMajorMinor |
-m|--minimum-major-minor |
MinVerSkip |
n/a (environment variable not supported) |
MinVerTagPrefix |
-t|--tag-prefix |
MinVerVerbosity |
-v|--verbosity |
MinVerVersionOverride |
n/a (environment variable supported) |
Note that the names of the MSBuild properties and environment variables are case-insensitive.
(With TL;DR answers inline.)
- Why not use GitVersion, Nerdbank.GitVersioning, or some other tool? (simplicity)
- Can I bump the major or minor version? (yes)
- Can I use my own pre-release versioning scheme? (yes)
- Can I prefix my tag names? (yes)
- Can I use my own branching strategy? (yes)
- Can I include build metadata in the version? (yes)
- Can I auto-increment the minor or major version after an RTM tag instead of the patch version? (yes)
- Can I change the default pre-release identifiers from
alpha.0
to something else? (yes) - Can I use the version calculated by MinVer for other purposes? (yes)
- Can I version multiple projects in a single repository independently? (yes)
- Can I ignore the height of the latest tag or root commit? (yes)
- Can I get log output to see how MinVer calculates the version? (yes)
- Can I use MinVer to version software which is not built using a .NET SDK style project? (yes)
- Can I disable MinVer? (yes)
- What if the history diverges, and more than one tag or root commit is found? (nothing bad)
- What if the history diverges, and then converges again, before the latest tag (or root commit) is found? (nothing bad)
- Why is the default version sometimes used in GitHub Actions, Azure Pipelines, and Travis CI when a version tag exists in the history? (shallow clones)
- Why is my version tag ignored? (MinVer is not running, the tag is misplaced, the version is superseded, the prefix is wrong, or the version is not valid SemVer 2.0)
Before starting MinVer, Adam Ralph evaluated both GitVersion and Nerdbank.GitVersioning, but neither of them worked in the way he wanted for his projects.
The TL;DR is that MinVer is simpler. "How it works" pretty much captures everything.
To some degree, MinVer is a subset of what GitVersion is. It's much simpler and doesn't do nearly as much. Some of the differences:
- No dependency on a specific branching pattern.
- No inference of version from branch names.
- No inference of version from YAML config.
- No inference of version from commit messages.
- No inference of version from CI build server environment variables.
- No creation of metadata code artifacts.
- No automatic fetching of tags, etc. from the repository.
- One package instead of a series of packages.
- No support for
AssemblyInfo.cs
.
MinVer is a different approach and, again, simpler. Some of the differences are already listed under the comparison with GitVersion above.
Essentially, Nerdbank.GitVersioning encapsulates the injection of the version into the build process from a config file. That means versions are controlled by commits to that config file. MinVer works purely on tags. That means MinVer doesn't need some of the types of things that come with Nerdbank.GitVersioning such as the config file bootstrapper, and it means the version is controlled independently of the commits. For example, you can tag a commit as a release candidate, build it, and release it. After some time, if the release candidate has no bugs, you can tag the same commit as RTM, build it, and release it.
Also, Nerdbank.GitVersioning uses the Git height for the patch version, which is undesirable. Either every patch commit has to be released, or there will be gaps in the patch versions released.
Yes! You probably want to do this because at a point in time, on a given branch, you are working on a specific MAJOR.MINOR
range, e.g. 1.0
, 1.1
, or 2.0
. The branch could be main
, develop
, a special release branch, a support branch, or anything else.
Before you create the first version tag on your branch, interim builds will use the latest version tag found in the commit history, which may not match the MAJOR.MINOR
range you are working on. Or if no version tag is found in the commit history, interim builds will have the default version 0.0.0-alpha.0
. If you prefer those interim builds to have a version in the range you are working on, you have two options:
Tag a commit in your branch with a version matching your MAJOR.MINOR
range, using your preferred default pre-release identifiers. For example:
git tag 1.0.0-alpha.0
This is not a version you will release, since the first "alpha" version will be 1.0.0-alpha.1
. The only purpose of this tag is to force MinVer to start versioning commits in your branch in the 1.0
range.
If you begin to release versions in the 1.0
range from another branch (e.g. a special release branch), tag a commit in your branch with 1.1.0-alpha.0
, 2.0.0-alpha.0
, or whatever MAJOR.MINOR
range your branch now represents.
Specify your range with MinVerMinimumMajorMinor
. For example:
<PropertyGroup>
<MinVerMinimumMajorMinor>1.0</MinVerMinimumMajorMinor>
</PropertyGroup>
MinVer will now use a default version of 1.0.0-alpha.0
.
If you begin to release versions in the 1.0
range from another branch (e.g. a special release branch), set MinVerMinimumMajorMinor to 1.1
, 2.0
, or whatever MAJOR.MINOR
range your branch now represents.
Note that MinVerMinimumMajorMinor
will be redundant after you create the first tag in your branch with same MAJOR.MINOR
. If you don't care that the versions of interim builds before that first tag will have a lower MAJOR.MINOR
, then simply don't specify MinVerMinimumMajorMinor
.
Also note that if the latest version tag found in the commit history has a higher MAJOR.MINOR
than MinVerMinimumMajorMinor
, then MinVerMinimumMajorMinor
will be ignored.
Yes! MinVer doesn't care what your pre-release versioning scheme is. The default pre-release identifiers are alpha.0
, but you can use whatever you like in your tags. If your versioning scheme is valid SemVer 2.x, it will work with MinVer.
For example, all these versions work with MinVer:
1.0.0-beta.1
1.0.0-pre.1
1.0.0-preview-20181104
1.0.0-rc.1
Yes! Specify the prefix with MinVerTagPrefix
.
For example, if you prefix your tag names with "v", e.g. v1.2.3
:
<PropertyGroup>
<MinVerTagPrefix>v</MinVerTagPrefix>
</PropertyGroup>
Note that the prefix is case-insensitive—in this example, both v1.2.3
and V1.2.3
would work.
Yes! MinVer doesn't care about branches. It's all about the tags!
That means MinVer is compatible with Git Flow, GitHub Flow, Release Flow, and any other exotic flow.
Yes! Specify build metadata with MinVerBuildMetadata
.
For example, in appveyor.yml
:
environment:
MINVERBUILDMETADATA: build.%APPVEYOR_BUILD_NUMBER%
You can also specify build metadata in a version tag. If the tag is on the current commit, its build metadata will be used. If the tag is on an older commit, its build metadata will be ignored. Build metadata in MinVerBuildMetadata
will be appended to build metadata in the tag.
Build metadata is only included in the assembly informational version. You can include it elsewhere using a custom target. E.g.:
<Target Name="MyTarget" AfterTargets="MinVer" Condition="'$(MinVerBuildMetadata)' != ''" >
<PropertyGroup>
<PackageVersion>$(PackageVersion)+$(MinVerBuildMetadata)</PackageVersion>
<Version>$(PackageVersion)</Version>
</PropertyGroup>
</Target>
Yes! Specify which part of the version to auto-increment with MinVerAutoIncrement
. By default, MinVer will auto-increment the patch version, but you can specify minor
or major
to increment the minor or major version instead.
Yes! Specify the default pre-release identifiers with MinVerDefaultPreReleaseIdentifiers
. For example, if you prefer to name your pre-releases as "preview":
<PropertyGroup>
<MinVerDefaultPreReleaseIdentifiers>preview.0</MinVerDefaultPreReleaseIdentifiers>
</PropertyGroup>
This will result in a post-RTM version of {major}.{minor}.{patch+1}-preview.0.{height}
, e.g. 1.0.1-preview.0.1
.
Note that the numeric part(s) of the default pre-release identifiers should usually be 0
(meaning "alpha 0", "preview 0", etc.), since any versions produced between an RTM tag and the next tag are not tagged for release, they are just versions produced between the RTM and the next RTM or pre-release. When you tag your first pre-release, you will typically increment the 0
to 1
, meaning "alpha 1", "preview 1", etc.
Yes! You can use any of the properties set by MinVer, or override their values, in a target which runs after MinVer.
For example, for pull requests, you may want to inject the pull request number and a variable which uniquely identifies the build into the version. E.g. using AppVeyor:
<Target Name="MyTarget" AfterTargets="MinVer" Condition="'$(APPVEYOR_PULL_REQUEST_NUMBER)' != ''" >
<PropertyGroup>
<PackageVersion>$(MinVerMajor).$(MinVerMinor).$(MinVerPatch)-pr.$(APPVEYOR_PULL_REQUEST_NUMBER).build-id.$(APPVEYOR_BUILD_ID).$(MinVerPreRelease)</PackageVersion>
<Version>$(PackageVersion)</Version>
</PropertyGroup>
</Target>
Or for projects which do not create NuGet packages, you may want to populate all four parts of AssemblyVersion
. E.g. using AppVeyor:
<Target Name="MyTarget" AfterTargets="MinVer">
<PropertyGroup>
<APPVEYOR_BUILD_NUMBER Condition="'$(APPVEYOR_BUILD_NUMBER)' == ''">0</APPVEYOR_BUILD_NUMBER>
<AssemblyVersion>$(MinVerMajor).$(MinVerMinor).$(APPVEYOR_BUILD_NUMBER).$(MinVerPatch)</AssemblyVersion>
</PropertyGroup>
</Target>
Or for projects which do create NuGet packages, you may want to adjust the assembly file version to include the build number, as recommended in the official guidance. E.g. when using AppVeyor:
<Target Name="MyTarget" AfterTargets="MinVer">
<PropertyGroup>
<APPVEYOR_BUILD_NUMBER Condition="'$(APPVEYOR_BUILD_NUMBER)' == ''">0</APPVEYOR_BUILD_NUMBER>
<FileVersion>$(MinVerMajor).$(MinVerMinor).$(MinVerPatch).$(APPVEYOR_BUILD_NUMBER)</FileVersion>
</PropertyGroup>
</Target>
Yes! You can do this by using a specific tag prefix for each project. For example, if you have a "main" project and an "extension" project, you could specify <MinVerTagPrefix>main-</MinVerTagPrefix>
in the main project and <MinVerTagPrefix>ext-</MinVerTagPrefix>
in the extension project. To release version 1.0.0
of the main project you'd tag the repository with main-1.0.0
. To release version 1.1.0
of the extension project you'd tag the repository with ext-1.1.0
.
Note that changes committed to a given project will affect the versions of all other projects because every commit affects height. To prevent this, you can ignore the height of the latest tag or root commit.
Yes! Set MinVerIgnoreHeight
to true
:
<PropertyGroup>
<MinVerIgnoreHeight>true</MinVerIgnoreHeight>
</PropertyGroup>
This is often useful if you are versioning multiple projects in a single repository independently.
Yes! MinVerVerbosity
may be set to:
quiet
minimal
(default)normal
detailed
diagnostic
.
These verbosity levels match those in MSBuild and therefore dotnet build
, dotnet pack
, etc. The default is minimal
, which matches the default in MSBuild.
The equivalent levels for the minver-cli -v|--verbosity
option are:
e[rror]
w[arn]
i[nfo]
(default)d[ebug]
t[race]
These verbosity levels match those typically used in console applications.
At the quiet
/error
and minimal
/warn
levels, you will see only warnings and errors. At the normal
/info
level you will see which commit is being used to calculate the version, and the calculated version. At the detailed
/debug
level you will see how many commits were examined, which version tags were found but ignored, which version was calculated, etc. At the diagnostic
/trace
level you will see how MinVer walks the commit history, in excruciating detail.
In a future version of MinVer, the verbosity level may be inherited from MSBuild, in which case MinVerVerbosity
will be deprecated.
Yes! MinVer is also available as a command-line tool. Run minver --help
for usage. The calculated version is printed to standard output (stdout).
Sometimes you may want to version both .NET projects and other outputs, such as non-.NET projects, or a container image, in the same build. In those scenarios, you should use both the command-line tool and the regular MinVer package. Before building any .NET projects, your build script should run the command-line tool and set the MINVERVERSIONOVERRIDE
environment variable to the calculated version. The MinVer package will then use that value rather than calculating the version a second time. This ensures that the command-line tool and the MinVer package produce the same version.
If the components of the version are required outside the context of a .NET project, they can easily be extracted from the version which is returned by the command-line tool. The major, minor, and patch numbers can be separated from the pre-release identifiers by splitting the version by the first instance of -
. The major, minor, and patch numbers and the pre-release identifiers can then be separated by splitting by .
.
Yes! Set MinVerSkip
to true
. For example, MinVer can be disabled for debug builds:
<PropertyGroup>
<MinVerSkip Condition="'$(Configuration)' == 'Debug'">true</MinVerSkip>
</PropertyGroup>
MinVer will use the tag with the higher version, or the tag or root commit on the first path followed where the history diverges. The paths are followed in the same order that the parents of the commit are stored in Git. The first parent is the commit on the branch that was the current branch when the merge was performed. The remaining parents are stored in the order that their branches were specified in the merge command.
What if the history diverges, and then converges again, before the latest tag (or root commit) is found?
MinVer will use the height on the first path followed where the history diverges. The paths are followed in the same order that the parents of the commit are stored in Git. The first parent is the commit on the branch that was the current branch when the merge was performed. The remaining parents are stored in the order that their branches were specified in the merge command.
Why is the default version sometimes used in GitHub Actions, Azure Pipelines and Travis CI when a version tag exists in the history?
By default, GitHub Actions, Azure Pipelines, and Travis CI use shallow clones. The GitHub Actions checkout action and Azure Pipelines default checkout step both clone with a depth of only a single commit, and Travis CI clones with a depth of 50 commits. In GitHub Actions and Azure Pipelines, if the latest version tag in the history is not on the current commit, it will not be found. In Travis CI, if the latest version tag in the history is at a height of more than 50 commits, it will not be found.
To build in GitHub Actions, Azure Pipelines, or Travis CI, configure them to fetch a sufficient number of commits.
For GitHub Actions, set the fetch-depth
of the checkout action to an appropriate number, or to zero for all commits (you can also set filter
to tree:0
to create a treeless clone, for better performance). For example:
- uses: actions/checkout@v4
with:
fetch-depth: 0
filter: tree:0
For Azure Pipelines, include an explicit checkout step and set the fetchDepth
to an appropriate number, or to zero for all commits:
steps:
- checkout: self
fetchDepth: 0
For Travis CI, set the --depth
flag to an appropriate number, or to false
for all commits:
git:
depth: false
Things to check:
- MinVer is running:
- Is
MinVerSkip
set?
- Is
- The tag is in the correct place:
- Is the tag on the current commit or on an ancestor of the current commit?
- The version is not superseded:
- Is there a version tag on a later ancestor of the current commit?
- Is there a version tag with a later version on the same commit?
- Is
MinVerVersionOverride
set? - Is
MinVerMinimumMajorMinor
set to a higher major and minor?
- The correct prefix is used:
- Does the tag prefix match
MinVerTagPrefix
?
- Does the tag prefix match
- The version is valid SemVer 2.0:
- Are the major, minor, and patch non-negative integers?
- Do the major, minor, patch, or numeric pre-release identifiers have leading zeros?
- Do the pre-release or build metadata identifiers contain anything other than ASCII alphanumerics and hyphens [0-9A-Za-z-]?
- Is any pre-release or build metadata identifier empty (two or more dots in a row)?
- Is there more than one plus sign?
If you still can't figure it out, increase MinVerVerbosity
to detailed
to see which tags are explicitly ignored.
Tag by Ananth from the Noun Project.