[TFS2012] Comment bien gérer l’incrémentation des versions de ses applications de manière automatique avec TFS 2012.

Tous les informaticiens le savent, la gestion des numéros de version est ce qu’il y a de plus cruciale durant la vie d’une application. On incrémente régulièrement les numéros de version afin d’indiquer les changements effectués sur la nouvelle application.

Lorsque l’on développe en équipe, les sources de l’application sont le plus souvent archivées dans un gestionnaire de sources à travers différentes versions (au fil des modifications apportées au développement de l’application). Ces versions sont le plus souvent identifiées dans le gestionnaire de sources via des labels qui sont plus ou moins créés et gérés automatiquement. D’expérience la gestion des labels dans un gestionnaire de sources est souvent folklorique…  Les labels doivent être posés et nommés correctement… Quand il y en a trop, on ne s’y retrouve plus… Lorsque l’on oublie de poser un label, c’est la panique car il est impossible de trouver la version de son application !

Pour pallier à ce problème, on essaye d’automatiser l’incrémentation des versions et la pose des labels. Cet article a pour but de vous montrer les différentes stratégies de changement version automatique que j’ai pu rencontrer durant mes différentes interventions et pour chacune leurs inconvénients. Dans la deuxième partie de l’article, je vous présenterai ma stratégie de gestion de versions que je propose à mes différents clients, qui est très simple à mettre en oeuvre si l’on s’appuie sur un logiciel comme TFS.

Petite introduction (rappel)

Dans l’environnement .NET, les assemblys doivent disposer d’un numéro de version qui comporte 4 composantes et devant suivre la convention suivante (extrait du MSDN de Microsoft) :

  • Majeur : Les assemblys portant le même nom, mais ayant des versions principales différentes, ne sont pas interchangeables. Un numéro de version supérieur peut indiquer une réécriture majeure d’un produit lorsque la compatibilité descendante ne peut pas être assumée.
  • Mineur : Si le nom et le numéro de version principal de deux assemblys sont identiques, mais que le numéro de version secondaire est différent, cela indique une amélioration significative avec intention de conserver une compatibilité descendante. Ce numéro de version secondaire supérieur peut indiquer une version ponctuelle d’un produit ou une nouvelle version d’un produit dont la compatibilité descendante est totale.
  • Build : une différence dans le numéro de build représente une recompilation de la même source. Différents numéros de build peuvent être utilisés lorsque le processeur, la plateforme ou le compilateur change.
  • Révision : Les assemblys portant le même nom et les mêmes numéros de versions principale et secondaire, mais ayant des révisions différentes sont destinés à être totalement interchangeables. Un numéro de révision supérieur peut être utilisé dans un build qui résout une faille de sécurité dans une version précédente d’un assembly.

Cette convention est plus ou moins respectée par les différentes équipes des différents produits Microsoft. Bien évidemment, comme toute convention, il est possible de ne pas la respecter et de faire sa propre convention, mais il est indispensable que votre propre convention soit appliquée durant toute la vie de l’application.

Les numéros de versions des assemblys sont spécifiés via les attributs d’assembly, AssemblyVersion et AssemblyFileVersion qui se trouvent le plus souvent dans un fichier intitulé AssemblyInfo.cs dans le répertoire spécial « Propriétés » des projets de Visual Studio.

Les différentes stratégies pour gérer de manière automatique les versions de ses applications

Incrémentation de la composante « Révision » à chaque recompilation

C’est une stratégie qui est simple à mettre en oeuvre, elle nécessite juste de déclencher un processus à la compilation qui de chargera d’incrémenter la composante « Révision » à chaque recompilation. Pour mettre en place cette stratégie il suffit d’utiliser la tâche MSBuild AssemblyInfoTask. Vous pouvez consulter un article consacré à AssemblyInfoTask que j’ai rédigé il y a 4 ans sur cet utilitaire. Les autres composantes du numéro de version sont quand à elles gérées manuellement par le développeur.

