You are on page 1of 26

CruiseControl.

Net Tutorial
1. Introduction 2. Resources 3. Installation 3.1. Install CruiseControl.NET 3.2. Create a CCNet Website in IIS 3.3. Install Nunit 4. CruiseControl.NET Server Configuration General 5. Structure of a Project Configuration File 6. Source Control Block 7. Trigger Block 8. Labeller Block 9. Tasks Block 10. MsBuild Task 10.1. MSBuild and ReferencePath CruiseControl.NET not resolving reference to Nunit 10.2. An Alternative MSBuild Logger Christian Rodemeyers MsBuildToCCNet 10.3. CruiseControl.NET Webdashboard fails in finding images if not installed in virtual directory 10.4. MSBuildToCCNET reports wrong number of compiled projects 10.5. CruiseControl.NET, MsBuild Task and Resources Assembly Linker 10.6. CruiseControl.NET, MsBuild Task and Web Application projects 11. Nunit Task 11.1. Nunit Task 11.2. Executable Task 12. Publishers Block 13. PreBuild Block 13.1. Install Nant 13.2. Nant Fundamentals 1. Introduction In this article series I will review the various steps needed to setup and configure a working CruiseControl.Net server. The goal is to set up a Continuous Integration process for a sample project as close as possible to a real-life project. I will point out several issues that might arise and I will provide solutions as well. I will try to underline all the details that you need to be aware of. Thanks to Frank Geerlings for making me aware of a casing problem with the xml samples. By talking with WordPress support it came out that there was a little bug and they suggested me how to fix the problem.

2. Resources During the article I will guide you through the installation process of the following software: Subversion CruiseControl.NET Nunit Nant FxCop NantContrib The following are optional: TortoiseSVN AnkhSVN You can find information about how to install: Subversion, TortoiseSVN and AnkhSVN in the article: Guide to Versioning a Visual Studio Solution with Subversion, TortoiseSVN and AnkhSVN. There you can also find a quick guide about how to create a Subversion repository and how to add a Visual Studio solution to source control. This tutorial assumes that you have Subversion 1.4.6 installed and that you have configured a Subversion repository at the url: svn://localhost/trunk as shown in the article: Guide to Versioning a Visual Studio Solution with Subversion, TortoiseSVN and AnkhSVN. If you already have a working environment with a repository and versioned files, you can go on reading and replace the paths and names in the examples with the paths and names in your environment. 3. Installation

3.1. Install CruiseControl.NET 3.2. Create a CCNet Website in IIS 3.3. Install Nunit

This tutorial will assume that you install CruiseControl.NET on the same machine on which you had installed the Subversion repository. So, first of all, lets go through the steps needed to install CruiseControl.NET on your server machine.
3.1. Install CruiseControl.NET

Download the latest CruiseControl.NET release from: SourceForge or the latest build from CCNetLive. You can find release notes here. For this article I used the installer for version 1.4 (CruiseControl.NET-1.4-Setup.exe), downloaded from the CCNetLive website. Run the setup executable file and, when prompted, you must specify your choices for a couple of actions: Install CC.Net server as windows service must be checked so that the setup will install CruiseControl as a Windows service. In the Services management console the CruiseControl service will be displayed with the name: CruiseControl.NET. Create virtual directory in IIS for Web dashboard will create a virtual directory named ccnet in the machines IIS web server. If you have an IIS server installation supporting multiple web sites (this is not the case for Windows XP Professional) and prefer to have a separate web site for the CCNet web dashboard you should uncheck this option. This is my choice. In the next paragraph I will show you how to create an IIS web site for the CruiseControl.NET web dashboard. When the install process is finished all the content will be installed in the folder: C:\%ProgramFiles%\CruiseControl.NET (e.g. C:\Program Files\CruiseControl.NET). Under this path you will find a directory called server, containing all the CCNet binary files and executables, and a directory called webdashboard, containing the CruiseControl.NET web interface.
3.2. Create a CCNet Website in IIS

Here I will add and configure a Website in IIS for the CruiseControl.NET Webdashboard: the administrative interface of CruiseControl.NET. Open IIS Manager in Administrative Tools, right-click on the Web Sites node and select New Web Site. The Web site creation wizard will start. Click Next and you will be prompted for a Web Site Description. Type CCNet and click Next, leave Unassigned the IPAddress, choose a port number (say: 222) and leave empty the Host Header for this site field, then click Next. Choose C:\%ProgramFiles%\CruiseControl.NET\webdashboard as the path for the Website and click Next. Allow Read and Run scripts, the default, and click Next. The wizard is finished and the new Website created.
3.3. Install Nunit

For the sake of our example we will need Nunit so, if you havent already done, install it on the same machine on which you have installed CruiseControl.NET. You can find the latest Nunit release here at SourceForge. This article is based on the release 2.4.6, currently the latest. You can install it by downloading: NUnit-2.4.6-net-2.0.msi.

