cogimator.net

Une ligne à la fois...

Integration continue : Partie 4, Microsoft Source Analysis Tool for C#

Initialement, la partie 4 devait parler de tests unitaires, mais la sortie récente de cet outil mérite qu'on fasse un petit détour. Concrètement, MSAT va effectuer une analyse statique de votre code. Il va donc vous indiquer les parties de votre code qui ne se conforment pas à un standard de codage. Il est possible de le configurer selon ses besoins (par exemple, la règle qui dit que les tabulations c'est mal à l'air de faire couler beaucoup d'encre...). Donc attention, le but d'un tel outil n'est pas de définir une norme de codage pour tous les développeurs C# du monde (rien que ça), mais plutôt de maintenir une cohérence au sein des équipes travaillant sur un même projet.

Installation de l'outil

Microsoft Source Analysis Tool for C# peut être téléchargé ici : http://code.msdn.microsoft.com/sourceanalysis/Release/ProjectReleases.aspx?ReleaseId=1047. L'installation est très simple, il faut juste s'assurer que la partie MSBuild est bien installée, sinon, pas d'intégration dans notre processus de build.

Intégration dans vos projets

Une fois l'outil installé, une nouvelle entrée apparait dans le menu "Outils" de Visual Studio : "Run Source Analysis". Elle vous permet d'exécuter l'analyse du code directement dans Visual Studio, et affichera les résultats dans l'onglet "Source Analysis". Un double clc sur un message vous emmene sur la ligne concerné. Rien d'inhabituel donc.

Il est également possible d'intégrer l'analyse de votre code source directement dans le procéssus de build, et là, celà devient vraiment intéressant : lorsqu'un développeur lancera une compilation, toutes les erreurs liées à l'analyse du code source apparaitront en tant que warning dans l'onglet "Liste d'erreurs".

Pour permettre cela, deux solutions :

Si MSAT est installé sur tous les postes de développeur

Ouvrez chaque fichier .csproj pour lequel vous souhaitez analyser le code, et modifiez le de la façon suivante :

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 
    [...] 
    <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> 
    <Import Project="$(ProgramFiles)\MSBuild\Microsoft\SourceAnalysis\v4.2\Microsoft.SourceAnalysis.targets" /> 
    [...] 
</Project> 

Si MSAT n'est pas installé sur tous les postes de développeur

Pour faire suite au billet sur l'arborescence projet, vous pouvez maintenant rajouter un autre répertoire à la racine de la solution : "External" (par exemple... vous pouvez aussi l'appeler Tools, ou Trucs, peu importe!). Créez un répertoire "SourceAnalysis", et copiez y le contenu du répertoire "C:\Program Files\Microsoft Source Analysis Tool for C#". Maintenant, ouvrez chaque fichier .csproj pour lequel vous souhaitez analyser le code, et modifiez le de la façon suivante :

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> 
    [...] 
    <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> 
    <Import Project="..\..\External\SourceAnalysis\Microsoft.SourceAnalysis.targets" /> 
    [...] 
</Project> 

Ensuite, intégrez ce répertoire dans le contrôle de code source. Les développeurs bénéficieront de l'analyse de code source dès qu'ils mettront leur copie locale à jour. Lors du premier rechargement du projet, vous obtiendrez un message d'avertissement :

avertissement_securite  

Choisissez de charger normalement le projet.

Et voilà, l'analyse statique intégrée dans Visual Studio, et dans le processus de compilation (et dans CruiseControl, de fait!).

Integration continue : Partie 3, Arborescence Projet

