Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Embed git commit hash in a .Net dll

Tags:

git

c#

You can embed a version.txt file into the executable and then read the version.txt out of the executable. To create the version.txt file, use git describe --long

Here are the steps:

Use a Build Event to call git

  • Right-click on the project and select Properties

  • In Build Events, add Pre-Build event containing (notice the quotes):

    "C:\Program Files\Git\bin\git.exe" describe --long > "$(ProjectDir)\version.txt"

    That will create a version.txt file in your project directory.

Embed the version.txt in the executable

  • Right click on the project and select Add Existing Item
  • Add the version.txt file (change the file chooser filter to let you see All Files)
  • After version.txt is added, right-click on it in the Solution Explorer and select Properties
  • Change the Build Action to Embedded Resource
  • Change Copy to Output Directory to Copy Always
  • Add version.txt to your .gitignore file

Read the embedded text file version string

Here's some sample code to read the embedded text file version string:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;

namespace TryGitDescribe
{
    class Program
    {
        static void Main(string[] args)
        {
            string gitVersion= String.Empty;
            using (Stream stream = Assembly.GetExecutingAssembly()
                    .GetManifestResourceStream("TryGitDescribe." + "version.txt"))
            using (StreamReader reader = new StreamReader(stream))
            {
                gitVersion= reader.ReadToEnd();
            }

            Console.WriteLine("Version: {0}", gitVersion);
            Console.WriteLine("Hit any key to continue");
            Console.ReadKey();
        }
    }
}

UPDATE:

Things have evolved since I originally answered this question. The Microsoft.NET.Sdk (meaning you must be using an sdk-style project) now includes support for adding the commit hash to both the assembly informational version as well as to the nuget package metadata, if some conditions are met:

  1. The <SourceRevisionId> property must be defined. This can be done by adding a target like this:
<Target Name="SetSourceRevisionId" BeforeTargets="InitializeSourceControlInformation">
    <Exec 
      Command="git describe --long --always --dirty --exclude=* --abbrev=8"
      ConsoleToMSBuild="True"
      IgnoreExitCode="False"
      >
      <Output PropertyName="SourceRevisionId" TaskParameter="ConsoleOutput"/>
    </Exec>
  </Target>

This target executes a command that will set SourceRevisionId to be the abbreviated (8 character) hash. The BeforeTargets causes this to be run before the assembly informational version is created.

  1. To include the hash in the nuget package metadata, the <RepositoryUrl> must also be defined.

  2. <SourceControlInformationFeatureSupported> property must be true, this causes the nuget pack task to pick up the SourceRevisionId as well.

I would steer people away from using the MSBuildGitHash package, since this new technique is cleaner and most consistent.

ORIGINAL:

I've created a simple nuget package that you can include in your project which will take care of this for you: https://www.nuget.org/packages/MSBuildGitHash/

This nuget package implements a "pure" MSBuild solution. If you'd rather not depend on a nuget package you can simply copy these Targets into your csproj file and it should include the git hash as a custom assembly attribute:

<Target Name="GetGitHash" BeforeTargets="WriteGitHash" Condition="'$(BuildHash)' == ''">
  <PropertyGroup>
    <!-- temp file for the git version (lives in "obj" folder)-->
    <VerFile>$(IntermediateOutputPath)gitver</VerFile>
  </PropertyGroup>

  <!-- write the hash to the temp file.-->
  <Exec Command="git -C $(ProjectDir) describe --long --always --dirty &gt; $(VerFile)" />

  <!-- read the version into the GitVersion itemGroup-->
  <ReadLinesFromFile File="$(VerFile)">
    <Output TaskParameter="Lines" ItemName="GitVersion" />
  </ReadLinesFromFile>
  <!-- Set the BuildHash property to contain the GitVersion, if it wasn't already set.-->
  <PropertyGroup>
    <BuildHash>@(GitVersion)</BuildHash>
  </PropertyGroup>    
</Target>

<Target Name="WriteGitHash" BeforeTargets="CoreCompile">
  <!-- names the obj/.../CustomAssemblyInfo.cs file -->
  <PropertyGroup>
    <CustomAssemblyInfoFile>$(IntermediateOutputPath)CustomAssemblyInfo.cs</CustomAssemblyInfoFile>
  </PropertyGroup>
  <!-- includes the CustomAssemblyInfo for compilation into your project -->
  <ItemGroup>
    <Compile Include="$(CustomAssemblyInfoFile)" />
  </ItemGroup>
  <!-- defines the AssemblyMetadata attribute that will be written -->
  <ItemGroup>
    <AssemblyAttributes Include="AssemblyMetadata">
      <_Parameter1>GitHash</_Parameter1>
      <_Parameter2>$(BuildHash)</_Parameter2>
    </AssemblyAttributes>
  </ItemGroup>
  <!-- writes the attribute to the customAssemblyInfo file -->
  <WriteCodeFragment Language="C#" OutputFile="$(CustomAssemblyInfoFile)" AssemblyAttributes="@(AssemblyAttributes)" />
</Target>

There are two targets here. The first one, "GetGitHash", loads the git hash into an MSBuild property named BuildHash, it only does this if BuildHash is not already defined. This allows you to pass it to MSBuild on the command line, if you prefer. You could pass it to MSBuild like so:

