Automated Coder

Exploring the Code of CruiseControl.Net

Archive for May, 2009

Improved NCover Integration

Posted by Craig Sutherland on 29 May, 2009

Is Your Code Covered?

One of the important metrics for TDD is code coverage – how much of the code base is actually being tested by unit tests. It’s not the only metric (even with 100% coverage you can’t guarantee that everything will be perfect), but it is a reasonably easy metric to measure.

There are various tools out there to measure this, but one of my favourites is NCover. NCover originally started as an open source tool, and has now moved into the commercial realm. I originally didn’t see the point in measuring code coverage, but since I’ve been using NCover at work, I now realise it is invaluable. And I certainly think they’ve improved things a lot with NCover 3.

If you want to find out more about NCover, they have a website with all the details – http://www.ncover.com/. They have even put together a pretty good tutorial on integrating CruiseControl.NET with NCover – http://docs.ncover.com/how-to/continuous-integration/cruisecontrol-net/.

However, the integration between the two products isn’t really that good. I should stress, it’s not because of NCover – they do everything right – it’s because CruiseControl.NET has a way of working, and we’ve so far forced everyone into using that path.

But the good news is this is changing!

The Current State

Currently CruiseControl.NET doesn’t directly handle running NCover – it has to be run from the <exec> task, or as part of a build script (e.g. <nant>, <msbuild>, etc.) Next, the results from NCover need a second task to merge them – otherwise they are ignored!

Finally, CruiseControl.NET uses XSL-T files to convert the results (which must be in XML) into HTML. So, if there is a huge amount of data, the result displays are very, very slow.

So, there are a number of hoops to jump through for getting NCover results into CruiseControl.NET. A much better approach would be some simple tasks to directly integrate and display the results – including NCover’s HTML reports.

And What’s New

As an alternate approach to using <exec> or build scripts, I have added two new tasks to CruiseControl.NET. These tasks allow CruiseControl.NET to run NCover directly and automatically merge the results!

Why two tasks? This is because of the way NCover works – there is a profiling application and a reporting application. Originally I disagreed with this approach, but since I’ve played around with it, I’ve come to agree with this approach. With the two applications, you can profile a project once and then generate multiple different reports. And if you have a huge suite of tests, it’s certainly good news to run the profiler only once!

Before I move into a tutorial on these tasks, I should mention that these tasks are designed to run with NCover 3.0. The profiling task might work with NCover 2.0, but I certainly haven’t tested it (nor will I), and the reporting task certainly won’t!

A Quick Tutorial

So, how do you run NCover from CruiseControl.NET?

First, you need to make sure NCover is installed and your code is built. Additionally you’ll need to know the location of both NCover and the unit test runner executables, and also the location of the binaries. Once you know this this, then you’re all ready to start!

Running NCover