After the installation you will have a new directory under %ProgramFiles% : C:\ %ProgramFiles%\NUnit 2.4.6. 4. CruiseControl.NET Server Configuration General All the configuration files were going to talk about are placed or are to be placed in: C:\%ProgramFiles%\CruiseControl.NET\server. CruiseControl.NET comes with two server executables: ccservice.exe which is the windows service installed by the installation setup and ccnet.exe which is a console application included for testing purposes. It is much easier to debug a console application that a service so I strongly suggest to make your initial tests with ccnet.exe and carefully read the console output to get familiar with CCNet behavior. Each of the two executables comes with a default configuration file (i.e.: ccservice.exe.config and ccnet.exe.config) that you dont need to change at the moment. Moreover both the server processes (windows service or console application) look for a file named ccnet.config in which you will place all the actual information needed by CCNet to learn what it is supposed to do and how it is supposed to do it. ccnet.config is an xml file with a root element named <cruisecontrol> and a child element, named <project>, for each set of activities that we want CruiseControl.NET to execute, as shown in the following example:
1 <cruisecontrol> 2 <project name="project1"> 3 ... 4 </project> 5 <project name="project2"> 6 7 ... </project>

8 </cruisecontrol>

In this tutorial we will setup our CruiseControl.NET configuration with two logical sets of activities. For the sake of clarity we will benefit of the usage of xml entities. Well define each project configuration in a separate file and import all the files in ccnet.config by means of entity definitions and entity declarations:
1 < !DOCTYPE cruisecontrol [ 2 3 < !ENTITY project1 SYSTEM "file:project1.xml.config"> < !ENTITY project2 SYSTEM "file:project2.xml.config">

4 ]> 5 <cruisecontrol> 6 7 &project1; &project2;

8 </cruisecontrol>

Lets focus, at first, only on the first project, so delete the second entity reference (i.e.: &project2; ) thus obtaining:
1 < !DOCTYPE cruisecontrol [ 2 3 4 ]> 5 <cruisecontrol> 6 &project1; 7 </cruisecontrol> < !ENTITY project1 SYSTEM "file:project1.xml.config"> < !ENTITY project2 SYSTEM "file:project2.xml.config">

5. Structure of a Project Configuration File What we need to do now is creating a file named: project1.xml.config in C:\ %ProgramFiles%\CruiseControl.NET\server and use it to setup CruiseControl.NET main activities. This file will contain what is called the Project Configuration Block for the first CCNet activity that we want to configure. First of all we need to assign a unique name to the root of the Project Configuration Block: the <project> tag. We then write:
1 <project name="1 - testProject"> 2 3 4 5 6 7 <webURL>http://192.168.15.2:222</weburl> <workingDirectory>C:\develop\CCnet\project1WorkingDir </workingDirectory> <artifactDirectory>C:\develop\CCnet\project1CCnetArtifacts </artifactDirectory> ...

8 </project>

Lets talk about the first three children nodes: represents the URL at which the current project is available through the web interface. We will see later why it is useful.

For now just make sure to set it to the IP of the server machine (in which you installed CCNet) and to the port that you chose for the WebDashboard website when you configured the web site in IIS (see: 3.2. Create a CCNet Website in IIS). is the path to the main directory of this project and is meant to contain the checked out version of the project under integration. This path will be accessible as an environment variable: %CCNetWorkingDirectory%, available to external scripts (we will see it later). I use to place all the projects managed with CCNET in folders under a common directory named: C:\develop\CCnet. For this test project I chose: C:\develop\CCnet\project1WorkingDir. It is convenient to choose a directory in which to place all the stuff that will be used by CCNet (script files or executable files that you may want CruiseControl.NET to execute) as well as the workingDirectory in which CCNET will checkout the versioned files and the artifactDirectory in which it will output log files. In the example above I chose the directory: C:\develop\CCnet as the container of all CCNet related stuff. is the path to the directory where all the build logs for this project will be placed. Apart from the three tags described above, the main blocks that were going to add to the project configuration file are:

Source Control Block Trigger Block Labeller Block 9. Tasks Block 13. PreBuild Block 12. Publishers Block

6. Source Control Block Source Control configuration block tells CruiseControl.NET that the project named 1 testProject is bound to a Subversion repository. This means that the task performed when executing this project depends on the status of that particular Subversion repository. As soon as CruiseControl.NET detects a new revision in the repository it updates its working copy and executes the tasks related to the current project. Here is the xml excerpt:
1 <sourcecontrol type="svn"> 2 <trunkUrl>svn://localhost/trunk</trunkUrl> 3 <workingDirectory>C:\develop\CCnet\project1WorkingDir 4 </workingDirectory>

<username>ccnet </username>

6 <password> ccnet </password> 7 </sourcecontrol>