L’inconvénient majeur de cette stratégie est qu’elle ne peut pas être utilisée dans une équipe, car l’incrémentation de la « Révision » n’est pas globale au niveau de l’équipe mais sur chaque poste des développeurs. L’autre inconvénient (plus-tôt esthétique) est le fait que si le développeur compile trop souvent son application, le numéro de version risque de s’incrémenter relativement vite…

Incrémentation de la composante « Révision » à chaque build

Le principe de cette stratégie est le même que celle évoquée précédemment, mais au lieu que l’incrémentation se fasse à chaque compilation, elle est réalisée par un moteur de build (comme celui de TFS par exemple). Au niveau du build la mise en oeuvre de cette stratégie nécessite de modifier le fichier AssemblyInfo.cs et de l’archiver (en prenant soin de ne pas re-déclencher à nouveau un build d’intégration continue). Je vous invite à consulter l’article de Vincent LABATUT qui explique la mise en place de cette stratégie. Avec cette stratégie il ne faut pas oublier d’appliquer un label afin de retrouver très facilement les différentes versions archivées dans TFS.

L’inconvénient de cette solution est qu’elle réalise des changeset « parasites » qui servent juste à mettre à jour le fichier AssemblyInfo.cs. De plus, si les labels sont mal gérés ou détruit, il sera difficile de retrouver ses différentes versions archivées.

Ma stratégie pour gérer l’incrémentation des versions

Après avoir vu très rapidement différentes stratégies d’incrémentation des versions, je vais vous exposer la mienne que je vous recommande d’utiliser si vous voulez gérer très simplement les numéros de version dans vos applications.

L’idée de cette stratégie est de tout d’abord considérer que la gestion des composantes « Majeur », « Mineur » et « Build » des numéros de version sont à la charge du chef du produit ou de l’équipe marketing. En effet, pour moi ces composantes représentent la version « commerciale » du produit. C’est ce que l’utilisateur verra dans sa belle petite fenêtre « A propos de… » de son application.

La composante « Revision » quant à elle, sera associée au changeset TFS des sources utilisées pour compiler l’application. Pourquoi choisir le changeset de TFS ? Et bien si vous remarquez bien, le changeset est un numéro unique automatiquement incrémenté par TFS (au niveau de la collection de projet) et qui est utilisé par le gestionnaire de sources pour gérer les différentes versions des fichiers.

L’avantage de cette solution est qu’il est maintenant beaucoup plus facile de retrouver ses sources en ayant le numéro de version de l’application. Par exemple, si je suis en train de reproduire un problème signalé par un utilisateur dans l’application ayant comme version « 2.10.7.25420 », il me suffira alors de récupérer tout simplement les sources du changeset « 25420 ».

Un autre avantage de cette stratégie est qu’il n’est plus nécessaire de gérer des labels. En effet, vous pouvez à n’importe quel instant retrouver le changeset d’une version de votre application ! Au passage, vous verrez qu’en appliquant cette stratégie, les développeurs de votre équipe utiliseront toujours le numéro du changeset pour pouvoir communiquer entre eux et non la version complète. Par exemple, il est plus simple et plus compréhensible de recevoir dans le sujet d’un mail : « Problème sur la version 25420 ». On comprend très vite qu’il suffit de récupérer le changeset 25420 présent sur le serveur TFS.

Maintenant une question que l’on devrait se poser est : comment allons-nous imposer le numéro du changeset dans le fichier AssemblyInfo.cs ? Et bien dans un premier temps, on pourrait utiliser le mécanisme que nous avons vu dans la section précédente « Incrémentation de la composante Revision à chaque build ». Il suffit à chaque déclenchement de build, de récupérer le numéro de changeset, de modifier la version dans le fichier AssemblyInfo.cs et de compiler l’application.