Arborescence projetAvant d'aller plus loin, je vais rapidement décrire l'arborescence projet que je vais utiliser pour les billets à venir (cf image):

  • Apps : applications Winforms, Console, WebForms, tout ce qui est exécutable
  • Libs : toutes les bibliothèques de classes, ou tout ce qui n'est pas exécutable
  • References : toutes les librairies externes référencéés dans votre solution (NHibernate, Castle Project, etc)(non présent dans la solution visual studio, uniquement dans l'arborescence "physique")
  • Scripts : scripts de compilation, de création de base de données
  • Tests : tests unitaires

Important : si vous créez des dossiers de solution dans Visual Studio, attention lors de la création d'un nouveau projet : par défaut, celui-ci est placé à la racine de votre dossier de solution. Il en est de même pour les éléments de solution (par ex : Demo.config et Demo.nunit), il est préférable de les créer physiquement dans le dossier voulu, et de les ajouter à la solution dans un deuxième temps (via "Ajouter un élément existant").

Cette arborescence n'est évidemment pas figée dans ses termes, il s'agit juste de "classer" un peu notre solution. Il est également possible de créer des sous dossiers, comme par exemple "Console" ou "WebApps" dans "Apps".

Concernant les librairies externes, j'ai pris le parti de les lier à chaque solution : cela permet d'éviter les problèmes lors d'une mise à niveau de librairie. Si tous vos projets référencent la meme librairie (NHibernate par exemple), qu'une mise à jour avec un changement d'API de leur part est effectuée, tous vos projets sont impactés. En liant les librairies par projet, vous pouvez effectuer les mises à jour dans chaque projet, si le besoin s'en fait ressentir.

Integration continue : Partie 2, Controle de code source

Pour cette partie sur le contrôle de code source, nous allons mettre en place et utiliser Subversion. Il vous faudra télécharger VisualSVN Server (http://www.visualsvn.com/server/), ainsi que TortoiseSVN (http://tortoisesvn.tigris.org/). Vous pouvez également installer AnkhSVN (http://ankhsvn.open.collab.net/) si vous souhaitez bénéficier de l'intégration dans Visual Studio (attention, pour Visual Studio 2008, prenez bien la version 1.0.3 ou supérieure).

Une fois VisualSVN Server installé sur le serveur, TortoiseSVN et AnkhSVN installés sur les clients, nous allons pouvoir commencer.

Sur le serveur

En premier lieu, il va falloir créer un repository. Un repository est un espace où sera stocké le code source. Toute modification de fichier (commit) dans le repository entraînera une incrémentation du numéro de version (revision) global au repository. Le repository sera physiquement stocké à l'endroit spécifié lors de l'installation de VisualSVN Server. C'est ce répertoire que vous devrez sauvegarder régulièrement. Pour aller plus loin : Subversion in action : revisions.

Donnez un nom explicite à votre repository (le nom de la solution Visual Studio, le codename du projet...). Attention à bien laisser cocher la case "Create default structure". Cette structure de répertoire est une convention :

  • trunk : code source "actuel" du projet
  • tags : versions spécifiques du projet, comme par exemple les différentes livraisons ou versions installées en production
  • branches : version en développement : ajout de nouvelles fonctionnalités.

Tous les détails sont expliqués dans ce post de Bill Simser.

Le repository étant crée, nous allons en autoriser l'accès à nos développeurs. Créez un (ou plusieurs) utilisateurs, via "Create new user" de la page d'accueil.

Il ne reste plus qu'à créer un groupe "developpers", via "Create new group" de la page d'accueil et enfin d'associer notre utilisateur au groupe.

La dernière étape consiste à accorder les droits d'accès à notre groupe sur le repository (ou sur tous les repositories, selon votre goût). Pour cela, clic-droit sur un objet dans l'arborescence, et "Security".

Avant de passer sur le poste de développeur, pensez à note l'url du repository (normalement de la forme http://serveur:8080/svn/NomDuRepository).

Sur le poste de développeur

Nous allons créer une copie locale du repository. Toute modification dans cette copie locale est répercutée via un "Commit", et toute modification du repository est repercutée sur la copie locale via un "Update". Pour gérer cette copie locale, nous allons utiliser TortoiseSVN (AnkhSVN reprend les mêmes principes, mais directement dans Visual Studio).

Après avoir installé TortoiseSVN, créez un nouveau dossier (par ex: d:\IntegrationContinue), faites un clic droit sur ce nouveau dossier, et choisissez "SVN Checkout". Renseignez l'url du repository crée plus tot (http://serveur:8080/svn/IntegrationContinue/trunk), et validez. Attention, l'url doit être suivie de "trunk". Votre copie locale est maintenant créée !

Ensuite, toujours dans ce dossier : faites un clic droit -> TortoiseSVN -> Settings. Dans l'entrée "General", "Subversion", "Global ignore patterns", vous pouvez saisir "bin obj *.suo *.user". Ceci indiquera a TortoiseSVN de toujours ignorer ces répertoires et ces fichiers, et évitera de placer les exécutables ainsi que les fichiers temporaires de compilation sous controle de code source. Les paramètres de solution/projet par utilisateur ne seront également pas placés sous contrôle de code source.

ignore_pattern

Il est également possible de définir quels fichiers/répertoires seront ignorés, et ce par projet, dans la fenêtre de commit :

ignore_property

Enfin, tant que vous êtes dans les paramètres de TortoiseSVN, vous pouvez également ajouter les valeurs suivantes dans la partie "Icon overlays" :

exclude

Cela vous évitera des petits soucis de fichiers vérouillés aléatoirement.

Ce qui nous amène à une autre convention : tout ce qui n'est pas nécessaire à la compilation n'est pas nécessaire dans le contrôle de code source. En effet, à quoi bon stocker N versions d'un code source, ainsi que N versions de l'éxecutable produit par ce code source ?

Processus de mise à jour des sources

Pour le moment, nous avons :

  • un serveur SVN
  • un client SVN

Il ne nous reste plus qu'à créer une nouvelle solution Visual Studio : si vous avez crée le dossier "IntegrationContinue" dans d:\, créez la solution également dans d:\, de manière à ce que le fichier IntegrationContinue.sln se trouve dans d:\IntegrationContinue\IntegrationContinue.sln. Ajoutez vos projets à cette solution.

Une fois ceci fait, retournez dans le dossier d:\IntegrationContinue, pour faire un "commit" avec un clic droit -> TortoiseSVN -> Commit, sélectionnez tous les fichiers, et cliquez sur "Commit". Tous vos fichiers se trouvent maintenant sous contrôle de code source!

Ce qui nous amène à une première version du processus de mise à jour du code source sous SVN.

1. Bob (notre développeur) fait un update de sa copie locale pour obtenir la dernière version du code source
2. Bob fait une modification dans le code
     2.1 Si le code ne compile pas, retour en 2.
     2.2 Si le code compile, passer en 3.
3. Bob fait un update de sa copie locale pour obtenir les modifications qui auraient été effectuées depuis 1.
     3.1 Si le code ne compile pas, retour en 2.
     3.2 Si le code compile, passer en 4.
4. Bob fait un commit pour mettre à jour le code source sur SVN.

Le point 3 est important, il permet de s'assurer que les modifications effectuées en 2 ne sont pas incompatibles avec une éventuelle modification ayant eu lieu entre 1 et 3.

Enfin, le point le plus important : le code source situé dans le contrôleur de code source (plus précisément dans la partie trunk), doit OBLIGATOIREMENT compiler. Si le code ne compile pas, on ne fait pas de commit de ce code.

Gestion de favoris avec del.icio.us

Il fut un temps, où mes favoris étaient tous stockés en local, et je considérais de les stocker chez un "fournisseur", un peu comme une hérésie... Paranoïa peut-être, résistance au changement sans doute.

Maintenant, deux problèmes sont posés par le modèle "traditionnel" du favori :

  • classement arborescent
  • réplication difficile entre plusieurs postes de travail

Le classement arborescent pose vite ses limites à partir de 100 favoris : à la fois pour la sauvegarde, et pour la lecture. Si on duplique un favoris dans deux sous branches de l'arborescence, on complexifie le processus de sauvegarde. Si on ne le place que dans une seule sous branche, on risque de ne plus retrouver son favori si on ne cherche pas exactement au bon endroit.

Concernant la réplication, il suffit de vouloir synchroniser ses favoris sur deux postes ou plus pour se rendre compte de la lourdeur de la chose.

Donc, la solution (avec un peu de retard, ce service n'est plus tout jeune!): del.icio.us. Un service de "stockage" de favoris en ligne, avec un classement par "tags". Au lieu de classer un favori dans une arborescence, on lui affecte des tags : des mots-clés. Et le plus beau dans tout ca, c'est qu'on peut retrouver une arborescence : si un favori est taggé "blog it sharepoint", il sera dans l'arborescence "blog / it / sharepoint", "sharepoint / blog / it", "it / sharepoint / blog", etc. Ceci permet de retrouver un favori bien plus facilement : on part d'un tag, et on voit immediatement les autres tags "voisins".

Le plus dur étant d'être rigoureux dans la taxinomie afin d'éviter les doublons du style graphic/graphics/graphism/images. Quand cette habitude est prise (avec l'aide l'extension Firefox fournie), le processus devient beaucoup plus naturel que le classement arborescent.

Une fois le compte crée, l'extension Firefox installée, et les anciens favoris importés (et reclassés), on ne peut plus s'en passer, rien que pour l'aspect synchronisation, et pour la barre d'outils, qui permet de placer des tags judicieusement choisis comme menus déroulants. Comme par exemple :

  • frequent : sites que je fréquente très ... fréquemment
  • reference : sites de reference (msdn, nhibernate doc, nant help, etc. )
  • toread : articles que j'estime interessants à lire tranquillement plus tard
  • tostudy : idem que toread, mais pour des outils

Avantages : classement par tags, synchronisation

Inconvenients : impossible de mettre des mots composés ("intégration continue" par exemple) comme tags


Integration continue : Partie 1, Introduction

Je vais tacher de vous expliquer comment mettre en place un environnement d'intégration continue au travers de cette petite série de billets.

Tout d'abord, il faut un environnement de développement : Visual Studio 2005 (ou 2008).

Les articles suivants viendront, dans l'ordre :

Formulaires (x)html : comment faire ?

Si comme moi vous aimez les standards et l'accessibilité vous avez déjà du vous arracher des cheveux sur les formulaires web. En plus d'entretenir une future calvitie, ca me chagrinais (oui je suis comme ça...) de ne pas avoir de "modèle". En voilà donc un, qui permet de gérer le traditionnel formulaire sur deux colonnes, avec à gauche les libellés, et à droite les champs de saisie.

Rien de bien revolutionnaire dans tout cela, on pourra juste noter que ce modèle

  • se base sur des position:absolute au lieu de float:left pour aligner les label, suite à la lecture de cet article.
  • permet d'avoir une zone de "droite" plus haute que celle de gauche (cf l'exemple).
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
 <head>
  <title>Test formulaires</title>
  <style type="text/css">
  .left {
    position: absolute;
    width: 250px;
    border: 1px solid black;
    padding: 5px;
    margin-top: 5px;
  }
  form {
   position: absolute;
   width: 500px;
   margin-left: 270px;
  }
  form label {
   position: absolute;
   width: 180px;
  }

  form label strong {
   color: red;
  }

  form .droite {
   padding-left: 190px;
   width: 350px;
   padding-bottom: 20px;
  }  

  form .droite .error {
   color: red;
   margin: 0;
  }

  form .droite .info {
   font-decoration: none;
   margin: 0;
  }

  form .boutons {
   margin-left: 190px;
  }
  </style>
 </head>
 <body>
  <div class="left">
   <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
  </div>
  <form action="#">
   <fieldset>
    <legend>test</legend>
    <label for="nom">Nom <strong>*</strong></label>
    <div class="droite">
     <input type="text" id="nom"/>
     <p class="error">Le nom est obligatoire</p>
    </div>
    
    <label for="prenom">Prenom <strong>*</strong></label>
    <div class="droite">
     <input type="text" id="prenom"/>
     <p class="error">Le prenom est obligatoire</p>
    </div>
   
    <label for="age">Age</label>
    <div class="droite">
     <input type="text" id="age"/>
     <p class="info">Purement indicatif</p>
    </div>
   
    <label for="couleur">Couleur préférée</label>
    <div class="droite">
     <select id="couleur">
      <option>rouge</option>
      <option>vert</option>
      <option>bleu</option>
     </select>
    </div>
   
    <label>Couleurs detestées</label>
    <div class="droite">
     <input type="checkbox" id="noir"/><label for="noir">Noir</label><br/>
     <input type="checkbox" id="gris"/><label for="gris">Gris</label>
    </div>
   
    <div class="boutons">
     <input type="submit" value="Valider"/>
     <input type="reset" value="Annuler"/>
    </div>
   </fieldset>
  </form>
 </body>
</html>

Vous pouvez voir le rendu ici formulaire_xhtml.html (2,47 kb)