Visual Studio 2017 (.NET Core) 中的自动版本控制

Posted

技术标签:

【中文标题】Visual Studio 2017 (.NET Core) 中的自动版本控制【英文标题】:Auto Versioning in Visual Studio 2017 (.NET Core) 【发布时间】:2017-08-18 14:12:31 【问题描述】:

我花了几个小时的大部分时间试图找到一种在 .NETCoreApp 1.1 (Visual Studio 2017) 中自动增加版本的方法。

我知道 AssemblyInfo.cs 正在文件夹中动态创建:obj/Debug/netcoreapp1.1/

它不接受以下旧方法: [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.*")]

如果我将项目设置为包,我可以在那里设置版本,但这似乎用于构建 AssemblyInfo.cs 文件。

我的问题是,有没有人想出如何控制 .NET Core(或 .NETStandard)项目中的版本。

【问题讨论】:

我不知道你在这方面走了多远,但看起来我用不同的方式问了几乎同样的问题(***.com/a/43280282/685341)——也许这个问题的公认答案会帮助你;您可以在构建脚本中将 /p: 标志传递给 dotnet msbuild 并设置版本、公司、版权......所有这些好东西。 感谢您的信息。这只是打开了其他选项。 以前 * 支持 AssemblyVersion,不支持 AssemblyFileVersion - 请参阅 Can I automatically increment the file build version when using Visual Studio? FWIW 不支持汇编版本中的通配符,因为对于这些新项目,编译器的“确定性”模式默认处于活动状态。由于自动增量会破坏确定性(相同的输入 > 相同的输出),因此在该模式下是不允许的。您可以在 csproj 中设置<Deterministic>False</Deterministic> 来使用它。 (或使用任何其他 MSbuild 逻辑来计算 <VersionPrefix>/<Version> 【参考方案1】:

在 .csproj 的 <PropertyGroup> 部分中添加 <Deterministic>False</Deterministic>

使 AssemblyVersion * 工作的解决方法在 “Confusing error message for wildcard in [AssemblyVersion] on .Net Core #22660” 中进行了描述

只有在构建不确定的情况下才允许使用通配符,这 是 .Net Core 项目的默认设置。 将<Deterministic>False</Deterministic> 添加到 csproj 可修复 问题。

.Net Core 开发人员认为确定性构建有益的原因在http://blog.paranoidcoding.com/2016/04/05/deterministic-builds-in-roslyn.html 中进行了描述 和Compilers should be deterministic: same inputs generate same outputs #372

但是,如果您使用的是 TeamCity、TFS 或其他 CI/CD 工具,最好保持版本号由它们控制和递增,并作为参数传递给 build(正如其他答案中所建议的那样),例如

msbuild /t:build /p:Version=YourVersionNumber /p:AssemblyVersion=YourVersionNumber

包号for NuGet packages

msbuild /t:pack /p:Version=YourVersionNumber   

【讨论】:

谢谢!我知道有一个隐藏的杠杆可以打开宝藏室!我正在将一个旧项目迁移到新的 .NET SDK,我真的很想快速完成这项工作,而无需寻找自动版本增量解决方案的麻烦。事实上,与旧方法越兼容,对我的情况就越好。 这是 IMO 的最佳答案。它允许构建工具正常工作。至少我现在可以使用外部机制将数字输入到构建中。 另外,请确保您的 .csproj 中没有 1.0.0【参考方案2】:

如果您使用 Visual Studio Team Services/TFS 或其他一些 CI 构建过程来内置版本控制,则可以利用 msbuild 的 Condition 属性,例如:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <Version Condition=" '$(BUILD_BUILDNUMBER)' == '' ">0.0.1-local</Version>
    <Version Condition=" '$(BUILD_BUILDNUMBER)' != '' ">$(BUILD_BUILDNUMBER)</Version>
    <TargetFramework>netcoreapp1.1</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <Folder Include="wwwroot\" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.0.0" />
    <PackageReference Include="Microsoft.AspNetCore" Version="1.1.2" />
    <PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="1.1.2" />
  </ItemGroup>

</Project>

这将告诉 .NET Core 编译器使用 BUILD_BUILDNUMBER 环境变量中的任何内容(如果存在),或者如果您在本地计算机上进行构建,则回退到 0.0.1-local

【讨论】:

很好,我喜欢这种方法,因为环境变量可以在构建服务器上设置,而这些条件决定了二进制文件中的程序集集。 似乎无法在 TFS 2010 上运行,但希望我们能尽快离开! 不错的解决方案,但如果解决方案有很多项目,可能会有点工作。 很好的解决方案。我确实得到了一个构建异常。我不得不稍微更改配置来修复它。 ***.com/a/59858009/106227 这适用于 .NET Core 2.1.2 和 TFS2017U3【参考方案3】:

我一直在寻找 VS2017 中使用 csproj 配置格式的 .NET Core 应用程序的版本增量器。

我找到了一个名为 dotnet bump 的项目,它适用于 project.json 格式,但很难找到适用于 .csproj 格式的解决方案。 dotnet bump的作者其实想出了.csproj格式的解决方案,叫做MSBump。

在 GitHub 上有一个项目:

https://github.com/BalassaMarton/MSBump

您可以在哪里看到代码,并且它也可以在 NuGet 上使用。只需在 Nuget 上搜索 MSBump。

【讨论】:

我推荐使用最新的2.1.0版本的MSBump,它更好地支持切换配置,并且还设置了当前构建的版本,而不是下一个(像以前的版本)。 我看到它现在也支持 MSBuild,而之前它需要 Visual Studio。 可以,而且还支持多目标项目。 考虑使用 GitVersioning。它可能适合在您的 CI 环境中运行。 github.com/AArnott/Nerdbank.GitVersioning MSBump 在每次构建时都会增加版本,即使您没有更改任何内容,从长远来看这会导致很多问题。有时版本不同步,一个版本落后于另一个。【参考方案4】:

您可以使用 MSBuild 属性函数根据当前日期设置版本后缀:

<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
  <VersionSuffix>pre$([System.DateTime]::UtcNow.ToString(yyyyMMdd-HHmm))</VersionSuffix>
</PropertyGroup>

这将输出一个名称如下的包:PackageName.1.0.0-pre20180807-1711.nupkg

有关 MSBuild 属性函数的更多详细信息:https://docs.microsoft.com/en-us/visualstudio/msbuild/property-functions

VersionVersionPrefixVersionSuffix 组合而成,或者如果VersionSuffix 为空,则仅VersionPrefix

<PropertyGroup>
  <VersionPrefix>1.0.0</VersionPrefix>
</PropertyGroup>

【讨论】:

【参考方案5】:

我想出了一个解决方案,它与带有星号 (*) 的旧 AssemblyVersion 属性几乎相同 - AssemblyVersion("1.0.*")

AssemblyVersionAssemblyFileVersion 的值在 MSBuild 项目 .csproj 文件中(不在 AssemblyInfo.cs 中)作为属性 FileVersion(生成 AssemblyFileVersionAttribute)和 AssemblyVersion(生成 AssemblyVersionAttribute)。 在 MSBuild 过程中,我们使用我们的自定义 MSBuild 任务来生成版本号,然后我们用任务中的新值覆盖这些 FileVersionAssemblyVersion 属性的值。

首先,我们创建自定义 MSBuild 任务GetCurrentBuildVersion

public class GetCurrentBuildVersion : Task

    [Output]
    public string Version  get; set; 
 
    public string BaseVersion  get; set; 
 
    public override bool Execute()
    
        var originalVersion = System.Version.Parse(this.BaseVersion ?? "1.0.0");
 
        this.Version = GetCurrentBuildVersionString(originalVersion);
 
        return true;
    
 
    private static string GetCurrentBuildVersionString(Version baseVersion)
    
        DateTime d = DateTime.Now;
        return new Version(baseVersion.Major, baseVersion.Minor,
            (DateTime.Today - new DateTime(2000, 1, 1)).Days,
            ((int)new TimeSpan(d.Hour, d.Minute, d.Second).TotalSeconds) / 2).ToString();
    

Task 类继承自 Microsoft.Build.Utilities.Core NuGet 包的 Microsoft.Build.Utilities.Task 类。 它在输入上采用 BaseVersion 属性(可选)并在版本输出属性中返回生成的版本。获取版本号的逻辑与 .NET 自动版本控制相同(内部版本号是自 2000 年 1 月 1 日以来的天数,修订版是自午夜以来的半秒)。

为了构建这个 MSBuild 任务,我们将 .NET Standard 1.3 类库项目类型用于此类。

.csproj 文件可以是这样的:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>netstandard1.3</TargetFramework>
    <AssemblyName>DC.Build.Tasks</AssemblyName>
    <RootNamespace>DC.Build.Tasks</RootNamespace>
    <PackageId>DC.Build.Tasks</PackageId>
    <AssemblyTitle>DC.Build.Tasks</AssemblyTitle>
  </PropertyGroup>
 
  <ItemGroup>
    <PackageReference Include="Microsoft.Build.Framework" Version="15.1.1012" />
    <PackageReference Include="Microsoft.Build.Utilities.Core" Version="15.1.1012" />
  </ItemGroup>
</Project>

这个任务项目也可以在我的 GitHub 上找到 holajan/DC.Build.Tasks

现在我们设置 MSBuild 以使用此任务并设置 FileVersionAssemblyVersion 属性。 在 .csproj 文件中,它看起来像这样:

<Project Sdk="Microsoft.NET.Sdk">
  <UsingTask TaskName="GetCurrentBuildVersion" AssemblyFile="$(MSBuildThisFileFullPath)\..\..\DC.Build.Tasks.dll" />
 
  <PropertyGroup>
    ...
    <AssemblyVersion>1.0.0.0</AssemblyVersion>
    <FileVersion>1.0.0.0</FileVersion>
  </PropertyGroup>
 
  ...
 
  <Target Name="BeforeBuildActionsProject1" BeforeTargets="BeforeBuild">
    <GetCurrentBuildVersion BaseVersion="$(FileVersion)">
      <Output TaskParameter="Version" PropertyName="FileVersion" />
    </GetCurrentBuildVersion>
    <PropertyGroup>
      <AssemblyVersion>$(FileVersion)</AssemblyVersion>
    </PropertyGroup>
  </Target>
 
</Project>

这里有重要的事情:

提到 UsingTaskDC.Build.Tasks.dll 导入 GetCurrentBuildVersion 任务。它假定此 dll 文件位于 .csproj 文件的父目录中。 我们调用任务的 BeforeBuildActionsProject1 目标必须在每个项目中具有唯一名称,以防我们在调用 GetCurrentBuildVersion 任务的解决方案中有更多项目。

此解决方案的优势在于它不仅适用于构建服务器上的构建,还适用于来自 dotnet build 或 Visual Studio 的手动构建。

【讨论】:

我建议在方法GetCurrentBuildVersionString() 中使用DateTime.UtcNow 而不是DateTime.Now,特别是如果代码是在自动构建机器上执行的。当您的计算机切换到/从夏令时时,它们可能会在凌晨 2 点或凌晨 3 点运行。在这种情况下使用DateTime.Now,您可能会在版本方面倒退。诚然,这是一个极端案例,我也承认我很挑剔。 :-) 此外,如果您在所有构建机器上配置相同的时区并且不调整为夏令时,问题也会消失。 是否有用于此的 NuGet 包? @Jonathan Allen 不,我没有 nuget 包的计划,因为每个项目中的名称不同。您可以在github.com/holajan/DC.Build.Tasks/tree/master/dist文件夹中下载已编译的构建任务程序集【参考方案6】:

我接受了上述答案,因为@Gigi 是正确的(截至目前),但我很生气并想出了以下 PowerShell 脚本。

首先,我的解决方案文件夹中有脚本 (UpdateBuildVersion.ps1):

#Get Path to csproj
$path = "$PSScriptRoot\src\ProjectFolder\ProjectName.csproj"

#Read csproj (XML)
$xml = [xml](Get-Content $path)

#Retrieve Version Nodes
$assemblyVersion = $xml.Project.PropertyGroup.AssemblyVersion
$fileVersion = $xml.Project.PropertyGroup.FileVersion

#Split the Version Numbers
$avMajor, $avMinor, $avBuild  = $assemblyVersion.Split(".")
$fvMajor, $fvMinor, $fvBuild = $fileVersion.Split(".")

#Increment Revision
$avBuild = [Convert]::ToInt32($avBuild,10)+1
$fvBuild = [Convert]::ToInt32($fvBuild,10)+1

#Put new version back into csproj (XML)
$xml.Project.PropertyGroup.AssemblyVersion = "$avMajor.$avMinor.$avBuild"
$xml.Project.PropertyGroup.FileVersion = "$fvMajor.$fvMinor.$fvBuild"

#Save csproj (XML)
$xml.Save($path)

我将此添加到 csproj 文件中:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <AssemblyVersion>0.0.1</AssemblyVersion>
    <FileVersion>0.0.1</FileVersion>
    <PreBuildEvent>powershell.exe –NonInteractive –ExecutionPolicy Unrestricted -command "& $(SolutionDir)UpdateBuildVersion.ps1"</PreBuildEvent>
  </PropertyGroup>
</Project>

即使将其设置为 PreBuildEvent,事实上在文件加载到内存之后版本号才会更新,因此版本号在下一次构建之前不会反映。事实上,你可以把它改成 PostBuildEvent ,效果也是一样的。

我还创建了以下两个脚本: (UpdateMinorVersion.ps1)

#Get Path to csproj
$path = "$PSScriptRoot\src\ProjectFolder\ProjectName.csproj"

#Read csproj (XML)
$xml = [xml](Get-Content $path)

#Retrieve Version Nodes
$assemblyVersion = $xml.Project.PropertyGroup.AssemblyVersion
$fileVersion = $xml.Project.PropertyGroup.FileVersion

#Split the Version Numbers
$avMajor, $avMinor, $avBuild  = $assemblyVersion.Split(".")
$fvMajor, $fvMinor, $fvBuild = $fileVersion.Split(".")

#Increment Minor Version - Will reset all sub nodes
$avMinor = [Convert]::ToInt32($avMinor,10)+1
$fvMinor = [Convert]::ToInt32($fvMinor,10)+1
$avBuild = 0
$fvBuild = 0

#Put new version back into csproj (XML)
$xml.Project.PropertyGroup.AssemblyVersion = "$avMajor.$avMinor.$avBuild"
$xml.Project.PropertyGroup.FileVersion = "$fvMajor.$fvMinor.$fvBuild"

#Save csproj (XML)
$xml.Save($path)

(UpdateMajorVersion.ps1)

#Get Path to csproj
$path = "$PSScriptRoot\src\ProjectFolder\ProjectName.csproj"

#Read csproj (XML)
$xml = [xml](Get-Content $path)

#Retrieve Version Nodes
$assemblyVersion = $xml.Project.PropertyGroup.AssemblyVersion
$fileVersion = $xml.Project.PropertyGroup.FileVersion

#Split the Version Numbers
$avMajor, $avMinor, $avBuild  = $assemblyVersion.Split(".")
$fvMajor, $fvMinor, $fvBuild = $fileVersion.Split(".")

#Increment Major Version - Will reset all sub nodes
$avMajor = [Convert]::ToInt32($avMajor,10)+1
$fvMajor = [Convert]::ToInt32($fvMajor,10)+1
$avMinor = 0
$fvMinor = 0
$avBuild = 0
$fvBuild = 0

#Put new version back into csproj (XML)
$xml.Project.PropertyGroup.AssemblyVersion = "$avMajor.$avMinor.$avBuild"
$xml.Project.PropertyGroup.FileVersion = "$fvMajor.$fvMinor.$fvBuild"

#Save csproj (XML)
$xml.Save($path)

【讨论】:

【参考方案7】:

这些值现在在.csproj 文件中设置:

<PropertyGroup>
    <TargetFramework>netcoreapp1.1</TargetFramework>
    <AssemblyVersion>1.0.6.0</AssemblyVersion>
    <FileVersion>1.0.6.0</FileVersion>
    <Version>1.0.1</Version>
</PropertyGroup>

这些值与您在项目设置中的 Package 选项卡中看到的值相同。虽然我认为您不能使用 * 自动增加版本,但您可以做的是引入一个后处理步骤来替换您的版本(例如,作为持续集成的一部分)。

【讨论】:

我担心这就是答案。我会看看我是否可以做一个预构建步骤来增加它。 正如另一个线程中所指出的,新的 csproj 格式允许您关闭 assemblyinfo 文件的自动生成并让您指定自己的。我在这里遵循了 natemcmaster 的建议并使用了标准的 AssemblyInfo.cs 文件:***.com/questions/42138418/… 他们为什么要删除自动增量?多年来,它对我来说非常有效而且非常简单。我推送 master,CI 构建和增量,然后使用一些 PS 脚本直接从构建的 DLL 中读取版本,然后在推送到 NuGet 时将该版本用作 arg。很简单。现在坏了。 @LukePuplett:参见 [“Confusing error message for wildcard in AssemblyVersion on .Net Core #22660”] (github.com/dotnet/roslyn/issues/22660),他们认为确定性构建有益的原因在 blog.paranoidcoding.com/2016/04/05/… 和编译器中描述应该是确定性的:相同的输入产生相同的输出#372github.com/dotnet/roslyn/issues/372> 这太棒了,我同意这个想法......但是......为什么不支持 * auto-increment-but-not-unless-the-source-actually-changed 功能? #rhetoricalQuestion【参考方案8】:

您可以在 csproj 文件中执行如下操作。我没有弄清楚数学。我在 Stack Overflow 上的其他地方发现了它,但这很有效,并且会为您提供类似于 1.0.* 的版本。

<PropertyGroup>
    <TargetFramework>netcoreapp3.1</TargetFramework>
    <FileVersion>1.0.$([System.DateTime]::UtcNow.Date.Subtract($([System.DateTime]::Parse("2000-01-01"))).TotalDays).$([System.Math]::Floor($([MSBuild]::Divide($([System.DateTime]::UtcNow.TimeOfDay.TotalSeconds), 1.32))))</FileVersion>
    <Version>1.0.$([System.DateTime]::UtcNow.Date.Subtract($([System.DateTime]::Parse("2000-01-01"))).TotalDays)</Version>
</PropertyGroup>

【讨论】:

在我的环境中似乎不适用于netstandard2.0。我正在使用 .NET Core SDK 3.1.403。是否需要其他东西来完成这项工作?使用dotnet build 运行时,版本号不会从默认值更改。 谢谢,简短且有效。 – @Manfred:也许改用 AssemblyVersion 或类似的东西。这对我来说是个问题。 FileVersion 已设置。但 AssemblyVersion 不是。另外: 不应再使用(如果我弄错了)。 另外:代替 1.0。 ... 我使用了 $(VersionPrefix)。 … 相反,然后我可以在 VersionPefix 属性中正确设置版本号。 仅供参考:为了匹配 .NET 对“1.0.*”所做的事情,我从使用“UtcNow”更改为“Now”,从“1.32”更改为“2”,尽管对我来说 UtcNow 做得更多使用感。【参考方案9】:

有没有人知道如何在 .NET Core(或 .NETStandard)项目中控制版本。

用途:

dotnet build /p:AssemblyVersion=1.2.3.4

我发现这个问题试图在 CI 构建的上下文中解决这个问题。我想将程序集版本设置为 CI 内部版本号。

【讨论】:

标题为“Visual Studio 2017 (.NET Core) 中的自动版本控制”。在哪里手动构建它符合“Visual Studio 2017”? 我在回复:“有没有人想出如何控制 .NET Core(或 .NETStandard)项目中的版本。”我发现这个问题试图在 CI 构建的上下文中解决这个问题。我想将程序集版本设置为 CI 内部版本号。如果您认为这与手头的问题无关,我们深表歉意。 这对我很有帮助,谢谢。我将把它用作 CI 解决方案的一部分 @ChrisMcKenzie:您的评论应包含在您的答案中,以明确您的意图 ** 当未指定 assemblyinfo.cs 并且版本在 csproj 中时,这对我在 netstandard 项目中不起作用...【参考方案10】:

我制作了一个简单的 CLI 工具来设置 .csproj .NET Core 版本字符串here。您可以将它与 GitVersion 等工具结合使用,以便在 CI 构建期间自动升级版本,如果这正是您所追求的。

【讨论】:

该死的天才。喜欢它!【参考方案11】:

感谢@joelsand 为我指明了正确的方向。

我不得不稍微改变他的回答,因为当 DevOps Build 运行时,我得到了以下异常

指定的版本字符串不符合推荐格式-major.minor.build.revision

我必须在 major.minor.build 部分的末尾添加 $(BUILD_BUILDNUMBER)。为了对实际版本进行重复数据删除,我还使用了版本前缀:

<PropertyGroup>
    <VersionPrefix>1.0.3</VersionPrefix>
    <Version Condition=" '$(BUILD_BUILDNUMBER)' == '' ">$(VersionPrefix)-local</Version>
    <Version Condition=" '$(BUILD_BUILDNUMBER)' != '' ">$(VersionPrefix)-$(BUILD_BUILDNUMBER)</Version>
</PropertyGroup>

【讨论】:

我遇到了同样的问题,您的回答解决了它。谢谢。【参考方案12】:

总结以上所有内容:您可以通过以下方式恢复旧的AssemblyInfo.cs 行为:

<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<Deterministic>false</Deterministic>

但不推荐这种方法,因为关闭GenerateAssemblyInfo 会导致infra 出现问题,for example。 更有选择性的方法:

<Deterministic>false</Deterministic>
<GenerateAssemblyFileVersionAttribute>false</GenerateAssemblyFileVersionAttribute>
<GenerateAssemblyInformationalVersionAttribute>false</GenerateAssemblyInformationalVersionAttribute>
<AssemblyVersion>1.2.*</AssemblyVersion>

你不再需要AssemblyInfo.cs

【讨论】:

【参考方案13】:

根据您的 GIT 设置启用 .NET Core / .NET 任何项目的版本控制,使用 GIT 的标记/描述功能。

我一直在使用 Prebuild.targets.xml 文件,该文件位于项目的根文件夹中,并包含在 csproj 文件中,例如:

<Project Sdk="Microsoft.NET.Sdk">
  <Import Project="PreBuild.targets.xml" />
  ...
  <PropertyGroup>
    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>

使用“GenerateAssembyInfo”标签禁用自动装配信息生成。

然后 Prebuild.targets.xml 将生成一个 CommonAssemblyInfo.cs 文件,您可以在其中根据您的 GIT 版本包含您想要的版本标签

注意:我在其他地方找到了 Prebuilds.targets.xml,所以没有费心清理它。)

Prebuild.targets.xml 文件:

    <?xml version="1.0" encoding="utf-8" ?>
    <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
     
      <UsingTask
        TaskName="GetVersion"
        TaskFactory="CodeTaskFactory"
        AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
        <ParameterGroup>
          <VersionString ParameterType="System.String" Required="true" />
          <Version ParameterType="System.String" Output="true" />
          <Commit ParameterType="System.String" Output="true" />
          <VersionSuffix ParameterType="System.String" Output="true" />
        </ParameterGroup>
        <Task>
          <!--<Reference Include="" />-->
          <Using Namespace="System"/>
          <Using Namespace="System.IO"/>
          <Using Namespace="System.Text.RegularExpressions" />
          <Code Type="Fragment" Language="cs">
            <![CDATA[
              var match = Regex.Match(VersionString, @"^(?<major>\d+)\.(?<minor>\d+)(\.?(?<patch>\d+))?-(?<revision>\d+)-(?<commit>[a-z0-9-]+)$");
              int major, minor, patch, revision;
              Int32.TryParse(match.Groups["major"].Value, out major);
              Int32.TryParse(match.Groups["minor"].Value, out minor);
              Int32.TryParse(match.Groups["patch"].Value, out patch);
              Int32.TryParse(match.Groups["revision"].Value, out revision);
              _Version = new Version(major, minor, patch, revision).ToString();
              _Commit = match.Groups["commit"].Value;
            ]]>
          </Code>
        </Task>
      </UsingTask>
     
      <UsingTask
        TaskName="GitExistsInPath"
        TaskFactory="CodeTaskFactory"
        AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >
        <ParameterGroup>
          <Exists ParameterType="System.Boolean" Output="true" />
        </ParameterGroup>
        <Task>
          <!--<Reference Include="" />-->
          <Using Namespace="System"/>
          <Using Namespace="System.IO"/>
          <Using Namespace="System.Text.RegularExpressions" />
          <Code Type="Fragment" Language="cs">
            <![CDATA[
            var values = Environment.GetEnvironmentVariable("PATH");
            foreach (var path in values.Split(';')) 
                var exeFullPath = Path.Combine(path, "git.exe");
                if (File.Exists(exeFullPath)) 
                    Exists = true;
                    return true;
                
                var cmdFullPath = Path.Combine(path, "git.cmd");
                if (File.Exists(cmdFullPath)) 
                    Exists = true;
                    return true;
            
            
            Exists = false;
            ]]>
          </Code>
        </Task>
      </UsingTask>
     
      <Target Name="CreateCommonVersionInfo" BeforeTargets="CoreCompile">
        <Message Importance="high" Text="CreateCommonVersionInfo" />
     
        <GitExistsInPath>
          <Output TaskParameter="Exists" PropertyName="GitExists"/>
        </GitExistsInPath>
        <Message Importance="High" Text="git not found!" Condition="!$(GitExists)"/>
          
        <Exec Command="git describe --tags --long --dirty > $(ProjectDir)version.txt" Outputs="$(ProjectDir)version.txt" WorkingDirectory="$(SolutionDir)" IgnoreExitCode="true" Condition="$(GitExists)">
          <Output TaskParameter="ExitCode" PropertyName="ExitCode" />
        </Exec>
        <Message Importance="high" Text="Calling git failed with exit code $(ExitCode)" Condition="$(GitExists) And '$(ExitCode)'!='0'" />
        
        <ReadLinesFromFile File="$(ProjectDir)version.txt" Condition="$(GitExists) And '$(ExitCode)'=='0'">
          <Output TaskParameter="Lines" ItemName="OutputLines"/>
        </ReadLinesFromFile>
        <Message Importance="High" Text="Tags: @(OutputLines)" Condition="$(GitExists) And '$(ExitCode)'=='0'"/>

        <Delete Condition="Exists('$(ProjectDir)version.txt')" Files="$(ProjectDir)version.txt"/>
     
        <GetVersion VersionString="@(OutputLines)" Condition="$(GitExists) And '$(ExitCode)'=='0'">
          <Output TaskParameter="Version" PropertyName="VersionString"/>
          <Output TaskParameter="Commit" PropertyName="Commit"/>
        </GetVersion>
          
        <PropertyGroup>
          <VersionString Condition="'$(VersionString)'==''">0.0.0.0</VersionString>
        </PropertyGroup>
     
        <Message Importance="High" Text="Creating CommonVersionInfo.cs with version $(VersionString) $(Commit)" />
     
        <WriteLinesToFile Overwrite="true" File="$(ProjectDir)CommonAssemblyInfo.cs" Encoding="UTF-8" Lines='using System.Reflection%3B
     
    // full version: $(VersionString)-$(Commit)
     
    [assembly: AssemblyVersion("$(VersionString)")]
    [assembly: AssemblyInformationalVersion("$(VersionString)")] 
    [assembly: AssemblyFileVersion("$(VersionString)")]' />
        
      </Target>
    </Project>

编辑:如果您使用 MSBUILD 构建

 $(SolutionDir)

可能会给您带来麻烦,请使用

 $(ProjectDir)

改为

【讨论】:

不错! VersionSuffix 最终会被设置或使用吗?好像没有【参考方案14】:

我们可以为dotnet publish -- version-suffix 1.2.3使用特殊参数

对于文件版本:

<AssemblyVersion Condition=" '$(VersionSuffix)' == '' ">0.0.1.0</AssemblyVersion>
<AssemblyVersion Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)</AssemblyVersion>

