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

问题描述 投票:82回答:13

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

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

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

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

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

c# visual-studio .net-core visual-studio-2017 .net-standard
13个回答
18
投票

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

我找到了一个名为dotnet bump的项目,该项目适用于project.json格式,但很难找到.csproj格式的解决方案。作者dotnet bump实际上提出了.csproj格式的解决方案,它被称为MSBump。

在GitHub上有一个项目:

https://github.com/BalassaMarton/MSBump

在那里你可以看到Nuget上的代码及其可用的代码。只需在Nuget上搜索MSBump。


3
投票

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

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

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

使用“生成AssemblyInfo”标记禁用自动assemblyinfo生成。

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

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

Prebuild.targets.xml文件:

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

编辑:如果你正在使用MSBUILD建设

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

可能会给你带来麻烦,使用

 $(SolutionDir)

代替


3
投票

Visual Studio的自动版本扩展现在支持简单的用户界面中的.Net Core和.Net Standard自动增量。

$(ProjectDir)


1
投票

我认为来自@joelsand的这个https://marketplace.visualstudio.com/items?itemName=PrecisionInfinity.AutomaticVersions是设置在VSTS上运行的dotnet核心版本号的正确答案

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

Answer实际上是一个BUILD_BUILDNUMBER

事实证明,有两个版本的预定义变量。

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

你只能在csproj中使用predefined variable


1
投票

我们可以使用Environment Variable Name的特殊参数

对于文件版本:

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

49
投票

如果您使用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


46
投票

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

在Qazxswpoi中描述了使AssemblyVersion *工作的变通方法

只有在构建不确定时才允许使用通配符,这是.Net Core项目的默认设置。将“Confusing error message for wildcard in [AssemblyVersion] on .Net Core #22660”添加到csproj可以解决这个问题。

.Net核心开发人员认为<Deterministic>False</Deterministic>http://blog.paranoidcoding.com/2016/04/05/deterministic-builds-in-roslyn.html中描述的确定性构建有益的原因

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

Compilers should be deterministic: same inputs generate same outputs #372

包裹号码msbuild /t:build /p:Version=YourVersionNumber /p:AssemblyVersion=YourVersionNumber

for NuGet packages

13
投票

我提出的解决方案与旧的AssemblyVersion属性几乎相同,只有星号(*) - AssemblyVersion(“1.0。”)*

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

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

msbuild /t:pack /p:Version=YourVersionNumber   

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

要构建此MSBuild任务,我们将.NET Class 1.3类库项目类型与此类一起使用。

.csproj文件可能如下所示:

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

这个任务项目也可以在我的GitHub <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> 中找到

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

holajan/DC.Build.Tasks

重要的事情:

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

此解决方案的优点是它不仅可以从构建服务器上构建,还可以在dotnet构建或Visual Studio的手动构建中工作。


9
投票

现在,这些值在<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> 文件中设置:

.csproj

如果您进入项目设置的“包”选项卡,则这些值与您看到的值相同。虽然我认为您不能使用<PropertyGroup> <TargetFramework>netcoreapp1.1</TargetFramework> <AssemblyVersion>1.0.6.0</AssemblyVersion> <FileVersion>1.0.6.0</FileVersion> <Version>1.0.1</Version> </PropertyGroup> 自动增加版本,但您可以做的是引入一个后处理步骤来替换您的版本(例如,作为持续集成的一部分)。


9
投票

dotnet build /p:AssemblyVersion=1.2.3.4

我回答说:“有没有人想出如何控制.NET Core(或.NETStandard)项目中的版本。”我发现这个问题试图在CI构建的上下文中解决这个问题。我想将程序集版本设置为CI内部版本号。


7
投票

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

*

这将输出一个名为PackageName.1.0.0-pre20180807-1711.nupkg的包。

有关MSBuild属性函数的更多详细信息:<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <VersionSuffix>pre$([System.DateTime]::UtcNow.ToString(yyyyMMdd-HHmm))</VersionSuffix> </PropertyGroup>


6
投票

我做了一个简单的CLI工具来设置.csproj .NET核心版本字符串https://docs.microsoft.com/en-us/visualstudio/msbuild/property-functions。您可以将它与GitVersion等工具结合使用,以便在CI构建期间实现自动版本冲突,如果这就是您所追求的。


6
投票

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

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

here

我把它添加到csproj文件中:

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

即使将其设置为PreBuildEvent,事实上版本号在文件加载到内存之后才会更新,因此版本号在下一次构建之前不会反映出来。实际上,您可以将其更改为PostBuildEvent,它将具有相同的效果。

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

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

(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 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)
© www.soinside.com 2019 - 2024. All rights reserved.