web-dev-qa-db-fra.com

Gestion automatique des versions dans Visual Studio 2017 (.NET Core)

J'ai passé quelques heures à essayer de trouver un moyen d'incrémenter automatiquement les versions dans un .NETCoreApp 1.1 (Visual Studio 2017).

Je sais que le AssemblyInfo.cs est créé dynamiquement dans le dossier: obj/Debug/netcoreapp1.1/

Il n'accepte pas l'ancienne méthode de: [Assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.*")]

Si je mets le projet en package, je peux y placer des versions, mais cela semble être utilisé pour créer le fichier AssemblyInfo.cs.

Ma question est la suivante: quelqu'un a-t-il découvert comment contrôler la version de projets .NET Core (ou .NETStandard d'ailleurs).

90
Jason H

Je recherchais un incrémenteur de version pour une application Net Core dans VS2017 en utilisant le format de configuration csproj.

J'ai trouvé un projet appelé Dotnet Bump qui fonctionnait pour le format project.json mais qui avait du mal à trouver une solution pour le format .csproj. L’écrivain the bump dotnet a en fait proposé la solution pour le format .csproj, il s’appelle MSBump.

Il y a un projet sur GitHub pour cela à:

https://github.com/BalassaMarton/MSBump

où vous pouvez voir le code et son disponible sur Nuget aussi. Il suffit de rechercher MSBump sur Nuget.

20
ravetroll

Ajoutez <Deterministic>False</Deterministic> dans une section <PropertyGroup> de .csproj

La solution permettant de faire fonctionner AssemblyVersion * est décrite dans "Message d'erreur confus pour un caractère générique dans [AssemblyVersion] sur .Net Core # 22660"

Les caractères génériques ne sont autorisés que si la construction n'est pas déterministe, ce qui est la valeur par défaut pour les projets .Net Core. L'ajout de <Deterministic>False</Deterministic> à csproj résout le problème.

Les raisons pour lesquelles les développeurs .Net Core considèrent les constructions déterministes bénéfiques décrites dans http://blog.paranoidcoding.com/2016/04/05/deterministic-builds-in-roslyn.html et Les compilateurs doivent être déterministes: les mêmes entrées génèrent les mêmes sorties # 372

Toutefois, si vous utilisez TeamCity, TFS ou un autre outil CI/CD, il est probablement préférable de garder le numéro de version contrôlé et incrémenté par eux et de passer à construire en tant que paramètre (comme suggéré dans d'autres réponses), par exemple.

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

Numéro de paquet pour les paquets NuGet

msbuild /t:pack /p:Version=YourVersionNumber   
52
Michael Freidgeim

Si vous utilisez Visual Studio Team Services/TFS ou un autre processus de génération de CI pour intégrer la gestion des versions, vous pouvez utiliser l'attribut Condition de msbuild, par exemple:

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

Ceci indiquera au compilateur .NET Core d'utiliser tout ce qui se trouve dans la variable d'environnement BUILD_BUILDNUMBER si elle est présente ou de se replier sur 0.0.1-local si vous construisez une version sur votre ordinateur local.

51
joelsand

Je suis venu avec une solution qui a fonctionné presque le même que l'ancien AssemblyVersion attribut avec étoile (*) - AssemblyVersion ("1.0. ") *

Les valeurs pour AssemblyVersion et AssemblyFileVersion sont dans le projet MSBuild . Csproj fichier (pas dans AssemblyInfo.cs ) en tant que propriété FileVersion (génère AssemblyFileVersionAttribute ) et AssemblyVersion (génère AssemblyVersionAttribute ). Dans le processus MSBuild, nous utilisons notre tâche personnalisée MSBuild pour générer des numéros de version, puis nous remplaçons les valeurs de ces FileVersion et AssemblyVersion. propriétés avec les nouvelles valeurs de la tâche.

Nous commençons donc par créer notre tâche MSBuild personnalisée 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();
    }
}

La classe de tâche hérite de Microsoft.Build.Utilities.Task classe de Microsoft.Build.Utilities.Core Paquet NuGet. Elle prend la propriété BaseVersion (facultative) en entrée et renvoie la version générée dans la propriété Version en sortie. La logique d'obtention des numéros de version est identique à celle du contrôle de version automatique .NET (le nombre de versions correspond au nombre de jours depuis le 1/1/2000 et à la révision, à une demi-seconde à compter de minuit).

Pour construire cette tâche MSBuild, nous utilisons le type de projet de bibliothèque de classes .NET Standard 1.3 avec cette classe.

Le fichier .csproj peut ressembler à ceci:

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

Ce projet de tâche est également disponible dans mon GitHub holajan/DC.Build.Tasks

