Smart Assembly Versionning

Or should I say ... just not as dumb as I usually am! :wink:

I'd been spoiled for a couple of years by having a full-time dedicated Build Engineer to manage our NAnt-based build system.

Then I joined a team that was a little behind the times in terms of automation, and when we were discussing future sprint backlog items, I made the egregious mistake of casually mentioning that I thought automated builds was A Good Thing.

For my sins ... I got nominated as the de facto Build Guy!

So now I'm scrambling to put some money where my mouth is and try to understand the current state of best practices related to all-things-build.
Nice job, Sam! :wink:

Pretty much since converting to C# back in the V1 beta days, I had been playing catch-up with smarter-minds-than-mine. After stumbling on the various [assembly:???] attributes in the AssemblyInfo.cs file, it was just natural to adopt the Major/Minor/Patch/Build usage.

The web has a plethora of pages explaining this, but the best that I've found is the Semantic Versionning site .1

To make matters .. complicated ... we had a dozen or so active projects in various states of release across both web, standalone app, shared libraries, etc., and I wanted a one-size-fits-all solution.

It's nice to want ... :sunglasses:
I stumbled on a couple of articles/posts hinting at using a shared AssemblyInfo.cs file, but could not find a ready-made solution that fit all our requirements.
Gosh ... I'll actually have to do some real work for a change!!! :smile:

Speaking of requirements, an important one was to leverage any existing mechanism for generating an auto-increasing build number. Some of our projects used the * wildcard marker to allow Visual Studio or TFS to auto-generate this value, some projects used a value managed via a SQLServer-based configuration table, etc.

Based on this, I decided to use a separate file for each of the version inputs, i.e., Major, Minor, Patch, etc.

But as one common denominator to all of our build mechanisms was that they are all Windows-based ... I could use a plain ol' vanilla DOS command script.

Drum roll ... please
Following are the comments from my UpdateVersionInfo.cmd script:

:: NOTES:
::==================
:: This command script concatenates the contents of the various VersionInfo*.txt files
:: into relevant AssemblyVersion and AssemblyInformationalVersion attributes in a 
:: VersionInfo.cs file that can be shared (as a linked file) across multiple projects.
::
:: Use of the AssemblyInformationalVersion attribute enables NuGet versionning support.
::
:: USAGE:
::==================
:: Following are the recommended steps for incorporating this script into your solution:
::
::     1. Create a sub-folder named "VersionHelpers" in the Solution folder.
::     2. Copy this script into this folder.
::     3. Create the following files in this folder:
::             VersionInfo.cs
::             VersionInfoMajor.txt
::             VersionInfoMinor.txt
::             VersionInfoPatch.txt
::
::          And optionally ...
:: 
::            VersionInfoBuild.txt
::            VersionInfoPreRelease.txt
:: 
::     4. Add a 'New Solution Folder' named "VersionHelpers" folder under the
::        "Solution Items" node in your solution.
::     5. Add 'Existing Items ...' and add all the files created in #3 above.
::     6. Add the VersionInfo.cs file into each project as a "linked" file. 
::        This will result in only a single file being used across all projects.
::     7. Remove any references to the AssemblyVersion and AssemblyInformationalVersion
::        attributes in the project AssemblyInfo.cs file. I usually replace them with
::        this comment:
::
::        // Version information control moved to the centralized VersionInfo.cs file.
::
::     8. Modify the PreBuild step of the first project-to-compile as follows:
:: 
::             $(SolutionDir)\VersionHelpers\UpdateVersionInfo.cmd
::
:: Set the values in the VersionInfo*.txt files as appropriate.
:: If any value is present in the VersionInfoPreRelease.text file, it
:: will be suffixed with the current date/time in format yyyyMMddHHmmss
:: and added to the Major/Minor/Patch values.
::
:: If the VersionInfoBuild.txt file is empty, the AssemblyVersion will
:: reflect only the Major/Minor/Patch values. This is helpful when 
:: strong-signing an assembly, as automatically incrementing the 
:: build number can cause problems for other strong-signed assemblies
:: referencing this assembly.
:: 
::

Two of the above steps require a wee bit of additional emphasis:

  • If you forget Step #7, you'll see the following error:
Duplicate 'AssemblyVersion' attribute  

The error references the shared VersionInfo.cs file, so you will need to manually locate the project AssemblyInfo.cs file and remove the offending reference(s).

  • Step #6 is more problematic; if you forget to click the drop-down arrow and choose the "Add as Link" option, the file will be copied into the project and will never get updated.

Presuming that everything is correctly configured - (which includes testing it all!!!) - then it just works!

One more important note: this doesn't get you completely out of managing the version values ... it just centralizes it!

I get ... you get ... NuGet ...
One of my projects involves deploying some shared libraries via NuGet format. After manually modifying the nuspec for the 40-gazillion'th time, I added support for using the AssemblyInformationalVersion value piped to a %VersionSpec% environment variable and then referenced as part of my nupkg generation:

NuGet pack %1 -Version %VersionSpec%

I adopted a NuGet-friendly approach based on their docs 2. The formatted value is:

%VersionMajor%.%VersionMinor%.%VersionPatch%%VersionPreReleaseTag%

This is done at line #106 in the UpdateVersionInfo.cmd script file, so feel free to modify to better suit your own needs.

I created a sample solution (download link below) which has nothing but a handful of empty related projects and all the required Version Helper files.

Of special note, the VersionInfoBuild.txt file contains just the * wildcard marker; do a Rebuild Solution a couple of times and watch how the assembly version magically changes.

That's it ... except for one final word and warning for those (like me!) who tend to forget the Build Engineer motto: You break it ... you buy it!!! :wink:

Have fun stormin' the castle ...

SC



Source Code & Examples

Comments powered by Disqus