在.Net dll 中嵌入 git 提交散列

我正在构建一个 C # 应用程序,使用 Git 作为我的版本控制。

有没有一种方法可以在构建应用程序时自动将最后一个提交散列嵌入到可执行文件中?

例如,将提交散列打印到控制台看起来如下:

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

请注意,这必须在 建造时间完成,而不是 运行时间,因为我部署的可执行文件将无法访问 git repo。

一个与 C + + 相关的问题可以在 给你中找到。

剪辑

按照@mattanja 的要求,我发布了我在项目中使用的 git hook 脚本:

  • 钩子是 linux shell 脚本,放置在: Path _ to _ project. git 挂钩下面
  • 如果使用的是 Msysgit,则 钩子文件夹已经包含一些示例脚本。为了让 git 调用它们,删除’。来自脚本名的 sample’扩展名。
  • 钩子脚本的名称与调用它们的事件相匹配。
  • 我的 AssemblyInfo.cs文件直接位于项目路径下(与 。 git文件夹相同级别)。它包含23行,我使用 git 生成第24行。

由于我的 linux 外壳有点生疏,脚本只是将 AssemblyInfo.cs的前23行读取到一个临时文件,将 git 散列回显到最后一行,并将文件重命名为 AssemblyInfo.cs。我相信还有更好的方法:

#!/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

希望这个能帮上忙。

58772 次浏览
  1. 我希望您知道如何调用外部程序并在构建时拦截输出。
  2. 我希望你知道如何在 git 的工作目录中忽略未版本化的文件。

正如@Learath2所指出的,git rev-parse HEAD的输出将为您提供普通的散列。

如果您在 Git 存储库中使用标记(并且您使用标记,它不是比 git rev-parse更具描述性和可读性吗?) ,则可以从 git describe接收输出(后来在 git checkout中也成功地使用了这些输出)

你可以调用 rev-parse | 描述:

  • 有些人上台表演
  • 在提交后挂钩
  • 在模糊滤波器中,如果你选择 污迹/清洁过滤器的实现方式

正如另一个答案已经提到的 git 位,一旦您有了 SHA,您可以考虑在预构建钩子中生成项目的 AssemblyInfo.cs文件。

一种方法是创建一个 AssemblyInfo.cs.tmpl模板文件,其中包含 SHA 的占位符,比如 $$GITSHA $$,例如。

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

然后,您的预构建钩子必须替换这个占位符,并输出 AssemblyInfo.cs 文件供 C # 编译器拾取。

要了解如何使用 SubWCRev for SVN 实现这一点,请参见 这个答案。为 Git 做类似的事情应该不难。

其他的方法可能是前面提到的“ make stage”,即编写一个执行类似操作的 MSBuild 任务。另一种方法可能是以某种方式后处理 DLL (ildasm + ilasm 说) ,但我认为上面提到的选项可能是最简单的。

我们在 git 中使用标记来跟踪版本。

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

你可以通过以下途径从 git 获得 hash 的版本:

git describe --long

我们的构建过程将 git 散列放在 AssemblyInfo.cs 文件的 AssemblyInformationalVersion 属性中:

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

一旦编译完成,就可以从文件资源管理器查看版本:

enter image description here

你也可以通过编程方式获得:

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

其中 YOURTYPE 是程序集中具有 AssemblyInformationalVersion 属性的任何类型。

可以将 < em > version. txt 文件嵌入到可执行文件中,然后从可执行文件中读取 < em > version. txt 。要创建 < em > version. txt 文件,请使用 git describe --long

以下是步骤:

使用生成事件调用 git

  • 右键单击项目并选择“属性”

  • 在 Build Events 中,添加包含以下内容的 Pre-Build 事件(请注意引号) :

    “ C: Program Files Git bin Git.exe”描述—— long > “ $(ProjectDir) version on.txt”

    它将在项目目录中创建一个 < em > version. txt 文件。

在可执行文件中嵌入 version. txt

  • 右键单击项目并选择“添加现有项”
  • 添加 < em > version. txt 文件(更改文件选择器筛选器,使您可以查看所有文件)
  • 添加 < em > version. txt 后,在解决方案资源管理器中右键单击它并选择“属性”
  • 将“生成操作”更改为嵌入式资源
  • 将“复制”更改为“输出目录”以便始终进行复制
  • < em > version. txt 添加到 吉蒂诺雷文件中

读取嵌入的文本文件版本字符串

