Comment puis-je incrémenter automatiquement la version de l'assembly C # via notre plateforme CI (Hudson)?


112

Mon groupe et moi-même sommes horribles pour incrémenter les numéros de version d'assembly et nous expédions fréquemment des assemblys avec les versions 1.0.0.0. Évidemment, cela cause beaucoup de maux de tête.

Nous nous améliorons beaucoup avec nos pratiques via notre plate-forme CI et j'aimerais vraiment la configurer pour incrémenter automatiquement les valeurs dans le assemblyinfo.csfichier afin que les versions de nos assemblys soient automatiquement mises à jour avec les changements de code dans cet assemblage.

J'avais précédemment configuré (avant que nous ayons trouvé Hudson ) un moyen d'incrémenter la valeur via msbuildla ligne de commande ou la ligne de commande ( je ne me souviens pas), mais avec Hudson, cela mettra à jour le référentiel SVN et déclenchera UNE AUTRE build. Cela entraînerait une boucle infinie lente car Hudson interroge SVN toutes les heures.

Avoir Hudson incrémenter le numéro de version est-il une mauvaise idée? Quelle serait une autre façon de le faire?

Idéalement, mes critères pour une solution seraient ceux qui:

  • Incrémente le numéro de build assemblyinfo.csavant une build
  • Incrémente uniquement le numéro de build dans les assemblys qui ont changé. Cela peut ne pas être possible car Hudson efface le dossier du projet à chaque fois qu'il fait une compilation
  • Valide l'assemblyinfo.cs modifié dans le référentiel de code (actuellement VisualSVN )
  • Ne provoque pas Hudson pour déclencher une nouvelle version la prochaine fois qu'il recherche les modifications

En travaillant cela dans ma tête, je pourrais facilement trouver une solution à la plupart de cela via des fichiers de commandes / commandes, mais toutes mes idées amèneraient Hudson à déclencher une nouvelle construction la prochaine fois qu'il analyse. Je ne cherche pas quelqu'un pour tout faire à ma place, juste me diriger dans la bonne direction, peut-être une technique pour amener Hudson à ignorer certains commits SVN, etc.

Tout ce que j'ai trouvé jusqu'à présent n'est qu'un article expliquant comment obtenir le numéro de version incrémenté automatiquement, rien ne prend en compte une plate-forme CI qui pourrait être transformée en une boucle infinie.

Réponses:


63

Une alternative simple consiste à laisser l'environnement C # incrémenter la version de l'assembly pour vous en définissant l'attribut version sur major.minor.*(comme décrit dans le modèle de fichier AssemblyInfo.)

Vous recherchez peut-être une solution plus complète, cependant.

EDIT (Réponse à la question dans un commentaire):

De AssemblyInfo.cs:

// Version information for an assembly consists of the following four values:
//
//      Major Version
//      Minor Version 
//      Build Number
//      Revision
//
// You can specify all the values or you can default the Build and Revision Numbers 
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]

Je n'ai jamais rencontré cela auparavant, pourriez-vous entrer un peu plus en détail sur ce que cela fait. Fonctionne-t-il uniquement dans un seul IDE ou fonctionne-t-il avec toute une équipe de développeurs avec une plate-forme CI?
Allen Rice le

ahhh j'ai vu ça avant, cela peut être une solution acceptable mais le # built n'est pas stocké dans subversion etc. Je devrai faire des recherches supplémentaires sur le fonctionnement de ce mécanisme, merci! Vous ne savez pas comment cela détermine ce qu'il faut mettre en valeur, n'est-ce pas?
Allen Rice le

1
Voir ma réponse ci-dessous pour la réponse à votre question. Les valeurs sont déterminées en fonction du temps de construction.
Kyle Trauberman le

Wow, je pense que cela fonctionnera. Je ne sais pas comment nous avons négligé une solution aussi simple
Allen Rice

J'espère que c'est le cas, heureux de pouvoir aider. Pourquoi faire quelque chose à la dure alors que la méthode la plus simple et la plus rapide est également la bonne? :)
Greg D

65

Voici ce que j'ai fait, pour tamponner l'attribut AssemblyFileVersion.

Suppression de AssemblyFileVersion de AssemblyInfo.cs

