3 Steps to Integrating CruiseControl with MSBuild, MSTest, and Code Coverage

Introduction

Putting together a continuous integration build using MSBuild and MSTest with Code Coverage is no simple task. I have spent some time to get this working for a couple of projects at work and thought I'd share what I have learned and help to make it a bit easier on the next person that attempts to wire all this up.

I am writing this mostly from memory so please let me know if there are gaps in what I discuss here and/or this just doesn't work for you.

Starting from scratch you'll want to grab the latest versions of the following:

Step 1 - Install and Configure CruiseControl

After you install CruiseControl.NET, the SDK, and the two MSBuild Task downloads (if you want/need them), on your build server, we'll need to configure CruiseControl. First, you'll want the CruiseControl service running as a user that has permissions to do the things you need to do in your build script (writing and deleting files, accessing a source repository, access network shares, etc.). Next, open up the CruiseControl config file. This is found in C:\Program Files\CruiseControl.NET\server\ccnet.config depending on your install.

Since every environment is different I won't cover all of the blocks. Refer to the CruiseControl.NET documentation for that -- it's good and thorough. Please make note of the notes about MSBuild, in particular, the one regarding the custom logger that you'll need to download and put at the root of your configured working directory.

Make sure to add the compile-msbuild.xsl and msbuild.xsl files to the dashboard.config and configure the FileMerge task to merge the msbuild output so that you get the reporting on the web dashboard. See the sample< ccnet.config at the end of this post.

Step 2 - Write Your MSBuild Script

Here is where you must decide what you want to do. Do you want to merely compile the project? Do you want to execute MSTest unit tests? Zip up the contents/output of the build? I will give you an example of what I am doing with a project and let you modify it from there.

The current build process that I have setup does the following:

  1. Modifies AssemblyInfo files, incrementing the build number and setting the version on the projects that have changed.
  2. Compiles a solution given a certain configuration.
  3. Executes any MSTest unit tests that have been added to a Test List called ContinuousIntegrationTests.
  4. Copies the test results xml report along with any Code Coverage output to a common build output directory.
  5. Code Coverage data is extracted using a tool written by Scott Langford from the binary data.coverage file to an xml report.
  6. Build output is Zipped into an archive with the version number and datetime stamp.
  7. A directory is created for this "release" at a common builds share and then the archive is copied to this location.
  8. Sends an email with a build report.

A bulk of the work occurs in a common targets file that I have written and included here for you to download and customize as you see fit. It's probably best to place it in the common MSBuild directory (C:\Program Files\MSBuild). With this common targets file to reference, writing you build script becomes as simple as setting a few properties. Here is a sample:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <Major>1</Major>
        <Minor>0</Minor>
        <Build>0</Build>
        <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
        <Solution>MySolution.sln</Solution>
        <SmtpServer>my.mail.server.net</SmtpServer>
        <EmailFrom>john@doe.com</EmailFrom>
    </PropertyGroup>

    <Choose>
        <When Condition=" '$(BuildType)' == '' ">
            <PropertyGroup>
                <TargetList>Build</TargetList>
                <BuildTypeTitle>Incremental</BuildTypeTitle>
            </PropertyGroup>
        </When>
        <Otherwise>
            <PropertyGroup>
                <TargetList>Clean;Build</TargetList>
                <BuildTypeTitle>Clean</BuildTypeTitle>
            </PropertyGroup>
        </Otherwise>
    </Choose>

    <Import Project="$(MSBuildExtensionsPath)/TestPackagePublish.targets" />
</Project>

Step 3 - Setup CruiseControl to Display MSTest and Code Coverage Results

Lastly, I created some style sheets to format and display the MSTest and Code Coverage xml reports that you are now merging into the CruiseControl build report. These are VERY basic but should serve as a good starting point to developing whatever reports are useful for you. There is a TON of data in these reports enabling you to create some very rich and dynamic xsl transformations. I wrote a quick tool to help me explore the proper XPath queries in the data as I developed the Xsl. It is included in the archive with the stylesheets for your enjoyment.

To configure these xsl style sheets to display the reporting information in the dashboard, add the following bolded linesto your C:\Program Files\CruiseControl.NET\webdashboard\dashboard.config:

<buildPlugins>
   <buildReportBuildPlugin>
      <xslFileNames>
         <xslFile>xslheader.xsl</xslFile>
         <xslFile>xslmodifications.xsl</xslFile>
         <xslFile>xslcompile.xsl</xslFile>         
         <xslFile>xslcompile-msbuild.xsl</xslFile>
         <xslFile>xslMsTestSummary.xsl</xslFile>
         <xslFile>xslcoverage.xsl</xslFile>
      </xslFileNames>
   </buildReportBuildPlugin>
   <buildLogBuildPlugin />
   <xslReportBuildPlugin description="MSBuild Output" 
                         actionName="MSBuildOutputBuildPlugin"
                         xslFileName="xslmsbuild.xsl" />
   <xslReportBuildPlugin description="MSTest Report" 
                         actionName="MSTESTReport" 
                         xslFileName="xslMsTestSummary.xsl"/>
   <xslReportBuildPlugin description="Code Coverage Report" 
                         actionName="CoverageReport" 
                         xslFileName="xslcoverage.xsl"/>
</buildPlugins>

Make sure the xsl files are copied into the {{{C:\Program Files\CruiseControl.NET\webdashboard\xsl directory as well.

Now, add the lines in the ccnet.config file to merge MSTest and CodeCoverage output:

<project>
   <publishers>
      <merge>
         <files>
            <file>ARTIFACT_PATHmstest-results.xml</file>
            <file>ARTIFACT_PATHcoverage-results.xml</file>
         </files>
      </merge>
   </publishers>
</project>

I am certain that I am probably missing some steps or have otherwise left gaps here. Please feel free to comment or email me if you run into any stumbling blocks and I'll do what I can to help you get up and running.