Hello again! Recently I faced the problem that I needed to recover the code, which generated a dll, only by looking at the dll itself.
The project had many independent contributors, and was deployed to a few different environments, what triggered a few versions from local branches.
Version numbers alone didn’t solve it, but let’s talk about them first.
Versioning is a common problem in software development, however, there isn’t a consensus about how to properly version a project. There are some guidelines, and a lot of discussion out there, but in the end, the team should choose what works best for them.
I like to use three numbers, major.minor.revision, starting at 1.0.0 it progresses like that:
- Increase major version whenever the changes aren’t backwards compatible. Usually those are great changes and this number shouldn’t be increased often.
- Increase minor version whenever you add a new feature which is backwards compatible.
- Increase revision version after minor changes, like bug fixes, organization commits etc.
I find those three numbers enough, along with the trick I’ll show you next, but a fourth number at right might be useful:
- Local version, it means how many local changes, not pushed commits for instance, were made.
Back to the initial problem, from its many solutions, I found best to input git info into the dll version, and make it automatic, then there is no chance to forget doing it. The trick was to use hooks.
First I tried git hooks, it didn’t work, but let’s take a look at them anyway :3
Git hooks are shell scripts that are hooked to an action, they are executed whenever the action is triggered, before or after it, depending on the hook. Those scripts must be put in the folder ‘ProjectFolder/.git/hooks’, you can see a few samples from git in that folder already. To activate a hook just remove the .sample extension and it’s ready.
The idea was to write the git hash in the version file right after a commit and amend the changes, that’s it, only a post-commit hook needed.
Little did I know that there is no amend, there is only removing the last commit, adding the new changes and making a new one, with a different hash, then the hash number in the version is meaningless.
Luckily there are also build hooks, the same principle of git hooks applied to the building process, then the solution was to write the git hash in the version file right before building the dll. I used Visual Studio 2013 and C# for this but it should apply to other tools as well. (This one works)
Actually, I preferred to create an additional version file, containing only the git info. Its possible to overwrite the standard version file but I didn’t want to unload the project for every version change. Probably this can also be avoided but I didn’t find an easy way :3
Visual Studio offers a visual interface to define hooks, go to Project Properties -> Build Events and you can see the text boxes for Pre-build and Post-build events. As far as I know those commands will be executed in the Windows PowerShell at the right time. You can also define the hooks directly in the csproj file, which I preferred.
This file is a XML file, where the propertie DefaultTargets, of the Project tag, register the hooks, having Build as the main build event. The events are executed in the same order they appear. Take a look:
<Project ToolsVersion="15.0" DefaultTargets="Version;Build;Clean" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
You can see that the Version hook, which creates the version file, is executed right before building the project. After building the Clean hook, which deletes the version file, is executed.
Ok, now we just need to register the hooks, luckily its very easy, just create a Target tag, right under the Project tag, with the hook name as a property, like this:
<Target Name="Clean"> <Delete Files="Properties\AssemblyGitInfo.cs" ContinueOnError="true" /> </Target>
The only missing piece is the git hash, to add it into the version I used the package MSBuildTasks, its available via NuGet. We just need to install it and add the following tag in the csproj file:
<Import Project="..\packages\MSBuildTasks.188.8.131.52\build\MSBuildTasks.Targets" />
Right under the tag:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
Beware of the MSBuildTasks version, check which one is installed in the packages folder inside the project folder.
With MSBuildTasks you can use the GitVersion tag, which defines a few environment variables, being one of them the git hash. Since a code snippet is worth a thousand words:
<Target Name="Version"> <GitVersion LocalPath="$(MSBuildProjectDirectory)"> <Output TaskParameter="CommitHash" PropertyName="CommitHash" /> </GitVersion> <AssemblyInfo CodeLanguage="CS" OutputFile="Properties\AssemblyGitInfo.cs" AssemblyInformationalVersion="git hash - $(CommitHash)" /> </Target>
You can see that the git directory is defined and that GitVersion outputs the parameter CommitHash from its inner property with the same name. Right after, this parameter is used in the AssemblyInformationalVersion as “git hash – $(CommitHash)”.
The whole csproj file will look similar to this:
<?xml version="1.0" encoding="utf-8"?> <Project ToolsVersion="15.0" DefaultTargets="Version;Build;Clean" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> ... <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="..\packages\MSBuildTasks.184.108.40.206\build\MSBuildTasks.Targets" /> <Target Name="Version"> <GitVersion LocalPath="$(MSBuildProjectDirectory)"> <Output TaskParameter="CommitHash" PropertyName="CommitHash" /> </GitVersion> <AssemblyInfo CodeLanguage="CS" OutputFile="Properties\AssemblyGitInfo.cs" AssemblyInformationalVersion="git hash - $(CommitHash)" /> </Target> <Target Name="Clean"> <Delete Files="Properties\AssemblyGitInfo.cs" ContinueOnError="true" /> </Target> </Project>
The result is that after building the project, the output file property “Product Version” contains the git hash.
Hope this can be useful to someone 🙂