首页 文章

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

提问于
浏览
68

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

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

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

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

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

13 回答

  • 7

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

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

    在GitHub上有一个项目:

    https://github.com/BalassaMarton/MSBump

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

  • 5

    如果你're using Visual Studio Team Services/TFS or some other CI build process to has versioning built-in, you can utilize msbuild' s 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 .

  • 6

    Add <Deterministic>False</Deterministic> 在.csproj的 <PropertyGroup> 部分内

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

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

    .Net核心开发人员认为http://blog.paranoidcoding.com/2016/04/05/deterministic-builds-in-roslyn.htmlCompilers should be deterministic: same inputs generate same outputs #372中描述的确定性构建有益的原因

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

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

    包裹编号for NuGet packages

    msbuild /t:pack /p:Version=YourVersionNumber
    
  • 38

    我提出了与旧的 AssemblyVersion 属性几乎相同的解决方案与星号() - AssemblyVersion(“1.0 . ”)

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

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

    要构建此MSBuild任务,我们在此类中使用 .NET Standard 1.3 class library 项目类型 .

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

    Importtant things here:

    • 提到 UsingTaskDC.Build.Tasks.dll 导入GetCurrentBuildVersion任务 . 它假定此dll文件位于.csproj文件的父目录中 .

    • 我们的 BeforeBuildActionsProject1 调用任务的目标必须具有每个项目的唯一名称,以防我们在解决方案中有更多项目调用GetCurrentBuildVersion任务 .

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

  • 12

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

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

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

  • 2

    dotnet build /p:AssemblyVersion=1.2.3.4

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

  • 36

    我做了一个简单的CLI工具来设置.csproj .NET Core版本字符串here . 您可以将它与GitVersion等工具结合使用,以便在CI构建期间实现自动版本冲突,如果之后的话 .

  • 0

    您可以使用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

  • 13

    我接受了上面的答案,因为@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)
    
  • 1

    To enable versioning of your .Net Core / .Net Whatever project based on your GIT setup, using the tags/describe functionality of 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)
    

    代替

  • 3

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

    https://marketplace.visualstudio.com/items?itemName=PrecisionInfinity.AutomaticVersions

  • 5

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

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

    BUILD_BUILDNUMBER 实际上是predefined variable .

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

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

    您只能在cproj中使用 Environment Variable Name .

  • 8

    我们可以使用 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.
    

相关问题