Version numbers in .NET Projects
Published by marco on
Any software product should have a version number. This article will answer the following questions about how Encodo works with them.
- How do we choose a version number?
- What parts does a version number have?
- What do these parts mean?
- How do different stakeholders interpret the number?
- What conventions exist for choosing numbers?
- Who chooses and sets these parts?
Stakeholders
In decreasing order of expected expertise,
- Developers: Writes the software; may *change* version numbers
- Testers: Tests the software; highly interested in version numbers that make sense
- End users: Uses the software as a black box
The intended audience of this document is *developers*.
Definitions and Assumptions
- Build servers, not developer desktops, produce artifacts
- The source-control system is Git
- The
quino
command-line tool is installed on all machines. This tool can *read* and *write* version numbers for any .NET solution, regardless of which of the many version-numbering methods a given solution actually uses. - A *software library* is a package or product that has a developer as an *end user*
- A *breaking change* in a software library causes one of the following
- a build error
- an API to behave differently in a way that cannot be justified as a bug fix
Semantic versions
Encodo uses semantic versions. This scheme has a strict ordering that allows you to determine which version is “newer”. It indicates pre-releases (e.g. alphas, betas, rcs) with a “minus”, as shown below.
Version numbers come in two flavors:
- Official releases:
[Major].[Minor].[Patch].[Build]
- Pre-releases:
[Major].[Minor].[Patch]-[Label][Build]
See Microsoft’s NuGet Package Version Reference for more information.
Examples
0.9.0-alpha34
: A pre-release of 0.9.00.9.0-beta48
: A pre-release of 0.9.00.9.0.67
: An official release of 0.9.01.0.0-rc512
: A pre-release of 1.0.01.0.0.523
: An official release of 1.0.0
The numbers are strictly ordered. The first three *parts* indicate the “main” version. The final *part* counts strictly upward.
Parts
The following list describes each of the parts and explains what to expect when it changes.
Build
- Identifies the build task that produced the artifact
- Strictly increasing
Label
- An arbitrary designation for the “type” of pre-release
Patch
- Introduces bug fixes but no features or API changes
- May introduce obsolete members
- May *not* introduce breaking changes
This part is also known as “Maintenance” (see versioning”>Software versioning on Wikipedia).
Minor
- Introduces new features that extend existing functionality
- May include bug fixes
- May cause minor breaking changes
- May introduce obsolete members that cause compile errors
- Workarounds must be documented in release notes or obsolete messages
Major
- Introduces major new features
- Introduces breaking changes that require considerable effort to integrate
- Introduces a new data or protocol format that requires migration
Conventions
Uniqueness for official releases
There will only ever be one artifact of an official release corresponding to a given “main” version number.
That is, if 1.0.0.523
exists, then there will never be a 1.0.0.524
. This is due the fact that the build number (e.g. 524) is purely for auditing.
For example, suppose your software uses a NuGet package with version 1.0.0.523
. NuGet will not offer to upgrade to 1.0.0.524
.
Pre-release Labels
There are no restrictions on the labels for pre-releases. However, it’s recommended to use one of the following:
alpha
beta
rc
Be aware that if you choose a different label, then it is ordered alphabetically relative to the other pre-releases.
For example, if you were to use the label pre-release
to produce the version 0.9.0-prealpha21
, then that version is considered to be higher than 0.0.0-alpha34
. A tool like NuGet will not see the latter version as an upgrade.
Release branches
The name of a release branch should be the major version of that release. E.g. release/1
for version 1.x.x.x.
Pre-release branches
The name of a pre-release branch should be of the form feature/[label]
where [label]
is one of the labels recommended above. It’s also OK to use a personal branch to create a pre-release build, as in mvb/[label]
.
Setting the base version
A developer uses the quino
tool to set the version.
For example, to set the version to 1.0.1, execute the following:
quino fix -v 1.0.1.0
The tool will have updated the version number in all relevant files.
Calculating final version
The build server calculates a release’s version number as follows,
- major: Taken from solution
- minor: Taken from solution
- maintenance: Read from solution
- label: Taken from the Git branch (see below for details)
- build: Provided by the build server
Git Branches
The name of the Git branch determines which kind of release to produce.
- If the name of the branch matches the glob
**/release/*
, then it’s an official release - Everything else is a pre-release
For example,
origin/release/1
origin/production/release/new
origin/release/
release/1
production/release/new
release/
The name of the branch doesn’t influence the version number since an official release doesn’t have a label.
Pre-release labels
The label is taken from the last part of the branch name.
For example,
origin/feature/beta
yieldsbeta
origin/feature/rc
yieldsrc
origin/mvb/rc
yieldsrc
The following algorithm ensures that the label can be part of a valid semantic version.
- Remove invalid characters
- Append an
X
after a trailing digit - Use
X
if the label is empty (or becomes empty after having removed invalid characters)
For example,
origin/feature/rc1
yieldsrc1X
origin/feature/linuxcompat
yieldslinuxcompat
origin/feature/12
yieldsX
Examples
Assume that,
- the version number in the solution is 0.9.0.0
- the build counter on the build server is at 522
Then,
- Deploying from branch
origin/release/1
produces artifacts with version number0.9.0.522
- Deploying from branch
origin/feature/rc
produces artifacts with version number0.9.0-rc522
Release Workflow
The following are very concise guides for how to produce artifacts.
Pre-release
- Ensure you are on a non-release branch (e.g.
feature/rc
,master
) - Verify or set the base version (e.g.
quino fix -v 1.0.2.0
- Push any changes to Git
- Execute the “deploy” task against your branch on the build server
Release
- Ensure you are on a release branch (e.g.
release/1
) - Verify or set the base version (e.g.
quino fix -v 1.0.2.0`
) - Push any changes to Git
- Execute the “deploy” task against your branch on the build server