Ajoutez un nouveau fichier vide appelé AssemblyFileInfo.cs au projet.

Installez l' ensemble d' outils des tâches de communauté MSBuild sur la machine de génération hudson ou en tant que dépendance NuGet dans votre projet.

Modifiez le fichier de projet (csproj), c'est juste un fichier msbuild, et ajoutez ce qui suit.

Quelque part, il y aura une <PropertyGroup>indication de la version. Changer cela pour qu'il se lit par exemple

 <Major>1</Major>
 <Minor>0</Minor>
 <!--Hudson sets BUILD_NUMBER and SVN_REVISION -->
 <Build>$(BUILD_NUMBER)</Build>
 <Revision>$(SVN_REVISION)</Revision>

Hudson fournit les variables d'environnement que vous voyez là lorsque le projet est construit sur hudson (en supposant qu'il soit extrait de subversion).

Au bas du fichier de projet, ajoutez

 <Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" Condition="Exists('$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets')" />
  <Target Name="BeforeBuild" Condition="Exists('$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets')">
    <Message Text="Version: $(Major).$(Minor).$(Build).$(Revision)" />
    <AssemblyInfo CodeLanguage="CS" OutputFile="AssemblyFileInfo.cs" AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)" AssemblyConfiguration="$(Configuration)" Condition="$(Revision) != '' " />
  </Target>

Cela utilise MSBuildCommunityTasks pour générer AssemblyFileVersion.cs afin d'inclure un attribut AssemblyFileVersion avant la génération du projet. Vous pouvez le faire pour n'importe lequel / tous les attributs de version si vous le souhaitez.

Le résultat est que, chaque fois que vous émettez une build hudson, l'assembly résultant obtient une AssemblyFileVersion de 1.0.HUDSON_BUILD_NR.SVN_REVISION, par exemple 1.0.6.2632, ce qui signifie le 6ème numéro de build dans hudson, issu de la révision subversion 2632.


1
Donc, juste pour mettre à jour ceci: la méthode fonctionne pour C #. Je l'utilise depuis un certain temps. Mais les assemblys C ++ (c'est-à-dire C ++ / CLI) sont toujours un problème. Pour autant que je sache, la tâche AssemblyInfo ne produit pas de C ++ valide. De plus, je pense que cette méthode a un léger inconvénient en ce qu'elle est un peu opaque pour que les autres développeurs comprennent ce qui se passe. Dommage que vous ne puissiez pas pousser les numéros de version directement dans MSBuild en tant que propriété ...
CJBrew

@CJBrew Vous pouvez simplement créer un petit fichier .bat qui produit le code C ++ pour AssemblyInfo et demander à msbuild de lancer ce scipt. Je ne suis pas sûr de ce que vous entendez en le poussant en tant que propriété, vous pouvez certainement insérer les chaînes de version dans n'importe quelle propriété que vous aimez - vous n'avez pas besoin d'utiliser le major / minor / build / revision que j'ai utilisé ici.
nos

Y a-t-il quelque chose à gagner en utilisant cette route par rapport à la simple mise en commentaire de AssemblyFileVersion et à la laisser se mettre automatiquement en correspondance avec [assembly: AssemblyVersion ("1.0. *")]?
cchamberlain

@ColeChamberlain Cela s'incrémentera automatiquement si vous le construisez à partir de Visual Studio sur votre propre PC, pas à partir de Hudson - et il n'y a aucun rapport avec le numéro de version et une version particulière et la révision du code source.
nos

42

Voici une solution élégante qui nécessite un peu de travail en amont lors de l'ajout d'un nouveau projet mais qui gère le processus très facilement.

L'idée est que chaque projet est lié à un fichier Solution qui contient uniquement les informations de version d'assembly. Ainsi, votre processus de construction n'a qu'à mettre à jour un seul fichier et toutes les versions d'assembly sont extraites d'un fichier lors de la compilation.

