123

我正在构建一个 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\hooks
  • 如果您使用的是msysgit,则hooks文件夹已经包含一些示例脚本。为了让 git 调用它们,请从脚本名称中删除“.sample”扩展名。
  • 挂钩脚本的名称与调用它们的事件相匹配。就我而言,我修改了 post-commitpost-merge
  • 我的AssemblyInfo.cs文件直接位于项目路径下(与.git文件夹相同级别)。它包含 23 行,我使用 git 生成第 24 行。

由于我的 linux-shelling 有点生疏,脚本只是将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

希望这可以帮助。

4

17 回答 17

87

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

以下是步骤:

使用构建事件调用 git

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

  • 在 Build Events 中,添加 Pre-Build 事件,其中包含(注意引号):

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

    这将在您的项目目录中创建一个version.txt文件。

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

  • 右键单击项目并选择添加现有项目
  • 添加version.txt文件(更改文件选择器过滤器以让您查看所有文件)
  • 添加version.txt后,在解决方案资源管理器中右键单击它并选择属性
  • 将构建操作更改为嵌入式资源
  • 将复制到输出目录更改为始终复制
  • version.txt添加到您的.gitignore文件中

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

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

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();
        }
    }
}
于 2013-02-28T20:33:27.620 回答
80

更新:

自从我最初回答这个问题以来,事情已经发生了变化。如果满足某些条件,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>property must be true,这会导致 nuget pack 任务也获取 SourceRevisionId。

我会引导人们远离使用 MSBuildGitHash 包,因为这种新技术更干净且最一致。

原来的:

我创建了一个简单的 nuget 包,您可以将其包含在您的项目中,它将为您解决这个问题:https ://www.nuget.org/packages/MSBuildGitHash/

这个 nuget 包实现了一个“纯”的 MSBuild 解决方案。如果您不想依赖 nuget 包,您可以简单地将这些目标复制到您的 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”文件夹下。您的项目也将在 Visual Studio 中或从命令行以相同方式构建。它也可以轻松地为您的项目定制,并将与您的 csproj 文件一起进行源代码控制。

于 2017-07-21T23:23:54.723 回答
68

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

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

您可以通过以下方式从 git 获取带有哈希的版本:

git describe --long

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

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

编译后,您可以从 windows 资源管理器中查看版本:

在此处输入图像描述

您还可以通过以下方式以编程方式获取它:

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

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

于 2013-02-28T19:14:54.387 回答
14

另一种方法是使用带有一些 On-Board Visual Studio 魔法的NetRevisionTool 。我将在此处为 Visual Studio 2013 专业版展示这一点,但这也适用于其他版本。

所以首先下载NetRevisionTool。您将 NetRevisionTool.exe 包含在您的 PATH 中或将其签入您的存储库并创建一个 Visual Studio 预构建和构建后操作并更改您的 AssemblyInfo.cs。

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

在此处输入图像描述

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

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

在显示的屏幕截图中,我在 External/bin 文件夹中检查了 NetRevisionTool.exe

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

在此处输入图像描述

希望这可以帮助那里的人

于 2015-03-23T12:04:37.627 回答
14

我认为这个问题值得一步一步给出完整的答案。这里的策略是从预构建事件中运行一个 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 忽略文件

于 2016-09-22T20:50:47.883 回答
4

现在,使用.NET Revision Task for MSBuild和使用 Visual Studio 2019 非常容易。

只需安装NuGetUnclassified.NetRevisionTask,然后按照GitHub 文档AssemblyInfo.cs中的说明在文件中配置所需的信息。

如果您只想要最后一次提交的哈希(长度=8):

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

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

在此处输入图像描述

于 2020-01-24T16:48:48.597 回答
3

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

一种方法是创建一个AssemblyInfo.cs.tmpl模板文件,在 $$GITSHA$$ 中为您的 SHA 提供一个占位符,例如

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

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

要了解如何使用 SubWCRev for SVN 完成此操作,请参阅此答案。为 git 做类似的事情应该不难。

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

于 2013-02-28T18:46:42.870 回答
3

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

更新:这是过时的,因为 Stamp.Fody 不再维护

于 2016-12-17T15:18:59.993 回答
2

您可以使用 powershell one-liner 使用提交哈希更新所有 assemblyinfo 文件。

$hash = git describe --long --always;gci **/AssemblyInfo.* -recurse | foreach { $content = (gc $_) -replace "\[assembly: Guid?.*", "$&`n[assembly: AssemblyMetadata(`"commithash`", `"$hash`")]" | sc $_ }
于 2016-12-02T08:10:29.450 回答
1
  1. 我希望您知道如何在构建时调用外部程序并拦截输出。
  2. 我希望你知道如何在 git 的工作目录中忽略未版本控制的文件。

正如@learath2 所指出的,输出git rev-parse HEAD将为您提供纯哈希。

如果您在 Git-repository 中使用标签(并且您使用标签,是不是比 更具描述性和可读性git rev-parse),可能会收到输出git describe(同时也可以在稍后成功使用git checkout

您可以在以下位置调用 rev-parse|describe:

  • 一些制作舞台
  • 在提交后挂钩中
  • 在涂抹过滤器中,如果您选择涂抹/清洁过滤器的实现方式
于 2013-02-28T18:30:12.443 回答
1
  • 打开.csproj并添加<GenerateAssemblyInfo>false</GenerateAssemblyInfo>到第一个PropertyGroup
    • 您可能希望复制文件夹中已经生成的内容,AssemblyInfo.cs这样obj您就不必自己编写所有内容。
  • 在属性文件夹中创建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 中不可用...)
于 2021-09-22T14:12:18.113 回答
1

我正在使用已接受的答案和一个小补充的组合。我安装了 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 标签 + 哈希。

于 2017-05-31T07:34:49.537 回答
1

另一种方法是从 Pre-Build 步骤生成一个 Version.cs 文件。我在一个小的概念验证项目中对此进行了探索,该项目打印出其当前的提交哈希。

Tha 项目已上传到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    
于 2018-08-01T11:00:45.563 回答
0

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

(至少在我的 C# WPF 应用程序中工作)

问题是预构建事件是在模板转换之后运行的,所以在克隆之后,git_version.txt文件不存在并且构建失败。在手动创建它以允许转换通过一次之后,它在转换后被更新,并且总是落后于.

我必须对 .csproj 文件进行两次调整(这至少适用于 Visual Studio Community 2017)

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在模板转换之前运行(以便在转换git_version.txt时存在VersionInfo.tt):

<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
于 2017-07-04T22:20:40.310 回答
0

受到@John Jesus 回答的极大启发,我创建了一个在每个 Build 上运行的 Powershell v1 脚本,以将 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 来启动它: 预建事件

于 2021-08-18T14:56:24.373 回答
0

地方

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

于 2020-02-17T06:15:09.143 回答
0

我将这些文件部署在我们的开发/登台系统上以快速查看:

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

(Visual Studio PreBuild 事件)

于 2022-03-04T13:26:26.080 回答