Maintenant, nous avons configuré MSBuild pour utiliser cette tâche et définir les propriétés FileVersion et AssemblyVersion . Dans le fichier .csproj cela ressemble à ceci:

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

Importantes choses ici:

  • Mentionné UsingTask importe la tâche GetCurrentBuildVersion de DC.Build.Tasks.dll . Il suppose que ce fichier dll se trouve dans le répertoire parent de votre fichier .csproj.
  • Notre BeforeBuildActionsProject1 La cible qui appelle la tâche doit avoir un nom unique par projet si nous avons plusieurs projets dans la solution qui appelle la tâche GetCurrentBuildVersion.

L’avantage de cette solution est qu’elle fonctionne non seulement à partir de versions sur un serveur de génération, mais également dans des versions manuelles à partir de dotnet build ou de Visual Studio.

13
HolaJan

Ces valeurs sont maintenant définies dans le fichier .csproj:

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

Ce sont les mêmes valeurs que vous voyez si vous allez dans l'onglet Package dans les paramètres du projet. Bien que je ne pense pas que vous puissiez utiliser * pour auto-incrémenter la version, vous pouvez également introduire une étape de post-traitement qui remplace les versions pour vous (par exemple, dans le cadre de votre intégration continue).

9
Gigi

Vous pouvez utiliser une fonction de propriété MSBuild pour définir le suffixe de version en fonction de la date actuelle:

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

Ceci produira un paquet avec un nom comme: PackageName.1.0.0-pre20180807-1711.nupkg .

Plus d'informations sur les fonctions de propriété MSBuild: https://docs.Microsoft.com/en-us/visualstudio/msbuild/property-functions

9
Fabricio Godoy

dotnet build /p:AssemblyVersion=1.2.3.4

Je répondais à: "Quelqu'un a-t-il découvert comment contrôler la version dans les projets .NET Core (ou .NETStandard d'ailleurs)". J'ai trouvé cette question essayant de résoudre ce problème dans le contexte d'une construction de CI. Je voulais définir la version d'assemblage sur le numéro de build CI.

9
Chris McKenzie

J'ai accepté la réponse ci-dessus parce que @Gigi est correct (pour le moment), mais j'étais ennuyé et j'ai créé les scripts PowerShell suivants.

J'ai d'abord le script dans mon dossier de solution (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)

J'ai ajouté ceci au fichier 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>

Même si le jeu est appelé PreBuildEvent, les numéros de version ne sont pas mis à jour avant APRÈS que le fichier ait été chargé en mémoire, de sorte que le numéro de version ne sera pas reflété avant la prochaine génération. En fait, vous pourriez le changer en PostBuildEvent et cela aurait le même effet.

J'ai également créé les deux scripts suivants: (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)
6
Jason H

J'ai créé un outil CLI simple pour définir les chaînes de version .csproj .NET Core ici . Vous pouvez le combiner avec des outils tels que GitVersion pour la gestion automatique des versions lors des versions de CI, si c'est ce que vous recherchez.

6
Tagc

L'extension Automatic Versions de Visual Studio prend désormais en charge l'auto-incrémentation de .Net Core et .Net Standard dans une interface utilisateur simple.

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

3
madamission

Pour activer le contrôle de version de votre .Net Core/.Net Quel que soit le projet basé sur votre configuration GIT, utilisez les balises/la fonctionnalité de description de GIT.

J'utilise un fichier Prebuild.targets.xml situé dans le dossier racine du projet et inclus dans le fichier csproj, comme:

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

Utilisez la balise "GenerateAssembyInfo" pour désactiver la génération automatique d'informations sur l'assemblage.

Ensuite, Prebuild.targets.xml générera un fichier CommonAssemblyInfo.cs dans lequel vous pourrez inclure les balises de version souhaitées en fonction de votre version de GIT.

NOTE: J'ai trouvé le Prebuilds.targets.xml ailleurs, je n'ai donc pas pris la peine de le nettoyer.)

Le fichier 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: Si vous construisez avec MSBUILD le

 $(SolutionDir)

Pourrait vous causer des ennuis, utilisez

 $(ProjectDir)

au lieu

3
Tue Skeltved

Nous pouvons utiliser un paramètre spécial pour dotnet publish -- version-suffix 1.2.3

Pour la version du fichier:

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

Pour la version:

<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.
1
Anatoli Klamer

Je pense que ceci Answer de @joelsand est la bonne réponse pour définir le numéro de version du noyau dotnet s'exécutant sur VSTS

Pour ajouter plus d'informations pour cette réponse,

BUILD_BUILDNUMBER est en réalité un variable prédéfinie .

Il s'avère qu'il existe 2 versions de variable prédéfinie.

L'un est build.xxxx, l'autre est BUILD_XXXX.

Vous ne pouvez utiliser que Environment Variable Name dans cproj.

1
maxisam