jeudi 19 mai 2011

Utilisation de MsBuild pour automatiser les déploiements

Le déploiement est une étape critique de la vie d’un projet. Cependant, la plupart du temps, il n’existe aucune procédure automatisée, et parfois même aucune documentation. Il existe pourtant de nombreux outils permettant d’automatiser la tâche de déploiement, parmi lesquels figurent NAnt et MSBuild par exemple.

Qu’est-ce que MSBuild?

MSBuild est le système de Build de Visual Studio, ce qui veut notamment dire que tous les fichiers projet (.csproj par exemple) utilisent ce système. Par exemple, si on crée une application console par Visual Studio et qu’on ouvre directement le fichier .csproj avec un éditeur de texte, on obtient :

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>04c1eb93-daac-4486-8781-7c0e8dd5967c</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ConsoleApplication1</RootNamespace>
<AssemblyName>ConsoleApplication1</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<PlatformTarget>x86</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>



On voit que MSBuild est en fait basé sur XML, et que les fichiers MSBuild respectent un schéma bien précis. Ce dernier permet de définir notamment :



  • Des Target identifiés par leur nom, permettant de regrouper des tâches et d’organiser leur exécution.

  • Des Task représentant une action unitaire pour le système MSBuild. La liste des tâches disponible permet de donner rapidement une vue d’ensemble de ce qu’on peut faire avec MSBuild (on y retrouve les tâches associées à la compilation notamment). Le système prévoit une extensibilité par l’implémentation d’une interface ITask et par l’utilisation de la balise Import.

  • Des ItemGroup contenant des Item représentant des fichiers, la plupart du temps en vue d’une compilation.

  • Des PropertyGroup représentant de définir des propriétés personnalisées.

Un véritable mini-langage pour le déploiement


MSBuild ne s’arrête pas là. A l’aide des éléments When, Choose et Otherwise et des attributs Condition et DependsOn (pour les Target), on peut très rapidement développer une procédure de déploiement complexe qui répond aux problématiques courantes :



  • Création automatique de backups estampillés à la date du jour.

  • Notification automatique de l’équipe projet lorsqu’un nouveau déploiement a lieu.

  • Prise en compte des différences d’environnement entre déploiements en intégration/préproduction/production (notamment pour prendre les bons fichiers de configuration et les bons identifiants pour les serveurs de destination).

  • Lancement automatique de tests unitaires avant tout déploiement.

  • Tout autre traitement automatisé à l’aide de la tâche Exec.

En outre, de nombreuses tâches récurrentes (envoi de mails, compression, archivage sur le contrôleur de code source, copie sur serveur FTP, etc.) sont déjà développées dans le MSBuild Community Tasks Project et viennent largement consolider les tâches MSBuild de base.


On dispose finalement d’un véritable mini-langage compréhensible et extensible pour la réalisation de tâches de déploiement. Pour exécuter le fichier MSBuild et lancer un déploiement, il suffit d’utiliser l’outil msbuild du Framework .NET. Par exemple, en créant un .bat avec le code suivant, où BuildAll.xml est le fichier MSBuild et “Package” un Target à l’intérieur du fichier MSBuild qu’on souhaite exécuter explicitement :

call "%VS100COMNTOOLS%vsvars32.bat"
msbuild BuildAll.xml /t:Package



Un exemple concret


Pour finir, voici un exemple concret d’utilisation d’MSBuild, où la cible Package permet de :



  • Compiler un projet en utilisant comme répertoire de sortie Build\

  • D’exécuter des tests unitaires

  • De créer un fichier Release.txt contenant la date et l’heure courante dans le répertoire Build\

  • De compresser le répertoire Build\

  • De supprimer le répertoire temporaire Build\
<Project DefaultTargets="Package" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="3.5">
<PropertyGroup>
<MSBuildCommunityTasksPath>chemin de la dll…</MSBuildCommunityTasksPath>
</PropertyGroup>
<Import Project="Dependencies\MSBuild.Community.Tasks.Targets"/>

<PropertyGroup>
<SourceDir>répertoire contenant les sources…</SourceDir>
<BuildDir>Build\</BuildDir>
</PropertyGroup>

<Target Name="Build">
<RemoveDir Directories="$(BuildDir)" ContinueOnError="true" />
<MakeDir Directories="$(BuildDir)" ContinueOnError="false" />

<MSBuild Projects="$(SourceDir)\ExempleDeDossier\ExempleDeProjet.csproj"
Properties="Configuration=Release;DebugSymbols=false;DebugType=none;OutDir=$(MSBuildProjectDirectory)\$(BuildDir)"
BuildInParallel="True">
</MSBuild>

<Prompt Text="Build Terminated. Hit [ENTER] ..." />
</Target>


<Target Name="Test" DependsOnTargets="Build">
<Exec Command="MsTest /noresults /testmetadata:&quot;$(SourceDir)\ExempleDeFichierDeTests.vsmdi&quot;" />

<Prompt Text="Tests Terminated. Hit [ENTER] ..." />
</Target>

<Target Name="Package" DependsOnTargets="Build;Test">
<Time Format="yyyyMMddHHmmss">
<Output TaskParameter="FormattedTime" PropertyName="CurrentDate" />
</Time>

<WriteLinesToFile File="$(BuildDir)\Release.txt" Lines="$(CurrentDate)" Overwrite="true" />

<ItemGroup>
<ZipFiles Include="$(BuildDir)\**\*.*" />
</ItemGroup>

<Zip Files="@(ZipFiles)"
ZipFileName="$(MSBuildProjectDirectory)\LeFichierDeBackup.zip" ZipLevel="9" />

<RemoveDir Directories="$(BuildDir)" ContinueOnError="false" />

<Prompt Text="Package Terminated. Hit [ENTER] ..." />
</Target>
</Project>

Aucun commentaire:

Enregistrer un commentaire