contains the url of the repository that we want to check for modifications (e.g., svn://svnserver/pathToRepo). As we saw in the post Guide to Versioning a Visual Studio Solution with Subversion, TortoiseSVN and AnkhSVN, Subversion could host several repositories under a common folder, say: C:\develop\TestRepo. But in that article, as well as in the current example, we set up only one repository and run svnserve with the command line: svnserve -d -r C:\develop\test\repo. So, specifying the server name (localhost) and the path to the particular repository were interested in, turns out to be simply: svn://localhost/trunk (instead of: svn://localhost/SpecificRepositoryFolder/trunk); is the directory that will contain the working copy checked out by CCNET; sets the timeout for the source control operation. It defaults to 10 minutes and you can set it to a different amount of time in milliseconds (default) or specifying units (millis, seconds, minutes, hours); I left it out thus accepting the default. If you want to set it to a different value remember to not choose a too short timeout value because if the timeout is exceeded the build will fail without providing information about the reason. In that case the only way you can retrieve the actual reason of the build failing is by analyzing CCNET log file in the CruiseControl.NET\server folder (if you set log4net to the debug log level) and must be set to a valid svn account that CCNET can use to access Subversion repository. 7. Trigger Block A trigger block is needed to specify when CruiseControl.NET will start a new integration cycle. We want to check for the repository status continuously so we need an Interval Trigger to tell CruiseControl.NET to perform integration periodically after a specified amount of time:
1 <triggers> 2 <intervalTrigger name="Subversion" seconds="10" /> 3 </triggers>

CruiseControl.NET, as far as project 1 TestProject is concerned, polls the repository every 10 seconds to see if any changes has been committed. The name attribute is used by CruiseControl.NET GUI to identify the trigger that requested the build; The seconds attribute is the amount of time before triggering the next integration cycle.

Each time the time interval elapses, CCNET checks for modifications and, by default, runs integration only if changes are detected. 8. Labeller Block A label is created, at each integration cycle, to identify the specific build occurred. Different labellers can be used to generate the label that CCNet will use to track the builds. Other than the Labeller Blocks that come with the CruiseControl.NET distribution, many people provided plugin Labeller Blocks to target the generation of labels with specific formats. A very good one, IMHO, is SvnRevisionLabeller by David Keaveny (much important to me because I recently contributed to the project). This plugin allows labelling CruiseControl.NET builds with Subversions repository revision numbers and I think this is a really useful feature. So this example will use SvnRevisionLabeller as the Labeller Block. For detailed information about this plugin see my post: CruiseControl.NET and Subversion SvnRevisionLabeller. To use the SvnRevisionLabeller plugin in your installation of CruiseControl.NET you need to unzip the downloaded package and copy the assembly: ccnet.SvnRevisionLabeller.plugin.dll in the server folder directory under the CruiseControl.NET install path (e.g.: C:\Program Files\CruiseControl.NET\server). Next you need to configure the Labeller Block in your project:
1 <labeller type="svnRevisionLabeller"> 2 3 <major>7</major> <minor>11</minor>

4 <url>svn://localhost/trunk</url> 5 </labeller>

SvnRevisionLabeller will produce build labels in the format: major.minor.svnRevision.build where major and minor are the two values you set in the configuration block while svnRevision is the current version in the Subversions repository related to this project. The build number is automatically incremented each time a new build is forced if no further modification has been committed to the repository. <url> is the path to the repository used to retrieve the revision number (e.g. svn://svnserver/pathToRepo/trunk). Actually it should be filled with the same path used

in the <trunkUrl> field of the Subversion Source Control Block. In our example it is:
1 <url>svn://localhost/trunk</url>

Following is a configuration for SvnRevisionLabeller with the complete set of fields:


1 <labeller type="svnRevisionLabeller"> 2 <prefix>Test-</prefix> 3 4 5 6 <major>7</major> <minor>11</minor> <url>svn://localhost/trunk</url> <username>ccnet</username>

7 <password>ccnet</password> 8 </labeller>

Such a configuration will produce a label with the following format: Testmajor.minor.svnRevision.build. <username> and <password> are the username and password of a valid Subversion account for the repository specified in the <url> field. You can use in an external script the build label produced, which is stored in an environment variable. It can be useful to flag release version of output dlls. You can access the build number from a script with the syntax: %CCNetLabel% or from a C# application with the following row:
1 Environment.GetEnvironmentVariable("ccnetlabel");

If you used the SolutionInfo approach in your Visual Studio solution (see my post: SolutionInfo and Partitioned Single Solution) you can use the build label produced by SvnRevisionLabeller to update the AssemblyFileVersion attribute in the SolutionInfo.cs file. In this way you can provide consistent versioning of the released assemblies together with easy trackability of the corresponding source codes revision. Be careful not to use the <prefix> tag in this case because you need to set AssemblyFileVersion to a string in the format: major.minor.svnRevision.build with all of the 4 fields being numerical values. 9. Tasks Block

The tasks block represents how the build of the project actually takes place. In our example we will use an MsBuild Task to accomplish the main purpose of our project, which is to compile the versioned Visual Studio solution. After that we will use an Executable Task to run our unit tests, if the build succeeds. Lets see the whole Tasks Block, at first:
01 <tasks> 02 <!-- compiles working copy -- > 03 04 05 06 07 08 10 11 12 13 14 15 16 17 18 20 21 22 23 24 25 26 27 28 <msbuild> <executable>C:\WINDOWS\Microsoft.NET\Framework\ v2.0.50727\MSBuild.exe </executable> <workingDirectory>C:\develop\CCnet\project1WorkingDir </workingDirectory> <buildArgs>/noconsolelogger /v:quiet /p:Configuration=Debug /p:ReferencePath="C:\Program Files\NUnit 2.4.7\bin" </buildArgs> <targets>ReBuild</targets > <timeout>600</timeout > <logger>c:\Program Files\CruiseControl.NET\server\ Rodemeyer.MsBuildToCCNet.dll</logger > </msbuild> <exec> <executable>C:\Program Files\NUnit 2.4.7\ bin\nunit-console.exe </executable > <buildArgs>/xml:..\project1CCnetArtifacts\nunit-results.xml /nologo Dummy.sln.nunit /exclude:LongRunning,AnotherCategoryName </buildArgs> </exec>

09 <projectFile>DummySolution.sln</projectFile >

19 <!-- launches nunit tests on working copy -- >

29 </tasks>

Lets focus on the MsBuild Task first and see how we can configure and customize it:

10. MsBuild Task Lets have a look at the meaning of the xml nodes children of the <msbuild> node: <executable>: contains the path to the msbuild executable file. You dont really need to set it because the default value is the standard installation path: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\MSBuild.exe. I decided to set it explicitly just to be sure about it . <workingDirectory>: is the directory in which MsBuild will be run, so it must be the directory containing our projects checked out working copy. You can provide a path relative to the current projects workinDirectory but I preferred to provide the full path. Actually, the path to the CCNET checked out working copy is the same as the <workingDirectory> field of the Source Control Block. <projectFile>: is the name of the project to build. MsBuild accepts a Visual Studio solution file as the project file to build. Obviously the MsBuild Task accepts it as well. <buildArgs> This row provides additional command line arguments to MsBuild. We tell it not to log events to the console (/noconsolelogger), to build the Debug configuration (/p:Configuration=Debug) and to provide a reduced output (/v:quiet). As far as the /p:ReferencePath buildArg is concerned it is worth to talk extensively about a problem that could arise with Nunit, so have a look at paragraph 10.1.
10.1 MSBuild and ReferencePath CruiseControl.NET not resolving reference to Nunit

It could happen that CCNET is not able to locate Nunit (or some other dependency assembly) depending on how your project file has been created by Visual Studio. Open your project file (DummyProject.csproj) with a text editor (e.g.: Notepad++). If you find an entry as follows in it:
1 <Itemgroup> 2 3 4 5 <Reference Include="nunit.framework, Version=2.4.7.0, Culture=neutral, PublicKeyToken=96d09a1eb7f44a77, processorArchitecture=MSIL" /> ....

6 </Itemgroup>

with no <HintPath> associated to the Nunit <Reference> it could be that CCNET will not be able to resolve that reference if you dont register nunit.framework.dll in the servers GAC. You can make sure that CCNET is able to resolve the dependency by providing an alternative search path in which to look for. Each assembly referenced in the Visual Studio project file needs to be located by MSBUild at compile time. The location of the referenced assemblies is resolved by MSBuild by looking in several

locations in a particular search order (as explained here). We could modify the .csproj file by providing a value as a child node of the node, e.g.:
1 <Reference Include="DummyLibrary, Version=1.0.0.0, 2 3 4 5 Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> <HintPath>..\..\CommonReferencedDLLs\DummyLibrary.dll </HintPath>

6 </Reference>

This approach has two drawbacks: 1. the hintpath is a relative path, thus making the build success depend on the location of the project relative to the referenced assembly (this could be a problem if we reference an external assembly); 2. we need to modify each Visual Studio project file by hand and we dont want to do it! Fortunately theres an easy way out: We can override all the project specific settings for path resolving by passing a ReferencePath property from the MSBuild command line. Such property accepts as value a list of paths (MSBuild DummySolution.sln /p:ReferencePath=<Path1;Path2;Path3>) and it is checked by the build process before checking other locations (HintPath for example). So the way to provide an alternative search path from the command line is to pass as argument a ReferencePath property. This is the reason why the command line provided in the <buildArgs> field contains the ReferencePath property pointing to the Nunit install path: /p:ReferencePath=C:\Program Files\NUnit 2.4.7\bin. Lets go on with the analysis of the xml nodes children of the <msbuild> node: <targets> specifies which targets to build in this msbuild project file. It represents the MSbuilds /target command line argument. We set it to Rebuild (clean and build). <timeout> is the number of seconds before assuming that the process has hung. If timeout is exceeded the process will be killed. <logger> specifies the path to the assembly containing the logger to use to format the log output of MSbuild . If you dont want to use the alternative logger that I used in the configuration shown above, you can rely on the default logger as explained here and skip the following paragraph 10.2. To use the default logger:

leave out the <logger> field, download the assembly containing the default xml logger here (find info here),

copy the downloaded assembly (ThoughtWorks.CruiseControl.MSBuild.dll) to the folder: C:\%ProgramFiles%\CruiseControl.NET\server.

I suggest to use the alternative logger (by Christian Rodemeyer) as explained in the following paragraph. The RodeMeyers logger provide lighter msbuild output and his modified stylesheets provide much more readable display of such information.
10.2. An Alternative MSBuild Logger Christian Rodemeyers MsBuildToCCNet

I chose to use a logger alternative to the default one: Christian Rodemeyers MsBuildToCCNet. You can find it at: Improved MSBuild Integration. From there either you can download separately the assembly and all related stuff needed to correctly display the results from Rodemeyers logger or you can download a zip file with the whole project and source code. In the page cited above you can find detailed instructions on how the logger works and how it should be configured. In the current paragraph Ill illustrate the installation process and add some useful tips for making it work. I suggest to download the full project that comes in a zip file named: MsBuildToCCNet.zip (download here). Unzipping the package you will have a directory named: MsBuildToCCNet. Inside this directory you will find, among the rest, a directory named Release, containing the assembly: Rodemeyer.MsBuildToCCnet.dll. Just copy the assembly to the \CruiseControl.NET\server folder (e.g.: c:\Program Files\CruiseControl.NET\server\). Theres another subdirectory of the MsBuildToCCNet folder, named ccnet where you can find the resources needed to correctly display the logs produced by Rodemeyers logger. Those resources are: cruisecontrol.css and msbuild2ccnet.xsl. You need to use those two files and to configure the Webdshboard:
1. Move into your CruiseControl.NET Webdashboard folder, under the path: C:\

%ProgramFiles%\CruiseControl.NET\webdashboard (e.g.: c:\Program Files\CruiseControl.NET\webdashboard\) and back up the file: cruisecontrol.css. Then replace it with the cruisecontrol.css file you found in the Rodemeyers MsBuildToCCNet folder (e.g.: copy MsBuildToCCNet\ccnet\cruisecontrol.css to c:\Program Files\CruiseControl.NET\webdashboard\cruisecontrol.css); 2. Theres a subdirectory of the CruiseControl.NET Webdashboard folder, named: xsl. You need to copy the other resource (msbuild2ccnet.xsl) you found in the MsBuildToCCNet\ccnet folder in that directory: C:\%ProgramFiles %\CruiseControl.NET\webdashboard\xsl\;

3. You need to modify the dashboard.config file in the CruiseControl.NET

Webdashboard folder in order to correctly show the output of the logger. Being that were talking about the Webdashboard configuration I will show you all the changes you will need to do to let it work with the following three components (even if well see two of them only in the following paragraphs): o MsBuildToCCNet o Nunit integration o FxCop integration First of all choose a 32 x 32 jpg image representing a smiling icon and place it in the CruiseControl.NET Webdashboard folder: C:\%ProgramFiles %\CruiseControl.NET\webdashboard. Rename the image file: your_happy_image.jpg and, when a new build succeeds, youll obtain a smiling icon in the Webdashboard report! (you can find a sample image here). Then open CruiseControl.NET\webdashboard\xsl\msbuild2ccnet.xsl and go to line 24:
1 <xsl :if test="@error_count = 0 and @warning_count = 0"> 2 <tr> 3 <td><img src="/ccnet/your_happy_image.jpg" alt="Happy Image <img src="http://s.wordpress.com/wp4 includes/images/smilies/icon_smile.gif" alt=":-)" class="wpsmiley"> " /> Juchuu !!!</td> 5 </tr> 6 </xsl>

replace the img src attribute: /ccnet/your_happy_image.jpg with /your_happy_image.jpg if you configured iis with a new website for the webdashboard instead of a virtual directory. If youre usign the default virtual directory named ccnet, dont modify that row. Next you need to locate the field: <buildPlugins> in C:\%ProgramFiles %\CruiseControl.NET\webdashboard\dashboard.config and arrange or delete its children nodes in order to obtain the following configuration (remember to back up the file first):
01 <buildplugins> 02 03 04 05 06 07 <buildreportbuildplugin> <xslfilenames> <xslfile>xsl\header.xsl</xslfile> <xslfile>xsl\modifications.xsl</xslfile> <xslfile>xsl\msbuild2ccnet.xsl</xslfile> <xslfile>xsl\unittests.xsl</xslfile>

08 09 10 11 12 13 14 15 16 17 18 19 20 21

<xslfile>xsl\compile.xsl</xslfile> <xslfile>xsl\fxcop-summary.xsl</xslfile> </xslfilenames> </buildreportbuildplugin> <buildlogbuildplugin /> <xslreportbuildplugin description="NUnit Details" actionName="NUnitDetailsBuildReport" xslFileName="xsl\tests.xsl" /> <xslreportbuildplugin description="NUnit Timings" actionName="NUnitTimingsBuildReport" xslFileName="xsl\timing.xsl" /> <xslreportbuildplugin description="FxCop Report" actionName="FxCopBuildReport" xslFileName="xsl\FxCopReport.xsl" />

22 </buildplugins>

As you can see, such configuration includes also stylesheet files for nunit and fxcop integration. We will soon configure the server to integrate those components. While adding the smiling image in the previous paragraph we had to change the path in the xsl file. The same problem could arise with other stylesheet files if you configured the webdashboard to be a website instead that a virtual directory named ccnet. Have a look at the next paragraph for details:
10.3. CruiseControl.NET Webdashboard Fails in Finding Images if Not Installed in Virtual Directory

If you unchecked Create virtual directory in IIS for Web dashboard as shown in part 1 of this tutorial at 3.1. Install CruiseControl.NET and installed the Webdashboard as a new website as shown in the paragraph: 3.2. Create a CCNet Website in IIS, the webdashboard could have problems in resolving image paths. You will realize it as soon as you will configure the server to integrate nunit or fxcop (will see it in following paragraphs). To make sure not to have this problems you must modify the following files: xsl\tests.xsl xsl\fxcop-summary.xsl under: C:\%ProgramFiles%\CruiseControl.NET\webdashboard\ you have to replace all the paths relative to the root of the website with relative paths, e.g:

in the file: xsl\tests.xsl you should replace all entries like:


1 eImg.src = "<xsl :value-of select="$applicationPath"/>/images/arrow_minus_small.gif";

with:
1 eImg.src = "<xsl :value-of select="$applicationPath"/>images/arrow_minus_small.gif";

and entries like:


1 <img src="{$applicationPath}/images/fxcop-error.gif"/>

with:
1 <img src="{$applicationPath}images/fxcop-error.gif"/>

that is, you simply need to delete the leading forward slash at the beginning of the path (just before the images folder name). you need to accomplish the same task with the file xsl\fxcop-summary.xsl, e.g.: you should replace entries like:
1 <xsl :attribute name="src"><xsl :value-of select="$applicationPath" />/images/fxcop-critical-error.gif</xsl>

with:
1 <xsl :attribute name="src"><xsl :value-of select="$applicationPath" />images/fxcop-critical-error.gif</xsl>

Actually, you should find all paths to images in those two files and delete the leading forward slash. Back to the MSBuildToCCNET alternative Logger for MsBuild, I will explain now why I decided to recompile the source code instead of using the assembly provided: MsBuildToCCNet\Release\Rodemeyer.MsBuildToCCnet.dll
10.4. MSBuildToCCNET Reports Wrong Number of Compiled Projects

I found that MsBuildToCCNet reported the wrong number of projects in the webdashboard in the page reporting the details of the last build. Theres a row that sounds like the following, displaied in that page: 15 Projects built with 2 warnings

Looking at the source code (in the file: Logger.cs) I realized that the list of Project type instances includes the solution file (DummySolution.sln) and a Project object named MSBuild, somehow representing the MsBuild process. Appearently this is the reason why the reported number of projects is wrong. I still havent tried to contact the author so I dont know very well how the Logger is supposed to work as far as this count is concerned. As a workaround I modified the following row:
1 w.WriteAttributeString("project_count", 2 XmlConvert.ToString(projects.Count));

turning it into:
1 w.WriteAttributeString("project_count", 2 XmlConvert.ToString(projects.Count - 2));

in the WriteLog(XmlWriter w) method (file: Logger.cs at row 104). This seems having fixed the problem with no side effects. You could have problems running msbuild task if your server machine (the one in which you installed CruiseControl.NET) is not updated with all the software installed in a developer workstation. Lets see which problems could arise:
10.5. CruiseControl.NET, MsBuild Task and Resources Assembly Linker

If you want to provide localization for any of your projects or somehow use resources files (.resx) you will get an error during the build on the CruiseControl.NET server if MsBuild is not able to locate the Assembly Linker. The error should look something like: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\Microsoft.Common.targets(1950, 9): error MSB3011: C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\AL.exe was not found. Either 1) Install the .NET Framework SDK, which will install AL.exe. Or 2) Pass the correct location of AL.exe into the ToolPath parameter of the AL task. AL.exe is used to produce the satellite assemblies and the executable file is placed in the .NET framework directory. But Al.exe is a .Net Framework SDK tool. It is not included in .Net Framework 2.0 runtime installation. You need to install the .NET framework SDK on the server machine if you dont want to encounter this problem. If you want to solve this particular issue in a tricky way without installing the whole SDK, you can copy al.exe.config e al.exe from a developer workstation and place them in the C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727 directory on the server

