SOMMAIRE
1.2. Object du présent document
1.4. Autres documents de référence
2.1.1.1. Installation de SUBVERSION sur WINDOWS
2.1.1.2. I. Téléchargement des outils
2.1.1.3. II. Installation du serveur et du client en ligne de commande
2.1.1.4. III. Création du repository et configuration des accès
2.1.1.5. IV. Démarrer le serveur manuellement, et créer un projet
2.1.1.6. V. Paramétrer le serveur comme service
2.1.1.7. VI. SUBVERSION au travers d'un réseau
2.10. SandCastle Help File Builder (SHFB)
2.11. Cruise Control MsBuild Logger
3.3. NUnit, NCover, NCoverExplorer
3.6. CCTRAY (pour cruise control sur le poste développeur)
L'intégration continue est une technique utilisée en génie logiciel.
Elle consiste à vérifier à chaque modification de code source que le résultat des modifications ne produit pas de régression de l'application en cours de développement. Bien que le concept existait auparavant, l"intégration continue" se réfère généralement à la pratique de l'extreme programming.
Pour appliquer cette technique, il faut d'abord que :
1) le code source soit partagé (en utilisant des logiciels de gestion de versions tels que CVS ou Subversion) ;
2) les développeurs intègrent (commit) quotidiennement (au moins) leurs modifications ;
3) des tests d'intégration soit développés pour valider l'application (avec NUnit par exemple).
4) Ensuite, il faut un outil d'intégration continue tel que CruiseControl pour le langage C# par exemple.
Les principaux avantages d'une telle technique de développement sont :
- les problèmes d'intégration sont détectés et réparés de façon continue, évitant les problèmes de dernière minute ;
- prévient rapidement en cas de code incompatible ou manquant ;
- test immédiat des unités modifiées ;
- une version est toujours disponible pour test,
démonstration ou distribution
Voici un descriptif des logiciels utilisés :
Nous allons distinguer 2 types de logiciel :
- Les logiciels installés du côté serveur.
- Les logiciels installés du côté client.
Du côté serveur, les logiciels nécessaires au bon fonctionnement de l’intégration continue sont les suivants :
1) Subversion
2) Cruise control.net
3) MSBuildCommunityTasks
4) FxCopAddin
5) NCover
6) NCoverExplorer
7)
Par Eric Reboisson (Site Web)
( parfois
abrégé SVN ) est un logiciel informatique de contrôle de version. Il reprend
les principes de CVS mais en l'améliorant ( ex : copie et renommage de fichiers
avec conservation de l'historique, commit atomiques, etc.).
I. Téléchargement des outils
II. Installation du serveur et du client en ligne de commande
III. Création du repository et configuration des accès
IV. Démarrer le serveur manuellement, et créer un projet
V. Paramétrer le serveur comme service
VI. Installation de TortoiseSVN
VI. SUBVERSION au travers d'un réseau
VIII. Conclusion
IX. Remerciements
Lancer svn-x.y.z-setup.exe et suivre simplement les étapes d'installation ( Note : Pour cet article SUBVERSION est installé dans le répertoire C:\Programmes\SVN)


Ouvrir ensuite le "Panneau de configuration" > "Système", aller sur l'onglet "Avancé", et cliquer sur le bouton "Variables d'environnement"
Cliquer sur le bouton "Nouveau" dans la zone des variables système, saisir SVN_EDITOR dans "Nom de la variable", et le chemin d'un éditeur de texte comme valeur de cette variable (ex: C:\WINDOWS\notepad.exe)

Créer un
répertoire svn_repos quelque part sur votre disque dur.
Ce répertoire contiendra le référentiel de fichiers de SUBVERSION.
Pour cet article je crée le répertoire du repository dans d:\svn_repos
Ouvrir une fenêtre de commande DOS et taper svnadmin create
"d:\svn_repos"

Ouvrir le répertoire d:\svn_repos\conf ( créé juste avant par la commande svnadmin ), et éditer les 2 fichiers de configuration suivants :
|
Contenu du fichier passwd |
|
Dans la fenêtre de commande DOS ( Vous ne l'avez pas fermée au moins ? sinon en ouvrir une nouvelle ) taper svnserve --daemon --root "d:\svn_repos"

Ouvrir une seconde fenêtre de commande DOS, et taper svn mkdir svn://localhost/monprojet

L'éditeur de texte que vous avez paramétré en variable
d'environnement s'ouvrira alors sur un fichier avec du texte déjà présent.
Taper un commentaire, par exemple "Création de mon projet" au début
du fichier ( avant la ligne commençant par "--" ). Sauvegarder le
fichier et fermer l'éditeur.
|
Contenu du fichier |
|
Dans la fenêtre de commande DOS de création, si votre
login SUBVERSION est le même que pour votre session WINDOWS,
alors taper votre mot de passe dans la fenêtre de commande ( celui que vous
avez saisi dans le fichier passwd ), et taper la touche "Entrée".
Si votre login SUBVERSION est différent de votre login WINDOWS,
alors taper la touche "Entrée" à l'invite du password dans la
fenêtre de commande, SUBVERSION vous demandera alors le login et le mot
de passe que vous avez saisi dans le fichier passwd.
SUBVERSION vous indiquera alors "Révision 1 propagée"

Félicitations! Vous venez de charger une première version de fichier.
Il est
courant d'avoir des sous-répertoires /trunk , /branches, et /tags pour chaque
projet.
Dans la fenêtre de commande DOS précédente taper svn mkdir svn://localhost/monprojet/trunk
et comme précédemment saisir un commentaire dans le fichier ouvert, sauvegarder
et fermer l'éditeur.
A noter qu'après avoir saisi votre commentaire, SUBVERSION ne vous
redemande pas le login/password.

Retourner
dans la fenêtre de commande DOS qui exécute svnserve. Taper Ctrl+C pour
l'arrêter et fermer la fenêtre.
Ouvrir l'archive SVNService.zip téléchargée précédemment.
Extraire SVNService.exe ( et les autres fichiers de l'archive ) dans le
répertoire bin de SUBVERSION ( Dans cet article :
d:\Programmes\SVN\bin).
Il est important que le contenu de l'archive soit au même endroit que
l'exécutable svnserve.exe du logiciel SUBVERSION. Ouvrir une
fenêtre de commande DOS, et taper svnservice -install --daemon --root
"d:\svn_repos"

Ouvrir ensuite le "Panneau de configuration" > "Outils d'administration" > "Services", double-cliquer le service SVNService, et changer le type de démarrage de "Manuel" en "Automatique", appliquer et cliquer OK.

Maintenant
SUBVERSION se lancera à chaque démarrage de WINDOWS.
Il faut cependant démarrer le service SVNService manuellement dans notre
cas ( en le sélectionnant dans la liste des services, et en cliquant Démarrer
via le menu contextuel )
Retourner à la fenêtre de commande DOS, et taper svn ls svn://localhost/

La liste
de tous les fichiers de la racine du repository apparaît.
Si tout s'est bien passé auparavant et que votre serveur SUBVERSION
fonctionne, vous devriez voir apparaître: monprojet/
|
|
Pour l'installation, il faut maintenant utiliser le fichier install.bat (contenu dans SVNService.zip) avec MS .NET Framework 1.1 en prérequis. Pour la configuration, il faut configurer le fichier SVNService.exe.config (fichier au format XML) pour indiquer le chemin de svnserve (SVNBinaryPath) ainsi que pour localiser le dépot (RepositoryRoot). |
Sur un
autre PC de votre réseau, installer TortoiseSVN ( voir précédemment )
Créer un répertoire monprojet sur d: et faire un "SVN
Checkout..." à l'intérieur de ce répertoire.
Quand vous spécifiez l'URL du repository, utilisez la même URL qu'auparavant,
mais remplacez "localhost" avec le nom de l'ordinateur sur
lequel s'exécute le service SUBVERSION ( Dans mon cas, l'URL du
repository est svn://portable/monprojet/trunk/ )
Vous verrez alors apparaître le fichier toto.txt avec le contenu
précédemment créé.
Voilà vous avez maintenant un serveur SUBVERSION installé qui pourra vous permettre :
Maintenant il existe d'autres outils de versionning et voici un lien qui vous permettra de comparer:
Afin de faire fonctionner le serveur CruiseControl.NET, vous devez avoir les éléments suivants installés
CruiseControl.NET Web Dashboard
|
|
Pour l'installation, il faut que le serveur IIS soit configurer pour utiliser le framework 2.0, sinon, le webdashboard ne fonctionne pas si le service a été lancé alors que IIS était configuré avec un framework 1.1.
|
L’application cruise control est composée de trois éléments :
- Le serveur
- Le service
- Le webdashboard
Tout cela peut être installé grâce à l’exe qui une programme d’installation standard windows qui s’occupe de :
1) Mettre les bonnes informations dans les variables d’environnement afin d’accéder à CCNET directement en ligne de commande
2) D’installer CCNET en tant que service windows (Nom du service = CruiseControl.Net Server)
3) De créer un répertoire virtuel dans IIS pour accéder au webdashboard.
Toute ces manipulations peuvent être effectuées manuellement pour plus de contrôle.
La méthode est décrite à l’adresse suivante : http://confluence.public.thoughtworks.org/display/CCNET/The+Server+Service+Application
Une fois l’exe installé, il faut lancer le service manuellement et le configure ensuite pour qu’il se lance automatiquement au démarrage de Windows.
Pour lancer le service après l’installation :


