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 中没有如果您使用 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
Version
由VersionPrefix
和VersionSuffix
组合而成,或者如果VersionSuffix
为空,则仅VersionPrefix
。
<PropertyGroup>
<VersionPrefix>1.0.0</VersionPrefix>
</PropertyGroup>
【讨论】:
【参考方案5】:我想出了一个解决方案,它与带有星号 (*) 的旧 AssemblyVersion 属性几乎相同 - AssemblyVersion("1.0.*")
AssemblyVersion 和 AssemblyFileVersion 的值在 MSBuild 项目 .csproj 文件中(不在 AssemblyInfo.cs 中)作为属性 FileVersion(生成 AssemblyFileVersionAttribute)和 AssemblyVersion(生成 AssemblyVersionAttribute)。 在 MSBuild 过程中,我们使用我们的自定义 MSBuild 任务来生成版本号,然后我们用任务中的新值覆盖这些 FileVersion 和 AssemblyVersion 属性的值。
首先,我们创建自定义 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 以使用此任务并设置 FileVersion 和 AssemblyVersion 属性。 在 .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>
这里有重要的事情:
提到 UsingTask 从 DC.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 不是。另外: 有没有人知道如何在 .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 更好。
如果构建缓慢或自动构建,可以使用 TotalMinutes
、TotalDays
等。
【讨论】:
【参考方案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 方法描述不起作用