machine. This is how I solved the problem but I suggest you to install the .NET framework SDK on the server machine.
10.6. CruiseControl.NET, MsBuild Task and Web Application projects

A particular note is due for Web projects. Updating Visual Studio 2005 you get the SP1. Together with the Service Pack 1 for Visual Studio you get the WebApplication project template. Such template lets us add a new kind of project to our solution: a web site project structured exactly like any other Visual Studio project. So you can quit creating new websites (File > New > Web Site) and start creating new WebApplication projects (File > New > Project and then choose ASP.NET Web Application). In order to use WebApplication projects you need to have Visual Studio installed on your machine and the WebApplication project plugin (that comes with the Visual Studio 2005 SP1). If the server in which CruiseControl.NET server is running is not a develpment workstation you will get an error when trying to build a WebApplication project, beacuse you miss those two prerequisites. You can easily fix this problem: you simply need to copy the file: Microsoft.WebApplication.targets that you can find under: C:\Program Files\MSBuild\Microsoft\VisualStudio\v8.0\WebApplications\ from a development workstation and paste it to the corresponding path on the server machine (creating the directories in the path if needed). Additionally, if youre using an ASP.NET AJAX Enabled WebApplication as the web project template you need to install the aspnet-ajax extensions as well. You can find the installer (ASPAJAXExtSetup.msi) for .NET framework 2.0 here 11. Nunit Task The second Task Block that you find in the xml fragment above is an Executable Task used to instruct CruiseControl.NET to run unit tests with Nunit. At first I tried to use a Nunit Task Block but I soon realized that it was not good for me because it was not possible to provide arguments to the task that should be used as arguments of the Nunit command-line executable. This is a problem because theres no way to let the Nunit task be aware of Nunit categories. For those who dont know, Nunit lets you specify a Category attribute in test methods, with the following syntax:

1 [Test] 2 [Category("LongRunning")] 3 public void VeryLongTest() 4 { /* ... */ }

This attribute allows you to instruct Nunit to treat all the methods belonging to the same category in the same way. Usually what we want to do is to exclude a cluster of tests from running. Typically we exclude tests that take too much to run, using the following syntax with the nunit command-line tool: nunit-console.exe /exclude:LongRunning,AnotherCategoryName You can configure excluded categories in nunit GUI as well, by clicking the Categories tab in the top left corner. A list of available categories will be shown. Just select the categories of interest and click the Add button. Then check the Exclude these categories checkbox at the bottom of the page and run the nunit project. It is very useful to be able to exclude some categories of tests from the continuous integration environment, still being able to run them on developer machines. This is the reason why I recently submitted a patch to CruiseControl.Net adding support for Nunit categories. Its not included in the official release yet, because I submitted it too late for this purpose. However you can find it in the current build which is publicly available at: ccnetlive. The patch is included starting from build: 3591. So now you have two chances to use categories:
1. If you really want to use the last officially released version, read the paragraph

11.2 about how to use an Executable Task (the method described can be useful also if you want to use any other unsupported Nunit command line argument); 2. Instead if you can download the latest build from ccnetlive you will be able to specify categories inside the Nunit Task block, as explained in paragraph 11.1
11.1. Nunit Task