En revanche, par rapport à la stratégie précédente, je n’archiverai pas le fichier AssemblyInfo.cs à chaque déclenchement de build… Pourquoi ? La première raison est que cela produit des changeset « parasites » et double le nombre de changeset dans la collection de projets TFS. L’autre raison est que le numéro du deuxième changeset produit pour l’archivage de l’AssemblyInfo.cs n’est pas en phase avec le numéro de version. Par exemple, imaginons qu’un développeur réalise un archivage (check-in) sur TFS qui provoque la création du changeset 2610. La modification du fichier AssemblyInfo.cs et son archivage sera donc réalisé dans le changeset suivant 2611 (voir plus si d’autres développeurs ont réalisé d’autres checkin dans la collection de projets). Ma solution est donc ne pas archiver les modifications apportées par le fichier AssemblyInfo.

Au niveau de la gestion des autres composantes de la version (« Majeur », « Mineur » et « Build »), cela reste à la charge du chef de produit qui se chargera de changer manuellement le numéro de version dans le fichier AssemblyInfo.cs.

Nous allons voir dans les sections suivantes comment mettre en oeuvre cette stratégie dans les builds de TFS.

Mise en place du fichier AssemblyInfo.cs.

Lors du développement d’une application, il y a de grande chance que l’on dispose d’une solution constituée de plusieurs projets. Ces projets une fois compilés vont donc générer des assemblys dont il faudra donner un numéro de version. Je recommande le plus souvent à mes clients, que tous les assemblys d’une application disposent du même numéro de version afin d’éviter des surprises lors de la mise en production. Pour simplifier le partage du numéro de version, il suffit de créer un fichier que l’on appellera AssemblyInfo.Common.cs qui contiendra les attributs « AssemblyVersion » (et AssemblyFileVersion si nécessaire) et qui sera partagé par tous les projets de la solution.

Voici un exemple du fichier AssemblyInfo.Common.cs :

using System.Reflection;

[assembly: AssemblyVersion("1.0.0.0")]

Je conseille de mettre ce fichier à la racine de vos projets (au même niveau que le fichier solution .sln par exemple). Ensuite nous ajoutons un lien vers ce fichier dans chaque projets de notre solution :

Aperçu de l'explorateur de solution avec une application exemple qui contient le fichier AssemblyInfo.Common.cs

Maintenant que nous disposons d’un fichier qui centralise le numéro de version de notre application, il suffira au niveau des builds TFS de changer celui-ci.

Personnalisation du workflow de build de TFS 2012.

Afin de modifier le numéro de version de notre fichier Assembly.Common.cs, il va falloir modifier le workflow utilisé par nos builds TFS. Pour savoir comment personnaliser les workflows de Team Build, je vous invite à consulter l’ensemble des articles de Ewald Hofman consacré à ce sujet.

Avant d’aller plus loin, faisons un inventaire de ce que nous avons besoin globalement :

  • Pouvoir spécifier l’emplacement du fichier à mettre à jour => On utilisera un argument du workflow de build.
  • Avoir la possibilité de spécifier le numéro de version dans le format du numéro de build => On ajoutera un paramètre $(Version) au paramètre « Build Format Number ».
  • Changer la version de notre fichier AssemblyInfo.Common.cs => On utilisera l’activité AssemblyInfo faisant parti du projet open source Community TFS Build Extensions disponible sur CodePlex.

Activité AssemblyInfo du projet open source TFS Build Extensions

Il suffit maintenant de modifier le workflow de build en s’inspirant de celui de Microsoft et d’éditer les éléments qui vont suivre.

Au niveau du workflow, il faut ajouter les références aux namespace suivant : Microsoft.TeamFoundation.VersionControl.Client

Au niveau de la séquence « Update Build Number for Triggered Build » il faut réaliser les opérations suivantes :

  • Récupérer le fichier AssemblyInfo.Common.cs dans le workspace en cours
  • Récupérer la version stockée dans le fichier AssemblyInfo.Common.cs précédemment récupéré.
  • Générer le nouveau numéro de version
  • Mettre à jour le nom du build en remplaçant la variable $(Version) (si existante) par la version générée précédemment.

Séquence "Update Build Number for Triggered Builds" modifiée

La séquence « Update Build Number for Triggered Build » doit contenir les variables suivantes :