下面是一些读取嵌入式文本文件版本字符串的示例代码:

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();
}
}
}

另一种方法是使用 NetRevisionTool和一些板载 VisualStudio 魔术。我将在这里为 VisualStudio2013ProfessionalEdition 展示这个版本,但是这个版本也可以用于其他版本。

首先下载 NetRevisionTool。 您可以将 NetRevisionTool.exe 包含在 PATH 中,或者将其签入到存储库中,然后创建一个可视化工作室预构建和一个后构建操作,并更改 AssemblyInfo.cs。

将 git-hash 添加到 AssemblyInformationVersion 的示例如下: 在项目设置中:

enter image description here

在项目的 AssemblyInfo.cs 中,更改/添加以下行:

[ Assembly: AssemblyInformationalVersion (“1.1. { dmin: 2015} . { chash: 6}{ ! }-{ Branch }”)]

在显示的屏幕快照中,我检查了外部/bin 文件夹中的 NetRevisionTool.exe

构建完成后,如果右键单击二进制文件并转到属性,那么应该会看到如下内容:

enter image description here

希望这能帮到别人

我认为这个问题值得给出一个完整的一步一步的答案。这里的策略是从预构建事件中运行 powershell 脚本,该脚本接受模板文件并生成一个 AssemblyInfo.cs 文件,其中包含 git 标记 + 提交计数信息。

步骤1: 在 Project Properties 文件夹中创建一个 AssemblyInfo _ template. cs 文件,该文件基于您原来的 AssemblyInfo.cs,但包含:

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

步骤2: 创建一个名为 InjectGitVersion.ps1的 powershell 脚本,其源代码是:

# 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;
}

步骤3: 将 InjectGitVersion.ps1文件保存到 BuildScripts 文件夹中的解决方案目录中

步骤4: 向项目的 Pre-Build 事件添加以下代码行

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

步骤5: 构建您的项目。

步骤6: 可选地,将 AssemblyInfo.cs 添加到您的 git 忽略文件

您可以使用 powershell 一行程序使用提交散列来更新所有汇编信息文件。

$hash = git describe --long --always;gci **/AssemblyInfo.* -recurse | foreach { $content = (gc $_) -replace "\[assembly: Guid?.*", "$&`n[assembly: AssemblyMetadata(`"commithash`", `"$hash`")]" | sc $_ }

对于一个完全自动化和灵活的方法检出 https://github.com/Fody/Stamp。我们已经成功地将其用于 Git 项目(以及用于 SVN 项目的 这个版本)

更新: 这是过时的,因为邮票。福迪不再维护

我用的是公认的答案和一个小小的补充。 我已经安装了 AutoT4扩展(https://marketplace.visualstudio.com/items?itemName=BennorMcCarthy.AutoT4) ,可以在构建之前重新运行模板。

从 Git 获取版本

在项目属性的预生成事件中有 git -C $(ProjectDir) describe --long --always > "$(ProjectDir)git_version.txt"。 将 git _ version. txt 和 VersionInfo.cs 添加到. gitignore 是一个很好的主意。

在元数据中嵌入版本

我已经在我的项目中添加了一个 VersionInfo.tt模板:

<#@ 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"));
}


#>

现在我有了“ ProductVersion”中的 git 标记 + hash。

参考另一个答案(https://stackoverflow.com/a/44278482/4537127) ,我也利用 VersionInfo.tt文本模板生成 AssemblyInformationalVersion没有 AutoT4。

(至少可以在我的 C # WPF 应用程序中使用)

问题是,预构建事件在模板转换之后运行,所以在克隆之后,git_version.txt文件不在那里并且构建失败。 在手动创建它以允许转换传递一次之后,它在转换之后被更新,并且始终是 一个承诺落后

我必须对.csproj 文件做两个调整(这至少适用于 VisualStudioCommunity2017)

1)导入文本转换目标,并在每次构建时进行模板转换: (参考 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>

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

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

2)在模板转换之前运行 git describe(这样当 VersionInfo.tt转换时 git_version.txt就在那里) :

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

. . 和 C # 代码得到的 AssemblyInformationalVersion(参考 https://stackoverflow.com/a/7770189/4537127)

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


return attribute.InformationalVersion;
}
}

. . 并将生成的文件添加到. gitignore

VersionInfo.cs
git_version.txt

更新:

自从我最初回答这个问题以来,事情已经发生了变化。Microsoft.NET.Sdk(意味着您必须使用 sdk 风格的项目)现在支持将提交散列添加到汇编信息版本和 nuget 包元数据,如果满足一些条件:

  1. 必须定义 <SourceRevisionId>属性。这可以通过添加如下目标来实现:
<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>


该目标执行一个命令,该命令将 SourceRevisionId设置为缩写的(8个字符)散列。在创建程序集信息版本之前,由 BeforeTargets 导致运行此命令。

  1. 要在 nuget 包元数据中包含散列,还必须定义 <RepositoryUrl>

  2. <SourceControlInformationFeatureSupported>属性必须是 true,这将导致 nuget pack 任务也拾取 SourceRevisionId。

我希望人们不要使用 MSBuildGitHash 包,因为这种新技术更简洁、更一致。

原创:

我已经创建了一个简单的 nuget 包,你可以包括在您的项目,这将为您照顾这个: https://www.nuget.org/packages/MSBuildGitHash/

这个 nuget 包实现了一个“纯”MSBuild 解决方案。如果你不想依赖于一个 nuget 包,你可以简单地将这些 Targets 复制到你的 csproj 文件中,它应该包含 git 散列作为一个自定义组装属性:

<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>

这里有两个目标。第一个函数“ GetGitHash”将 git 散列加载到名为 BuildHash 的 MSBuild 属性中,如果 BuildHash 尚未定义,则 只有将执行此操作。如果您愿意,这允许您在命令行上将其传递给 MSBuild。您可以像这样将它传递给 MSBuild:

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

第二个目标“ WriteGithash”将把散列值写入名为“ customassemblyinfo.cs”的临时“ obj”文件夹中的一个文件。这个文件将包含一行内容,如下所示:

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

这个 customassemblyinfo.cs 文件将被编译到你的程序集中,所以你可以在运行时使用反射来查找 AssemblyMetadata。下面的代码演示在同一程序集中包含 AssemblyInfo类时如何进行此操作。

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;
}
}

这种设计的一些好处是,它不会触及您的项目文件夹中的任何文件,所有的变异文件都在“ obj”文件夹下。项目还将从 VisualStudio 内部或从命令行生成相同的内容。它也可以很容易地为您的项目定制,并将源代码控制随着您的 csproj 文件。

另一种方法是从 Pre-Build 步骤生成一个 Version.cs 文件。我在一个概念验证小项目中研究了这个问题,该项目打印出当前的提交散列。

这个项目是上传到 https://github.com/sashoalm/GitCommitHashPrinter

创建 Version.cs 文件的批处理代码如下:

@echo off


echo "Writing Version.cs file..."


@rem Pushd/popd are used to temporarily cd to where the BAT file is.
pushd $(ProjectDir)


@rem Verify that the command succeeds (i.e. Git is installed and we are in the repo).
git rev-parse HEAD || exit 1


@rem Syntax for storing a command's output into a variable (see https://stackoverflow.com/a/2340018/492336).
@rem 'git rev-parse HEAD' returns the commit hash.
for /f %%i in ('git rev-parse HEAD') do set commitHash=%%i