![]()
Pour qu’il se lance automatiquement au démarrage :


Afin de vérifier que le service fonctionne correctement, ouvrez un navigateur et allez sur l’url http://localhost/ccnet.
Vous devez obtenir la fenêtre suivante.

Nous verrons dans un autre chapitre comment configurer les fichiers de configuration du serveur et du webdashboard.
NUnit est un framework de tests unitaires pour tous les langages .NET.
Il s’agit simplement d’installer l’executable.
Nous verrons dans un chapitre suivant comment utiliser NUnit et surtout comment l’intégrer avec CruiseControl et MSBuild.
MSBuild est l’outil de compilation en ligne de commande livré avec le Framework 2.0.
Il permet d’exécuter la compilation sur un projet Visual Studio ou bien à partir de fichiers de code c#, VB.Net …
Il se trouve par défaut à l’emplacement suivant : C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe
Ce package regroupe un ensemble de Tâches prédéfinies pour MSBuild permettant un grand nombre d’action (gestion de NUnit, FxCop …).
Ce package est par défaut installé dans le répertoire
Lien : http://msbuildtasks.tigris.org/
Après avoir lancé l’exe, les tâches se retrouvent à l’emplacement C:\Program Files\MSBuild\MSBuildCommunityTasks
Ce programme permet de vérifier que le code d’un développeur est conforme aux best practices de Microsoft.
Il se présente sous la forme d’un exécutable.
Il s’agit d’un ensemble de Tâches pour MSbuild relatives à FXCop.
L’installation est faite par le lancement du fichier FxCopAddIn-Install.bat qui copie les fichiers dans le répertoire C:\Program Files\MSBuild\FxCopAddIn
Logiciel permettant de déterminer le taux de couverture du code.
Ceci permet de valider que l’ensemble des méthodes d’une classe par exemple ont été testées.
Permet d’utiliser le résultat de NCover en ligne de commande depuis MSBUILD (à vérifier dans les faits).
Ensemble de tâches pour MSBuild et Cruise Control.
Pour générer des documents d’aide au format HTML
Logiciel permettant de générer de la documentation au format VS2005 à partir des commentaires (exportés sous forme d’un fichier xml) d’une solution Visual Studio.
L’exécutable sera ensuite utilisé par MSBuild pour générer la documentation par l’intermédiaire de Cruise Control.
C’est une interface graphique et un exe en ligne de commande (style Ndoc) qui s’appuie sur Sandcastle pour générer de la documentation au format chm à partir des commentaires xml d’une application.
L’utilisation de cette application permet de définir les options pour la création de l’aide et sert de « bootstrap » pour le processus de création du document.
L’installation du logger consiste en la copie de la dll ThoughtWorks.CruiseControl.MSBuild.dll dans le répertoire C:\Program Files\CruiseControl.NET\server.
Ce logger sera ensuite instancié dans les fichiers ccnet de la façon suivante :
<msbuild>
<executable>C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe</executable>
<workingDirectory>D:\Dev\ProjetsSVN\Sources\cinema.aliceadsl.fr</workingDirectory>
<projectFile>build.proj</projectFile>
<targets>Build</targets>
<logger>ThoughtWorks.CruiseControl.MsBuild.XmlLogger,C:\Program Files\CruiseControl.NET\server\ThoughtWorks.CruiseControl.MsBuild.dll</logger>
</msbuild>
Voici la liste des logiciels qui doivent être installés sur le serveur et qui vont par la suite servir à générer l’application finale, la documentation, les tests unitaires, la couverture de code, l’application des règles de développement …
Afin d’avoir une homogénéité au niveau des méthodes de développement, il serait préférable que tout le monde utilise VS2005 pour les développements.
Plug in permettant d’utiliser SVN directement à l’intérieur de Visual Studio (Chekin, Checkout …).
Ces logiciels doivent également être installés sur le poste client afin de pouvoir faire tous les tests unitaires avant un « Commit » de la solution dans CVS.
Outil permettant de faire des tests unitaires à l’intérieur de VS2005.
TortoiseSVN est un logiciel qui permet d'interagir avec SUBVERSION
dans l'explorateur de fichiers WINDOWS.
Lancer l'installeur de TortoiseSVN téléchargé précédemment, choisir
d'installer toutes les options sur le disque dur et suivre les étapes
d'installation ( Note : Pour cet article TortoiseSVN est installé dans
le répertoire D:\Programmes\TortoiseSVN ).


Créer ensuite un répertoire "monprojet"
quelque part sur votre disque dur ( Dans cet article d:\monprojet ).
Cliquer droit quelque part à l'intérieur du répertoire et sélectionner "SVN
Checkout..."
Taper svn://localhost/monprojet/trunk/ comme URL du repository et
cliquer sur le bouton OK puis sur le bouton OK de la fenêtre suivante.

Créer maintenant un fichier toto.txt dans ce répertoire.
|
Contenu du fichier toto.txt |
|
Cliquer droit sur le fichier et sélectionner TortoiseSVN
> Add puis valider en cliquant OK sur la fenêtre suivante.
Noter qu'un icône particulière ( un + )apparaît sur l'icône du fichier.

Le fichier n'est pas encore chargé dans SUBVERSION, pour cela il faudra faire un commit en cliquant droit sur le fichier toto.txt et sélectionner "SVN Commit".

Saisir un commentaire et cliquer sur OK

Cliquer sur OK
Prérequis :
CCTRAY nécessite Microsoft.NET
Framework Version 2.0 (download
)
Cet utilitaire permet de voir après un commit dans SVN et la prise en charge dans Cruise Control que toutes les operations se sont déroulées correctement.
L’application se présente sous la forme d’un icône dans la barre des tâches.
![]()
Cette fenêtre va lister tous les projets présents dans CCnet. Nous verrons cela en détail un plus tard.