If you downloaded the most recent build and followed the installation instructions (see: 3. Installation) you can go on reading this paragraph. If you installed the official release or a build older than 1.4.0.3591, you can still modify your installation with the following instructions: download the zipped package (here) or source code. Unzip it in a temporary directory and locate the assembly: ThoughtWorks.CruiseControl.Core.dll (which, for the zip package,

is in the directory: server). Copy and replace it to the original one in your CCNET installation directory (back up the original one first) that should be: Program Files\CruiseControl.NET\server. Thats it! Now you can use the CruiseControl.NETs Nunit Task Block also for specifying Nunit categories (see: Nunit categories for reference). It is possible to specify a list of the categories of tests that we want to be excluded. It is possible, as well, to specify a list of the only categories that we want to be included as allowed by the Nunit Command-line or GUI interface. The configuration syntax for the Nunit task becomes:
01 <nunit> 02 <path>C:\Program Files\NUnit 2.4.7\bin\nunit-console.exe 03 </path> 04 05 06 07 08 09 10 <assemblies> <assembly>Dummy.sln.nunit</assembly> </assemblies> <excludedCategories> <excludedCategory>LongRunning</excludedCategory> <excludedCategory>Category 2</excludedCategory> </excludedCategories>

11 </nunit>

for excluded categories, or:


01 <nunit> 02 <path>C:\Program Files\NUnit 2.4.7\bin\nunit-console.exe 03 </path> 04 05 06 07 08 09 10 <assemblies> <assembly>Dummy.sln.nunit</assembly> </assemblies> <includedCategories> <includedCategory>LongRunning</includedCategory> <includedCategory>Category 2</includedCategory> </includedCategories>

11 </nunit>

for included categories. You can find the official reference at this page on the official website.

The Nunit task output log file is automatically integrated in the CCNET build results. So you dont need to specify the Merge task (needed if you use the procedure explaine in paragraph 11.2 instead of this one) in the Publishers block as explained in paragraph 12:
1 <merge> 2 3 4 <files> <file>..\project1CCnetArtifacts\nunit-results.xml</file> </files>

5 </merge>

Instead you still need to delete the previous Nunit log file before each build process as explained in the paragraph 13 PreBuild Block.
11.2. Executable Task

If youre working with the official release of CruiseControl.Net (release 1.4) Nunit Task Block syntax only allows you to specify the target solution and little more. In our example it would be:
1 <nunit> 2 <path>C:\Program Files\NUnit 2.4.7\bin\nunit-console.exe 3 </path> 4 5 6 <assemblies> <assembly>Dummy.sln.nunit</assembly> </assemblies>

7 </nunit>

where Dummy.sln.nunit is the Nunit project file for our solution. The solution I strongly suggest is to replace the Nunit Task with an Executable Task like the one shown below:
1 <exec> 2 3 4 5 6 7 8 <executable> C:\Program Files\NUnit 2.4.7\bin\nunit-console.exe </executable> <buildArgs>/xml:..\project1CCnetArtifacts\nunit-results.xml /nologo Dummy.sln.nunit /exclude:LongRunning,AnotherCategoryName </buildArgs>

9 </exec>

You only need to specify the full path to the Nunit command-line executable file in the <executable> field and the command-line arguments in the <buildArgs> field. In the <buildArgs> field you specify arguments as if provided directly to nunitconsole.exe:
1. specify the path to the file in which nunit will write its output:

/xml:..\project1CCnetArtifacts\nunit-results.xml. The file should be produced in the artifactDirectory of the current CCNET project. By default the executable run by an executable task is run in the Project Working Directory, so the path to the output file is relative to such directory. 2. You can pass many things as Nunit targets (assemblies, Visual Studio projects or Nunit project files). I suggest to create an Nunit project and pass it as argument to nunit-console.exe as shown in the sample above (where the nunit project file is called: Dummy.sln.nunit). 3. you can then add: /exclude:LongRunning,AnotherCategoryName thus excluding unwanted tests. Specifying the name of the output file is not enough. In order to make the output written by nunit in the file nunit-results.xml (arbitrary name specified in the buildArgs tag), available to CruiseControl.NET we need to use a File Merge Task. If we used Nunit task the output file would have been automatically merged with other output for CruiseControl.NET. Using the Executable Task we need to explicitly configure CruiseControl.NET to merge the Nunit output file in the log file parsed by CruiseControl.NET. We will tell CruiseControl.NET to do it at the end of the build process, namely in the Publishers section. 12. Publishers Block We will add a File Merge Task at the beginning of the Publishers section. You can see below the publishers section as it is defined in our project configuration file:
01 <publishers> 02 03 04 05 06 07 08 <merge> <files> <file>..\project1CCnetArtifacts\nunit-results.xml </file> </files> </merge> <xmllogger />

09 10 11 12 13

<statistics /> <modificationHistory onlyLogWhenChangesFound="true" /> <artifactcleanup cleanUpMethod="KeepLastXBuilds" cleanUpValue="20" /> ...

14 </publishers>

