5

This question is a follow-up of this other one.

In that question, one mentions the usage of [assembly: AssemblyVersion(...)] to the file AssemblyInfo.cs file, and in the meanwhile I've found out that it's forbidden to execute any processing before such a line, the only thing which is allowed is something like:

[assembly: AssemblyVersion("1.0.0.0" + "-" + Namespace.Class.Attribute)], or:
[assembly: AssemblyVersion("1.0.0.0" + "-" + Namespace.Class.Method())]

Original question:
So my question: is there a Namespace.Class.Attribute or Namespace.Class.Method() which contains the commit hash (or sha or shortened sha) of a C# application?

Edit after more investigation
In the meantime I've learnt that the command git describe --always gives me the information I'm looking for, so what I need is something like:

[assembly: AssemblyVersion("1.0.0.0-" + Launch("git describe --always")]

... but how can I execute that Launch()?

I already know that I can launch a commandline command using System.Diagnostics.Process(), like this example:

System.Diagnostics.Process.Start(foldervar + "application.exe", "inputfile.txt");

... but this way does not catch the result of that command.

New question:
So, does anybody know a C# one-liner for launching commandline commands and getting their result?

Thanks in advance

13
  • Why the request to close this question? Commented Dec 14, 2021 at 16:06
  • Read the documentation for GitVersion. It should clear up your questions. Commented Dec 14, 2021 at 16:18
  • 3
    There is nothing built into .NET for this. Typically it's the build system that handles injecting things like this into code. Commented Dec 14, 2021 at 16:19
  • 2
    You can't write the code you have shown, at all. The parameters to those attributes have to be constants. If you concatenate constants, you can use a constant member somewhere to add something, but you cannot call a method, and thus you cannot launch git. This would have to be done outside of the C# compiler as a pre-step, that would then synthesize a file with the requires assembly attributes in C# syntax, and included in the build. Commented Dec 21, 2021 at 15:49
  • 3
    Think, you can use Build Events for a project from your solution. Take a look here: learn.microsoft.com/en-us/visualstudio/ide/…. There you can call a bat-file that runs git and stores a full version number in a text file. Then as another pre-build step, you can re-create a version file with the new content. Check this question for the version file creation example: stackoverflow.com/questions/26021684/…. Commented Dec 22, 2021 at 12:32

4 Answers 4

7
+100

Instead of using the AssemblyVersionAttribute, you can now use MsBuild properties to set the version (among other attributes). The advantage of using MsBuild, is that you can calculate these values as part of the build process.

This will work for any "new style"/"SDK style" project.

Check out: Assembly attribute properties.

You'll end up with something along these lines:

ConsoleApp5.csproj:
<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net6.0</TargetFramework>
        <ImplicitUsings>enable</ImplicitUsings>
        <Nullable>enable</Nullable>
    </PropertyGroup>

    <!-- Add the property group and the target below to your project file -->
    <PropertyGroup>
        <GenerateAssemblyVersionAttribute>true</GenerateAssemblyVersionAttribute>
        <GenerateAssemblyFileVersionAttribute>true</GenerateAssemblyFileVersionAttribute>
        <GenerateAssemblyInformationalVersionAttribute>true</GenerateAssemblyInformationalVersionAttribute>
    </PropertyGroup>

    <Target Name="AddGitShaToAssemblyVersions" BeforeTargets="GetAssemblyVersion" Returns="AssemblyVersion, FileVersion, InformationalVersion">
        <Exec ConsoleToMsBuild="true" Command="git describe --always">
            <Output TaskParameter="ConsoleOutput" PropertyName="GitSha" />
        </Exec>

        <PropertyGroup>
            <AssemblyVersion>1.1.1.1</AssemblyVersion>
            <FileVersion>1.1.1.1-$(GitSha)</FileVersion>
            <InformationalVersion>1.1.1.1-$(GitSha)</InformationalVersion>
        </PropertyGroup>
    </Target>

    <!-- Ignore the warning generated for the AssemblyFileVersion -->
    <PropertyGroup>
      <NoWarn>$(NoWarn);7035</NoWarn>
    </PropertyGroup>
</Project>

A file is generated in the obj folder and it's automatically included in the build. You can open this file in your editor to look at the generated content:

enter image description here

Generated content:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.42000
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Reflection;

[assembly: System.Reflection.AssemblyCompanyAttribute("ConsoleApp5")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.1.1.1-3654148")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.1.1.1-3654148")]
[assembly: System.Reflection.AssemblyProductAttribute("ConsoleApp5")]
[assembly: System.Reflection.AssemblyTitleAttribute("ConsoleApp5")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.1.1.1")]

// Generated by the MSBuild WriteCodeFragment class.

To me the advantage is that all of the logic is nicely encapsulated in the build files themselves and won't depend on my having to run any commands prior to build or putting build logic into another script, like a powershell script, scripts that Visual Studio isn't aware of.

Do make sure you remove your existing, manually added, AssemblyVersion etc attributes from your project, or you'll end up with a warning the attribute was specified twice.

Sign up to request clarification or add additional context in comments.

Comments

7

Update .net 8 Sdk

The commit hash is now suffixed into the informational version.

Old

As Lasse V. Karslen already commented, your code will trigger a compiler error CS0182. Next problem is the required format for AssemblyVersion - major[.minor[.build[.revision]]], there are other assembly metadata fields that can be used for strings - e.g. InformationalVersion.

There is more than one way to add assembly meta data while building... there are probably more then these five:

  • assembly attribute
  • dotnet-cli parameter
  • msbuild parameter
  • csproj config entry
  • Build Events / Scripting is also a way to reach your goal, but is more fiddeling.

Using assembly attribute

The problem with the assembly attribute is that it need to be constant expression. That is not straight forward to achieve, but Gitinfo managed to serve a solution. An other disadvantage is the need to disable the compiler to generate the AssemblyInfo.cs that will break writing some values configured in csproj to the final assembly.

Install gitinfo

PM> Install-Package GitInfo

Set in your csproj file

<GenerateAssemblyInfo>false</GenerateAssemblyInfo>

Add/Create AssemblyInfo.cs

[assembly: AssemblyInformationalVersion(ThisAssembly.Git.Commit)]

Using dotnet-cli (using powershell)

Thats straight forward.

$GitHash=git describe --always
dotnet build -p:InformationalVersion=$GitHash

Using modified csproj

Modified csproj files are a common approach used in ci.

Add 'InformationVersion' to your csproj:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <InformationalVersion>$(GitHash)</InformationalVersion>
  </PropertyGroup>
</Project>

Set the enviroment variable 'GitHash' before building to the output of 'git describe --always':

$env:GitHash=git describe --always
dotnet build

E.g. GithubactionsHellowWord is a project example that uses github actions built in environment variable to write sha1 hash to the assembly metadata.

The explorer will show the value of informationalVersion in the field 'Product Version'.

Assembly properties

Comments

3

Not a one liner, but this article https://galdin.dev/blog/show-git-commit-sha-in-net-applications/ shows a simple way to execute the command and access the result. The steps described in this article are as follows,

To do this we create a pre-build event that'll execute a git command and save it in a text file. We then add that generated text file as a string resource. The resource can will now be accessible from C#.

  1. Right click on the web application and click on properties.

  2. Go to the build events tab and type the following in the Pre-build event command line:

    git rev-parse HEAD --short > "$(ProjectDir)\CurrentCommit.txt"
    

    The command can obviously be replaced by anything you see fit.

  3. Save and build the project. (Ctrl+Shift+S, Ctrl+Shift+B)

  4. A new file called CurrentCommit.txt should be created in the root of your project.

  5. Go ahead and exclude it from Source Control

  6. In the projects properties page, go to the Resources tab and Add Existing File CurrentCommit.txt as a resource.

The contents of the generated file can now be accessed as:

Your.NameSpace.Properties.Resources.CurrentCommit;

Comments

0

This might not answer the question directly, a running time tooling to update version. I will use C# to update the AssemblyInfo.cs file more or less I am not depending on external commands and have all codebases in one place.

First I find where is my working path for my project and for the solution that has git repo in.

string workingDirectory = Environment.CurrentDirectory;
var root = Directory.GetParent(workingDirectory)?.Parent;
var repositoryPath = $@"{root?.Parent?.FullName}\.git";
string projectDirectory = root?.FullName;
string path = $@"{projectDirectory}\Properties\AssemblyInfo.cs";

Next step I install LibGit2Sharp NuGet package. Now I use this package to get the commit id.

string commitId;
using (var repo = new Repository(repositoryPath))
{
    var headCommit = repo.Head.Commits.First();
    commitId = headCommit.Id.Sha;
}

Now I create a method (inspired from here) that read AssemblyInfo.cs and add commit id.

static void VersionWithGitInfo(string path, string commitId)
{
    if (File.Exists(path))
    {
        var file = File.ReadAllText(path);
        var newVersion = Regex.Replace(
            file,
            @"(?<=\[assembly: AssemblyFileVersion\(""[0-9]*.[0-9]*.[0-9]*.[0-9]*)(-[a-z0-9]{0,7})?(?=""\)\])",
            m => $"-{commitId}"
                );
        File.WriteAllText(path, newVersion);
    }
}

The method simply search for any thing similar to

[assembly: AssemblyFileVersion("1.0.0.0")]

Or

[assembly: AssemblyFileVersion("1.0.0.0-bc2050b")]

And add -bc2050b or edit -bc2050b to new value if it changes and save it.

Now lets call VersionWithGitInfo(path, commitId.Substring(0, 7));

and my

[assembly: AssemblyFileVersion("1.0.0.0")]

to

[assembly: AssemblyFileVersion("1.0.0.0-bc2050b")]

You can of course extend the method to cover other parameters, or even use it to update your version number.

1 Comment

So when do you call this function? This code won't work at runtime, so is this something you only run in your development environment? I'm curious.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.