对于版本:

<Version Condition=" '$(VersionSuffix)' == '' ">0.0.1</Version>
<Version Condition=" '$(VersionSuffix)' != '' ">$(VersionSuffix)</Version>

https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-publish?tabs=netcore21

--version-suffix <VERSION_SUFFIX>     Defines the value for the $(VersionSuffix) property in the project.

【讨论】:

【参考方案15】:

作为替代方案,您可以尝试使用基于当前日期的后缀的固定主编号:

  <PropertyGroup>
    <VersionPrefix>1</VersionPrefix>
    <VersionSuffix>$([System.DateTime]::UtcNow.ToString(yyMM)).$([System.DateTime]::UtcNow.ToString(ddHH)).$([System.DateTime]::UtcNow.ToString(mmss))</VersionSuffix>
    <Version Condition=" '$(VersionSuffix)' == '' ">$(VersionPrefix).0.0.1</Version>
    <Version Condition=" '$(VersionSuffix)' != '' ">$(VersionPrefix).$(VersionSuffix)</Version>
  </PropertyGroup>

【讨论】:

你有一个很好的解决方案。不过,并不是每个人都喜欢在他们的版本中使用“日期时间”。【参考方案16】:
  <PropertyGroup>
    <SecondsSinceEpoch>$([System.DateTime]::UtcNow.Subtract($([System.DateTime]::MinValue)).TotalSeconds)</SecondsSinceEpoch>
    <Revision>$([System.Math]::Truncate($([System.Decimal]::Remainder($(SecondsSinceEpoch), 100000))))</Revision>
    <Version>1.7.0.$(Revision)</Version>
    <AssemblyVersion>$(Version)</AssemblyVersion>
    <FileVersion>$(Version)</FileVersion>
  </PropertyGroup>

