
April 7, 2004
Whether or not you have been reading my articles here on The Server Side.NET,
I hope that you have learned and embraced the art of unit testing and automated
builds. Among other things, both techniques reduce the amount of redundant work
you have to do on a given project. They key feature of each technique is repeatability.
Once unit tests have been written, it is simple to run them against any version
of the codebase. Once your build script is complete, it is a single simple command
in order to build your project.
This month’s column builds on those ideas, and introduces you to the
technique that makes them both enterprise-ready: continuous integration. With
thanks to Martin Fowler (http://www.thoughtworks.com),
I’ll reiterate what he has already said; continuous integration
is an idea that has been around forever. It isn’t necessarily “agile”,
and the XP movement didn’t invent it. However, it is essential to these
approaches to software development, and is a welcome member of the pantheon
of strategies you should be aware of.
We’ll examine the whys and wherefores of continuous integration, and
examine two of the leading (open source) tools for providing this service: Draco.NET
and CruiseControl.NET. We’ll see how to get each up and running, and compare
their strengths and weaknesses to determine when each is a better fit for your
organization.
What is Continuous Integration?
Continuous integration is the strategy of making sure that changes to the project’s
codebase are built, tested and reported on as soon as possible after they are
introduced. This assumes several prerequisites:
- Your code is maintained in a central location, preferably a source code
control product like CVS or Visual SourceSafe.
- You have made a build script of some kind, like a NAnt .build file or a
Make script.
- Optionally, you have included unit tests in the codebase as part of the
project.
Once these are in place, you can create a continuous integration environment.
This is a central server that monitors the source code repository and springs
into action when it notices changes (commits). Its job is to check out a full
version of the codebase whenever any part of it changes, run your build file
and report on the results. What constitutes a build is up to you; normally,
a build is a full compile of all source files, running unit tests and creating
the deployment archive.
Why You Need Continuous Integration
All development teams (read: more than one programmer) have to deal with integration
builds. This is where you pull together all the bits and pieces that the different
team members were working on, and check to see if you have a fully functioning
product or a Frankenstein’s monster. Like many other project management
tasks, though, it can be complex and repetitive. This unfortunate combination
means that over time, you will likely devote less brain power to running the
integration build, even less to documenting the results, and are likely not
only to miss important problems but fail to have a documentation trail that
you can look back on later to see the mistake. A continuous integration tool
takes away the repetitive task, makes it repeatable and automatic and builds
a document trail as it works for historical accuracy.
Protecting Your Codebase
This means that you are providing yet more insurance for your codebase. Source
control was the first step; providing an historical repository of the changes
to your code allows you to recover from mistakes. The second step was unit testing,
to give you instant feedback onto the health of a given unit of code. CI is
the third step: ensuring as soon as possible that ALL the code works as intended,
that it builds and that the unit tests run as expected.
Shortening the Feedback Loop
The key to successful project development is having the shortest possible feedback
loops, whether than mean the time between writing code and testing it, between
writing documents and sending them to the customer for feedback, what have you.
An often overlooked feedback cycle is the introduction of supposedly isolated
changes into a large codebase, and the next time a full compile is tried.
Many teams rely on the tried and true method of building the product every
Friday afternoon, for instance. This means code added on Saturday night at 2:00am
(you know this happens) doesn’t get “integrated” for a full
6 days. By that time, you can’t even remember if it was Saturday or Sunday
night that you pulled the all-nighter, let alone what you actually wrote or
why.
Continuous integration means that, at the very least, by Sunday morning when
you wake up, or even better, right before you pack up on Saturday night, you
get feedback about how the changes affected the project as a whole. This information
is vital: so often, code that looks good and passes unit tests in isolation
either breaks when introduced to the rest of the code or, worse yet, breaks
the rest of the code. It is better to know this sooner rather than later, since
you can either back out the offending commit or take other appropriate action
to alleviate the problem without having to dig through your memory and your
source control repository for the exact changes that caused the problem.
Managing Your Testing
Writing unit tests and running them often is very important. Most developers,
though, just trash the results after a run. The important thing was to know
there was a problem; historical analysis is a distant concern. On a large, distributed
project, though, it is vital that you keep a running tab on the health of the
project so that your enterprise customers (internal or external) can be kept
adequately updated about the overall health of the project.
With a CI tool in place running your builds constantly, you can easily trap
the results of not only the compilation process but the testing process as well.
This means you can provide a simple tool for analyzing ongoing project health,
without lifting a finger on your own to provide it.
Getting Started with Draco.NET
Draco.NET is a free, open source tool from Chive Software. It is written in
.NET and runs as a Windows service. You can configure it to watch one or more
source code repositories and perform builds upon detecting changes. There is
a server install plus a client utilities package that make up the whole application.
The client utilities are optional; the server is, of course, mandatory.
Whenever Draco performs a build on your behalf, it notifies you through two
channels: the output of any trace listeners configured for the service, and
through email. We’ll see later how to configure these settings appropriately
for your application, and also some third-party tools for extending this list
of outputs.
Installation
Draco.NET ships as either a zipped archive of the source code or as two MSI
files, one for the server and one for the client tools. To install the server,
run the Draco-Server-x.x.msi file (where x.x is the desired version number).
This will unpack a .NET executable and configure it as a Windows service. Everything
else happens in the configuration file for Draco and your project’s build
files.
Configuration
You control Draco through the draco.exe.config file, located in the bin folder
of the application (by default, c:\Program Files\Draco.NET Service\bin). This
file is broken up into three main sections: the Framework-related settings,
the default values for all Draco builds, and settings for individual projects
to watch.
Framework-Related Settings
For the most part, you can ignore these values if you are just getting started.
The first time I installed it, I didn’t change a thing, and it worked
like a charm. Chances are, however, that as you start using this tool for your
enterprise development, the IT team might have some input on how such a service
can be installed and used. You should know what your options are for when that
conversation happens.
The first section of the .config file registers a new configuration section
handler for Draco-specific settings. DO NOT edit this element; the latter two
sections of the configuration file are encased in an element, <draco>,
and this line registers the class that examines those values and takes appropriate
action. Deleting it or modifying it to point to a different element or class
will break the service.
Next come the supported and required runtime settings. Currently, Draco only
runs with 1.1.4322, so editing these would break your build as well. Future
versions of the tool may allow you to run builds against multiple versions of
the CLR. For now, leave these alone.
In the <system.diagnostics>
setting, you control how you want to view information that the service sends
to the logging API. While the service is running, it sends notifications out
about its current status. These settings determine which notifications make
it to the log, and where and how they should be displayed and/or stored. The
first value is TraceLevelSwitch, which defaults to “4”, or regular,
verbose information. Your other choices are:
- 1 – errors only
- 2 – errors and warnings
- 3 – error details
The default setting of 4 is good for most users, since it is the full verbose
output.
Once you have determined what kind of trace information you are interested
in, you have to determine how you want to log it. By default, Draco is configured
to use its own FileTraceListener
class to output log files, and to turn off the default console logging (the
application runs as a service, so the idea of console output is meaningless).
You can configure any trace listener you want here, whether you have written
your own or purchased one. Since trace listeners are invoked serially, you can
leave the default handling on and use a secondary listener to provide an alternate
handling of the trace messages. The default FileTraceListener
appends trace information to a text file called draco.log which lives in the
service’s root folder (c:\Program Files\Draco.NET Service\).
Finally, there are elements for defining the remoting settings. This is so
that the Draco client tools can interact with the service (more about that later).
Generally, you’ll want to leave this section alone unless you want to
change the TCP port it registers to listen for incoming connections from the
client tool. Just look for the <channel> element and change the “port”
attribute to whatever you want, with 8086 being the default setting.
Default Values
The next section is where you define some global defaults for Draco-specific
functionality, namely how often to check for changes and the mail settings.
Under the <draco> element,
you will find the following options:
- <pollperiod> -- this
is how often Draco will hit the repository looking for changes (in seconds).
- <quietperiod> --
this is how long Draco will wait after detecting a change to begin the build
(in seconds). If any changes happen in the quietperiod, the timer starts again.
This is to prevent the triggering of partial builds if a commit to the source
control server has to happen over multiple steps.
- <mailserver> --
what outgoing SMTP server to use to send notifications
- <fromaddress> --
the email address that notifications from your server will be sent as.
The values you use for <pollperiod>
and <quietperiod>
will depend on several factors. First, how big are your builds? If a single
build takes only 30 seconds or so, then you can safely have a low number for
<pollperiod>, since your build machine will easily
be able to keep up. If builds take hours, you will want a relatively well-spaced
out value to give the machine a chance to complete everything before another
build is set to begin. Notice that the poll period is a regular timed interval,
NOT an absolute time/date stamp. You can not configure Draco.NET to, for instance,
run every night at midnight. There are strategies for doing so, which we’ll
see in the Other Tools section below.
Similarly, the quiet period is useful if you have several developers coordinating
a commit to the source control server. If one developer happens to complete
their check-in right as the poll period fires, while the second developer is
still committing, then the resulting build will be based on incomplete code.
With an appropriate quiet period, once the poll period fires and sees changes,
it provides a window for other commits to come in before the build actually
begins. This value should be relatively short (60 seconds or less), since commits
to the database ought to be relatively atomic and not spread out over hours
or even minutes.
Project Settings
Finally, you can configure one or more projects for Draco to monitor. Under
the <builds> section, you create individual <build>
elements for each project. Each build gets a name, can override the default
global values from above, and configures notification targets, build scripts,
source control repositories and a series of ignore settings.
The <name> element is vital, since it is what will be displayed to your
developers in the emails they receive and is also how you tell Draco to fire
a build manually through the client tools (more on that later). Make sure the
value you provide for <name> is unique across the whole configuration
file.
If you need to, each individual project can override the values for <pollperiod>
and <quietperiod>. If your build machine is managing many projects for
your team, and some are small with relatively low-footprint builds, you can
use a small number for the default values, then override them with larger values
for that one humongous build that every team is saddled with.
Under the <notifications> group, you can configure a list of email recipients
and file recipients for your information. Interestingly, these two destinations
receive different views of the results. For the <file> section, you can
specify a <dir> to receive the build results; the output will be an XML
file. Conversely, any email recipients you define will receive the same information
as found in the XML file, but transformed via an XSLT script called modifications.xsl,
found in the same folder as the draco.log file. If you want to change the way
the results are displayed to your users, just modify modifications.xsl. As shipped,
it creates an HTML display of the data; you might want to send plain-text only
emails for your team, or change the styling. The <email> element can contain
any number of <recipient> elements.
Next comes the build-tool configuration section. Currently, you can use either
NAnt (nant.sourceforge.net) or Visual Studio. Obviously, I highly recommend
using NAnt for your build management, and will ignore direct Visual Studio builds
for this article. Visual Studio builds are for local project development; a
centralized build management server should not even have Visual Studio installed
on it, much less invoked automatically on your behalf.
Add a <nant> element to the configuration file. If the nant.exe file
is on your system path, then it should work as is. If you need to configure
the service so it knows exactly how to find NAnt, just add a “program”
attribute, like this:
<nant program=”c:\path\to\nant.exe”>
You can then specify the build file to invoke for this project, a specific
target or targets to call when launching the build script, and any properties
to pass in:’
<nant>
<buildfile>dracoproject.build</buildfile>
<targets>buildall</targets>
<properties>
<property name="archive.server"
value="ftp://myserver.com" />
<property name="admin.email"
value="justin@relevancellc.com" />
</properties>
</nant>
Note that the build file you specify has to be in the same source control repository
as the code. If the build file is not located at the root of the project hierarchy,
then you have to provide the path to it relative to the root (i.e. /builddir/dracoproject.build).
Also, you should have verified by hand that the build file works on the build
server by checking out the whole tree once and running the build by hand. If
not, you’ll start getting automatic notification of build failures the
first time something changes in the repository.
Finally, configure the source control repository you use. It can be, currently,
CVS, Visual SourceSafe, Subversion or PVCS. This article focuses on CVS. Add
a <cvs> element to your build settings, once again providing a “program”
attribute if cvs.exe is not on your system path:
<cvs program=”c:\path\to\cvs.exe”>
You must then provide a <cvsroot>, <module> and optional <branch>.
For your CVSROOT, I strongly recommend the sspi protocol. sspi uses the current
domain account token to verify the request against the repository, so you don’t
have to configure a username or password in the CVSROOT. Just make sure the
service is running as a domain user that has access to the repository, and authentication
happens for you.
<cvs>
<cvsroot>:sspi:cvsrepo.relevancellc.com:/jbg</cvsroot>
<module>dracoproject</module>
<branch>v1_6--features</branch>
</cvs>
If, for some reason, you need to use a protocol that requires authentication,
like pserver, then you can specify the username required in the cvsroot and
then add a password element.
<cvs>
<cvsroot>:sspi:justin@cvsrepo.relevancellc.com:/jbg
<password>THIS_IS_NOT_VERY_SECURE
<module>dracoproject
<branch>v1_6--features
</cvs>
I generally don’t recommend storing usernames and passwords in plain
text files and leaving them laying around on the network, so I strongly recommend
making sure you can use sspi and the current user token to authenticate against
the repository.
Finally, you can establish some rules for ignoring changes to the repository
to avoid unwanted builds. Under <ignorechanges> you can choose to ignore
by which user made the change, what comment was applied to the change, or a
combination of the two. If, for example, your team has a member whose job it
is to keep the documents up to date, and the docs are stored in the repository,
you can have Draco ignore any updates made by the documentation specialist so
you aren’t invoking a compile-link-unittest cycle just because the docs
changed.
<ignorechanges>
<ignore user="doc_guy"/>
</ignorechanges>
Results
Once you have everything configured, you just start up the service (either
through the Services control panel, or with “net start Draco.net”.
Draco will start polling the configured repositories, looking for updates. When
it builds, the XML build output ends up in the directory you specified, in a
file named like this: PROJECTNAME-TIMEDATE.xml (ie. Dracoproject-2004040ST110549.xml).
The output itself looks like:
<?xml version="1.0"?>
<BuildResult xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Success>true</Success>
<BuildOutput>NAnt 0.84 (Build 0.84.1455.0; net-1.0.win32; release; 12/26/2003)
Copyright (C) 2001-2003 Gerry Shaw
http://nant.sourceforge.net
Buildfile: file:///C:/WINDOWS/TEMP/tmp1C8.tmp/dracoproject.build
Target(s) specified: build
build:
[solution] Starting solution build.
[solution] Building Services [debug]...
[solution] Building ServiceBroker [debug]...
[copy] Copying 2 files to C:\WINDOWS\TEMP\tmp1C8.tmp\ServiceBroker\bin\Debug\.
[solution] Building dracoproject [debug]...
[copy] Copying 4 files to C:\WINDOWS\TEMP\tmp1C8.tmp\bin\Debug\.
Read in 1 resources from 'C:\WINDOWS\TEMP\tmp1C8.tmp\Form1.resx'
Writing resource file... Done.
BUILD SUCCEEDED
Total time: 3.1 seconds.
</BuildOutput>
<Module>dracoproject</Module>
<BuildTime>2004-04-05T11:05:49.8750000-04:00</BuildTime>
<Modifications>
<Modification>
<Date>2004-04-05T11:01:04.0000000-04:00</Date>
<User>jgehtland</User>
<Comment>should blow up</Comment>
<Files>
<File>
<Type>Add</Type>
<Directory>\jbg\dracoproject</Directory>
<File>Form1.cs</File>
<Revision>1.9</Revision>
</File>
</Files>
</Modification>
<Modification>
<Date>2004-04-05T11:04:48.0000000-04:00</Date>
<User>jgehtland</User>
<Comment>should be fixed</Comment>
<Files>
<File>
<Type>Add</Type>
<Directory>\jbg\dracoproject</Directory>
<File>Form1.cs</File>
<Revision>1.10</Revision>
</File>
</Files>
</Modification>
</Modifications>
</BuildResult>
And the emailed result looks like:

The log file shows entries like the following:
4/5/2004 11:04:39 AM - dracoproject: Detected no changes to module
4/5/2004 11:04:49 AM - dracoproject : Checking module for changes
4/5/2004 11:04:49 AM - dracoproject : Detected 2 change(s) to module
4/5/2004 11:04:49 AM - dracoproject : Entering 60 second sleep period
4/5/2004 11:05:49 AM - Check again: 4/3/2004 4:57:40 PM
4/5/2004 11:05:49 AM - Check again: 2
4/5/2004 11:05:49 AM - dracoproject : Detected 0 additional modification(s)
4/5/2004 11:05:49 AM - dracoproject : Building module
4/5/2004 11:05:55 AM - dracoproject : Build completed with exit code 0
4/5/2004 11:05:55 AM - dracoproject : Sending build results email to: justin@relevancellc.com
4/5/2004 11:05:55 AM - dracoproject : Writing build results to 'C:\temp\dracoproject\BuildOutput\dracoproject-20040405T110549.xml'
4/5/2004 11:06:05 AM - dracoproject : Checking module for changes
4/5/2004 11:06:05 AM - dracoproject : Detected no changes to module
4/5/2004 11:06:15 AM - dracoproject : Checking module for changes
Other Tools
Draco.NET Client Tools
With the client tools, you really just get a single executable, dracocli.exe.
Dracocli.exe allows you to send commands to the Draco service, manually starting
or stopping builds, checking status on a given project, and listing available
projects. For instance,
C:>dracocli /start:dracoproject
causes dracoproject to be built, regardless of whether there were changes to
the repository.
If you want to create a scheduled build, say, every night at midnight, we already
saw that you can’t configure the Draco server to do this. However, you
can create a scheduled task using the Draco client tools, and have it manually
launch a build every night at midnight.
C:>schtasks /create /tn "dracoproject"
/tr “c:\Draco.NET Client Tools\bin\dracocli /start:dracoproject”
/sc daily /st 00:01:00
(To do this, you have to be an Administrator on the box you are setting up
to run the scheduled task). Dracocli.exe has its own .config file that allows
you to specify the server and port to contact for sending these requests.
Web-based Log Viewer
James Geurts has written a web based log viewer for Draco.NET (http://blogs.biasecurities.com/jim/archive/2004/02/16/337.aspx).
This tool is simple to install and provides a visual repository of log results
which allows you to turn off email notification but still have a simple way
to examine the results of the various build attempts. I recommend installing
this in addition to sending email notifications, as this provides a central
historical repository for the team to go back through the results without having
to save every email notification they get from the server.
Getting Started with CruiseControl.NET
CruiseControl.NET is a more heavyweight and complete application, being a port
of the granddaddy of CI tools, the original CruiseControl for Java. You can
find it at Thoughworks’ website, ccnet.thoughtworks.com, along with a
variety of resources (like Martin Fowler’s original Continuous Integration
article). CruiseControl.NET ships with a web-based interface for monitoring
and launching all of your managed builds as well as a rich toolset for capturing,
storing and reporting the build results.
Installation
Installation is more complex than with Draco, since CruiseControl.NET comes
with a variety of web-based tools and optional goodies that you will want to
take advantage of. First, unzip the distribution into the directory of your
choice. You will end up with the following folder structure:
- Cctray – a folder containing a local tool for showing build results
in the Windows tray. More on this later.
- Doc – the documentation folder
- Server – this is where the main CC.NET server and configuration files
live
- Web – an examplar of a project web folder for monitoring a single
project’s status
- Webdashboard – the central web application for monitoring all the
projects
- Webservice – an optional web service for manually launching builds
or checking status
You will need to make a copy of the Web folder for each different build project
you want CC.NET to maintain for you, and make sure it is mapped to a virtual
directory in IIS. The webdashboard folder should also be mapped to a virtual
directory (you only need ONE webdashboard folder, but you need as many copies
of the web folder as you have projects, and each should be named accordingly).
To start the server, you simply run the StartCCNet.bat file in the folder with
the application and its configuration files.
Configuring the Server
Like Draco, configuration of CC.NET happens in a .config file. For CC.NET,
though, the settings are spread across two files: ccnet.exe.config handles all
the Framework-related and global defaults, while ccnet.config is for managing
specific
In general, these settings are identical to the ones found in Draco (which
is not surprising, since Draco was “inspired by” CruiseControl.NET,
and most of these settings are mandatory for a remoting app anyway). Again,
the only things you will want to edit are the TCP port registered under system.runtime.remoting
if the default of 1234 is unacceptable, and the message level for your trace
listeners under system.diagnostics. Also, you can add your own TraceListener
classes if you so desire (CC.NET comes with two, the ConsoleTraceListener and
LogFileTraceListener).
Configuring the Projects
Use ccnet.config to manage your individual build projects. The root element,
<cruisecontrol> contains one or more <project> elements, each with
a unique “name” attribute (the value must be unique across the whole
file). Each project has four major sections: main settings, source control,
build script and publishers.
Main Settings
The first setting is <weburl>, which is the full url to the virtual directory
housing the project page for this project. For instance, if the project is called
ccproject, and you made a copy of the web folder and renamed it ccproject (with
an identically named virtual directory), this setting would be:
<weburl>http://my.cruisecontrolserver.com/ccproject</weburl>
Next, you schedule the checks against the repository. Again, you define how
long (in seconds) between inquiries to see if changes have been posted.
<schedule type=”schedule” sleepSeconds=”60”/>
If, however, you want to have a regularly scheduled build instead of one based
on watching the repository, you can use the (undocumented):
<schedule type=”daily” integrationTime=”14:45”/>
Substitute whatever time you want for the value of integrationTime. I do not
recommend employing this technique, for two reasons:
- the whole purpose of CI is to have builds happen as changes are made
to the code, not on some predefined regular schedule. If you are making
changes to the code base, the normal method will cause a build in the appropriate
time frame, and if you are not, then a build would be redundant anyway
- the feature is undocumented for a reason. It doesn’t always work.
Finally, you can set up a window for allowing further changes to come in before
actually launching the build (just like the quiet period in Draco) called <modificationDelaySeconds>.
Source Control
Once again, you have multiple choices of which kind of repository to use. CC.NET
supports CVS, SourceSafe, Perforce, PVCS, Starteam and Subversion. This document
covers CVS, but the documentation for CC.NET covers the configuration settings
for each environment.
Create a <sourcecontrol> element with the appropriate “type”
attribute, in this case, cvs. You need to tell CruiseControl.NET exactly where
the executable is; do not rely on the system path to provide this value. This
is handled via an <executable> element. Next, you have to give it a <workingDirectory>
which is where the local version of the source will be checked out and builds
run. Finally, tell it how to connect to CVS via a <cvsroot> element. Once
again, I recommend using sspi (see the Draco section for details) as your authentication
protocol. If you want to use any other protocol, the configuration can become
quite hairy (see the part of the documentation about using SSH via Putty, http://ccnet.thoughtworks.com/docs/server/usefultips.html).
Make sure you perform an initial checkout into the working directory to give
CruiseControl.NET something to compare the repository against.
<sourcecontrol type="cvs">
<executable>c:\program files\cvsnt\cvs.exe</executable>
<workingDirectory>c:\temp\ccsample</workingDirectory>
<cvsroot>:sspi:my.cvsserver.com:/mymodule</cvsroot>
</sourcecontrol>
Build Tool
NAnt is again the de facto standard for build tools, though there is talk of
the product supporting MSBuild when it is released. Using NAnt is different
with CruiseControl than with Draco. With Draco, you merely told it what the
name of your build file was; Draco would execute it after it checked the file
out of the repository. For CC.NET, you have to write a new build script that
checks out your source for you before building. CC.NET can only, on its own,
compare your local copy to the repository; it is up to you to write a script
that will check out the latest version then build it.
First, define a new <build> section with a “type” attribute
set to “nant”. Make sure you provide the full path to the executable
in the <executable> element. You will next tell CC.NET what the base directory
of the build script is. Normally, this is the root folder of your project, but
that may change depending on how your team configures your source tree.
Then, give CC.NET the information it needs to launch your new build script.
This build script is often referred to as the “bootstrap” build
file, and its job is to check out the latest version of the source then run
the real build script. CC.NET needs to know the <baseDirectory> to launch
the script from, what the build file’s name is (<buildFile>), any
argument you may need to pass in to it (<buildArgs>), the list of targets
to call (<targetList>) and a timeout for how long the build can try to
run before failing.
If your team is like most development groups, you have to work on multiple
projects. Each project has its own NAnt build script, which you won’t
have to modify to work with CruiseControl. However, you want to avoid writing
a brand new “bootstrap” build file for each and every project, since
you are just doing the same tasks over and over (checkout source, launch real
build file). Instead, you should write a generic build file:
<?xml version="1.0"?>
<project name="ccnetlaunch" default="go">
<property name="cvs.executable" value="cvs.exe"/>
<property name="build.file" value="default.build"/>
<property name="default.target" value="build"/>
<target name="go" depends="update,build"/>
<target name="update">
<ifnot propertyexists="cvs.executable">
<fail message="cvs.executable property not set, so can't update" />
</ifnot>
<echo message="CVS Executable at [${cvs.executable}]" />
<exec basedir="." program="${cvs.executable}"
commandline="-q update -P -d"/>
</target>
<target name="build">
<nant buildfile="${build.file}"
target="${default.target}"
inheritall="true"/>
</target>
</project>
The build file has three targets: go, which is the default, and requires update
and build. Update, which checks the source out of the repository, and build,
which launches the project’s build file. Instead of hard coding the values
for the cvs server and the local build file into the build script, they are
defined as parameters which are passed in from CC.NET via the configuration
settings in ccnet.config. To call this script, your <build> section in
ccnet.config should look something like this:
<build type="nant">
<executable>C:\@Tools\nant\bin\nant.exe</executable>
<baseDirectory>c:\temp\ccsample\ccsample</baseDirectory>
<buildFile>ccnetlaunch.build</buildFile>
<buildArgs>-D:cvs.executable="C:\Program
Files\cvsnt\cvs.exe" -D:build.file="ccsample.build"
-D:default.target=build</buildArgs>
<targetList>
<target>go</target>
</targetList>
<buildTimeoutSeconds>300</buildTimeoutSeconds>
</build>
Here, we are using the <buildArgs> element to pass in all the appropriate
information to ccnetlaunch.build, and it can now be used with each project.
Simply drop a copy of it into each project’s local directory and configure
CC.NET as above.
Publishers
The final section is the collection of publishers, or how you expect to send/store
the results of the build process. There are two types of publishers: email and
the xmllogger. When you configure email recipients, you have to define both
users and groups. The groups determine what kinds of notifications a user receives.
Ever user must belong to one group.
<email from="buildmaster@ccnet.com" mailhost="localhost"
includeDetails="TRUE">
<projectUrl>http://localhost/ccnet</projectUrl>
<users>
<user name="Justin" group="buildmaster"
address="justin@relevancellc.com"/>
</users>
<groups>
<group name="buildmaster" notification="always"/>
</groups>
</email>
The “notification” attribute of the <group> can be set to
either “always” or “change”, meaning either on every
build, or every time a build changes the status of the project (between Succeeded
and Failed).
The xmllogger generates the log files that the web based control center uses
to display the results of the builds. You have to supply a directory for the
logs to be sent to, and then specify any extra files you want merged into the
log. Any files you specify must be valid XML or the logging will fail. This
is particularly useful for dropping in the output from your NUnit tests or from
FxCop. I have also found it useful to dump the output of NAnt build to an xml
file and merge that in. By default, the output of the NAnt build is dumped to
plain text on the console, and dropped into the log by CruiseControl.
<build date="4/5/2004
5:30:28 PM" buildtime="00:00:03">NAnt 0.84 (Build
0.84.1455.0; net-1.0.win32; release; 12/26/2003) Copyright (C) 2001-2003 Gerry
Shaw http://nant.sourceforge.net Buildfile: file:///c:/temp/ccsample/ccsample/ccnetlaunch.build
Target(s) specified: go update: [echo] CVS Executable at [C:\Program Files\cvsnt\cvs.exe]
[exec] C:\Program Files\cvsnt\cvs.exe -q update -P -d ? ccnetlaunch.log ? ccnetlaunch.xml
? Services/bin M bin/Debug/ccsample.exe.incr build: [nant] c:\temp\ccsample\ccsample\ccsample.build
build Buildfile: file:///c:/temp/ccsample/ccsample/ccsample.build Target(s)
specified: build build: [solution] Starting solution build. [solution] Building
Services [debug]... [solution] Building ServiceBroker [debug]... [solution]
Building ccsample [debug]... BUILD SUCCEEDED Total time: 1.1 seconds. go: BUILD
SUCCEEDED Total time: 2.2 seconds.</build>
Whereas, you could tell NAnt to log its output to XML format instead by changing
the <buildArgs> line from the <build> section:
<buildArgs>-l:ccnetlaunchlog.xml
-logger:NAnt.Core.XmlLogger -D:cvs.executable="C:\Program Files\cvsnt\cvs.exe"
-D:build.file="ccsample.build" -D:default.target=build</buildArgs>
Then, add this to your <mergeFiles>:
<mergeFiles>
<file>c:\your\base\path\*log.xml</file>
</mergeFiles>
The resulting log file will have all the same build information, except all
of it stored as XML for future parsing if needed.
Results
Each time a build runs and you are configured to receive the results (depending
on your group membership), you will receive a relatively straightforward email
with the results.

If you log into the Web Dashboard, you will see a screen showing you all the
projects configured on your server, and the latest status of each.

From here, you can navigate to the project details by clicking on the project
name, see the status and last build time and number, see what state the project
is currently in (normally, it is Sleeping, but it might also be Building). Finally,
you can force a build from here by clicking the Force Build link.
From the project link, you will be able to see the full details of the build.

From here, you can get to the details of any particular build with the list
running down the left side. The main contents show the build results, all the
unit tests and their results, as well as the list of modifications that caused
this build to fire. In addition, you can follow the links in the upper right
to the test details, showing how many successes, failures and tests not run
per TestFixture, the time it took each test to run, the XML log file that underpins
this report, and any FxCop output.
Other Tools
In addition to all this, CruiseControl.NET ships with a web service that can
be used to grab project details as an XML file or force a build of a particular
project. This is useful if you have your own reporting/project management intranet
and need a quick way to add polling and building to it. In addition, the CCTray
tool gives you a handy tray icon which uses polls the service at regular intervals,
checking for the latest status. If the icon is green, the last build succeeded;
if it is red, the last build failed. If you click on the tray icon, a menu pops
up allowing you to navigate to the project home page.
Which to Use?
After all this, which tool is better for your project? It really depends on
what you are looking for, and what kind of project it is. Draco is much easier
to get set up and rolling with. It provides more detailed email output (it automatically
includes the actual build output from the NAnt build script) but doesn’t
come with a web based viewer (though you can download one or write one yourself).
Draco runs as a Windows service, whereas the default install of CruiseControl
does not (though there is a separate executable that can be installed as a service
if you would rather).
CruiseControl has better tools for checking status and forcing builds, and
is better integrated with NUnit and FxCop. In addition, CruiseControl has slightly
better control over who gets notified and when.
If you are working on a small team with a medium to small sized project, I
would recommend Draco. It is light, fast and easy to get going with. The larger
your team or project, the more I would lean towards CruiseControl.NET, since
it is more configurable and has better monitoring tools, allowing your team
members to get access to the data in a variety of ways. Plus, CruiseControl.NET
has a large team of developers working on it, which means that though the two
projects will probably have very similar features over time, CruiseControl.NET
might get them faster (think MSBuild support).
It really doesn’t matter which tool you use, but that you choose to use
one. Continuous integration is an extremely positive force in any application
development environment, and the peace of mind you get in seeing your builds
succeed within minutes of changes being introduced will go a long way towards
eliminating the guesswork from distributed team development.
Authors
 | Justin Gehtland is a founding member of Relevance, LLC, a consultant group dedicated to elevating the practice of software development. He is the co-author of Windows Forms Programming in Visual Basic .NET (Addison Wesley, 2003) and Effective Visual Basic (Addison Wesley, 2001). Justin is an industry speaker, and instructor with DevelopMentor in the .NET curriculum. |
|