Versioning. Here we are. Again.
Over the years I have always worked hard to make versioning a foundational piece of every CI / CD solution I’ve setup. Reliable, logical versioning becomes key to long-term maintenance and troubleshooting efforts, and whatever you can do to make it a “no-brainer” is worth it (your future self will thank you).
The move to .Net Core changed the way a few items work in the .Net world, including versioning, and besides, I am always looking for ways to make versioning easier.
So here’s my cheat-sheet for versioning your solutions. It won’t suit all application types, but for my use case (.Net Web Apps) it works just fine. It will work with Azure DevOps and newer TFS versions too.
I haven’t tested on VB projects, but this should work for them just as easily as C#.
NET Core: Setup Your Project File
Versioning has been simplified in the .Net Core world. Edit your csproj and modify it as follows:
<PropertyGroup> <Version Condition=" '$(BUILD_BUILDNUMBER)' == '' ">1.0.0.0</Version> <Version Condition=" '$(BUILD_BUILDNUMBER)' != '' ">$(BUILD_BUILDNUMBER)</Version> </PropertyGroup>
If your file doesn’t have a version node, add the above. This tip comes from Stack Overflow, but I’ve modified it slightly.
The above setup will mean debugging locally will give you a version of 1.0.0.0, and in the event you build in a non-Azure DevOps / TFS environment you will also end up with a 1.0.0.0 version. $(BUILD_BUILDNUMBER) is an environment variable set by Team Build and which will be updated at build time by Azure DevOps or TFS.
NET Framework: Add Custom Task
In the “old” .Net world we have to update the properties of the AssemblyInfo file that is a part of the project, specifically targeting File Version and Assembly Version.
There isn’t an in-built build Task to do this for you, and rather than hack together a script, why not use a great custom task from the marketplace (which also supports TFS)?
I’m using the “Assembly Info” task from Bleddyn Richards, primarily because it has the most recent updated date out of the similar tasks available, which means it’s hopefully getting plenty of love and care from the owner 🙂
Add the above Task to your build definition (make sure to do it before you build the Solution / project) and then set the version numbering as shown below.
Setup Build Versioning
The above steps are great, but they will count for nothing (or cause a compile fail) if we don’t have a valid versioning number.
The default Azure DevOps build version number format takes this format:
$(date:yyyyMMdd)$(rev:.r)
This results in a build number that looks like this:
20180201.1 (for the first build on February 1 2018).
This isn’t a valid .Net Version number, so we need to change it.
First, let’s add two Variables to our build definition: MajorVersion and MinorVersion.
You can set these to any valid integer value. These can be manually controlled over time as you determine the need to increment Major and Minor version numbers. Note you can make them whatever you like, keeping in mind the size restriction I mention below.
Now let’s change the Build Numbering scheme to use these variables, a specific date format, and the revision:
$(MajorVersion).$(MinorVersion).$(date:yy)$(DayOfYear)$(rev:.r)
Which produces a build number that looks like this:
2.0.18037.1 (for first build on February 6 2018 for Major Version 2, Minor Version 0).
You can choose a format that works for you, with one proviso that each version segment must be less than 65,000, which sounds like a lot, until you consider that 20180201 (Feb 1, 2018) is, as an integer (20,180,201) larger than 65,000. Hence my decision to drop to using YY (if you’re reading this in the year 2065 I apologise for my shortsightedness).
The result of these changes will mean that you’ll have a lovely version number automatically written into your solution at build time. An example from a .Net Framework solution is shown below.
Happy Days 😎
Reblogged this on Kloud Blog.
Why is it a good thing to let the build determine the version number?
When I trigger a build of an older commit I get that brand-new version number; should this not be a code-thing. The code is either version 1 or 2 or 3…
Sven – that’s a good point and one you could certainly implement by pulling the commit hash (I assume from Git) at build time and appending it to the Product Version or Assembly Informational Version (File Version has specific restrictions on format). Here’s an example of how you can achieve this outside of VSTS: https://github.com/jeromerg/NGitVersion, or this marketplace add-in (GitVersion): https://marketplace.visualstudio.com/items?itemName=gittools.gitversion
I’m not saying that my approach is the only way to achieve a result, but it’s worked for me and allowed me to consistently version across .Net Framework and Core builds. If I am interested in what constituted the build I can go and review the VSTS / TFS Release Management and / or Build History which will give the commit at which the Build was run – one benefit of operating end-to-end on the VSTS ecosystem.
Hope this helps – Simon.
I agree with SvenL. It doesn’t make sense to me to version builds beforehand when in the end I end up discarding them, wasting the version number. If I’d ship, version numbers would leave gaps. I have yet to find a guide on how to properly set this up.
Each to their own, but my question is: how do track those discarded versions? Surely they have some value to showing how quality is changing over time? How would you expect this to work then? Fixed Major and Minor version numbers with what values contained for Build and Revision numbers? See here for format definition recommended by Microsoft: https://docs.microsoft.com/en-us/dotnet/framework/app-domains/assembly-versioning