Avant d’aller plus en avant dans le processus d’intégration continue. Les applications concernées seront développées en utilisant VS2005. Nos applications couvrent à la fois des applications web, des applications Console (partie DIS), des librairies … Nous utilisons Subversion pour contrôler le code source.
Le but de l’intégration continue est d’avoir des « packages » permettant à un développeur de gérer l’ensemble d’une application sur un poste en local.
Des questions restent en suspend, notamment celles concernant la gestion de l’historique des bases de données, ainsi que toutes les ressources partagées par l’ensemble des applications (images, css, fichiers Xml centralisés sur un filer (Generated)).
Le but est que de pouvoir à tout moment mettre en place en place un environnement de développement fonctionnel simplement en récupérant la dernière version du « repository ».
Pour faciliter tout cela, il est nécessaire de standardiser les « packages » en adoptant la structure de répertoires suivante pour une solution.
Dependencies
External (assemblies tierces)
Internal (assemblies développées par TI, utilisées uniquement dans ce projet, code source se trouve dans ce répertoire).
Common (assmeblies développées par TI, utilisées par plusieurs projets. Code source se trouve dans un autre projet SVN)
Website
NomDeSite (par exemple : cinema.aliceadsl.fr) Bin Css Images
Scripts
Templates
Controls
Data
AdminWebSite (par exemple admincinema.aliceadsl.fr) Bin Css Images Scripts Templates Controls Data
DISJobs NomProfils Exe Bat XslData
Help
SQL ScriptsProjet SQL server 2005 s'il en existe un
Data Jeu de données sql
Builds (tous les scripts permettants d'automatiser la configuration d'un projet sur le poste client du développeur).
Code Metrics
Le répertoire Builds Contient les scripts pour créer l’environnement de développement sur le poste du développeur.
Il peut également contenir une répertoire différent pour chaque Build et ses « Build Artefacts ».
Website : répertoire contenant l’ensemble des solutions web pour une application. Il y aura logiquement, au maximum, un site web et un site d’administration.
Le répertoire Code Metrics contient toutes les mesures qui sont générées pour l’application comme par exemple les résultats de la couverture de code.
Le répertorie Help contient les fichiers de configuration de l’aide ainsi que l’aide au format MSDN qui sont générés à partir des commentaires XML qui se trouvent dans le code.
Le répertorie Dependencies contient les librairies qui sont utilisées comme référence pour les différents projets.
Ces librairies peuvent être internes (Internal), à savoir développée dans le cadre de l’application et propres au projet (elles ne seront pas utilisées par une autre application et apparaissent comme un projet au sein de la solution). Elles sont référencées en tant que projet.
Elles peuvent être externes (External). Ce sont en général les librairies tierces qui sont utilisées dans toutes les applications (Nunit, log4Net, Obout …)
Elles peuvent enfin être Communes (Common). Ce seront alors les librairies développées par Telecom Italia qui sont partagées au travers des applications.
Le répertoire DISJobs contient l’ensemble des applications, des .bat, des scripts nécessaires à planifier la génération des fichiers appelés par l’application.
Cela concerne tous les éléments qui sont ensuite intégrés dans notre outil d’intégration (DIS pour Data Integration System).
Le répertoire SQL va quant à lui contenir tous les éléments propres à la base de données de l’application.
Cela reflète pour une version donnée les scripts à jouer pour avoir une base données fonctionnelle (structure des tables, procédures stockées, droits, utilisateurs de la BDD, fonctions, triggers …).
Cette structure est amenée à évoluer, mais elle doit permettre de gérer tous les types d’applications que nous allons être amenés à gérer.
Process :
L’idéal pour le process d’Intégration Continue serait que le développeur puisse simuler sur sa machine le même process que sur le serveur de Build avant de « commiter » ses modifications dans SVN. Le serveur de Build va « jouer » quelques tâches supplémentaires comme par exemple pour le déploiement, mais les mêmes process de compilations et de tests doivent pouvoir être utilisé dans le « bac à sable » du développeur et sur la machine de build.
Notre build doit pouvoir exécuter les tâches suivantes :
Cela peut être représenté schématiquement de la façon suivante :

