Embed git commit hash in a .Net dll

2020-01-30 02:18发布

I'm building a C# application, using Git as my version control.

Is there a way to automatically embed the last commit hash in the executable when I build my application?

For example, printing the commit hash to console would look something like:

class PrintCommitHash
{
    private String lastCommitHash = ?? // What do I put here?
    static void Main(string[] args)
    {
        // Display the version number:
        System.Console.WriteLine(lastCommitHash );
    }
}

Note that this has to be done at build time, not runtime, as my deployed executable will not have the git repo accessible.

A related question for C++ can be found here.

EDIT

Per @mattanja's request, I'm posting the git hook script I use in my projects. The setup:

  • The hooks are linux shell scripts, which are placed under: path_to_project\.git\hooks
  • If you are using msysgit, the hooks folder already contains some sample scripts. In order to make git call them, remove the '.sample' extension from the script name.
  • The names of the hook scripts match the event that invokes them. In my case, I modified post-commit and post-merge.
  • My AssemblyInfo.cs file is directly under the project path (same level as the .git folder). It contains 23 lines, and I use git to generate the 24th.

As my linux-shelling a bit rusty, the script simply reads the first 23-lines of AssemblyInfo.cs to a temporary file, echos the git hash to the last line, and renames the file back to AssemblyInfo.cs. I'm sure there are better ways of doing this:

#!/bin/sh
cmt=$(git rev-list --max-count=1 HEAD)
head -23 AssemblyInfo.cs > AssemblyInfo.cs.tmp
echo [assembly: AssemblyFileVersion\(\"$cmt\"\)] >> AssemblyInfo.cs.tmp
mv AssemblyInfo.cs.tmp AssemblyInfo.cs

Hope this helps.

标签: c# git
13条回答
小情绪 Triste *
2楼-- · 2020-01-30 02:55

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

查看更多
祖国的老花朵
3楼-- · 2020-01-30 02:55

As the other answer already mentions the git bit, once you have the SHA you can consider generating the AssemblyInfo.cs file of your project in a pre-build hook.

One way to do this is to create an AssemblyInfo.cs.tmpl template file, with a placeholder for your SHA in say $$GITSHA$$, e.g.

[assembly: AssemblyDescription("$$GITSHA$$")]

Your pre build hook then has to replace this placeholder and output the AssemblyInfo.cs file for the C# compiler to pick up.

To see how this can be done using SubWCRev for SVN see this answer. It shouldn't be hard to do something similar for git.

Other ways would be a "make stage" as mentioned, i.e. write an MSBuild task that does something similar. Yet another way may be to post process the DLL somehow (ildasm+ilasm say), but I think the options mentioned above are probably easiest.

查看更多
啃猪蹄的小仙女
4楼-- · 2020-01-30 02:56

I'm using a combination of the accepted answer and a small adition. I have th AutoT4 extension installed (https://marketplace.visualstudio.com/items?itemName=BennorMcCarthy.AutoT4) to re-run the templates before build.

getting version from GIT

I have git -C $(ProjectDir) describe --long --always > "$(ProjectDir)git_version.txt" in my pre-build event in project properties. Adding git_version.txt and VersionInfo.cs to .gitignore is quite a good idea.

embedding version in metadata

I have added a VersionInfo.tt template to my project:

<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.IO" #>
<#@ output extension=".cs" #>

using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

<#
if (File.Exists(Host.ResolvePath("git_version.txt")))
{
    Write("[assembly: AssemblyInformationalVersion(\""+ File.ReadAllText(Host.ResolvePath("git_version.txt")).Trim() + "\")]");
}else{
    Write("// version file not found in " + Host.ResolvePath("git_version.txt"));
}

#>

Now I have my git tag + hash in "ProductVersion".

查看更多
女痞
5楼-- · 2020-01-30 03:00

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();
        }
    }
}
查看更多
地球回转人心会变
6楼-- · 2020-01-30 03:00

Referring to the another answer (https://stackoverflow.com/a/44278482/4537127) i also utilised the VersionInfo.tt text template to generate AssemblyInformationalVersion without AutoT4.

(Atleast works in my C# WPF application)

Problem was that the Pre-build events were run after template transformations, so after cloning, the git_version.txt file was not there and build fails. After creating it manually to allow transformation to pass once, it was updated after transformation, and was always one commit behind.

I had to make two adjustments to the .csproj file (this applies at least for Visual Studio Community 2017)

1) Import the Text Transformation Targets and make template transformations to run on every build: (Ref https://msdn.microsoft.com/en-us/library/ee847423.aspx)

<PropertyGroup>
    <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
    <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
    <TransformOnBuild>true</TransformOnBuild>
    <TransformOutOfDateOnly>false</TransformOutOfDateOnly>
</PropertyGroup>

and after <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

<Import Project="$(VSToolsPath)\TextTemplating\Microsoft.TextTemplating.targets" />

2) Make the git describe run before template transformations (so that git_version.txt is there when VersionInfo.tt is transformed) :

<Target Name="PreBuild" BeforeTargets="ExecuteTransformations">
  <Exec Command="git -C $(ProjectDir) describe --long --always --dirty &gt; $(ProjectDir)git_version.txt" />
</Target>

..And the C# code to get the AssemblyInformationalVersion (Ref https://stackoverflow.com/a/7770189/4537127)

public string AppGitHash
{
    get
    {
        AssemblyInformationalVersionAttribute attribute = (AssemblyInformationalVersionAttribute)Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false).FirstOrDefault();

        return attribute.InformationalVersion;
    }
}

..And add the generated files to .gitignore

VersionInfo.cs
git_version.txt
查看更多
老娘就宠你
7楼-- · 2020-01-30 03:01

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

查看更多
登录 后发表回答