The File Merge Task specifies the paths to the files that we want to be merged by the Xml Log Publisher Task with the rest of its own output (you dont need to specify nunit output file if you used the Nunit Task as in paragraph 11.1. Nunit Task). All of this output is placed by default in the buildlogs directory under the Projects Artifact Directory. So the File Merge Task should appear before the Xml Log Publisher Task in the publishers section. The Xml Log Publisher Task (xmllogger) is needed for making the web dashboard work correctly. The statistics field collects and updates statistics for each build. You can see them clicking: View Statistics on the left side of the Web Dashboard. The modificationHistory field logs all the modifications for each build. With onlyLogWhenChangesFound you can choose to log info only for builds happened when changes take place (not for forced builds). You can see the modification history by clicking: View Modification History on the left side of the Web Dashboard. I then added an artifactcleanup field in order to keep memory of the last 20 builds only. This task allows us to choose between two clean up modes of the past build logs: - deleting logs older than a specified number of days - keeping only a specified number of logs: the more recent ones (cleanUpMethod=KeepLastXBuilds) In the sample above we typed the second choice specifying 20 as cleanUpValue thus telling CruiseControl.NET to keep the log files for the last 20 builds only. 13. PreBuild Block Theres another issue to solve when integrating Nunit using either an Exec Task or the Nunit Task: the Nunit log file is not deleted after the build succeeds or fails. So upon the next build well still have the old Nunit log file until the Task running Nunit is executed. To understand what Im going to explain now, keep in mind that if a task in the Tasks Block fails all the subsequent tasks in the block will be skipped while the tasks in the Publishers Block will be executed. If, during the next build, the MSBuild Task fails, the Exec Task (Nunit Task) launching Nunit isnt executed at all so the File Merge Task will merge the previous Nunit log file

with the current Xml Log Publisher output, thus leading to an incorrect report: still displaying the Nunit results relative to the previous build. We would obtain the current MSBuild report but the old Nunit report. This behavior can lead to misunderstanding of the results so it is a good practice to delete the old Nunit log file before every new build takes place. The place to accomplish this task is the PreBuild Block. I used a Nant build file to drive the steps needed to obtain the desired result (simply delete a file if it exists).
13.1. Install Nant

You need to install the Nant Build tool on the server machine. Just download the zip package: nant-0.85-bin.zip from here and unzip it into the folder: C:\Program Files\Nant.
13.2. Nant Fundamentals

Nant is a build tool driven by xml configuration files (Nant build files). When you call nant.exe from the command line and you dont specify a build file (you can specify one passing as argument: -buildfile:pathToDir\FileName.build) NAnt looks for a file ending with .build (e.g.: NAnt.build) in the current directory. If it finds such file, it uses it as the reference for the tasks to execute. The file structure is shown in the following sample xml file:
01 < ?xml version="1.0"?> 02 <project name="dummy" default="target1" basedir="."> 03 04 05 06 07 08 09 10 11 <description>dummy project</description> <target name="target1" description="target1 description"> ... (a list of Nant tasks will be placed here) </target> <target name="target2" description="target2 description"> <delete file="pathToFile\FileName" failonerror="false" /> ... (a list of other Nant tasks will be placed here) </target>

12 </project>

A build file contains one <project> field with one or more children <target> fields each containing different Nant tasks (e.g.: the <delete> task). When invoking nant.exe you can specify the name of the target to be executed and Nant will execute all the tasks contained in that target. If you dont provide a target, the default

one will be executed (i.e. the one specified in the default attribute of the <project> field). Once youve got Nant installed on the server machine you have to create a file named nant.build and place it, for convenience, in the directory in which you placed all the CruiseControl.NET related stuff (in this example: C:\develop\CCnet). At the moment we need just one target to delete Nunit log files. Later we will add another target. The actual file content is the following:
1 < ?xml version="1.0"?> 2 <project name="Dummy" default="cleanNunit" basedir="."> 3 <description>CCNET Tasks</description> 4 5 6 7 8 <target name="cleanNunit" description="removes nunit log file"> <delete file="${CCNetArtifactDirectory}\nunit-results.xml" failonerror="false" /> </target>

9 </project>

When we tell Nant to execute the cleanNunit target, the delete task will be executed and the nunit-results.xml file will be deleted. We use one of the environment variables provided by CCNET to retrieve the path to the artifact directory: ${CCNetArtifactDirectory} so this Nant build file will only work when run from CruiseControl.NET. In the Prebuild Block we will instruct CruiseControl.NET to run a Nant task by adding a CruiseControl.NET Nant Task Block. Such block lets us instruct CruiseControl.NET to execute Nant and lets us specify a Nant build file and the list of Nant targets to execute. Remember that we named the build file: nant.build and we placed it in the directory: C:\develop\CCnet. Now we provide this information to the CruiseControl.NET Nant Task Block as shown in the following example:
01 <prebuild> 02 <!-- clean nunit output to avoid CCNET reporting 03 about previous build tests if current build fails --> 04 05 06 <nant> <executable>C:\Program Filse\Nant\bin\nant.exe </executable>

07 08 09 10 11 12 13

<baseDirectory>C:\develop\CCnet</baseDirectory> <nologo>false</nologo> <buildFile>nant.build</buildFile> <targetList> <target>cleanNunit</target> </targetList> </nant>

14 </prebuild>

the <executable> field specifies the path to the version of nant.exe you want to run, the <baseDirectory> specifies the directory to run the NAnt process in, <nologo> passes the -nologo argument to the Nant command line, <buildFile> specifies the path to the build file to use (relative to the <baseDirectory>), <targetList> is used to specify a list of targets, each one in a <target> field. Now we can be sure that each build has the Nunit log file deleted before being executed.

You might also like