@rem Syntax for printing multiline text to a file (see https://stackoverflow.com/a/23530712/492336).
(
echo namespace GitCommitHashPrinter
echo {
echo     class Version
echo     {
echo         public static string CommitHash { get; set; } = "%commitHash%";
echo     }
echo }
)>"Version.cs"


popd

现在使用 MSBuild 的.NET 修订任务和 VisualStudio2019非常容易。

只需安装 NuGet软件包 非机密 NetRevisionTask,然后按照 GitHub 文档中的描述在 AssemblyInfo.cs文件中配置所需的信息。

如果只想要最后一次提交的散列(length = 8) :

[assembly: AssemblyInformationalVersion("1.0-{chash:8}")]

构建您的项目/解决方案,您将得到如下内容:

enter image description here

地点

<Target Name="UpdateVersion" BeforeTargets="CoreCompile">
<Exec Command="php &quot;$(SolutionDir)build.php&quot; $(SolutionDir) &quot;$(ProjectDir)Server.csproj&quot;" />
</Target>

YOUR_PROJECT_NAME.csproj

<?php


function between(string $string, string $after, string $before, int $offset = 0) : string{
return substr($string, $pos = strpos($string, $after, $offset) + strlen($after),
strpos($string, $before, $pos) - $pos);
}


$pipes = [];
$proc = proc_open("git rev-parse --short HEAD", [
0 => ["pipe", "r"],
1 => ["pipe", "w"],
2 => ["pipe", "w"]
], $pipes, $argv[1]);


if(is_resource($proc)){
$rev = stream_get_contents($pipes[1]);
proc_close($proc);
}


$manifest = file_get_contents($argv[2]);
$version = between($manifest, "<Version>", "</Version>");
$ver = explode("-", $version)[0] . "-" . trim($rev);
file_put_contents($argv[2], str_replace($version, $ver, $manifest));


echo "New version generated: $ver" . PHP_EOL;


深受@John Jesus 答案的启发,我创建了一个 Powershell v1脚本,它运行在每个 Build 上,以将 Assembly Version 调整为当前的 Git 标记。

Powershell 脚本

# Get build running directory
$scriptPath = split-path -parent $MyInvocation.MyCommand.Path
try {
$v = git describe --tags
}
catch [System.Management.Automation.CommandNotFoundException] {
# Git not found
exit
}


# Letters are incompatible with AssemblyVersion.cs so we remove them
$v = $v -replace "v", ""
# Version format is major[.minor[.build[.revision]] so we remove them
$v = $v -replace "-(\D.*)", ''
$v = $v -replace "-", '.'


# We replace versions inside AssemblyInfo.cs content
$info = (Get-Content ($scriptPath + "/properties/AssemblyInfo.cs"))
$av = '[assembly: AssemblyVersion("'+$v+'")]'
$avf = '[assembly: AssemblyFileVersion("'+$v+'")]'
$info = $info -replace '\[assembly: AssemblyVersion\("(.*)"\)]', $av
$info = $info -replace '\[assembly: AssemblyFileVersion\("(.*)"\)]', $avf
Set-Content -Path ($scriptPath + "/properties/AssemblyInfo.cs") -Value $info -Encoding UTF8

放置解决方案文件夹并设置一个预生成事件来启动它: prebuild-event

  • 打开 .csproj并将 <GenerateAssemblyInfo>false</GenerateAssemblyInfo>添加到第一个 PropertyGroup
    • 您可能希望将已经生成的 AssemblyInfo.cs的内容复制到 obj文件夹中,这样就不必自己编写所有内容。
  • 在 properties 文件夹中创建 AssemblyInfo.tt(T4模板)。
  • 粘贴以下内容 + 以前自动生成的 AssemblyInfo.cs的旧内容
<#@ template debug="true" hostspecific="True" language="C#" #>
<#@ assembly name="System.Core" #>
<# /*There's a bug with VS2022 where you have to be real specific about envDTE.*/ #>
<#@ assembly name="./PublicAssemblies/envdte.dll" #>
<#@ import namespace="System.IO" #>
<#@ import namespace="System.Text.RegularExpressions" #>
<#@ import namespace="System.Globalization" #>
<#@ output extension=".cs" #>
<#
var dte = ((IServiceProvider)this.Host).GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE;
string buildConfig = dte.Solution.SolutionBuild.ActiveConfiguration.Name;
string solutionDirectory = Path.GetDirectoryName(dte.Solution.FullName);


var (gitRevision, gitBranch, gitCompactRevision) = ("", "", "");


using(var process = new System.Diagnostics.Process() {
StartInfo = new System.Diagnostics.ProcessStartInfo() {
WorkingDirectory = solutionDirectory,
FileName = @"cmd.exe",
Arguments = "/C git rev-parse HEAD & git rev-parse --abbrev-ref HEAD",
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
}
}) {
process.Start();
string[] lines = process.StandardOutput.ReadToEnd().Split();
gitRevision = lines[0].Trim();
gitBranch = lines[1].Trim();
gitCompactRevision = gitRevision.Substring(0, 6);
}
string appPurpose         = "Launcher"; // & Updater
string companyShort       = "todo";
string companyFull        = "todo";
string productNameShort   = "todo";
string productName        = $"{companyShort} {productNameShort}";
string fileName           = $"{companyShort}{productNameShort}";
string exeNAME            = $"{fileName}Launch";
string originalFilename   = $"{exeNAME}.exe";
string CALLEXE            = $"{fileName}.exe";
string BROWSEREXE         = $"{fileName}Browser.exe";
string FULLINSTALLER      = $"{fileName}Setup.exe";


DateTime dtBuiltDate      = DateTime.UtcNow;
string cBuildYear         = dtBuiltDate.Year.ToString();
string cBuildDay          = dtBuiltDate.ToString("dd");
string cBuildMonth        = dtBuiltDate.ToString("MM");
string cBuildTime         = dtBuiltDate.ToString("T", DateTimeFormatInfo.InvariantInfo);
string assemblyVersion    = $"3.0.{cBuildYear}.{cBuildMonth}{cBuildDay}";


string JOB_NAME           = System.Environment.GetEnvironmentVariable("JOB_NAME") ?? "0.0";
string buildVersion       = System.Environment.GetEnvironmentVariable("BUILD_NUMBER") ?? "0-dev";
string buildSeries        = Regex.Replace(JOB_NAME, @"[^0-9\.]+", "");
string buildNumber        = Regex.Replace(buildVersion, @"[^0-9\.]+", "");
string InternalVersion    = $"{JOB_NAME}.{buildVersion}";
string fileVersion        = Regex.Replace(InternalVersion, @"[^0-9\.]+", "");
#>
using System.Reflection;


[assembly: System.Runtime.InteropServices.ComVisible(false)]
[assembly: System.Resources.NeutralResourcesLanguageAttribute("en")]
[assembly: AssemblyConfigurationAttribute("<#= buildConfig #>")]
[assembly: AssemblyProduct("<#= productName #>")]
[assembly: AssemblyTitle("<#= $"{companyShort}{productNameShort}" #>")]
[assembly: AssemblyCompany("<#= companyFull #>")]
[assembly: AssemblyDescription("<#= $"{companyShort} {productNameShort} .... {appPurpose} - ...... by {companyFull}" #>")]
[assembly: AssemblyCopyright("<#= $"© 1983-{cBuildYear} {companyFull}" #>")]
[assembly: AssemblyTrademark("<#= $"{productName} is a trademark of {companyFull}, Inc." #>")]
[assembly: AssemblyInformationalVersion("<#= InternalVersion #>")]
[assembly: AssemblyVersion("<#= assemblyVersion #>")]
[assembly: AssemblyFileVersion("<#= fileVersion #>")]
[assembly: AssemblyMetadataAttribute("OriginalFilename",    "<#= originalFilename #>")]
[assembly: AssemblyMetadataAttribute("NAME",                "<#= $"{productName} {appPurpose}" #>")]
[assembly: AssemblyMetadataAttribute("EXENAME",             "<#= exeNAME #>")]
[assembly: AssemblyMetadataAttribute("DIRNAME",             "<#= productNameShort #>")]
[assembly: AssemblyMetadataAttribute("CALLEXE",             "<#= $"{fileName}.exe" #>")]
[assembly: AssemblyMetadataAttribute("BROWSEREXE",          "<#= $"{fileName}Browser.exe" #>")]
[assembly: AssemblyMetadataAttribute("FULLINSTALLER",       "<#= $"{fileName}Setup.exe" #>")]
[assembly: AssemblyMetadataAttribute("COMPANY",             "<#= companyFull #>")]
[assembly: AssemblyMetadataAttribute("License",             "<#= $"Contains copyrighted code and applications ..." #>")]
[assembly: AssemblyMetadataAttribute("TermsOfUse",          "<#= "https://www.company.com/en-us/terms-of-use/" #>")]
[assembly: AssemblyMetadataAttribute("Website",             "<#= "https://www.company.com/en-us" #>")]
[assembly: AssemblyMetadataAttribute("UpdateURL",           "https://subdomain.product.net/version_check")]


[assembly: AssemblyMetadataAttribute("BuildYear",           "<#= cBuildYear #>")]
[assembly: AssemblyMetadataAttribute("BuildDay",            "<#= cBuildDay #>")]
[assembly: AssemblyMetadataAttribute("BuildMonth",          "<#= cBuildMonth #>")]
[assembly: AssemblyMetadataAttribute("BuildTime",           "<#= cBuildTime #>")]
[assembly: AssemblyMetadataAttribute("DateModified",        "<#= $"{dtBuiltDate.ToString("MMM dd, yyyy", DateTimeFormatInfo.InvariantInfo)} at {cBuildTime}" #>")]


[assembly: AssemblyMetadataAttribute("BuildSeries",         "<#= buildSeries #>")]
[assembly: AssemblyMetadataAttribute("BuildNumber",         "<#= buildNumber #>")]
[assembly: AssemblyMetadataAttribute("BuildDate",           "<#= dtBuiltDate.ToString("s") #>")]
[assembly: AssemblyMetadataAttribute("BuildMachine",        "<#= Environment.MachineName #>")]
[assembly: AssemblyMetadataAttribute("BuildMachineUser",    "<#= Environment.UserName #>")]
[assembly: AssemblyMetadataAttribute("BuildOSVersion",      "<#= Environment.OSVersion #>")]
[assembly: AssemblyMetadataAttribute("BuildPlatform",       "<#= Environment.OSVersion.Platform #>")]
[assembly: AssemblyMetadataAttribute("BuildClrVersion",     "<#= Environment.Version #>")]


[assembly: AssemblyMetadataAttribute("BuildBranch",         "<#= gitBranch #>")]
[assembly: AssemblyMetadataAttribute("BuildRevision",       "<#= gitCompactRevision #>")]
[assembly: AssemblyMetadataAttribute("CommitHash",          "<#= gitRevision #>")]
[assembly: AssemblyMetadataAttribute("RepositoryUrl",       "")]
[assembly: AssemblyMetadataAttribute("RepositoryType",      "")]
<#+


#>

您现在可以使用 C # 的全部功能来生成任何您想要的东西,比如您当前使用的 git 分支和修订。一些小贴士:

  • 变量可以在 <# #>块中的任何地方声明
  • 您希望使用的任何方法都必须在 <#+ #>块中的文件末尾声明。(+符号非常重要,它必须是文件末尾的最后一个符号)
  • <# #>块之外的所有内容都是纯文本。
  • Vs2019没有语法突显或智能。.tt文件是纯文本。我建议在安装 T4 Support扩展之后使用 vscode 编辑它(vs2019中没有...)

我将这些文件部署到我们的 dev/stage 系统上,以便快速查看:

git.exe -C "$(ProjectDir.TrimEnd('\'))" describe --long > "$(ProjectDir)_Version.info":

结果: 10.02.0.3-247-gbeeadd082

git.exe -C "$(ProjectDir.TrimEnd('\'))" branch --show-current > "$(ProjectDir)_Branch.info"

我的结果: 功能/JMT-3931-美洲虎

(VisualStudio 预生成事件)

下面是一个在 VisualStudio2019中运行的简单解决方案,它将 git 提交散列直接放入 C # 文件中。将下面的 C # 代码添加到您的解决方案中:

namespace MyNameSpace
{


[System.AttributeUsage(System.AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)]
sealed class GitHashAttribute : System.Attribute
{
public string Hash { get; }
public GitHashAttribute(string hsh)
{
this.Hash = hsh;
}
}
var hash = Assembly.GetEntryAssembly().GetCustomAttribute<GitHashAttribute>().Hash;
}

变量 hash将包含所需的字符串,如果您将以下行添加到 .csproj文件中。

<Target Name="SetSourceRevisionId" BeforeTargets="InitializeSourceControlInformation">
<Exec Command="git.exe describe --long --always --dirty --exclude='*' --abbrev=40"
ConsoleToMSBuild="True" IgnoreExitCode="False">
<Output PropertyName="SourceRevisionId" TaskParameter="ConsoleOutput" />
</Exec>
</Target>


<Target Name="SetHash" AfterTargets="InitializeSourceControlInformation">
<ItemGroup>
<AssemblyAttribute Include="MyNameSpace.GitHashAttribute">
<_Parameter1>$(SourceRevisionId)</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
</Target>

确保两个文件中的 MyNameSpace都符合您的要求。 这里的重点是,ItemGroup必须嵌入到具有适当 AfterTargets集的 Target中。

只有一行,添加对 Microsoft.SourceLink.GitHub的引用

    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />

and git commit hash is auto imbedded to .Net dll.

更多细节请阅读 医生

我觉得这样加起来更方便

<ItemGroup>
<PackageReference Include="GitVersion.MsBuild" Version="5.10.3">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

这是建立在非常灵活的 GitVersion工具之上的。

这将在生成的 dll 中设置 Product Version。您可以通过创建一个包含以下内容的 GitVersion.yml文件来控制要输出的信息

assembly-informational-format: '{Sha}'

其他格式也可以使用,它们在 GitVersion 文档的 版本变量中列出。

另一种选择是使用由微软自己提供的 SourceLink。说实话,我发现这种方法有很多怪癖和局限性(比如 SourceLinks 想知道回购的主机在哪里,必须根据 Azure DevOps,GitHub,GitLab,BitBucket,gitea 等不同的配置)。