我对通过.csproj 设定一个体面的价值的看法。不幸的是,如果您的下一次重建间隔为100000 秒后,它将是相同的值。比 MSBump 使每个 Build 都成为 Rebuild 更好。

如果构建缓慢或自动构建,可以使用 TotalMinutesTotalDays 等。

【讨论】:

【参考方案17】:

我认为来自@joelsand 的Answer 是为在VSTS 上运行的dotnet 核心设置版本号的正确答案

要为此答案添加更多信息,

BUILD_BUILDNUMBER 实际上是一个predefined variable。

原来有两个版本的预定义变量。

一个是build.xxxx,另一个是BUILD_XXXX。

您只能在 cproj 中使用 Environment Variable Name

【讨论】:

build.xxxx 不是在前端用于在管道内引用,BUILD_XXXX 是相同的值,但在 PS 中引用变量需要稍微修改语法?【参考方案18】:

对我有用的是使用 PropertyGroup 定义 Patch 和 Revision,然后您可以将此变量用于版本(如果需要,还可以使用前缀)。版本号必须是短数字,所以我使用 YearMonth 作为 Patch,使用 MinutesOfDay 作为 Revision。将此行添加到您的 csproj 文件中:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

    <PropertyGroup>
        <VersionMajor>0</VersionMajor>
        <VersionMinor>9</VersionMinor>
        <VersionPatch Condition="'$(VersionPatch)' == ''">$([System.DateTime]::UtcNow.ToString("yyMM"))</VersionPatch>
        <VersionRevision Condition="'$(VersionRevision)' == ''">$([System.DateTime]::UtcNow.TimeOfDay.TotalMinutes.ToString("0"))</VersionRevision>
    </PropertyGroup>

    <PropertyGroup>
        <OutputType>...</OutputType>
        <TargetFramework>net5.0</TargetFramework>
        <Title>Software Title</Title>
        <Description>...</Description>
        <Authors>...</Authors>
        <Version>$(VersionMajor).$(VersionMinor).$(VersionPatch).$(VersionRevision)</Version>
    </PropertyGroup>

    ....