Pas:

  1. Ajoutez une classe à votre fichier de solution * .cs fichier, j'ai nommé min SharedAssemblyProperties.cs
  2. Supprimez toutes les informations cs de ce nouveau fichier
  3. Coupez les informations d'assemblage à partir d'un fichier AssemblyInfo: [assembly: AssemblyVersion ("1.0.0.0")] [assembly: AssemblyFileVersion ("1.0.0.0")]
  4. Ajoutez l'instruction "using System.Reflection;" dans le fichier, puis collez les données dans votre nouveau fichier cs (ex SharedAssemblyProperties.cs)
  5. Ajoutez un élément existant à votre projet (attendez ... lisez la suite avant d'ajouter le fichier)
  6. Sélectionnez le fichier et avant de cliquer sur Ajouter, cliquez sur la liste déroulante à côté du bouton Ajouter et sélectionnez "Ajouter en tant que lien".
  7. Répétez les étapes 5 et 6 pour tous les projets existants et nouveaux dans la solution

Lorsque vous ajoutez le fichier en tant que lien, il stocke les données dans le fichier projet et, lors de la compilation, extrait les informations de version d'assembly de ce fichier.

Dans votre contrôle de code source, vous ajoutez un fichier bat ou un fichier script qui incrémente simplement le fichier SharedAssemblyProperties.cs et tous vos projets mettront à jour leurs informations d'assemblage à partir de ce fichier.


merci, Mark. Désolé pour le lien mort, il s'avère que le serveur de communauté n'est pas si facile à déplacer. Je devrais chercher de l'aide sur ce sujet ...
sondlerd

11

Hudson peut être configuré pour ignorer les modifications apportées à certains chemins et fichiers afin de ne pas demander une nouvelle construction.

Sur la page de configuration du travail, sous Gestion du code source , cliquez sur le bouton Avancé . Dans la zone Régions exclues , vous entrez une ou plusieurs expressions régulières pour correspondre aux exclusions.

Par exemple, pour ignorer les modifications apportées au fichier version.properties , vous pouvez utiliser:

/MyProject/trunk/version.properties

Cela fonctionnera pour les langages autres que C # et vous permet de stocker vos informations de version dans subversion.


1
Hudson peut également ignorer les validations de certains utilisateurs ou ne pas déclencher de build en fonction du message de validation. De cette façon, vous pouvez ignorer tous les commits d'Hudson.
Peter Schuetze

9

.NET fait cela pour vous. Dans votre fichier AssemblyInfo.cs, définissez votre version d'assembly sur major.minor. * (Par exemple: 1.0. *).

Lorsque vous créez votre projet, la version est générée automatiquement.

Les numéros de version et de révision sont générés en fonction de la date, en utilisant l'époque Unix, je crois. La construction est basée sur le jour en cours et la révision est basée sur le nombre de secondes depuis minuit.


21
<ring, ring> "bonjour, assistance produit, comment puis-je vous aider?" <customer> "J'ai une erreur" <support> "ok, quelle version utilisez-vous?" <customer> "version un point deux révision huit cinq deux cinq trois sept quatre build sept quatre six trois cinq deux neuf ..." <support> "tenez bon, en tapant simplement ça ... hmmm ... veuillez répéter la version chiffres, nous ne semblons pas avoir cette version et cette révision listées ... "- GRRR!
Jimbo

haha beau commentaire. Je ne suis pas non plus fan de ce système incrémentiel: p
Joshua Hayes

3
L'auto-incrémentation en studio visuel est vraiment nul.
Kugel

8
@Jimbo: Bien que nous soyons tous d'accord pour dire que votre commentaire était drôle, dans la pratique, cela n'a pas d'importance. Lorsque vous parlez de votre installation VS, disposez-vous de Visual Studio 2008 SP1 ou VS2008 9.0.30729.1 SP? L'utilisation des numéros de version d'auto-incrémentation est un schéma très courant et peut être très facilement «corrigé» en incrémentant les numéros de version majeure / mineure lorsqu'une version de version sort.
Marek

le plus haut que nous ayons choisi avec un numéro de build est 678 avant de revenir à 0 pour une augmentation de révision mineure (bien sûr, cruisecontrol, il semblait plus facile à réinitialiser que hudson comme dans cruisecontrol, vous venez de le sauvegarder et de le sauvegarder à 0 dans le projet , mais tout le reste à Hudson est meilleur)
Dean Hiller

8

Je n'ai jamais vu cette fonctionnalité 1.0. * Fonctionner dans VS2005 ou VS2008. Y a-t-il quelque chose à faire pour que VS incrémente les valeurs?

Si AssemblyInfo.cs est codé en dur avec 1.0. *, Alors où sont stockées la vraie version / révision?

Après avoir mis 1.0. * Dans AssemblyInfo, nous ne pouvons pas utiliser l'instruction suivante car ProductVersion a maintenant une valeur non valide - il utilise 1.0. * Et non la valeur attribuée par VS:

Version version = new Version(Application.ProductVersion);

Soupir - cela semble être l'une de ces choses sur lesquelles tout le monde s'interroge, mais il n'y a jamais de réponse solide. Il y a des années, j'ai vu des solutions pour générer un numéro de révision et l'enregistrer dans AssemblyInfo dans le cadre d'un processus de post-construction. J'espérais que ce genre de danse ne serait pas nécessaire pour VS2008. Peut-être VS2010?


10
Vous devez supprimer AssemblyFileVersion. À part cela, cela fonctionne génial pour nous, la réponse acceptée qui est.
Allen Rice

1
Oui, la suppression de AssemblyFileVersion permet à la version de se mettre à jour, et plus d'erreurs avec Version. Agréable. Remarque: deux opérations de construction n'incrémentent la révision qu'une seule fois, mais si vous reconstruisez, la révision est mise à jour. Comme l'a dit ktrauberman, cela ressemble à build.revision = date.time, ce qui explique pourquoi les données ne sont stockées nulle part sauf dans l'assembly. Je dois maintenant obtenir une configuration MSI standard pour générer un nouveau ProductCode lorsque le projet de sortie principal est mis à jour. Les configurations ne permettent pas la révision, seulement la construction. Je souhaite installer sur une installation existante pour effectuer une mise à jour. Besoin de recherches.
TonyG

5

Je suppose que l'on peut également le faire avec un modèle de texte dans lequel vous créez les attributs d'assemblage en question à la volée à partir de l'environnement comme le fait AssemblyVersion.tt ci-dessous.

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<#
var build = Environment.GetEnvironmentVariable("BUILD_NUMBER");
build = build == null ? "0" : int.Parse(build).ToString();
var revision = Environment.GetEnvironmentVariable("SVN_REVISION");
revision = revision == null ? "0" : int.Parse(revision).ToString();    
#>
using System.Reflection;
[assembly: AssemblyVersion("1.0.<#=build#>.<#=revision#>")]
[assembly: AssemblyFileVersion("1.0.<#=build#>.<#=revision#>")]

3

Dans le prolongement de la réponse de MikeS, je voulais ajouter que VS + Visual Studio Visualization and Modeling SDK doit être installé pour que cela fonctionne, et que vous devez également modifier le fichier de projet. Doit également être mentionné, j'utilise Jenkins comme serveur de construction fonctionnant sur une boîte de serveur Windows 2008 R2 avec module de version, où j'obtiens le BUILD_NUMBER.

Mon fichier de modèle de texte version.tt ressemble à ceci

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ output extension=".cs" #>
<#
var build = Environment.GetEnvironmentVariable("BUILD_NUMBER");
build = build == null ? "0" : int.Parse(build).ToString();
var revision = Environment.GetEnvironmentVariable("_BuildVersion");
revision = revision == null ? "5.0.0.0" : revision;    
#>
using System.Reflection;
[assembly: AssemblyVersion("<#=revision#>")]
[assembly: AssemblyFileVersion("<#=revision#>")]

J'ai ce qui suit dans les groupes de propriétés

<PropertyGroup>
    <TransformOnBuild>true</TransformOnBuild>
    <OverwriteReadOnlyOutputFiles>true</OverwriteReadOnlyOutputFiles>
    <TransformOutOfDateOnly>false</TransformOutOfDateOnly>
</PropertyGroup>

après l'importation de Microsoft.CSharp.targets, j'ai ceci (en fonction de l'endroit où vous installez VS

<Import Project="C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\TextTemplating\v10.0\Microsoft.TextTemplating.targets" />

Sur mon serveur de construction, j'ai ensuite le script suivant pour exécuter la transformation de texte avant la construction réelle, pour obtenir le dernier numéro de jeu de modifications sur TFS

set _Path="C:\Build_Source\foo"

pushd %_Path% 
"%ProgramFiles(x86)%\Microsoft Visual Studio 10.0\Common7\IDE\tf.exe" history . /r /noprompt /stopafter:1 /Version:W > bar
FOR /f "tokens=1" %%foo in ('findstr /R "^[0-9][0-9]*" bar') do set _BuildVersion=5.0.%BUILD_NUMBER%.%%foo
del bar
popd

echo %BUILD_NUMBER%
echo %_BuildVersion%
cd C:\Program Files (x86)\Jenkins\jobs\MyJob\workspace\MyProject
MSBuild MyProject.csproj /t:TransformAll 
...
<rest of bld script>

De cette façon, je peux garder une trace des builds ET des ensembles de modifications, donc si je n'ai rien vérifié depuis la dernière version, le dernier chiffre ne devrait pas changer, mais j'aurais peut-être apporté des modifications au processus de construction, d'où la nécessité de l'avant-dernier numéro . Bien sûr, si vous effectuez plusieurs vérifications avant une compilation, vous n'obtiendrez que le dernier changement reflété dans la version. Je suppose que vous pourriez concaténer cela est nécessaire.

Je suis sûr que vous pouvez faire quelque chose de plus sophistiqué et appeler TFS directement à partir du modèle tt, mais cela fonctionne pour moi.

Je peux ensuite obtenir ma version au moment de l'exécution comme ceci

Assembly assembly = Assembly.GetExecutingAssembly();
FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location);
return fvi.FileVersion;

1

Ma solution ne nécessite pas l'ajout d'outils externes ou de langages de script - elle est à peu près garantie de fonctionner sur votre machine de construction. Je résous ce problème en plusieurs parties. Tout d'abord, j'ai créé un fichier BUILD.BAT qui convertit le paramètre Jenkins BUILD_NUMBER en une variable d'environnement. J'utilise la fonction "Execute Windows batch command" de Jenkins pour exécuter le fichier batch de construction en entrant les informations suivantes pour la construction Jenkins:

     ./build.bat --build_id %BUILD_ID% -build_number %BUILD_NUMBER%

Dans l'environnement de construction, j'ai un fichier build.bat qui commence comme suit:

     rem build.bat
     set BUILD_ID=Unknown
     set BUILD_NUMBER=0
     :parse_command_line
     IF NOT "%1"=="" (
         IF "%1"=="-build_id" (
             SET BUILD_ID=%2
             SHIFT
             )
         IF "%1"=="-build_number" (
             SET BUILD_NUMBER=%2
             SHIFT
         )
         SHIFT
         GOTO :parse_command_line
     )
     REM your build continues with the environmental variables set
     MSBUILD.EXE YourProject.sln

Une fois que j'ai fait cela, j'ai cliqué avec le bouton droit sur le projet à créer dans le volet Explorateur de solutions de Visual Studio et sélectionné Propriétés, sélectionnez Événements de génération et entré les informations suivantes en tant que ligne de commande d'événement de pré-construction, qui crée automatiquement un fichier .cs contenant des informations sur le numéro de build basées sur les paramètres actuels de la variable d'environnement:

     set VERSION_FILE=$(ProjectDir)\Properties\VersionInfo.cs
     if !%BUILD_NUMBER%==! goto no_buildnumber_set
     goto buildnumber_set
     :no_buildnumber_set
     set BUILD_NUMBER=0
     :buildnumber_set
     if not exist %VERSION_FILE% goto no_version_file
     del /q %VERSION_FILE%
     :no_version_file
     echo using System.Reflection; >> %VERSION_FILE%
     echo using System.Runtime.CompilerServices; >> %VERSION_FILE%
     echo using System.Runtime.InteropServices; >> %VERSION_FILE%
     echo [assembly: AssemblyVersion("0.0.%BUILD_NUMBER%.1")] >> %VERSION_FILE%
     echo [assembly: AssemblyFileVersion("0.0.%BUILD_NUMBER%.1")] >> %VERSION_FILE%

Vous devrez peut-être vous adapter à votre goût de construction. Je construis le projet manuellement une fois pour générer un fichier Version.cs initial dans le répertoire Properties du projet principal. Enfin, j'inclus manuellement le fichier Version.cs dans la solution Visual Studio en le faisant glisser dans le volet Explorateur de solutions, sous l'onglet Propriétés de ce projet. Dans les futures générations, Visual Studio lit ensuite ce fichier .cs au moment de la génération Jenkins et en obtient les informations de numéro de build correctes.


1

Nous avons donc un projet avec une solution qui contient plusieurs projets qui ont des assemblys avec des numéros de version différents.

Après avoir étudié plusieurs des méthodes ci-dessus, je viens de mettre en œuvre une étape de construction pour exécuter un script Powershell qui effectue une recherche et un remplacement sur le fichier AssemblyInfo.cs. J'utilise toujours le numéro de version 1.0. * Dans le contrôle de code source, et Jenkins met simplement à jour manuellement le numéro de version avant que msbuild ne s'exécute.

dir **/Properties/AssemblyInfo.cs | %{ (cat $_) | %{$_ -replace '^(\s*)\[assembly: AssemblyVersion\("(.*)\.\*"\)', "`$1[assembly: AssemblyVersion(`"`$2.$build`")"} | Out-File $_ -Encoding "UTF8" }
dir **/Properties/AssemblyInfo.cs | %{ (cat $_) | %{$_ -replace '^(\s*)\[assembly: AssemblyFileVersion\("(.*)\.\*"\)', "`$1[assembly: AssemblyFileVersion(`"`$2.$build`")"} | Out-File $_ -Encoding "UTF8" }

J'ai ajouté l'option -Encoding "UTF8" car git a commencé à traiter le fichier .cs comme des fichiers binaires si je ne le faisais pas. Certes, cela n'avait pas d'importance, puisque je n'ai jamais réellement validé le résultat; il est venu juste pendant que je testais.

Notre environnement CI a déjà une fonctionnalité pour associer la construction Jenkins au commit git particulier (merci le plugin Stash!), Donc je ne crains pas qu'il n'y ait pas de commit git avec le numéro de version qui lui est attaché.


0

C'est un mécanisme plus simple. Cela implique simplement l'ajout d'une étape de génération de tâche de commande Windows Batch avant l'étape MSBuild et l'utilisation d'un simple programme de recherche et de remplacement (FART).

L'étape du lot

fart --svn -r AssemblyInfo.cs "[assembly: AssemblyVersion(\"1.0.0.0\")]" "[assembly: AssemblyVersion(\"1.0.%BUILD_NUMBER%.%SVN_REVISION%\")]"
if %ERRORLEVEL%==0 exit /b 1
fart --svn -r AssemblyInfo.cs "[assembly: AssemblyFileVersion(\"1.0.0.0\")]" "[assembly: AssemblyFileVersion(\"1.0.%BUILD_NUMBER%.%SVN_REVISION%\")]"
if %ERRORLEVEL%==0 exit /b 1
exit /b 0

Si vous utilisez un contrôle de code source autre que svn, modifiez l'option --svn pour celle qui convient à votre environnement scm.

Télécharger Fart


0

J'ai décidé d'utiliser quelques méthodes en utilisant un script Powershell pré-build ( https://gist.github.com/bradjolicoeur/e77c508089aea6614af3 ) pour incrémenter chaque build réussie, puis dans Global.asax, je vais quelque chose comme ceci:

  // We are using debug configuration, so increment our builds.
  if (System.Diagnostics.Debugger.IsAttached)
  {
      string version = System.Reflection.Assembly.GetExecutingAssembly()
                                                       .GetName()
                                                       .Version
                                                       .ToString();

      var psi = new ProcessStartInfo(@"svn", "commit -m \"Version: " + version + "\n \"");
      psi.WorkingDirectory = @"C:\CI\Projects\myproject";
      Process.Start(psi); 
  }

Je pense toujours que l'ensemble du processus est trop compliqué et je vais examiner une méthode plus efficace pour obtenir le même résultat. Je le voulais principalement pour passer la version dans SVN, puis dans Jenkin sans trop d'outils supplémentaires.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.