The lost post: Embedding commit hashes in C♯ binaries
I seem to remember that I've started to write this post no less than 3 times, and I've managed to lose the source each and every time. If you're reading this it means that I've managed to complete it this time :D
Imagine you've got a confused client on the phone, asking why the newest feature X in your program doesn't work. You ask them whether they have the latest version of your program installed... and they say that they don't know. Version numbers and changelogs to the rescue! Except.... that last release was rather rushed and you forgot to finish updating the changelog. This is just one scenario in which embedding the latest commit hash into your program is useful.
You could embed the short hash (the first 7 characters) into the version string, for example v3.6.1-375ae31
. Then you could compare it against the revision history to see exactly what your codebase looked like when your client's version was built.
For a while now I've wanted to do just this, and now I've finally figured out how to do it cross-platform. This post documents how I went about doing it, and how you can do it too.
The basic principle of the idea is to run a command that will output the latest commit hash to a file before the build starts, and then embed that file into the resulting binary. To achieve this, we need to go about it in 2 parts. Firstly, we need to fiddle with the project file to add an optional pre-build event. Open the project file (MyProject.csproj
) in your favourite text editor (but preferably not your favourite IDE such as Visual Studio or MonoDevelop) and add this to the bottom, just before the closing </Project>
:
<Target Name="BeforeBuild" BeforeTargets="Build">
<Exec Command="git rev-parse HEAD >git-hash.txt" WorkingDirectory="$(ProjectDir)" IgnoreExitCode="true" />
</Target>
If you don't use Git, then change git rev-parse HEAD >git-hash.txt
in the above to the equivalent command for your version control system. For SVN, this stackoverflow question looks like it'll do the job for Windows - for Linux you should go here.
Once done, the next step is to add the generated file as an embedded resource. We can't do this with the GUI easily here since the file in question hasn't been generated yet! Add the following to the bottom of the csproj
file, again just before the </Project>
:
<ItemGroup>
<EmbeddedResource Include="git-hash.txt" />
</ItemGroup>
Remember to change the git-hash.txt
to whatever you changed it to above.
Next, save it and reopen the solution in your IDE. The final step is to actually utilise the commit hash (or revision number in SVN) in your program. Since it's just an embedded file, you can simply find it with a bit of reflection, and read it in with a StreamReader. I've written a good tutorial on how to do that over on my Embedding files in C♯ binaries post.
Make sure that your program is prepared to handle junk instead of a commit hash - you can't predict the contents of the embedded file if Git (or SVN) isn't installed on the machine used to build your project. If you want to require that the commit hash (or revision number) is actually present, just remove the IgnoreExitCode="true"
from the first snippet above.
Sources
- Optional PreBuildEvent in MSBuild? question asked by me on Stackoverflow
- Embedding files in C♯ binaries written by me
- Exec.IgnoreExitCode Property on the Microsoft Developer Network
- How to: Extend the Visual Studio Build Process on the Microsoft Developer Network