</Project>

它可以通过使用Directory.build.props 文件的通用方式来实现。更多信息在这里:https://docs.microsoft.com/en-us/visualstudio/msbuild/customize-your-build?view=vs-2019

只需在项目文件夹中添加一个具有此名称的文件,然后将这些行放在那里。


我在这里寻找共享项目的解决方案。在我的情况下,我解决了它在我的共享项目中添加一个Version.build.props 文件,结构如上所示,并且在任何 csproj 文件中只添加一个新行用于使用我的共享代码的项目:

<!-- Shared project import -->
<Import Project="..\Shared\Shared.projitems" Label="Shared" /> 
<!-- Version number generator -->
<Import Project="$([MSBuild]::GetPathOfFileAbove('Version.Build.props', '$(MSBuildThisFileDirectory)../Shared/'))" />

我会把这段代码留在这里,以防有人需要。

*针对 .Net5 测试的解决方案,但应该适用于早期版本。

【讨论】:

【参考方案19】:

我的 OSS 项目"RelaxVersioner" 可以在 git 存储库中全自动插入属性和常量文字,仅安装 NuGet 包,无需任何工具相关操作。

应用信息示例:

sing System.Reflection;
[assembly: AssemblyVersion("1.0.21")]
[assembly: AssemblyFileVersion("2020.12.20.33529")]
[assembly: AssemblyInformationalVersion("1.0.21-561387e2f6dc90046f56ef4c3ac501aad0d5ec0a")]
[assembly: AssemblyMetadata("Date","Sun, 20 Dec 2020 09:37:39 GMT")]
[assembly: AssemblyMetadata("Branch","master")]
[assembly: AssemblyMetadata("Tags","")]
[assembly: AssemblyMetadata("Author","Kouji Matsui <k@kekyo.net>")]
[assembly: AssemblyMetadata("Committer","Kouji Matsui <k@kekyo.net>")]
[assembly: AssemblyMetadata("Message","Merge branch 'devel'")]
[assembly: AssemblyMetadata("Build","")]
[assembly: AssemblyMetadata("Generated","Sun, 20 Dec 2020 09:37:43 GMT")]
[assembly: AssemblyMetadata("Platform","AnyCPU")]
[assembly: AssemblyMetadata("BuildOn","Unix")]
[assembly: AssemblyMetadata("SdkVersion","5.0.101")]