Variables de la séquence "Update Build Number for Triggered Builds"

  • localAssemblyInfoPath : de type System.String avec comme valeur par défaut System.IO.Path.GetTempFileName()
  • version : de type System.Version

Voici les différents paramètres de ces 4 activités :

  • Retrieve AssemblyInfo file
    • Activité de type : Microsoft.TeamFoundation.Build.Workflow.Activities.DownloadFile
    • LocalPath : localAssemblyInfoPath
    • ServerPath : AssemblyInfoPath
    • Version : BuildDetail.SourceGetVersion
    • VersionControlServer : BuildDetail.BuildServer.TeamProjectCollection.GetService(Of VersionControlServer)()
  • Gets the version of the AssemblyInfo file
    • Activité de type : TfsBuildExtensions.Activities.Framework.AssemblyInfo
    • AssemblyVersion : « $(current).$(current).$(current).$(current) »
    • Files : New String() { localAssemblyInfoPath }
    • MaxAssemblyVersion : version
  • Sets the changeset to the version
    • Activité de type : System.Activities.Statements.Assign
    • To : version
    • Value : New Version(version.Major, version.Minor, version.Build, Convert.ToInt32(BuildDetail.SourceGetVersion.Substring(1)))
  • Update Build Number (à modifier)
    • BuildNumberFormat : BuildNumberFormat.Replace(« $(Version) », version.ToString())

Maintenant, au niveau de l’agent il faut modifier le fichier AssemblyInfo.Common.cs après l’avoir récupérer dans le workspace et avant de lancer la compilation. L’emplacement stratégique pour réaliser ce traitement se trouve après la séquence « Initialize Workspace ».

Il faut rajouter une séquence qui réalise les opérations suivantes :

  • Récupérer le nom du chemin local du fichier AssemblyInfo.Common.cs en fonction du workspace initialisé dans la séquence précédente.
  • Mettre à jour le fichier AssemblyInfo.Common.cs avec le numéro du changeset.

Sequence "Update the version of AssemblyInfo"

La nouvelle séquence « Update the version of AssemblyInfo » doit contenir les 2 activités suivantes :

  • Gets the local path of the AssemblyInfo file
    • Activité de type : Microsoft.TeamFoundation.Build.Workflow.Activities.ConvertWorkspaceItem
    • Input : AssemblyInfoPath
    • Result : localAssemblyInfoPath
    • Workspace : Workspace
  • Update the version of the AssemblyInfo file
    • AssemblyVersion : « $(current).$(current).$(current). » + BuildDetail.SourceGetVersion.Substring(1)
    • Files : New String() { localAssemblyInfoPath }

Il suffit alors d’archiver le workflow modifié et de le paramétrer au niveau des arguments « AssemblyInfoPath » et « Build Number Format » :

Paramètres du processus de build

Et voilà le résultat au niveau des builds une fois le build déclenché :

Résultat du build avec sa version

Et bien évidemment au niveau de notre application :

Résultats du numéro de version de l'application exempleCréer une release d’un changeset spécifique

Pour créer une release avec un changeset spécifique, rien de plus simple, il suffit de définir le numéro de changeset dans la propriété « Get Version » de la fenêtre « Queue Build« . Il faut précéder ce numéro du changeset par la lettre « C » :

Spécification d'une version spécifique à construire dans une releaseConclusion

A travers cet article, j’ai voulu vous montrer la mise en place d’une stratégie de numérotage automatique des versions de votre application. La dernière composante « Revision » du numéro de version est utilisé pour spécifié le changeset au niveau de notre gestionnaire de sources, tandis que les autres composantes sont destinées à version commerciale de l’application. Il n’est donc maintenant plus nécessaire de labéliser vos versions dans votre gestionnaire de sources…

J’applique et je diffuse cette stratégie depuis plus de 8 ans à mes clients avec succès car elle permet une vraie gestion simplifiée des numéros de versions avec un gestionnaire de sources.

Vous pouvez télécharger le workflow modifié DefaultTemplate.11.1 et l’application exemple de cet article.

Leave a comment