MSBuild.exe myproj.csproj /p:BuildHash=MYHASHVAL

The second target, "WriteGitHash", will write the hash value to a file in the temporary "obj" folder named "CustomAssemblyInfo.cs". This file will contain a line that looks like:

[assembly: AssemblyMetadata("GitHash", "MYHASHVAL")]

This CustomAssemblyInfo.cs file will be compiled into your assembly, so you can use reflection to look for the AssemblyMetadata at runtime. The following code shows how this can be done when the AssemblyInfo class is included in the same assembly.

using System.Linq;
using System.Reflection;

public static class AssemblyInfo
{
    /// <summary> Gets the git hash value from the assembly
    /// or null if it cannot be found. </summary>
    public static string GetGitHash()
    {
        var asm = typeof(AssemblyInfo).Assembly;
        var attrs = asm.GetCustomAttributes<AssemblyMetadataAttribute>();
        return attrs.FirstOrDefault(a => a.Key == "GitHash")?.Value;
    }
}

Some benefits to this design is that it doesn't touch any files in your project folder, all the mutated files are under the "obj" folder. Your project will also build identically from within Visual Studio or from the command line. It can also be easily customized for your project, and will be source controlled along with your csproj file.


We use tags in git to track versions.

git tag -a v13.3.1 -m "version 13.3.1"

You can get the version with hash from git via:

git describe --long

Our build process puts the git hash in the AssemblyInformationalVersion attribute of the AssemblyInfo.cs file:

[assembly: AssemblyInformationalVersion("13.3.1.74-g5224f3b")]

Once you compile, you can view the version from windows explorer:

enter image description here

You can also get it programmatically via:

var build = ((AssemblyInformationalVersionAttribute)Assembly
  .GetAssembly(typeof(YOURTYPE))
  .GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false)[0])
  .InformationalVersion;

where YOURTYPE is any Type in the Assembly that has the AssemblyInformationalVersion attribute.


Another way to do this is to use the NetRevisionTool with some On-Board Visual Studio magic. I will showcase this here for Visual Studio 2013 Professional Edition, but this will work with other versions as well.

So first download the NetRevisionTool. You include the NetRevisionTool.exe in your PATH or check it in into your repository and create a visual studio pre-build and a post-build action and change your AssemblyInfo.cs.

An example that would add your git-hash to your AssemblyInformationVersion would be the following: In your project settings:

enter image description here

in the AssemblyInfo.cs of your project you change/add the line:

[assembly: AssemblyInformationalVersion("1.1.{dmin:2015}.{chash:6}{!}-{branch}")]

in the shown screenshot i checked in NetRevisionTool.exe in the External/bin folder

After build, if you then right-click your binary and go to properties then you should see something like the following:

enter image description here

Hope this helps somebody out there


I think this question is worth giving a complete step by step answer. The strategy here to is run a powershell script from the pre-build events that takes in a template file and generates an AssemblyInfo.cs file with the git tag + commit count information included.

Step 1: make an AssemblyInfo_template.cs file in the Project\Properties folder, based on your original AssemblyInfo.cs but containing:

[assembly: AssemblyVersion("$FILEVERSION$")]
[assembly: AssemblyFileVersion("$FILEVERSION$")]
[assembly: AssemblyInformationalVersion("$INFOVERSION$")]

Step 2: Create a powershell script named InjectGitVersion.ps1 whose source is:

# InjectGitVersion.ps1
#
# Set the version in the projects AssemblyInfo.cs file
#


# Get version info from Git. example 1.2.3-45-g6789abc
$gitVersion = git describe --long --always;

# Parse Git version info into semantic pieces
$gitVersion -match '(.*)-(\d+)-[g](\w+)$';
$gitTag = $Matches[1];
$gitCount = $Matches[2];
$gitSHA1 = $Matches[3];

# Define file variables
$assemblyFile = $args[0] + "\Properties\AssemblyInfo.cs";
$templateFile =  $args[0] + "\Properties\AssemblyInfo_template.cs";

# Read template file, overwrite place holders with git version info
$newAssemblyContent = Get-Content $templateFile |
    %{$_ -replace '\$FILEVERSION\$', ($gitTag + "." + $gitCount) } |
    %{$_ -replace '\$INFOVERSION\$', ($gitTag + "." + $gitCount + "-" + $gitSHA1) };

# Write AssemblyInfo.cs file only if there are changes
If (-not (Test-Path $assemblyFile) -or ((Compare-Object (Get-Content $assemblyFile) $newAssemblyContent))) {
    echo "Injecting Git Version Info to AssemblyInfo.cs"
    $newAssemblyContent > $assemblyFile;       
}

Step 3: Save the InjectGitVersion.ps1 file to your solution directory in a BuildScripts folder

Step 4: Add the following line to the project's Pre-Build events

powershell -ExecutionPolicy ByPass -File  $(SolutionDir)\BuildScripts\InjectGitVersion.ps1 $(ProjectDir)

Step 5: Build your project.

Step 6: Optionally, add AssemblyInfo.cs to your git ignore file