{"id":239,"date":"2018-03-19T15:27:19","date_gmt":"2018-03-19T13:27:19","guid":{"rendered":"https:\/\/spurgius.com\/blog\/?p=239"},"modified":"2018-03-19T15:27:19","modified_gmt":"2018-03-19T13:27:19","slug":"embedding-git-commit-hash-inside-of-a-net-assembly","status":"publish","type":"post","link":"https:\/\/spurgius.com\/blog\/2018\/03\/19\/embedding-git-commit-hash-inside-of-a-net-assembly\/","title":{"rendered":"Embedding git commit hash inside of a .net assembly"},"content":{"rendered":"<h3>The need<\/h3>\n<p>The need is to easily identify which version of the code is deployed in an environment. Since the project in question wasn&#8217;t being build with TeamCity I needed a custom solution.<\/p>\n<h3>msbuild to the rescue<\/h3>\n<p>msbuild fires <code>BeforeBuild<\/code> and <code>AfterBuild<\/code> events during build process for .net application. We can use these events to modify (and reset) <code>AssemblyInfo.cs<\/code> file to include git commit hash as metadata. Problem with <code>AfterBuild<\/code> is that it doesn&#8217;t run if the build fails so after quite some research I found a <code>PostBuildEvent<\/code> target which does the job nicely.<\/p>\n<p>The solution looks like this:<\/p>\n<p>In\u00a0<code>AssemblyInfo.cs<\/code> file define an attribute to store git commit hash metadata<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">[assembly: AssemblyMetadata(\"GitCommitHash\", \"\")]<\/pre>\n<p>Then in the <code>csproj<\/code> file inside some property group without any conditions set this property. Without it <code>PostBuildEvent<\/code> doesn&#8217;t fire on failed builds and you are left with a modified <code>AssemblyInfo.cs<\/code> which you don&#8217;t want.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"xml\">&lt;RunPostBuildEvent&gt;Always&lt;\/RunPostBuildEvent&gt;<\/pre>\n<p>Then use this joy \ud83d\ude42<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"xml\">&lt;UsingTask TaskName=\"SetGitCommitHash\" TaskFactory=\"CodeTaskFactory\" AssemblyFile=\"$(MSBuildToolsPath)\\Microsoft.Build.Tasks.v4.0.dll\"&gt;\r\n  &lt;ParameterGroup&gt;\r\n    &lt;ProjectPath ParameterType=\"System.String\" Required=\"True\" Output=\"False\" \/&gt;\r\n    &lt;GitCommitHash ParameterType=\"System.String\" Required=\"False\" Output=\"False\" \/&gt;\r\n  &lt;\/ParameterGroup&gt;\r\n  &lt;Task&gt;\r\n    &lt;Using Namespace=\"System\" \/&gt;\r\n    &lt;Using Namespace=\"System.IO\" \/&gt;\r\n    &lt;Code Type=\"Fragment\" Language=\"cs\"&gt;&lt;![CDATA[\r\nvar lines = File.ReadAllLines(ProjectPath + @\"\\Properties\\AssemblyInfo.cs\");\r\nint hashIndex = Array.FindIndex(lines, l =&gt; l.StartsWith(\"[assembly: AssemblyMetadata(\\\"GitCommitHash\\\"\"));\r\nlines[hashIndex] = \"[assembly: AssemblyMetadata(\\\"GitCommitHash\\\", \\\"\" + GitCommitHash + \"\\\")]\";\r\nFile.WriteAllLines(ProjectPath + @\"\\Properties\\AssemblyInfo.cs\", lines);\r\n]]&gt;&lt;\/Code&gt;\r\n  &lt;\/Task&gt;\r\n&lt;\/UsingTask&gt;\r\n&lt;!-- To modify your build process, add your task inside one of the targets below and uncomment it. \r\n      Other similar extension points exist, see Microsoft.Common.targets.--&gt;\r\n&lt;Target Name=\"BeforeBuild\"&gt;\r\n  &lt;Exec Command=\"git rev-parse HEAD\" ConsoleToMSBuild=\"true\" ContinueOnError=\"True\" Condition=\" '$(GitCommitHash)' == '' \"&gt;\r\n    &lt;Output TaskParameter=\"ConsoleOutput\" PropertyName=\"GitCommitHash\" \/&gt;\r\n  &lt;\/Exec&gt;\r\n  &lt;SetGitCommitHash ProjectPath=\"$(MSBuildProjectDirectory)\" GitCommitHash=\"$(GitCommitHash)\" \/&gt;\r\n&lt;\/Target&gt;\r\n&lt;Target Name=\"PostBuildEvent\"&gt;\r\n  &lt;SetGitCommitHash ProjectPath=\"$(MSBuildProjectDirectory)\" GitCommitHash=\"\" \/&gt;\r\n&lt;\/Target&gt;\r\n&lt;Target Name=\"AfterBuild\" \/&gt;<\/pre>\n<p>On line 1 we define a <code><span class=\"st0\">SetGitCommitHash<\/span><\/code> task which finds the line with target attribute inside of <code>AssemblyInfo.cs<\/code> file and sets the hash value to whatever is passed to it.<br \/>\nOn line 20 a git command is executed to retrieve current commit hash and then on line 21 it&#8217;s outputted to a <code>GitCommitHash<\/code> property. The condition is there for the cases when git command is not available and you are passing in this parameter to msbuild from the outside.<br \/>\nFinally lines 23 and 26 call the <code>SetGitCommitHash<\/code> to set the hash before build and clean it up after. This leaves no trace that <code>AssemblyInfo.cs<\/code> was ever modified.<\/p>\n<h3>Using the hash<\/h3>\n<p>I ended up with this solution. Extracting commit hash from assembly into a static variable during app start and then using it wherever necessary.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"csharp\">public class Global : HttpApplication\r\n{\r\n    public static string CommitHash = string.Empty;\r\n\r\n    private void Application_Start(object sender, EventArgs e)\r\n    {\r\n        SetCommitHash();\r\n    }\r\n\r\n    private static void SetCommitHash()\r\n    {\r\n        string hash = typeof(Global).Assembly\r\n            .GetCustomAttributes(typeof(System.Reflection.AssemblyMetadataAttribute), false)\r\n            .Cast&lt;System.Reflection.AssemblyMetadataAttribute&gt;()\r\n            .First(a =&gt; a.Key == \"GitCommitHash\").Value;\r\n\r\n        CommitHash = hash;\r\n    }\r\n}<\/pre>\n<h3>The caveat<\/h3>\n<p>One caveat to remember is that if the build is happening locally and and you xcopy Release folder output or Publish from VS, make sure that the latest changes are committed or otherwise the code in assemblies will not reflect the commit hash captured during build.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The need The need is to easily identify which version of the code is deployed in an environment. Since the project in question wasn&#8217;t being build with TeamCity I needed a custom solution. msbuild to the rescue msbuild fires BeforeBuild and AfterBuild events during build process for .net application. We can use these events to [&hellip;]<\/p>\n<a role=\"link\" class=\"rtp-readmore\" title=\"Read more on Embedding git commit hash inside of a .net assembly\" href=\"https:\/\/spurgius.com\/blog\/2018\/03\/19\/embedding-git-commit-hash-inside-of-a-net-assembly\/\" rel=\"nofollow\">Read More &rarr;<\/a>","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1],"tags":[],"_links":{"self":[{"href":"https:\/\/spurgius.com\/blog\/wp-json\/wp\/v2\/posts\/239"}],"collection":[{"href":"https:\/\/spurgius.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/spurgius.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/spurgius.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/spurgius.com\/blog\/wp-json\/wp\/v2\/comments?post=239"}],"version-history":[{"count":19,"href":"https:\/\/spurgius.com\/blog\/wp-json\/wp\/v2\/posts\/239\/revisions"}],"predecessor-version":[{"id":260,"href":"https:\/\/spurgius.com\/blog\/wp-json\/wp\/v2\/posts\/239\/revisions\/260"}],"wp:attachment":[{"href":"https:\/\/spurgius.com\/blog\/wp-json\/wp\/v2\/media?parent=239"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/spurgius.com\/blog\/wp-json\/wp\/v2\/categories?post=239"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/spurgius.com\/blog\/wp-json\/wp\/v2\/tags?post=239"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}