Автоматическое ведение версий в Visual Studio 2017 (.NET Core)
Я потратил большую часть нескольких часов, пытаясь найти способ автоматического увеличения версий в .NETCoreApp 1.1 (Visual Studio 2017).
Я знаю, что AssemblyInfo.cs создается динамически в папке: obj/Debug/netcoreapp1.1/
Он не принимает старый метод:
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.*")]
Если я устанавливаю проект в пакет, я могу установить версии там, но это, похоже, используется для сборки файла AssemblyInfo.cs.
Мой вопрос: кто-нибудь понял, как управлять версией в проектах .NET Core (или .NETStandard в этом отношении).
Ответы
Ответ 1
Я искал инкрементатор версии для приложения Net Core в VS2017, используя формат конфигурации csproj.
Я нашел проект под названием dotnet bump, который работал для формата project.json, но изо всех сил пытался найти решение для формата .csproj. Автор, который столкнулся с дотнетом, предложил решение для формата .csproj, которое называется MSBump.
Для этого существует проект на GitHub:
https://github.com/BalassaMarton/MSBump
где вы можете увидеть код и его доступны на Nuget тоже. Просто найдите MSBump на Nuget.
Ответ 2
Добавьте <Deterministic>False</Deterministic>
в раздел <PropertyGroup>
.csproj
Обходной путь, чтобы заставить AssemblyVersion * работать, описан в "Непонятное сообщение об ошибке для подстановочного знака в [AssemblyVersion] в .Net Core # 22660"
Подстановочные знаки допускаются, только если сборка не является детерминированной, что по умолчанию для .Net Core проектов. Добавление <Deterministic>False</Deterministic>
в csproj исправляет проблема.
Причины, по которым разработчики .Net Core считают полезными детерминированные сборки, описаны в http://blog.paranoidcoding.com/2016/04/05/deterministic-builds-in-roslyn.html
и компиляторы должны быть детерминированными: одни и те же входные данные генерируют одинаковые выходные данные # 372
Однако, если вы используете TeamCity, TFS или другой инструмент CI/CD, вероятно, лучше держать номер версии под контролем и увеличивать их и передавать в качестве параметра (как было предложено в других ответах), например,
msbuild /t:build /p:Version=YourVersionNumber /p:AssemblyVersion=YourVersionNumber
Номер пакета для пакетов NuGet
msbuild /t:pack /p:Version=YourVersionNumber
Ответ 3
Если вы используете 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
, если вы делаете сборку на своей локальной машине.
Ответ 4
Я придумал решение, которое работало почти так же, как старый атрибут AssemblyVersion со звездочкой (*) - AssemblyVersion ( "1.0." ) *
Значения для AssemblyVersion и AssemblyFileVersion находятся в файле MSBuild .csproj (не в AssemblyInfo.cs) как свойство FileVersion (генерирует AssemblyFileVersionAttribute) и AssemblyVersion strong > (генерирует 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();
}
}
Класс задачи наследует класс Microsoft.Build.Utilities.Task из пакета Microsoft.Build.Utilities.Core NuGet.
Требуется свойство BaseVersion (необязательно) для ввода и возвращает сгенерированную версию в свойствах вывода Version. Логика для получения номеров версий такая же, как и для автоматического автоматического управления версиями .NET(номер сборки - количество дней с 1 января 2000 года, а версия - половина секунд с полуночи).
Чтобы создать эту задачу 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>
Импортируйте вещи здесь:
- Упомянутый UseTask импортирует задачу GetCurrentBuildVersion из DC.Build.Tasks.dll. Предполагается, что этот DLL файл находится в родительском каталоге из вашего .csproj файла.
- Наша BeforeBuildActionsProject1 Цель, вызывающая задачу, должна иметь уникальное имя для каждого проекта, если у нас больше проектов в решении, которое вызывает задачу GetCurrentBuildVersion.
Преимущество этого решения заключается в том, что он работает не только от сборки на сервере сборки, но также и в ручных сборках из сборки dotnet или Visual Studio.
Ответ 5
Вы можете использовать функцию свойства 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>
Ответ 6
Эти значения теперь задаются в файле .csproj
:
<PropertyGroup>
<TargetFramework>netcoreapp1.1</TargetFramework>
<AssemblyVersion>1.0.6.0</AssemblyVersion>
<FileVersion>1.0.6.0</FileVersion>
<Version>1.0.1</Version>
</PropertyGroup>
Это те же значения, которые вы видите, если вы заходите на вкладку "Пакет" в настройках проекта. Хотя я не думаю, что вы можете использовать *
для автоматического обновления версии, что вы можете сделать, это ввести шаг последующей обработки, который заменяет версии для вас (например, как часть вашей непрерывной интеграции).
Ответ 7
dotnet build/p:AssemblyVersion=1.2.3.4
Я отвечал: "Кто-нибудь понял, как управлять версией в проектах .NET Core (или .NETStandard)". Я нашел этот вопрос, пытаясь решить эту проблему в контексте сборки CI. Я хотел установить версию сборки на номер сборки CI.
Ответ 8
Я принял вышеуказанный ответ, потому что @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)
Ответ 9
Я сделал простой инструмент CLI для установки строк .csproj.NET Core версии здесь. Вы можете комбинировать его с инструментами, такими как GitVersion, для автоматического перебора версий во время сборки CI, если это вам нужно.
Ответ 10
Чтобы включить управление версиями вашего .Net Core/.Net Независимо от проекта, основанного на настройке GIT, используя теги/описать функциональные возможности 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 version
ПРИМЕЧАНИЕ. Я нашел файл Prebuilds.targets.xml где-то в другом месте, поэтому havent потрудился его очистить.)
Файл 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>
EDIT: если вы создаете с помощью MSBUILD
$(SolutionDir)
Может возникнуть проблема, используйте
$(ProjectDir)
вместо
Ответ 11
Расширение Automatic Versions для Visual Studio теперь поддерживает автоинкремент .Net Core и .Net Standard в простом пользовательском интерфейсе.
https://marketplace.visualstudio.com/items?itemName=PrecisionInfinity.AutomaticVersions
Ответ 12
Я думаю, что этот ответ от @joelsand является правильным ответом для установки номера версии для ядра dotnet, работающего на VSTS
Чтобы добавить больше информации для этого ответа,
BUILD_BUILDNUMBER
на самом деле является предопределенной переменной.
Оказывается, есть две версии предопределенной переменной.
Один - build.xxxx, другой - BUILD_XXXX.
Вы можете использовать Environment Variable Name
только в cproj.
Ответ 13
Мы можем использовать специальный параметр для 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.