Scripts MSBUILD
Groupes de propriétés communes
<Project DefaultTargets="BuildCode;BuildTests" InitialTargets="GetPaths;GetProjects;GetEnvironment"
xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- ASCII Constants -->
<PropertyGroup>
<NEW_LINE>%0D%0A</NEW_LINE>
<TAB>%09</TAB>
<DOUBLE_QUOTES>%22</DOUBLE_QUOTES>
<SPACE>%20</SPACE>
<CARRIAGE_RETURN>%0D</CARRIAGE_RETURN>
<LINE_FEED>%0A</LINE_FEED>
<APOSTROPHE>%27</APOSTROPHE>
<AMPERSAND>%26</AMPERSAND>
<PERCENT_SIGN>%25</PERCENT_SIGN>
</PropertyGroup>
<!-- Environment Settings -->
<PropertyGroup>
<DBServer Condition=" '$(DBServer)' == '' ">(local)</DBServer>
<DBMS Condition=" '$(DBMS)' == '' ">sql2005</DBMS>
<SqlCmdRunner Condition=" '$(DBMS)' == 'sql2000' " >osql</SqlCmdRunner>
<SqlCmdRunner Condition=" '$(DBMS)' == 'sql2005' " >sqlcmd</SqlCmdRunner>
<SmtpServer>smtp.telecomitalia.fr</SmtpServer>
</PropertyGroup>
<!-- 3rd Party Program Settings -->
<PropertyGroup>
<SubversionPath>D:\Programmes\SVN\Subversion\bin</SubversionPath>
<SubversionCmd>$(SubversionPath)\svn.exe</SubversionCmd>
<NCoverPath>C:\Program Files\NCover\</NCoverPath>
<NCoverExplorerPath>C:\Program Files\NCoverExplorer\</NCoverExplorerPath>
<NUnitPath>C:\Program Files\NUnit 2.4.3\bin\</NUnitPath>
<NUnitCmd>$(NUnitPath)nunit-console.exe</NUnitCmd>
<SandCastleHFBPath>C:\Program Files\EWSoftware\Sandcastle Help File Builder\</SandCastleHFBPath>
<SandCastleHFBCmd>$(SandCastleHFBPath)SandcastleBuilderConsole.exe</SandCastleHFBCmd>
</PropertyGroup>
<!-- Solution Folders -->
<PropertyGroup>
<CodeFolder>$(MSBuildProjectDirectory) </CodeFolder>
<SqlFolder>$(MSBuildProjectDirectory)\Sql</SqlFolder>
<LibFolder>$(MSBuildProjectDirectory)\Dependencies</LibFolder>
<DocFolder>$(MSBuildProjectDirectory)\Documentation</DocFolder>
<DocOutputFolder>$(DocFolder)\Help</DocOutputFolder>
<CodeOutputFolder>$(CodeFolder)\Deploy</CodeOutputFolder>
<SqlScriptsFolder>$(SqlFolder)\Scripts</SqlScriptsFolder>
<CodeMetricsFolder>$(MSBuildProjectDirectory)\Code Metrics</CodeMetricsFolder>
</PropertyGroup>
<!-- Solution Files -->
<PropertyGroup>
<SolutionName>qualite.aliceadsl.fr.sln</SolutionName>
<IterationNumberFile>$(MSBuildProjectDirectory)\IterationNumber.txt</IterationNumberFile>
<EnvironmentFile>$(MSBuildProjectDirectory)\Environment.txt</EnvironmentFile>
<LastTestRunSucceededFile>LastTestRunSucceeded</LastTestRunSucceededFile>
<LastCodeAnalysisSucceededFile>LastCodeAnalysisSucceeded</LastCodeAnalysisSucceededFile>
<InstallBuildEmailFile>$(Temp)\InstallBuildEmailFile.htm</InstallBuildEmailFile>
<InstallBuildEmailTemplate>$(InstallFolder)\InstallBuildEmailTemplate.htm</InstallBuildEmailTemplate>
<NUnitFile>TestResult.xml</NUnitFile>
<FxCopFile>CodeAnalysisLog.xml</FxCopFile>
<NCoverFile>Coverage.xml</NCoverFile>
<NCoverLogFile>Coverage.log</NCoverLogFile>
<NCoverSummaryFile>CoverageSummary.xml</NCoverSummaryFile>
<NCoverHtmlReport>CoverageSummary.html</NCoverHtmlReport>
<SandCastleHFBProject>qualite.aliceadsl.fr.shfb</SandCastleHFBProject>
<DBBackupScript>$(SqlScriptsFolder)\Create DB Backups automated build.cmd</DBBackupScript>
</PropertyGroup>
<!-- Subversion Settings -->
<PropertyGroup>
<SvnLocalPath>$(MSBuildProjectDirectory)</SvnLocalPath>
<SvnServerPath>https://build1.devpdc/qualite.aliceadsl.fr</SvnServerPath>
<SvnTrunkFolder>$(SvnServerPath)/trunk</SvnTrunkFolder>
<SvnTagsFolder>$(SvnServerPath)/tags</SvnTagsFolder>
</PropertyGroup>
<!-- Overriding .csproj Project settings -->
<PropertyGroup>
<OutputPath Condition=" '$(OutputPath)' == '' ">$(CodeOutputFolder)</OutputPath>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<RunCodeAnalysis Condition=" '$(RunCodeAnalysis)' == '' ">false</RunCodeAnalysis>
</PropertyGroup>
<!-- Only import the other targets here after the property definitions above have been defined -->
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets"/>
<UsingTaskTaskName="NCoverExplorer.MSBuildTasks.NCoverExplorer"
AssemblyFile="C:\Program Files\MSBuild\NCoverExplorer\NCoverExplorer.MSBuildTasks.dll"/>
<UsingTaskTaskName="NCoverExplorer.MSBuildTasks.NCover"
AssemblyFile="C:\Program Files\MSBuild\NCoverExplorer\NCoverExplorer.MSBuildTasks.dll"/>
<UsingTaskTaskName="NCoverExplorer.MSBuildTasks.NUnitProject"
AssemblyFile="C:\Program Files\MSBuild\NCoverExplorer\NCoverExplorer.MSBuildTasks.dll"/>
<!-- Add our CleanSolution task to the general Clean task -->
<PropertyGroup>
<CleanDependsOn>
$(CleanDependsOn);
CleanSolution
</CleanDependsOn>
</PropertyGroup>
<ItemGroup>
<SqlProjects Include="$(SqlFolder)\**\*.proj"/>
<Developers Include="
Fabien.cousin@telecomitalia.fr;
Olivier.gregoire@telecomitalia.fr;
Thierry.boyer@telecomitalia.fr;"
/>
</ItemGroup>
Ce script définit différents groupes de propriétés pour les contenus ASCII, le paramètres d’environnement, les programmes tiers, les répertoires de solution, les informations sur les versions … Ces propriétés sont utilisées par les différentes « targets » du projet. Le projet importe également les targets pour NCOverExplorer, NCover et les MSBuild.Community.Tasks.
Voici quelques observations supplémentaires :
Targets Générales :
GetPaths
<Target Name="GetPaths">
<GetFrameworkSdkPath>
<Output
TaskParameter="Path"
PropertyName="FrameworkSdkPath" />
</GetFrameworkSdkPath>
<GetFrameworkPath>
<Output
TaskParameter="Path"
PropertyName="FrameworkPath" />
</GetFrameworkPath>
</Target>
La Target GetPaths stocke les chemins du .Net Framework et du FrameWork SDK dans deux propriétés utilisées plus tard par les tasks GetFrameWorkSdkPath et GetFrameWorkPath
GetProjects
<Target Name="GetProjects">
<!-- Get all the projects associated with the solution -->
<GetSolutionProjects Solution="$(CodeFolder)\$(SolutionName)">
<Output TaskParameter="Output" ItemName="SolutionProjects" />
</GetSolutionProjects>
<!-- Filter out solution folders and non .csproj items -->
<RegexMatch Input="@(SolutionProjects)" Expression=".[\.]csproj$">
<Output TaskParameter="Output" ItemName="Projects"/>
</RegexMatch>
<!-- Add Code folder to all solution project paths -->
<RegexReplace Input="@(Projects)" Expression="(qualite.*)\\" Replacement="Code\$1\\" Count="-1">
<Output TaskParameter="Output" ItemName="CSProjects"/>
</RegexReplace>
<!-- Resolve the test projects -->
<RegexMatch Input="@(CSProjects)" Expression=".[\.](Testing|UnitTest|IntegrationTest).*[\.]csproj$">
<Output TaskParameter="Output" ItemName="TestProjects"/>
</RegexMatch>
<!-- Resolve the code projects -->
<CreateItem Include="@(CSProjects)"
Exclude="@(TestProjects);
@(SqlProjects)">
<Output TaskParameter="Include" ItemName="CodeProjects"/>
</CreateItem>
<Message Text="$(NEW_LINE)Resolved the following solution projects:" Importance="high" />
<Message Text="CodeProjects:$(NEW_LINE)$(TAB)@(CodeProjects->'%(RelativeDir)%(FileName)%(Extension)', '$(NEW_LINE)$(TAB)')"
Importance="high"/>
<Message Text="TestProjects:$(NEW_LINE)$(TAB)@(TestProjects->'%(RelativeDir)%(FileName)%(Extension)', '$(NEW_LINE)$(TAB)')"
Importance="high"/>
<Message Text="SqlProjects:$(NEW_LINE)$(TAB)@(SqlProjects->'%(RecursiveDir)%(FileName)%(Extension)', '$(NEW_LINE)$(TAB)')"
Importance="high"/>
</Target>
La target « GetProjects » parse tous les fichiers .sln pour récupérer une liste de projets sur lesquels agir.
Dans l’hypothèse où différents fichiers .sln qui contiennent plusieurs projets que nous voulons compiler existent dans la solution, cette target permet de récupérer uniquement les fichiers csproj contenus dans la solution passée dans le paramètre « SolutionName ». Souvenez vous que le développeur doit pouvoir exécuter sur son poste les mêmes scenarii de compilation et de tests que sur le serveur de build. La target GetProjects utilise les Tâches GetSolutionsProjects, RegExMatch et RegExReplace de MSBuildCommunityTasks pour filtrer les différents fichier projets contenus dans les fichiers .sln selon des conventions de nommage prédéfinies (voir avec Cyril et Guillaume pour définir ces conventions de nommage).
Dans le cadre de gros projets, l’utilisation de plus fichiers de solutions permet d’accroitre la productivité des développeurs.
Dans le cas de nos solutions, il n’y aura logiquement qu’un seul fichier sln pour l’ensemble de la solution.
Cette tâche pourrait alors être remplacée par
<PropertyGroup>
< SolutionFile >qualite.aliceadsl.fr.sln</ SolutionFile >
</PropertyGroup>
<MSBuild
Projects="$(SolutionFile)"
Targets="Build">
<Output
TaskParameter="TargetOutputs"
ItemName="AssembliesBuiltByChildProjects" />
</MSBuild>
Etant donné la structure des solutions, nous pouvons distinguer 3 types de projet.
Les projets qui peuvent être définis comme des projets de présentations.
Il s’agira des projets se trouvant dans le répertoire WebSites.
Ces projets ne doivent pas contenir de logique devant faire l’objet de tests untitaires.
Il en est de même des projets se trouvant dans le répertoire DIS qui logiquement ne font qu’appeler des méthodes et des classes se trouvant dans le répertoire Dependencies\Internal\NomDuProjet
Tous les projets incorporant du code doivent donc se retrouver dans le répertoire Dependencies\Internal\NomDuProjet. Dans ce cas, tous les projets dans ce répertoire doivent faire l’objet de tests unitaires.
Cela donne l’utilisation de l’expression régulière suivante pour regrouper tous ces projets dans un « Item » :
^Dependencies\\Internal\\.*\\.*\.csproj$
GetEnvironment
<Target Name="GetEnvironment">
<!-- Read the the Environment file contents -->
<ReadLinesFromFile File="$(EnvironmentFile)">
<Output TaskParameter="Lines" ItemName="EnvironmentFileContents"/>
</ReadLinesFromFile>
<!-- Assign file contents to Environment property -->
<CreateProperty Value="@(EnvironmentFileContents->'%(Identity)')">
<Output TaskParameter="Value" PropertyName="Environment"/>
</CreateProperty>
<Message Text="Running build for Environment = $(Environment)" importance="high" />
</Target>
Observations :
1. Lignes 4-6 : Nous utilisons ici un fichier texte pour definir l’environnement de compilation. Ce fichier se trouve à la racine du répertoire contenant la solution (Environment.txt). Cela permet de compiler pour différents environnements simplement en modifiant ce fichier.
2. Lignes 9-11 : Le contenu du fichier est assigné à la propriété « Environment » utilisée pour compiler pour un environnement spécifique.
Cette section peut également être gérée en passant différents paramètres dans le fichier appelé par ccnet (qualite.aliceadsl.fr.ccnet dans notre cas).
Il faudra tester quelle solution est la plus adaptée.
GetIterationNumber
<Target Name="GetIterationNumber">
<!-- Read the the iteration number file contents -->
<ReadLinesFromFile File="$(IterationNumberFile)"><Output TaskParameter="Lines" ItemName="IterationNumberFileContents"/>
</ReadLinesFromFile>
<!-- Assign file contents to IterationNumber property -->
<CreateProperty Value="@(IterationNumberFileContents->'%(Identity)')">
<Output TaskParameter="Value" PropertyName="IterationNumber"/>
</CreateProperty>
</Target>
La Target GetIterationNumber lit le numéro d’itération dans un fichier et l’assigne à la propriété IterationNUmber. Nous utilisons le numéro d’itération comme numéro de Build pour le versioning des assemblies, de la base de données … La principale raison de stocker le numéro d’itération dans un fichier est de permettre d’incrémenter automatiquement la version quand la « DeploymentBuild » s’est effectué correctement.
GetRevisionNumber
<Target Name="GetRevisionNumber">
<!-- Get the revision number of the local working copy -->
<SvnInfo LocalPath="$(MSBuildProjectDirectory)">
<Output TaskParameter="Revision" PropertyName="Revision"/>
</SvnInfo>
</Target>
La Target GetRevisionNumber lit le numéro de révision à partir du repository Subversion et l’assigne à la propriété « Revision ». Cela est réalisé en utilisant la tâche SvnInfo définies dans les CommunityTasks. Nous utilisons le numéro de Revision pour versionné la base de données, les assemblies …
CleanSolution
<Target Name="CleanSolution">
<Message Text="Cleaning Solution Output"/>
<!-- Create item collection of custom artifacts produced by the build -->
<CreateItem Include=
"@(TestProjects->'%(RootDir)%(Directory)bin\$(Configuration)\*$(NUnitFile)');
@(TestProjects->'%(RootDir)%(Directory)bin\$(Configuration)\*$(LastTestRunSucceededFile)');
@(TestProjects->'%(RootDir)%(Directory)bin\$(Configuration)\*$(NCoverFile)');
@(TestProjects->'%(RootDir)%(Directory)bin\$(Configuration)\*$(NCoverLogFile)')">
<Output TaskParameter="Include" ItemName="SolutionOutput" />
</CreateItem>
<!-- Delete all the solution created artifacts -->
<Delete Files="@(SolutionOutput)"/>
<!-- Change the default BuildTarget to include a Clean before a Build -->
<CreateProperty Value="Clean;$(BuildTargets)">
<Output TaskParameter="Value" PropertyName="BuildTargets"/>
</CreateProperty>
</Target>
La target CleanSolution s’occupe de supprimer tous les fichiers temporaires créés par notre build. Cela comprend les fichiers temporaires que nous créons pour faire les builds incrémentaux. Cela ajoute également la target Clean à la liste par défaut des BuildTargets à exécuter.
SvnVerify
<Target Name="SvnVerify">
<Error Text="No UserName or Password for accessing the repository has been specified"
Condition=" '$(SvnUsername)' == '' Or '$(SvnPassword)' == '' " />
<Error Text="No SvnTrunkFolder has been specified" Condition=" '$(SvnTrunkFolder)' == '' "/>
<Error Text="No SvnLocalPath has been specified" Condition=" '$(SvnLocalPath)' == '' "/>
</Target>
La target SvnVerify vérifie que les détails nécessaires à l’intégration au repository Subversion ont bien été spécifiés.
Configurations communes de CruiseControl.NET
Sans rentrer dans les détails des options de configuration de CruiseControl.net, nous allons juste décrire comment on peut utiliser les « DTD entities » pour éviter la duplication au travers des différents éléments de configuration xml utilisés par nos Builds. Nous pouvons utiliser cet fonctionnalité afin de partager des entités communes au travers des différentes configurations CC.NET.
<!DOCTYPE cruisecontrol [
<!ENTITY pub "<xmllogger />
<email from='buildserver@yourdomain.fr' mailhost='smtp.yourdomain.fr' includeDetails='true'>
<users>
<user name='BuildGuru' group='buildmaster' address='developer1@yourdomain.fr'/>
<user name='Developer2' group='developers' address='developer2@yourdomain.fr'/>
</users>
<groups>
<group name='developers' notification='change'/>
<group name='buildmaster' notification='always'/>
</groups>
</email>">
<!ENTITY links "<externalLinks>
<externalLink name='Wiki' url='http://wikiserver.yourdomain.fr/wiki' />
</externalLinks>">
<!ENTITY header "<webURL>http://buildserver.yourdomain.fr/ccnet</webURL>
<workingDirectory>C:\Projects\CCNet.Demo</workingDirectory>">
<!ENTITY svn "<sourcecontrol type='svn'>
<executable>C:\Program Files\Subversion\bin\svn.exe</executable>
<trunkUrl>https://svn.yourdomain.co.za/ccnet.demo/trunk</trunkUrl>
<workingDirectory>C:\Projects\CCNet.Demo</workingDirectory>
<username>login</username>
<password>password</password>
</sourcecontrol>">
<!ENTITY svnrevert "<exec>
<executable>C:\Program Files\Subversion\bin\svn.exe</executable>
<buildArgs>revert C:\Projects\CCNet.Demo --recursive</buildArgs>
</exec>">
]>
Au lieu de dupliquer ces éléments, on peut maintenant simplement les référencer en incluant la référence d’entité, par exemple : &svnrevert; ou &svn; dans les différents fichiers de configuration svn.
Cette rubrique concernait toutes les targets communes utilsées par le build. Nous allons maintenant nous attarder sur les targets nécessaires pour le « DeveloperBuild ».
DeveloperBuild
MSBuild Scripts
Comme cela a été précisé, le développeur doit être capable
de compiler et tester le code dans son bac à sable en
utilisant les mêmes ‘targets’ que serveur de Build.
Les ‘targets’ du DeveloperBuild peuvent dont être exécutées en continu par les
développeurs dans leurs bacs à sable.
Avant d’aller plus en avant, il est important de comprendre comment MSBuild
s'appuie fait des builds incrémentaux. Veuillez vous référer à la documentation
afin de bien comprendre cette notion. Il est également important de connaître
d’autres concepts de MSBuild : le batching et
les transformations.
Toutes les ‘targets’ de build du DeveloperBuild utilisent les builds
incrémentaux pour compiler / tester les bases de données et le code. L'avantage
est bien sûr une compilation plus rapide.
BuildDatabases
Cette section est à titre indicatif, aucune solution n’a encore été trouvée pour la gestion des bases de données.
Dans l’exemple de référence, voici ce qui est fait :
Toutes les bases de données sont scriptées dans divers fichiers .sql ce qui permet de créer la structure des bases de données ainsi que le contenu de n’importe quelle base pour une révision spécifique du ‘repository’ Subversion.
Les fichiers de scripts sont stockés dans des sous répertoires du répertorie SQL. Il existe aussi des fichiers batch qui créé la base de données en lançant les fichiers de script qui utilisent osql/sqlcmd. L’exemple supporte à la fois Sql Sercer 2000 + 2005. Ces scripts peuvent également être lancés sur une machine de base de données dédiée.
1 <Target Name="BuildDatabases">
2
3 <MSBuild Projects="@(SqlProjects)"
4 Targets="$(BuildTargets)"
5 Properties="Configuration=$(Configuration);Platform=$(Platform);SqlCmdRunner=$(SqlCmdRunner);DBServer=$(DBServer)"/>
6
7 </Target>
Le groupe d’items SqlProjects contient tous les projets de base de données. SqlCmdRunner est utilisé pour être lancé sur Sql Server 2000/2005 alors que la propriété DBServer est utilisée pour créer la base de données sur une serveur de base de données séparé. Pour chaque base de données, un projet MSBuild a été créé. Il ressemble à ce qui suit :
1 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
2
3 <PropertyGroup>
4 <SqlCmdRunner Condition=" '$(SqlCmdRunner)' == '' ">sqlcmd</SqlCmdRunner>
5 <DBServer Condition=" '$(SqlCmdRunner)' == '' ">(local)</DBServer>
6 <DeploymentBuild Condition=" '$(DeploymentBuild)' == '' ">false</DeploymentBuild>
7 </PropertyGroup>
8 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
9 <OutputPath>bin\Debug\</OutputPath>
10 </PropertyGroup>
11 <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
12 <OutputPath>bin\Release\</OutputPath>
13 </PropertyGroup>
14
15 <ItemGroup>
16 <Sql Include="*.sql" />
17 <Sql Include="Install Database1.bat" />
18 <SqlOutput Include="$(TEMP)\Database1.LastUpdateSucceeded" />
19 </ItemGroup>
20
21 <Target Name="Build"
22 Inputs="@(Sql)"
23 Outputs="@(SqlOutput)">
24
25 <Exec Command="%22Install Database1.bat%22 $(SqlCmdRunner) $(DBServer)"/>
26
27 <Touch Files="@(SqlOutput)" AlwaysCreate="true"/>
28
29 </Target>
30
31 <Target Name="Rebuild"
32 DependsOnTargets="Clean;Build"/>
33
34 <Target Name="Clean">
35 <Delete Files="@(SqlOutput)"/>
36 </Target>
37
38 </Project>
Le fichier de projet est très explicite. Il fournit des Targets pour les targets standards Clean, Buid et ReBuild qui permettent de compiler en utilisant les tâches MSBuild comme nous le ferions pour n’importe quel fichier .csproj. Notez l’utilisation des builds incrémentaux aux lignes 21-23. Tous les fichiers de script .sql et le fichier de batch sont combinés dans un group Sql et utilisés comme « Inputs » pour la Target « Build ». Ceci est comparé au groupe SqlOutput qui contient un fichier Database1.LastUpdateSucceeded unique. Si le « FileStamps » des « items » du groupe Sql est plus ancien que le fichier .LastUpdateSucceeded MSBuild va exécuter la targer « Build » en utilisant sa logique de build incrémental. La ligne 27 exécute la target ‘touch’ pour modifier la date d’accès et de modification du fichier .LastUpdateSucceeded et la mettre à plus tard que les celles des fichiers d’inputs après une création de base de données réussie.
BuildCode
1 <Target Name="BuildCode">
2
3 <!-- Build the assemblies -->
4 <MSBuild Projects="@(CodeProjects)"
5 Targets="$(BuildTargets)"
6
Properties="Environment=$(Environment);Configuration=$(Configuration);Platform=$(Platform);RunCodeAnalysis=$(RunCodeAnalysis)">
7 <Output TaskParameter="TargetOutputs"
8 ItemName="CodeAssemblies"/>
9 </MSBuild>
10
11 <!-- Add the compiled code assemblies to master list of all compiled assemblies for the build -->
12 <CreateItem Include="@(CodeAssemblies)">
13 <Output TaskParameter="Include" ItemName="CompiledAssemblies"/>
14 </CreateItem>
15
16 <!-- If code analysis was run, create an item collection of the FxCop result files -->
17 <CreateItem Include="%(CodeAssemblies.FullPath).$(FxCopFile)"
18 Condition=" '$(RunCodeAnalysis)' == 'true' ">
19 <Output TaskParameter="Include" ItemName="FxCopResults"/>
20 </CreateItem>
21
22 <Message Text="FxCopResults:$(NEW_LINE)$(TAB)@(CodeAssemblies->'%(FullPath).$(FxCopFile)', '$(NEW_LINE)$(TAB)')$(NEW_LINE)"
23 Condition=" '$(RunCodeAnalysis)' == 'true' "
24 Importance="high"/>
25
26 </Target>
Cette section définit la compilation des projets de code. Le fonctionnalité de build incrémental de VS2005 est ici utilisée. La liste des assemblies compilées est ajoutée au groupe d’items CodeAssemblies et CompiledAssemblies pour uneutilisation ultérieure. Le groupe CompiledAssemblies représente la liste de toutes les assemblies compilées – projets de tests y compris.
Les lignes 16-24 peuvent être ignorées pour le moment. Elles sont utilisées uniquement par CodeStatisticsBuild.
BuildTests
1 <Target Name="BuildTests">
2
3 <!-- Build the assemblies -->
4 <MSBuild Projects="@(TestProjects)"
5 Targets="$(BuildTargets)"
6 Properties="Configuration=$(Configuration);Platform=$(Platform);RunCodeAnalysis=$(RunCodeAnalysis)">
7 <Output TaskParameter="TargetOutputs"
8 ItemName="TestAssemblies"/>
9 </MSBuild>
10
11 <!-- Add the compiled test assemblies to master list of all compiled assemblies for the build -->
12 <CreateItem Include="@(TestAssemblies)">
13 <Output TaskParameter="Include" ItemName="CompiledAssemblies"/>
14 </CreateItem>
15
16 <!-- If code analysis was run, create an item collection of the FxCop result files -->
17 <CreateItem Include="%(TestAssemblies.FullPath).$(FxCopFile)"
18 Condition=" '$(RunCodeAnalysis)' == 'true' ">
19 <Output TaskParameter="Include" ItemName="FxCopResults"/>
20 </CreateItem>
21
22 </Target>
La compilation des projets de test ressemble à la target BuidCode. La liste des assemblies compilées est ajoutées aux groupe d’Items TestAssemblies et CompiledAssemblies pour une utilisation ultérieure. Les lignes 16-20 peuvent être ignorées pour le moment puisqu’elle sont utilisées uniquement par CodeStatisticsBuild.
BuildAll
1 <Target Name="BuildAll"
2 DependsOnTargets="BuildDatabases;BuildCode;BuildTests"/>
3
La target BuildAll regroupe les différentes targets de build.
Test
1 <Target Name="Test"
2 DependsOnTargets="BuildTests"
3 Inputs="@(TestAssemblies)"
4 Outputs="@(TestAssemblies->'%(FullPath).$(LastTestRunSucceededFile)')">
5
6 <Message Text="$(NEW_LINE)Running Tests for:$(NEW_LINE)$(TAB)@(TestAssemblies->'%(FileName)', '$(NEW_LINE)$(TAB)')$(NEW_LINE)"
7 Importance="high"/>
8
9 <!-- Remove the success file for the projects being tested -->
10 <Delete Files="@(TestAssemblies->'%(FullPath).$(LastTestRunSucceededFile)')"/>
11
14
15 <NUnit Assemblies="@(TestAssemblies)"
16 ToolPath="$(NUnitPath)"
17 WorkingDirectory="%(TestAssemblies.RootDir)%(TestAssemblies.Directory)"
18 OutputXmlFile="@(TestAssemblies->'%(FullPath).$(NUnitFile)')"
19 ContinueOnError="true">
20 <Output TaskParameter="ExitCode" ItemName="NUnitExitCodes"/>
21 </NUnit>
24
25 <!-- Build an item collection of the test projects with their individual test results.
26 The XmlQuery task creates the attributes of the xml node as additional meta-data items. -->
27 <XmlQuery XmlFileName="%(TestAssemblies.FullPath).$(NUnitFile)"
28 XPath="/test-results">
29 <Output TaskParameter="Values" ItemName="NUnitResults"/>
30 </XmlQuery>
31
32 <Message Text="NUnitResults:$(NEW_LINE)$(TAB)@(NUnitResults->'%(name)[%(failures)]', '$(NEW_LINE)$(TAB)')$(NEW_LINE)"
Importance="high"/>
33
34 <!-- Write a success file for the project if the tests for the project passed -->
35 <Touch Files="%(NUnitResults.name).$(LastTestRunSucceededFile)"
36 Condition=" '%(NUnitResults.failures)' == '0' "
37 AlwaysCreate="true">
38 </Touch>
39
40 <!-- Copy the test results for the CCNet build before a possible build failure (see next step) -->
41 <CallTarget Targets="CopyTestResults" />
42
43 <!-- Fail the build if any test failed -->
44 <Error Text="Test error(s) occured" Condition=" '%(NUnitExitCodes.Identity)' != '0'"/>
45
46 </Target>
Observations :
Lignes 1-4: les ‘outputs’ de la ‘target’ BuildTest sont utilisés par la target Test. Ils sont comparés à la ligne 4 aux fichiers .LastTestRunSucceededFile de chaque assembly de test compilée afin de determiner si la target Test doit être exécutée en se basant sur sa fonctionnalité de build incrémental. Cela permet de ne pas lancé la tâche ‘Test ‘ si aucune assembly de test n’a été recompilée.
Ligne 10 : Avant de lancer les tests, le fichier .LastTestRunSucceeded pour chaque projet compilé de test est supprimé.
Ligne 15-21 : On utilise la tâche NUnit de MSBuildCommunityTasks pour lancer les tests utilisant NUnit. La propriété ContinueOnError est établie à true afin d’exécuter tous les tests. La sortie (ExitCode) pour chaque test lancé est ajoutée à une liste de NUnitExitCodes.
Ligne 41 : Nous devons copier les fichiers de résultats des tests pour CC.NET sur le serveur de build pour faire un ‘merge’. Ces fichiers seront affichés comme rapport de build dans CC.NET.
Ligne 44 : Après avoir lancé les tests, nous utilisons la liste de codes de sortie de NUnitExitCodes afin de faire échouer le build si l’on a un code de sortie différent de zéro.
Afin de supporter les tests incrémentaux, le fichier .LastTestRunSucceeded n’est créé que pour les projets qui ont été compilés avec succès. Ainsi, les projets de tests qui n’ont pas été modifiés ne sont pas relancés.
Il est par contre nécessaire d’associer un code de sortie à ces projets de test. La tâche XmlQuery de MSBuildCommunityTasks arrive alors à la rescousse. Nous l’utilisons pour parser la sortie de NUnit qui est stockée dans un fichier xml. Le résumé des résultats des tests de NUnit sont stockés en tant qu’attributs dans le nœud root ‘tests-results’. La tâche XmlQuery va stocker ces attributs en tant que meta-data personnalisées dans sa prorpiété de sortie Values.
Cette meta-data pourra être utilisée dansles lignes 34-37 afin de faire un ‘touch’ sur les fichiers LastTestRunSucceeded pour tous les projets dont l’attribut ‘faillure’ ==0.
CopyTestResults
1 <Target Name="CopyTestResults"
2 Condition=" '$(CCNetProject)' != '' ">
3
4 <!-- Create item collection of test results -->
5 <CreateItem Include="%(TestAssemblies.FullPath).$(NUnitFile)">
6 <Output TaskParameter="Include" ItemName="NUnitResults"/>
7 </CreateItem>
8
9 <Message Text="NUnitResults:$(NEW_LINE)$(TAB)@(NUnitResults->'%(FullPath)', '$(NEW_LINE)$(TAB)')$(NEW_LINE)" Importance="low"/>
10
11 <CreateItem Include="$(CCNetArtifactDirectory)\*.$(NUnitFile)">
12 <Output TaskParameter="Include" ItemName="ExistingNUnitResults"/>
13 </CreateItem>
14
15 <Delete Files="@(ExistingNUnitResults)"/>
16 <Copy SourceFiles="@(NUnitResults)"
17 DestinationFolder="$(CCNetArtifactDirectory)"
18 ContinueOnError="true"/>
19
20 </Target>
21
Les résultats des tests doivent être copiés simplement si les tests sont lancés sur le ‘buildserver’. Pour cela, la propriété CCNetProject est testée pour determiner si le build a été invoquée par CC..NET. La propriété CCNetProject est l’une des propriétés ‘diffusées’ par CC.NET lorsqu’on utilise la tâche MSBuild de CC.NET.
On créé un groupe pour les résultats des tests et on les copie dans le répertoire défini par CCNetArtifactDirectory sur le serveur de build pour fusionner les résultats des builds.
CodeCoverage
1 <Target Name="CodeCoverage">
2
3 <!-- Find all Presentation assemblies -->
4 <RegexMatch Input="@(CodeAssemblies)" Expression=".[\.](CCNet.Demo.Presentation).*[\.]dll$">
5 <Output TaskParameter="Output" ItemName="PresentationAssemblies"/>
6 </RegexMatch>
7
8 <!-- Filter out the Presentation assemblies from the coverage analysis -->
9 <CreateItem Include="@(CodeAssemblies)"
10 Exclude="@(PresentationAssemblies)">
11 <Output TaskParameter="Include" ItemName="CodeCoverageAssemblies"/>
12 </CreateItem>
13
14 <Message Text="$(NEW_LINE)Running CodeCoverage for:$(NEW_LINE)$(TAB)@(CodeCoverageAssemblies->'%(FileName)',
'$(NEW_LINE)$(TAB)')$(NEW_LINE)"
15 Importance="high"/>
16
17 <!-- Remove the old coverage files for the projects being analysed -->
18 <Delete Files="@(TestAssemblies->'%(FullPath).$(NCoverFile)')"/>
19
25
26 <NCover
27 ToolPath="$(NCoverPath)"
28 CommandLineExe="$(NUnitCmd)"
29 CommandLineArgs="$(DOUBLE_QUOTES)%(TestAssemblies.FullPath)$(DOUBLE_QUOTES) /nologo"
30 CoverageFile="%(TestAssemblies.FullPath).$(NCoverFile)"
31 LogLevel="
32 LogFile="%(TestAssemblies.FullPath).$(NCoverLogFile)"
33 WorkingDirectory="%(TestAssemblies.RootDir)%(TestAssemblies.Directory)"
34 ExcludeAttributes=""
35 RegisterProfiler="false"
36 Assemblies="@(CodeCoverageAssemblies)"
37 ContinueOnError="true"/>
38
40
41 <!-- Create Item collection of all the coverage results -->
42 <CreateItem Include="%(TestAssemblies.FullPath).$(NCoverFile)">
43 <Output TaskParameter="Include" ItemName="NCoverResults"/>
44 </CreateItem>
45
46 <Message Text="NCoverResults:$(NEW_LINE)$(TAB)@(NCoverResults->'%(FileName).$(NCoverFile)', '$(NEW_LINE)$(TAB)')$(NEW_LINE)"
Importance="low"/>
47
48 <!-- Merge all the coverage results into a single summary Coverage file -->
49 <NCoverExplorer ToolPath="$(NCoverExplorerPath)"
50 ProjectName="$(MSBuildProjectName)"
51 OutputDir="$(Temp)"
52 Exclusions=""
53 CoverageFiles="@(NCoverResults)"
54 SatisfactoryCoverage="75"
55 ReportType="ModuleClassSummary"
56 HtmlReportName="$(CodeMetricsFolder)\$(NCoverHtmlReport)"
57 XmlReportName="$(CodeMetricsFolder)\$(NCoverSummaryFile)"
58 FailMinimum="False"/>
59
60 </Target>
61
Observations:
Ligen 3-12 : Nous créons un groupe CodeCoverageAssemblies avec les assemblies pour lesquelles nous voulons créer des statistiques de couverture. Les assemblies pour l’IHM sont filtrées dans la mesure où il n’existe pas de tests pour ces projets.
Ligne 18 : Avant de lancer les tests, les anciens fichiers de couverture NCover pour chaque projet de tests sont supprimés.
Lignes 26-36 : Nous utilisons les tâches MSBuild NCover de NCoverExplorer.Extras pour générer les statistiques de couverture.
Ligne 48-57 : On utilise la tâche MSbuild NCoverExplorer de NCoverExplorer.Extras pour fusionner les résultats de couverture et pour générer un résumé au format xml ainsi qu’un rapport html.
Configuration de CruiseControl .NET
Maintenant que nous avons couvert toutes les targets necessaries pour le DeveloperBuild, regardons la configuration de la section DeveloperBuild du fichier ccnet.config utilisé par DeveloperBuild.
1 <project name="DeveloperBuild" queue="CCNet.Demo" queuePriority="1">
2 &header;
3 <category>Continuous</category>
4 <artifactDirectory>C:\Projects\CCNet.Demo\Builds\DeveloperBuild\Artifacts</artifactDirectory>
5
6 <triggers>
7 <filterTrigger startTime="12:30" endTime="14:00">
8 <trigger type="intervalTrigger" seconds="60" />
9 <weekDays>
10 <weekDay>Friday</weekDay>
11 </weekDays>
12 </filterTrigger>
13 </triggers>
14
15 <state type="state" />
16
17 &svn;
18
19 <labeller type="defaultlabeller"/>
20
21 <!-- NB! Do not remove the log files from the previous build as a prebuild action as incremental builds
22 will not re-create the test run logs for projects that were not updated and thus retested -->
23
24 <tasks>
25 <msbuild>
26 <executable>C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe</executable>
27 <workingDirectory>C:\Projects\CCNet.Demo</workingDirectory>
28 <projectFile>CCNET.Demo.proj</projectFile>
29 <buildArgs>/noconsolelogger /v:normal
/p:SolutionName=CCNet.Demo.sln;Configuration=Debug;DeploymentBuild=false;RunCodeAnalysis=false;DBMS=sql2005;DBServer=(local)</buildArgs>
30 <targets>BuildAll,Test</targets>
31 <timeout>1800</timeout> <!-- 30 minutes -->
32 <logger>C:\Program Files\CruiseControl.NET\server\ThoughtWorks.CruiseControl.MSBuild.dll</logger>
33 </msbuild>
34 </tasks>
35
36 <publishers>
37 <merge>
38 <files>
39 <file>C:\Projects\CCNet.Demo\Builds\DeveloperBuild\Artifacts\*.TestResult.xml</file>
40 </files>
41 </merge>
42
43 <statistics>
44 <statisticList>
45 <firstMatch name="Svn Revision" xpath="//modifications/modification/changeNumber" />
46 </statisticList>
47 </statistics>
48
49 &pub;
50 </publishers>
51
52 &links;
53 </project>
La seule chose intéressante à noter dans ce fichier est le filtre ‘trigger’ qui empêche le build entre 12 :30 et 14 :00 le vendredi.
Part 4: DeploymentBuild
MSBuild Scripts
La fréquence du deployement build reste encore à determiner.
Il doit fournir une version en mode release prête à être mise enprod.
Logiquement le site web doit être déployé sur différents environnements. Dans ces environnements, il existe différents paramètres d’environnements (chemin d’accès à certains fichiers …). Afin de gérer ces différences, il est possible de créer un fichier MSBuild qui copie les fichiers de configuration spécifiques dans le répertoire de sortie en fonction de la valeur définie par la propriété d’environnement. Voici à quoi ressemble le fichier de projet.
1 <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
2 <ItemGroup>
3 <ConfigFiles Include="App.config" />
4 <ConfigFiles Include="ObjectFactories.config" />
5 <ConfigFiles Include="DataSources.config" />
6 <EnvironmentConfigFiles Include="DEV\Environment.config">
7 <Env>DEV</Env>
8 </EnvironmentConfigFiles>
9 <EnvironmentConfigFiles Include="PROD\Environment.config">
10 <Env>PROD</Env>
11 </EnvironmentConfigFiles>
12 <EnvironmentConfigFiles Include="QA\Environment.config">
13 <Env>QA</Env>
14 </EnvironmentConfigFiles>
15 <EnvironmentConfigFiles Include="TST\Environment.config">
16 <Env>TST</Env>
17 </EnvironmentConfigFiles>
18 </ItemGroup>
19 <ItemGroup>
20 <EnvironmentSetting Include="..\..\Environment.txt" />
21 <BindingFiles Include="..\Xml\Bindings\*.xml" />
22 <ConfigOutput Include="$(TEMP)\CCNet.Demo.Startup.LastUpdateSucceeded" />
23 </ItemGroup>
24 <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
25 <PropertyGroup>
26 <Environment Condition=" '$(Environment)' == '' ">DEV</Environment>
27 </PropertyGroup>
28 <Target Name="Build" Inputs="@(EnvironmentSetting);@(EnvironmentConfigFiles);@(ConfigFiles);@(BindingFiles)" Outputs="@(ConfigOutput)">
29 <Copy SourceFiles="@(BindingFiles)" DestinationFolder="$(OutputPath)" />
30 <!-- Copy the environment unspecific files -->
31 <Copy SourceFiles="@(ConfigFiles)" DestinationFolder="$(OutputPath)" />
32 <!-- Copy the environment specific files -->
33 <Copy SourceFiles="@(EnvironmentConfigFiles)" DestinationFolder="$(OutputPath)" Condition=" '%(EnvironmentConfigFiles.Env)' ==
'$(Environment)' " />
34 <Touch Files="@(ConfigOutput)" AlwaysCreate="true" />
35 </Target>
36 <Target Name="Rebuild" DependsOnTargets="Clean;Build" />
37 <Target Name="Clean">
38 <Delete Files="@(ConfigOutput)" />
39 </Target>
40 </Project>
Ce pacakge permet de créer un template « évolué » de solution Visual Studio. Ce template une fois créé se présentera sous la forme d’un msi distribuable et ajoutera dans l’interface de création de nouveaux projets de Visual Studio un nouveau format de solution. Nous verrons cela plus en détail prochainement.
Cet automation Toolkit permet entre autres de gérer différents projets au sein d’un même solution Visual Studio, de créer des wizards, d’incorporer des templates dans la solution … Il permet de gérer
Tout d’abord il faut installer les deux msi suivants :
GuidanceAutomationExtensions.msi
Et
GuidanceAutomationToolkit.msi
Une fois installés, une nouvelle entrée apparaît dans Visual Studio :

Il s’agit ici d’un template standard que nous allons pouvoir customisé de la façon suivante :
Pour une customisation simple d’un template, deux fichiers sont importants :

Le fichier tempCcnet. Xml qui à l’image d’un fichier de configuration msbuild va contenir des propriétés pour le template et le fichier SampleSolution.vstemplate qui va contenir la structure de la solution finale qui va être utilisée.
La solution au final doit intégrer :
Du log.
Toutes les références internes et externes partagées au sein des applications.
Des templates pour la structure d’une page (masterpage).
Tous les contrôles communs à l’ensemble des applications.