Continuous Integration with CruiseControl.NET and Draco.NETContinuous Integration with CruiseControl.NET and Draco.NETContinuous Integration with CruiseControl.NET and Draco.NET
Posted on 2005-03-24 09:29 hbiftsaa 阅读(1039) 评论(0) 收藏 举报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. |

浙公网安备 33010602011771号