namespace YourApp

  internal static class ThisAssembly
  
    public const string AssemblyVersion = "1.0.21";
    public const string AssemblyFileVersion = "2020.12.20.33529";
    public const string AssemblyInformationalVersion = "1.0.21-561387e2f6dc90046f56ef4c3ac501aad0d5ec0a";
    public static class AssemblyMetadata
    
      public const string Date = "Sun, 20 Dec 2020 09:37:39 GMT";
      public const string Branch = "master";
      public const string Tags = "";
      public const string Author = "Kouji Matsui <k@kekyo.net>";
      public const string Committer = "Kouji Matsui <k@kekyo.net>";
      public const string Message = "Merge branch 'devel'";
      public const string Build = "";
      public const string Generated = "Sun, 20 Dec 2020 09:37:43 GMT";
      public const string Platform = "AnyCPU";
      public const string BuildOn = "Unix";
      public const string SdkVersion = "5.0.101";
    
  

【讨论】:

以上是关于Visual Studio 2017 (.NET Core) 中的自动版本控制的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio 2017 (.NET Core) 中的自动版本控制

远程部署/调试 Visual Studio 2017 .NET Core

从 Visual Studio 2017 中的现有 VB.NET 项目创建 dll

安装 Visual Studio 2017 Enterprise 后 LINQPad .net 方法描述不起作用

将 Visual Studio 2017 与 .Net Core SDK 3.0 一起使用

Visual Studio 2017 ASP.NET MVC 核心模板中的 Bower 替换