Now, open ccnet.config (the default location of this is in the server folder in the install location). If you’ve already used CruiseControl.NET, you should have one or more projects set up – otherwise take a look at the documentation on how to do this (http://confluence.public.thoughtworks.org/display/CCNET/Configuring+the+Server). So you should have something that looks like this:

<project name="MyProject">
    <webURL>http://mybuildserver/ccnet/</webURL>
    <workingDirectory>C:\Integration\MyProject\WorkingDirectory</workingDirectory>
    <artifactDirectory>C:\Integration\MyProject\Artifacts</artifactDirectory>
    <!-- <triggers /> -->
    <!-- <sourcecontrol /> -->
    <tasks>
        <!-- Build task here, e.g. <nant>, <msbuild>, etc. -->
        <!-- other tasks here ... -->
    </tasks>
    <publishers>
        <xmllogger />
    </publishers>
</project>

To add NCover profiling, add an <ncoverProfile> task to the tasks block. I would recommend adding this after the test binaries have been built so you are profiling the latest version. As a bare minimum you need to specify the locations of NCover, your test runner and the binaries.

Your project will then look something like the following:

<project name="MyProject">
  <webURL>http://mybuildserver/ccnet/</webURL>
  <workingDirectory>C:\Integration\MyProject\WorkingDirectory</workingDirectory>
  <artifactDirectory>C:\Integration\MyProject\Artifacts</artifactDirectory>
  <!-- <triggers /> -->
  <!-- <sourcecontrol /> -->
  <tasks>
    <!-- Build task here, e.g. <nant>, <msbuild>, etc. -->
    <!-- other tasks here ... -->
    <ncoverProfile>
      <executable>C:\Program Files\NCover\NCover.Console.exe</executable>
      <program>tools\nunit\nunit-console.exe</program>
      <testProject>myproject.test.dll</testProject>
      <workingDir>build\unittests</workingDir>
      <includedAssemblies>myproject.*.dll</includedAssemblies>
    </ncoverProfile>
  </tasks>
  <publishers>
    <xmllogger />
  </publishers>
</project>

This tell CruiseControl.NET to run NCover from C:\Program Files\NCover\NCover.Console.exe. The test runner is nunit-console (this is located in C:\Integration\MyProject\WorkingDirectory\tools\nunit\nunit-console.exe). The project that will be tested is myproject.tests.dll (this could be any valid nunit project).

This will run NCover and automatically merge the results. You could stop at this point and just use the old NCover XSL-T files to display the results.

However, NCover also comes with an impressive suite of reports, which be also be run directly. To do this you need to add one or more <ncoverReport> tasks. Each report task can run one or more reports – this allows you to change the parameters.

For example, to produce a summary report with a minimum coverage of 95%:

<ncoverReport>
  <executable>C:\Program Files\NCover\NCover.Reporting.exe</executable>
  <outputDir>ncover\reports</outputDir>
  <reports>
    <report>Summary</report>
  </reports>
  <minimumThresholds>
    <coverageThreshold metric="SymbolCoverage" value="95"/>
  </minimumThresholds>
</ncoverReport>

Or to generate a full report that is ordered by coverage percentage in a descending order:

<ncoverReport>
  <executable>C:\Program Files\NCover\NCover.Reporting.exe</executable>
  <outputDir>ncover\reports</outputDir>
  <reports>
    <report>FullCoverageReport</report>
  </reports>
  <sortBy>CoveragePercentageDescending</sortBy>
</ncoverReport>

The full details on what can be reported on is available from the NCover site (http://docs.ncover.com/ref/3-0/ncover-reporting/).

Coming back to our example project, I’ve added the default report (full report):

<project name="MyProject">
  <webURL>http://mybuildserver/ccnet/</webURL>
  <workingDirectory>C:\Integration\MyProject\WorkingDirectory</workingDirectory>
  <artifactDirectory>C:\Integration\MyProject\Artifacts</artifactDirectory>
  <!-- <triggers /> -->
  <!-- <sourcecontrol /> -->
  <tasks>
    <!-- Build task here, e.g. <nant>, <msbuild>, etc. -->
    <!-- other tasks here ... -->
    <ncoverProfile>
      <executable>C:\Program Files\NCover\NCover.Console.exe</executable>
      <program>tools\nunit\nunit-console.exe</program>
      <testProject>myproject.tests.dll</testProject>
      <workingDir>build\unittests</workingDir>
      <includedAssemblies>myproject.*.dll</includedAssemblies>
    </ncoverProfile>
    <ncoverReport>
      <executable>C:\Program Files\NCover\NCover.Reporting.exe</executable>
      <outputDir>ncover\reports</outputDir>
    </ncoverReport>
  </tasks>
  <publishers>
    <xmllogger />
  </publishers>
</project>

Again, this will automatically merge the results for you. Additionally the reports are also stored in the ncover/reports folder (this will be in the C:\Integration\MyProject\Artifacts folder).

And that’s all that is required to run NCover in CruiseControl.NET! Nice and easy, but still allowing the full power of NCover.

Displaying Reports

As well as using the XSL-T reports (from the NCover tutorial) you can also directly display the HTML reports within the dashboard.

To display a report within the dashboard is very easy. First open dashboard.config (the default location of this is in the webdashboard folder in the install location). Next find the <buildPlugins> and add an <htmlReportPlugin> element for each report to display.

For example, to display the full coverage report your <buildPlugins> section would look like the following:

<buildPlugins>
  <buildReportBuildPlugin>
    <xslFileNames>
      <xslFile>xsl\header.xsl</xslFile>
      <xslFile>xsl\modifications.xsl</xslFile>
    </xslFileNames>
  </buildReportBuildPlugin>
  <buildLogBuildPlugin />
  <htmlReportPlugin description="NCover Report" actionName="NCoverBuildReport" htmlFileName="ncover\fullcoveragereport.html" />
</buildPlugins>

And that’s all that is required – no need to copy any files! This is what it will look like:

NCover

As you can see, this is the full report from NCover. In this case, the report is actually a frameset with sub-reports! Clicking around within the report works just like expected. Additionally, if you don’t have enough screen space, you can open up the report in its own window (so you don’t have all the CruiseControl.NET stuff around it.)

You can add as many <htmlReportPlugin> elements as desired – just make sure the description and the actionName are different for each.

One Final Note

This new functionality is all part of the 1.5.0 release currently. This means if you are using 1.4.4 or earlier, it won’t work. Since this is still in development, I have included the full reference for the new tasks below. The binaries and installer can be downloaded from http://ccnetlive.thoughtworks.com/CCNet-builds/1.5.0/.

So feel free to try this functionality – if you have ay problems with it, either leave me a comment or post a message on the mailing list.

Task Reference

This is the full reference for the new tasks. Since these tasks map through to the NCover executables I have also included the mappings for the command-line arguments. The full details of these arguments are available at http://docs.ncover.com/ref/3-0/ncover-reporting/command-line.

ncoverProfile

Name Description Type Required Default
program The program to execute and collect coverage statistics from String Yes n/a
testProject The project that contains the tests. If relative, this will be relative to baseDir String No None
programParameters The parameters to pass to the program. String No None
executable The executable to use. String No Ncover.Console
description A description of the task. String No None
timeout The time-out period in seconds. If the task does no finish running in this time it will be terminated. Int No 600
baseDir The base directory to use. All relative parameters will be relative to this parameter. String No Working directory for the project
workingDir The working directory for the executable. If relative, this will be relative to baseDir.

Maps to //w

String No baseDir
publish Whether to publish the results or not. Boolean No True
logFile The location of the NCover log file. If relative, this will be relative to baseDir.

Maps to //l

String No None
logLevel The profiler log level.

Maps to /ll

Enum (Default, None, Normal, Verbose) No Default
projectName The name of the project.

Maps to //p

String No None
coverageFile The location to write the coverage file to. If relative, this will be relative to baseDir.

Maps to //x

String No Coverage.xml
coverageMetric The coverage metric to use.

Maps to //ct

String No None
excludedAttributes The attributes to exclude.

Maps to //ea

String No None
excludedAssemblies The assemblies to exclude.

Maps to //eas

String No None
excludedFiles The files to exclude.

Maps to //ef

String No None
excludedMethods The methods to exclude.

Maps to //em

String No None
excludedTypes The types to exclude.

Maps to //et

String No None
includedAttributes The attributes to include.

Maps to //ia

String No None
includedAssemblies The assemblies to include.

Maps to //ias

String No None
includedFiles The files to include.

Maps to //if

String No None
includedTypes The types to include.

Maps to //it

String No None
disableAutoexclusion Whether to turn off auto-exclusion or not.

Maps to //na

Boolean No False
processModule The module to process.

Maps to //pm

String No None
symbolSearch The symbol search policy to use.

Maps to //ssp

String No None
trendFile The location to write the trend file to. If relative, this will be relative to baseDir.

Maps to //at

String No None
buildId A custom build id to attach.

Maps to //bi

String No The build label
settingsFile A location to read the settings from. If relative, this will be relative to baseDir.

Maps to //cr

String No None
register Temporarily register NCover.

Maps to //reg

Boolean No False
applicationLoadWait The amount of time NCover will wait for the application to start up.

Maps to //wal

Int No None
iis Whether to cover IIS or not.

Maps to //iis

Boolean No False
serviceTimeout The time-out period for covering a service or IIS.

Maps to //st

Int No None
windowsService A Windows service to cover.

Maps to //svc

String No None
ncoverReport

Name Description Type Required Default
executable The executable to use. String No NCover.Reporting
description A description of the task. String No None
timeout The time-out period in seconds. If the task does no finish running in this time it will be terminated. Int No 600
baseDir The base directory to use. All relative parameters will be relative to this parameter. String No Working directory for the project
workingDir The working directory for the executable. If relative, this will be relative to baseDir.

Maps to //w

String No baseDir
coverageFile The location to read the coverage date from. String No Coverage.xml
clearFilters Should the coverage filters be cleared.

Maps to //ccf

Boolean No None
filters The coverage filters to apply.

Maps to //cf

Array of coverageFilter No None
minimumThresholds The minimum coverage thresholds.

Maps to //mc

Array of coverageThreshold No None
minimumCoverage Whether to use minimum coverage or not.

Maps to //mcsc

Boolean No False
xmlReportFilter The type of report filtering to use.

Maps to //rdf

Enum (Default, Assembly, Namespace) No Default
satisfactory The satisfactory coverage thresholds.

Maps to //sct

Array of coverageThreshold No None
numberToReport The maximum number of items to report.

Maps to //sct

Int No -1
trendOutput The file to output the trend information to. If relative, this will be relative to baseDir.

Maps to //at

String No None
trendInput The file to import the trend information from. If relative, this will be relative to baseDir.

Maps to //lt

String No None
buildId A custom build id to attach.

Maps to //bi

String No The build label
hide The elements to hide.

Maps to //hi

String No None
outputDir The directory to output the reports to. If relative, this will be relative to baseDir. String No None
reports The reports to generate.

Maps to //or

Array of Enum (FullCoverageReport, Summary, UncoveredCodeSections, SymbolSourceCode, SymbolSourceCodeClass, SymbolSourceCodeClassMethod, MethodSourceCode, MethodSourceCodeClass, MethodSourceCodeClassMethod, SymbolModule, SymbolModuleNamespace, SymbolModuleNamespaceClass, SymbolModuleNamespaceClassMethod, SymbolCCModuleClassFailedCoverageTop, SymbolCCModuleClassCoverageTop, MethodModuleNamespaceClass, MethodModuleNamespaceClassMethod, MethodCCModuleClassFailedCoverageTop, MethodCCModuleClassCoverageTop) No FullCoverageReport
projectName The project name to use in the reports.

Maps to //p

String No None
sortBy The sort order to use.

Maps to //so

Enum (None, Name, ClassLine, CoveragePercentageAscending, CoveragePercentageDescending, UnvisitedSequencePointsAscending, UnvisitedSequencePointsDescending, VisitCountAscending, VisitCountDescending, FunctionCoverageAscending, FunctionCoverageDescending) No None
uncoveredAmount The amount of uncovered items to report on.

Maps to //tu

Int No None
mergeMode The merge mode to use.

Maps to //mfm

Enum (Default, KeepSourceFilters, Destructive, AppendFilters) No Default
mergeFile The file to store the merged data in. If relative, this will be relative to baseDir.

Maps to //s

String No None
coverageFilter

Name Description Type Required Default
data The pattern to use for matching items, String Yes n/a
type The type of item. Enum (Default, Assembly, Namespace, Class, Method, Document) No Default
regex Whether this is a regex or not. Boolean No False
include Whether to include items or not. Boolean No False
coverageThreshold

Name Description Type Required Default
metric The coverage metric. Enum (SymbolCoverage, BranchCoverage, MethodCoverage, CyclomaticComplexity) Yes n/a
value The minimum coverage value Int No None
type The type of item. Enum (Default, View, Module, Namespace, Class) No None
pattern The matching pattern to use. String No None

Posted in CruiseControl.Net | Tagged: | 3 Comments »

CruiseControl.NET and Ohloh

Posted by Craig Sutherland on 26 May, 2009

Introducing Ohloh

Ohloh is a social site for open-source development – it allows developers to track what is happening with their projects, to “toot their horns” so to speak. Their website is at http://www.ohloh.net/ – and you can even see CruiseControl.NET on there (https://www.ohloh.net/p/cruisecontrol).

Some More Stats

Since we are both open source projects, I thought I’d add a quick link between the two. This link means you can now see Ohloh stats for your open source project within CruiseControl.NET itself.

Actually, I didn’t think of this idea myself – instead I got it from the Subtext instance of CruiseControl.NET (http://build.subtextproject.com/ccnet/ViewFarmReport.aspx). This is another open source project – one that uses both Ohloh and CruiseControl.NET. Actually they’ve done a very nice job of customising CruiseControl.NET – take a look sometime.

Rather than using their approach, I’ve taken a more limited approach (their Ohloh stats are displayed on the main pages – which is ok for them since they have CruiseControl.NET for only one project). Instead I’ve added a plug-in that enables a project to be linked to Ohloh.

Here are some pictures of what I’ve implemented:

Ohloh-1

This first picture shows the Ohloh quick summary next to the project title. Also, there is now a “View Ohloh" Stats” menu command (on the left). Clicking on this link brings up the Ohloh widgets in their own page:

Ohloh-2

Configuring this requires two steps:

Configure the server

since the stats are on a per-project basis the actual link is stored in ccnet.config. To configure a project to link through to Ohloh, add the following configuration:

<linkedSites>
  <namedValue name="ohloh" value="322"/>
</linkedSites>

In this case the project id for CruiseControl.NET in Ohloh is 322. This would be replaced with the identifier of the project.

Add the Ohloh Stats Package

The first step configured the server, this step configures the plug-in in the dashboard. The quick summary will be enabled when the Ohloh link is added, to add the “View Obloh Stats” command this plug-in must be installed.

In Conclusion

This was a nice simple feature to implement (for a change!) If you have an open source project, give it a spin and let me know what you think (sorry – this is only for open source).

Posted in CruiseControl.Net | Tagged: | Leave a Comment »

Working Towards a Vision

Posted by Craig Sutherland on 25 May, 2009

What Was Past

One of the changes that I recently added to CruiseControl.NET (for the 1.5 release) is a common communications library. This project is a cut-down version of Remote – I stripped out everything that had nothing to do with communications (e.g. exceptions, NetReflector, lots of interfaces, etc.)

The aim for this library is simple – it contains everything that is needed for a client to communicate with a server. This includes all communications protocols – currently HTTP and .NET Remoting – plus an implementation of every possible method. Additionally it contains a number of classes to simplify the process of communicating with a server.

Considering a lot of this already existed within Remote, you might ask why I have added another library to do this? The answer is simple – Remote contained the basic components for communications, it didn’t actual provide anything to facilitate the process. Instead, each client had to write its own mechanisms for communications – which is why CCTray has one way for communications, the dashboard has another and the server uses yet another way (when it needs to communicate with other servers.) Making a change in one wouldn’t affect the other – so if there was a common change it needed to be made to all three!

Finally, these mechanisms were deeply entrenched within each client. I couldn’t just take the communications part of CCTray and use it – I had to include everything else (and CCTrayLib has a lot of stuff in it!) I certainly tried and did produce a simple command-line client that used CCTrayLib. The problem was it needed all the other dependencies for CCTray, and it wasn’t the easiest thing to use.

What Is Now

As I wrote above, I have added a new communications only project to CruiseControl.NET. Remote still contains everything, plus the new communications work, while the communications library only contains the communications classes (hence it is a subset of remote.)

The heart of this library is a class called CruiseServerClientBase. This class is actually an abstract class, but it exposes all of the communications methods. The actual implementations of this class take the methods and pass them onto the server for processing. This means a developer does not need to worry about how the methods are transmitted, just that they are transmitted.

Since CruiseServerClientBase is an abstract class there is a static factory class called CruiseServerClientFactory that generates instances of it. This can be as simple as just saying “I want a client class” or as complex as saying “I want a HTTP client class that communicates to the localhost server”. Eventually over time this factory will handle a whole variety of communications protocols – while hiding the complexity from the developer.

The following shows the only line that is needed to instantiate a CruiseServerClientBase:

var client = CruiseServerClientFactory.GenerateClient("tcp://localhost:21234");

This will create a client for connecting to the server on localhost. Since the protocol is tcp:// it will create a .NET remoting client. There are also methods to create HTTP or .NET Remoting clients directly – GenerateRemotingClient() and GenerateHttpClient().

Under the hood these methods all create an instance of CruiseServerClient. This is another facade class that provides common functionality. With the 1.5 release CruiseControl.NET is moving to a message-style method interface. This style means every message has a request message and a response message. CruiseServerClient generates the request messages, passes them onto a IServerConnection instance and then processes the response. The IServerConnection instance is actually responsible for transmitting the messages.

As you can guess, there are HTTP and .NET Remoting implementations of IServerConnection. These serialise the message, send it using the correct protocol and then deserialise the response. This response is then returned from the implementation.

As such, it is possible to completely bypass CruiseServerClientFactory and CruiseServerClient and use the IServerConnection instance directly. However this would mean you’d need to write the code to generate the messages yourself, and you’d need to process the responses yourself. CruiseServerClient does all of this plumbing for you.

The following shows how it is possible to trigger a force build with just two lines of code:

var client = CruiseServerClientFactory.GenerateRemotingClient("tcp://localhost:21234");
client.ForceBuild("A project");

As an added bonus, it is very easy to change from .NET Remoting to HTTP, you’d just change the address. The communications library hides all the underlying complexity for you.

However, before I say everything is roses, there is one current limitation: this will only work with the new 1.5 version server. There is no backwards compatibility.

Which brings me to…

What Is Coming

This work all started out of the security work I did for CruiseControl.NET. I got frustrated by the number of places that needed changing, and the number of different ways, so I merged and simplified them. This opened up some ways of making things simpler under the hood.

However there is still the question of previous versions of CruiseControl.NET. The new library will only work with 1.5 or later versions of CruiseControl.NET servers. It would also be nice to allow backwards compatibility. Now this is much more challenging as a number of base classes have been changed to work with messaging, but I still think it is a worthwhile goal.

I should also mention that HTTP compatibility is broken between 1.4.4 and 1.5. Since CCTray promises that HTTP can be used between versions, I am aiming to ensure that at least HTTP compatibility is ok, and then if possible .NET Remoting. That is the reason why there is a CruiseServerClientBase base class, with only one implementation. These older versions do not use the messaging style method calls, and so won’t be able to use CruiseServerClient. Instead they will need to make their own implementations.

Another step for current functionality is to convert CCTray, the dashboard and the server to all use the new client. These currently still have their existing ways of doing things (although I have started converting CCTray). This means in future, we will only need to change one place to change all three clients! Now that is exciting news to me :-)

And finally I can write about what I think is really exciting – a CruiseControl.NET SDK.

One of the great points about CruiseControl.NET is its flexibility. It has a lot of functionality in-built, plus a lot of ways of configuring this functionality. And if that is not enough, people can write their own extensions. But that’s where the good news stops – there isn’t exactly a lot of documentation on how to write extensions, how to configure them or how to use them. Plus just to complicate things, the code of CruiseControl.NET isn’t very well documented.

For the communications library I’m going to build up an SDK. This will include:

  • The library itself (the API)
  • A help document (both compiled and HTML)
  • Examples (both code and help-based)
  • Quick starts (processes to get someone up and running)

The idea behind this is to provide a knowledge base for a person new to CruiseControl.NET (although no new to programming) and help them to quickly build applications for CruiseControl.NET.

So as you can see, I have a vision that I’m aiming for with the new Communications library. Over time I’ll try and write up some more posts about this library. This library – together with the server extensions – opens up a whole world of possibilities for expanding CruiseControl.NET.

Stay tuned…

Posted in CruiseControl.Net, Tools | Tagged: | Leave a Comment »

Dynamic Build Parameters – Some Examples

Posted by Craig Sutherland on 12 May, 2009

Some New Exciting Functionality

In the 1.5.0 release we are adding the ability to define dynamic build parameters. A dynamic build parameter is a value that can change for different builds – and it can be set by the user when they perform a force build.

This addresses a simple issue in the current CruiseControl.NET – if even one parameter in a project needs to differ between builds, multiple projects are needed! Now, an administrator can define just one project and allow multiple different variations on it.

In this post I’ll go through some of the possibilities for this feature. I won’t cover everything, and the feature itself is still in its infancy, but hopefully this will provide a start for people to trial it.

A Very Basic Configuration

To keep things simple, I’m only going to work with a very simple configuration (ccnet.config). This is the base config:

<cruisecontrol>
  <project name="Test Project">
    <description>A demonstration project to show the features of the dynamic build parameters.</description>
    <sourcecontrol type="svn">
      <trunkUrl>svn://svn.mycompany.com/firstproject/trunk</trunkUrl>
      <workingDirectory>C:\SourceControl\FirstProject\</workingDirectory>
    </sourcecontrol>
    <triggers>
      <intervalTrigger />
    </triggers>
    <tasks>
      <nant>
        <buildFile>App.build</buildFile>
        <targetList>
          <target>Dev</target>
        </targetList>
        <buildArgs>-D:reason=testing</buildArgs>
      </nant>
      <gendarme>
        <assemblies>
          <assemblyMatch expr="*.dll"/>
        </assemblies>
        <limit>100</limit>
      </gendarme>
    </tasks>
    <publishers>
      <rss />
      <xmllogger />
    </publishers>
  </project>
</cruisecontrol>

This project uses Subversion as the source code repository, using a repository called firstproject. It has the default interval trigger (checks for modifications every 60 seconds). When a build is triggered it will run an NAnt build script called App.Build and then perform a Gendarme analysis. Finally it will generate an RSS feed and log the XML results (so the dashboard can use it.)

Basically I’m going to add three dynamic parameters:

  • Define the target to build
  • Change the reason for the build (a buildArg for the NAnt script)
  • And vary the limit on the Gendarme analysis

And to finish things off, I’ll show how the same project can be used to generate different builds via a schedule.

So, let’s get the show on the road.

Defining a Target

The first parameter I want to vary is the build type. This is a target in the NAnt script, and I’m going to allow three different targets: Dev, Test and Prod (this could also be done as a buildArg, but I want to leave that for later!)

The first thing to do is to add the parameter to the project. Parameters are defined at the project level for two reasons:

  • It saves duplicates if the parameter needs to be used in multiple places
  • It makes it simpler for the server to work out which parameters need to be sent to the client (thus it is faster)

Since the build target can only be one of three values, I’m going to define a range parameter:

<parameters>
  <rangeParameter name="Type">
    <description>The type of build to perform.</description>
    <allowedValues>
      <value>Dev</value>
      <value>Test</value>
      <value>Prod</value>
    </allowedValues>
    <default>Dev</default>
  </rangeParameter>
</parameters>

All parameters must be defined within a parameters block within the project. As a minimum definition, each parameter must have a name – which is what is sent to the client.

This definition defines a parameter called “Type”, it is a range parameter with three possible values: “Dev”, “Test” and “Prod”. Finally, the default value is “Dev”. When a force build is triggered, the user will be displayed a combo box (or a drop down list depending on the client) with these three values in it, and “Dev” will be the initial selection.

The next step is to use the parameter in a task (currently they are limited to tasks and publishers, this may change if there is enough support for it.) The following shows how I would modify the <nant> task to use the parameter:

<nant>
  <!-- Omitted to save space -->
  <dynamicValues>
    <directValue parameter="Type" property="targetList.target[0]">
      <default>Dev</default>
    </directValue>
  </dynamicValues>
</nant>

This simply says replace the first target in the targetList with the parameter value. This is a direct replacement – the entire value will be replaced with whatever value was selected for Type.

Note that there is also a default value here. This is used for when a value has not been selected (i.e. when the interval trigger detects changes).

This is a very simple example of a pre-defined value that entirely replaces the existing value. Now, let’s see some of the other parameter types.

A Numeric Parameter – Gendarme Limit

The next thing I’ll show is how to use a numeric parameter value. Again, this needs to be defined at the project level in the parameters block:

<parameters>
  <!-- Omitted to save space -->
  <numericParameter name="GendarmeLimit">
    <description>The limit of Gendarme errors.</description>
    <minimum>50</minimum>
    <maximum>500</maximum>
    <default>100</default>
  </numericParameter>
</parameters>

This says we want to also have a GendarmeLimit parameter, this is a numeric value and must be between 50 and 500. The default value will be 100.

To use this, it is the same as the target type, just add a direct replacement dynamic value:

<gendarme>
  <!-- Omitted to save space -->
  <dynamicValues>
    <directValue parameter="GendarmeLimit" property="limit">
      <default>100</default>
    </directValue>
  </dynamicValues>
</gendarme>

Note, the value will automatically be converted into the correct data type for use in the task. If there is a data type mismatch (e.g. a test value is put into a numeric property), then the build will fail with an error.

Now, let’s add one more parameter – one that doesn’t use direct replacement.

A Build Reason

This is a bit of a contrived example, but here we are going to pass a reason for the build into the NAnt task.

First, let’s define the parameter:

<parameters>
  <!-- Omitted to save space -->
  <textParameter name="Reason">
    <description>Reason for the build being forced.</description>
    <minimum>10</minimum>
    <maximum>255</maximum>
    <required>true</required>
  </textParameter>
</parameters>

This is a text parameter with a minimum length of 10 and a maximum length of 255.

This time, to use the parameter we are going to use a replacementValue:

<nant>
  <!-- Omitted to save space -->
  <dynamicValues>
    <!-- Omitted to save space -->
    <replacementValue property="buildArgs">
      <format>-D:"{0}"</format>
      <parameters>
        <namedValue name="Reason" value="Triggered" />
      </parameters>
    </replacementValue>
  </dynamicValues>
</nant>

This has the same property attribute, but otherwise is very different. A standard .NET format string is required (internally this is literally passed onto string.Format()!) And there is also a list of parameters to use. As you might have guessed, this dynamic value can use multiple parameters. If a property needs to use more than one parameter, then this is the syntax that is required.

Note for the parameters a namedValue is used. The name is the name of the parameter, while the value is the default value. This is needed because there can be multiple parameters, each with its own default value!

Scheduling A Prod Build

The final item I’ll cover is how to use parameters together with triggers. In this case, I’m going to schedule a build every night that uses the Prod target.

This is fairly easy to do, all we need to do is add a new parameterTrigger that contains the schedule trigger. The following shows how to do this:

<triggers>
  <intervalTrigger />
  <parameterTrigger>
    <trigger type="scheduleTrigger">
      <time>1:00</time>
      <buildCondition>ForceBuild</buildCondition>
    </trigger>
    <parameters>
      <namedValue name="Type" value="Prod"/>
      <namedValue name="Reason" value="Scheduled"/>
      <namedValue name="GendarmeLimit" value="500"/>
    </parameters>
  </parameterTrigger>
</triggers>

This will schedule a build every night at 1am and pass in “Prod” for the Type, “Scheduled” for the Reason and 500 for the GendarmeLimit.

The Final Configuration

Hopefully this has provided a quick introduction to how parameters and dynamic values can be used. If you can think of how this can be improved, let me know and I’ll see what I can do.

And to finish up, here is the complete configuration:

<cruisecontrol>
  <project name="Test Project">
    <description>A demonstration project to show the features of the dynamic build parameters.</description>
    <sourcecontrol type="svn">
      <trunkUrl>svn://svn.mycompany.com/firstproject/trunk</trunkUrl>
      <workingDirectory>C:\SourceControl\FirstProject\</workingDirectory>
    </sourcecontrol>
    <triggers>
      <intervalTrigger />
      <parameterTrigger>
        <trigger type="scheduleTrigger">
          <time>1:00</time>
          <buildCondition>ForceBuild</buildCondition>
        </trigger>
        <parameters>
          <namedValue name="Type" value="Prod"/>
          <namedValue name="Reason" value="Scheduled"/>
          <namedValue name="GendarmeLimit" value="500"/>
        </parameters>
      </parameterTrigger>
    </triggers>
    <tasks>
      <nant>
        <buildFile>App.build</buildFile>
        <targetList>
          <target>Dev</target>
        </targetList>
        <buildArgs>-D:reason=testing</buildArgs>
        <dynamicValues>
          <directValue parameter="Type" property="targetList.target[0]">
            <default>Dev</default>
          </directValue>
          <replacementValue property="buildArgs">
            <format>-D:"{0}"</format>
            <parameters>
              <namedValue name="Reason" value="Triggered" />
            </parameters>
          </replacementValue>
        </dynamicValues>
      </nant>
      <gendarme>
        <assemblies>
          <assemblyMatch expr="*.dll"/>
        </assemblies>
        <limit>100</limit>
        <dynamicValues>
          <directValue parameter="GendarmeLimit" property="limit">
            <default>100</default>
          </directValue>
        </dynamicValues>
      </gendarme>
    </tasks>
    <publishers>
      <rss />
      <xmllogger />
    </publishers>
    <parameters>
      <rangeParameter name="Type">
        <description>The type of build to perform.</description>
        <allowedValues>
          <value>Dev</value>
          <value>Test</value>
          <value>Prod</value>
        </allowedValues>
        <default>Dev</default>
      </rangeParameter>
      <numericParameter name="GendarmeLimit">
        <description>The limit of Gendarme errors.</description>
        <minimum>50</minimum>
        <maximum>500</maximum>
        <default>100</default>
      </numericParameter>
      <textParameter name="Reason">
        <description>Reason for the build being forced.</description>
        <minimum>10</minimum>
        <maximum>255</maximum>
        <required>true</required>
      </textParameter>
    </parameters>
  </project>
</cruisecontrol>

Posted in CruiseControl.Net | Tagged: | 5 Comments »

Extended Configuration – Beyond NetReflector

Posted by Craig Sutherland on 11 May, 2009

Some Background

In CruiseControl.NET we have a file merge task. This task will load one or more files and merge them into a single XML document. From this document the reports get generated.

Recently we made some changes to CruiseControl.NET to allow reports and report artefacts to come from a different location, which now allows images and HTML reports to be included. This was actually a flow-on effect from the new NDepend and NCover tasks, as these tasks needed to display non-XML data.

These worked so well, one of the other developers asked how to include items from other tasks in these reports, and even potentially user-specified files. The bad news at the time is this was not possible. The only merging task we had was the merge files task (<merge> element) and this task merges into the XML document.

This left us with two options:

  1. Add a new task to merge non-XML files
  2. Modify the merge task to do non-merge copying

Since I didn’t particularly like the idea of a new task just for this specialised functionality, I decided to look at extending the current merge task.

The Problem

An example configuration for the current merge task would look something like this:

<merge>
    <files>
        <file>File 1</file>
        <file>File 2</file>
        <file>File 3</file>
    </files>
</merge>

In contrast, this is what I wanted to achieve:

<merge target="somewhere">
    <files>
        <file>File 1</file>
        <file action="Copy">File 2</file>
        <file action="Merge">File 3</file>
    </files>
</merge>

The problem with this lies in the action attributes – NetReflector does not handle them. The closest we can get is something like:

<merge target="somewhere">
    <files>
        <file>File 1</file>
        <file><action>Copy</action><name>File 2</name></file>
        <file><action>Merge</action><name>File 3</name></file>
    </files>
</merge>

Which would break existing config files! So, based on plain-vanilla NetReflector we would be stuck.

But, there is a way around this, and one that doesn’t involve modifying NetReflector at all.

Expanding the Attribute

All of the configuration is based on NetReflector attributes and most of it uses ReflectorPropertyAttribute. Like all the NetReflector attributes it has a constructor that requires a name, which is the name of the element. However, this attribute is unique – it also has a second constructor allows the developer to pass in a factory Type.

The factory argument allows the developer to override the default serialisation/de-serialisation behaviour with their own custom behaviour.

The factory type must implement ISerialiserFactory. This is a simple interface with one method that creates a new serialiser. This serialiser can then serialise/de-serialise in any way it wants!

The serialiser must implement IXmlMemberSerialiser, which also includes IXmlSerialiser. IXmlSerialiser has a Read() and a Write() method, while IXmlMemberSerialiser provides two properties and an extra method (I haven’t figured out what they are needed for yet!)

While it is possible to directly implement these interfaces, a simpler way is to inherit from XmlMemberSerialiser. This class implements all of the methods and properties – we just choose which ones we want to override (normally Read() and Write()).

Both the Read() and the Write() methods work directly with XML. The Read() method receives in the node to be de-serialised, while the Write() method receives an XmlWriter to work with. Generally, if you going to override one, you should override both – especially as both are used in CruiseControl.NET.

The only down-side to this approach is you have to completely implement the serialisation/de-serialisation behaviour. The default serialiser automatically handles whether it is an array, a list or a single value, our custom serialiser needs to do this for itself.

Reading a Node

As an example of how to implement a custom serialiser, I’ll go over what I have done for the new serialiser for the merge file task. This serialiser handles the desired XML I showed above.

The code for the Read() method is:

public override object Read(XmlNode node, NetReflectorTypeTable table)
{
    var fileList = new List<MergeFileInfo>();

    if (node != null)
    {
        // Validate the attributes
        if (node.Attributes.Count > 0)
        {
            throw new NetReflectorException("A file list cannot directly contain attributes.\r\nXML: " + node.OuterXml);
        }

        // Check each element
        foreach (XmlElement fileElement in node.SelectNodes("*"))
        {
            if (fileElement.Name == "file")
            {
                // Make sure there are no child elements
                if (fileElement.SelectNodes("*").Count > 0)
                {
                    throw new NetReflectorException("file cannot contain any sub-items.\r\nXML: " + node.OuterXml);
                }

                // Load the filename
                var newFile = new MergeFileInfo();
                newFile.FileName = fileElement.InnerText;

                // Load the merge action
                var typeAttribute = fileElement.GetAttribute("action");
                if (string.IsNullOrEmpty(typeAttribute))
                {
                    newFile.MergeAction = MergeFileInfo.MergeActionType.Merge;
                }
                else
                {
                    try
                    {
                        newFile.MergeAction = (MergeFileInfo.MergeActionType)Enum.Parse(
                            typeof(MergeFileInfo.MergeActionType),
                            typeAttribute);
                    }
                    catch (Exception error)
                    {
                        throw new NetReflectorConverterException("Unknown action :'" + typeAttribute + "'\r\nXML: " + node.OuterXml, error);
                    }
                }
                fileList.Add(newFile);
            }
            else
            {
                // Unknown sub-item
                throw new NetReflectorException(fileElement.Name + " is not a valid sub-item.\r\nXML: " + node.OuterXml);
            }
        }
    }
    return fileList.ToArray();
}

The arguments are an XmlNode and a NetReflectorTable. The node is the complete node to be de-serialised, in this case the <files> element and everything in it. The NetReflectorTable is a type custom to NetReflector – it basically contains a dictionary of all the available types that can be loaded. In this scenario we are not going to use it, but it is used in other serialisers (e.g. for hash-tables).

The majority of the work in this method is validation – since this will override the automatic behaviour we have to do everything ourselves! Since the end objective is to return an array of MergeFileInfo instances, I need to make sure the user isn’t trying anything that is not allowed.

The main working lines are instantiating a new MergeFileInfo, setting the file name from the text of the element and then loading the action from the attribute. If the attribute is not there, then it assumes a Merge action. Once this is done it is added to a List<MergeFileInfo>. This gets done for every child element, and then at the end it returns an array of MergeFileInfo items.

Writing an Element

The Read() was a fairly long method because it included lots of validation (plus it might need some more later one). In contrast the Write() method is very simple. Here is the code that I wrote for it:

public override void Write(XmlWriter writer, object target)
{
    var list = target as MergeFileInfo[];
    if (list != null)
    {
        writer.WriteStartElement(base.Attribute.Name);
        foreach (var file in list)
        {
            writer.WriteStartElement("file");
            writer.WriteAttributeString("action", file.MergeAction.ToString());
            writer.WriteString(file.FileName);
            writer.WriteEndElement();
        }
        writer.WriteEndElement();
    }
}

The writer is where the XML will be written to, the target is the item to be serialised.

The only check is that the target is actually an array of MergeFileInfo. Once this is verified, then is is a simple matter of iterating through the array and dumping all the values to XML.

Beyond the Ordinary – Serialisers

I should mention that once this was implemented, the rest of the modifications were very simple. Now the <merge> task has the ability to copy files, rather than just merge them. And not only that, but it is also backwards compatible with previous versions!

So, while this post started off talking about the <merge> task, the real focus is how we can extend NetReflector serialisation by using a custom serialiser and type factory. if you want to see how it is done, take a look at the MergeFileSerialiser, MergeFileSerialiserFactory and MergeFilesTask classes.

On a final note, I should mention this did not need any changes to NetReflector. This functionality has always been there, even though I have only just discovered it. As well as the new merge files, time-outs also use this functionality – take a look at the TimeoutSerializer and TimeoutSerializerFactory classes.

So hopefully this opens some more possibilities, happy coding…

Posted in CruiseControl.Net, NetReflector | Tagged: | Leave a Comment »

CCTray Backups

Posted by Craig Sutherland on 7 May, 2009

Sometimes when I have some spare time at work I add some minor improvements to CruiseControl.NET. There is only one problem – when I’m adding improvements I don’t want CCTray to point to our actual CruiseControl.NET instance.

What I normally do is open up the settings, remove all my current projects and then add the test projects. When I’ve finished I then need to go through the same process again to add my work projects! As you can guess this is not the best approach :-(

So, I’ve added a new tab to the settings in CCTray:

Backup

As you can see, it’s nothing flash. But it does allow me to save my current settings and then reload them.

The actual code is pretty simple, it just opens a dialog (either load or save depending on the action) and waits for user input. When the user enters a valid file name it uses CCTrayMultiConfiguration to perform the actual work. under the hood this class uses XML serialisation to perform the actual saving, so it’s pretty easy to use.

The only gotta that I came across was in loading the settings. I also need to reload the settings in the configuration window – otherwise it continued to display the old settings. But it wasn’t too bad – other than a couple of quirks in the way the display is loaded.

So, hopefully this will make things easier for other people as well.

Posted in CruiseControl.Net | Tagged: | Leave a Comment »

Some jQuery Goodness

Posted by Craig Sutherland on 6 May, 2009

Why Re-invent the Wheel?

We are slowly working on improving the web dashboard and making it easier and faster to use. One of the hot things now-a-days is interactive pages – no longer do we need to perform post-backs to do things.

We’ve been slowly adding some interactivity to our pages, I’ve added some pop-up dialogs to various plug-ins, and I thought I’d add some more interactive goodness to the tables. Basically I think it would be nice that we didn’t have to do post-backs to sort the tables, after all, the data is already there.

Now rather than try and build my own table sorting code, I had a look around jQuery and found there were already some plug-ins to allow this functionality. Wow! That made my job so much easier. So, I decided to add one of these plug-ins to CruiseControl.NET.

What I Did and Why

Rather than bore you with all the details, I’ll give you a quick overview of what I’ve done.

First, I went and modified the table templates. These have embedded styling in them (yuk!) and not only that, but it’s not CSS styling (anyone remember the <font> tag?) Since I was modifying the templates I decided to also tidy them up. I’ve added some more styles to the main style sheet and then went and removed all the embedded styles. This had the benefit of also making the pages slightly smaller.

The main reason for modifying the tables is the way the plug-in works. Most of the plug-ins require a well-defined table that has <thead> and <tbody> tags. So I modified the tables to include these tags. This was the only required change to the HTML for sorting.

Next, I downloaded the plug-in and added it to our scripts folder. Originally I tried the data tables plug-in (http://plugins.jquery.com/project/DataTables), but I just couldn’t get it working with our tables! So in the end I went with tablesorter (http://plugins.jquery.com/project/tablesorter). It doesn’t have as much built-in functionality, but I was only after the sorting so it meets my requirements. Since I wanted this script to be available in multiple locations, I added a reference to it in sitetemplate.vm.

Now the only remaining step was to call the script. This used the following code for each table to sort:

$(document).ready(function(){
  $('#QueueStatus').tablesorter({
      sortList:[[0,0]]
    });
});

And this is what it looks like:

jquery-1

Clicking on any of the column headers will sort that column. Multiple columns can be selected by holding down the shift key – and it didn’t take any extra work on my part :-)

But Wait, There’s More!

I did run into one major problem with the new sorting – it also sorted the status message (this is the message that is displayed underneath the row containing the project details). This resulted in the status message being in some weird places.

I played around for a while, but couldn’t get it to work, so I decided on an alternate approach. I have completely removed these rows from the table, instead they now appear as pop-ups when the mouse hovers over the project status details:

jquery-2

Again, I didn’t do the Javascript side of things – this is using the Tipsy plug-in for jQuery (http://plugins.jquery.com/project/tipsy).

So hopefully, these will make the tables easier to use. I’ll slowly go through and make sure all the other tables also have this functionality.

Posted in CruiseControl.Net | Tagged: | Leave a Comment »