You are on page 1of 13

CruiseControl.NET from Scratch - Version 1.

0
Introduction
The idea of this article is to help you get started with CruiseControl.NET. I’m
assuming that you already know a bit about Continuous Integration and Test Driven
Development and that you want to get up and running with CruiseControl.NET as
quickly as possible.
I’m going to concentrate on setting up a system based on this environment:
• .NET Framework v1.1
• Visual Studio .NET 2003
• Visual SourceSafe 6.0

This is a pretty common set-up at the moment. However a lot of the ideas in this
article are equally applicable to different versions of the CLR and Visual Studio .NET,
and to different source control providers.
I’ve found that the many configuration options available in CruiseControl.NET can be
a bit daunting so we are going to create a simple CruiseControl.NET setup by
performing a series of steps. You will end up with a working system which you can
refine to meet your own needs. Our configuration will consist of:
• the CruiseControl.NET continuous integration server, initially running from
the command prompt and later as a service
• the Web Dashboard application which provides detailed information about
completed builds
• the CCTray system tray application, which runs on client machines and
permits easy launch of the web application, forcing of builds etc

I have found these three applications to be excellent for the purposes of our
development team.
Hardware
We are going to work with three machines:

• the CruiseControl.NET build server, which will run the continuous integration
server and the Web Dashboard – referred to in the configuration files that follow
as "%buildserver%"
• the server which hosts the SourceSafe database – referred to in the
configuration files as "%sourceserver%"
• a client machine which will run the CCTray system tray application and also
view the CruiseControl.NET Web Dashboard using its browser

You can run everything on the same machine if you wish.

Software
To start with you’ll need the bits and pieces:
• Visual Studio .NET 2003
• Visual SourceSafe. Version 6.0.
• CruiseControl.NET. Version 1.0 from: http://ccnet.thoughtworks.com. It
now comes with a neat installer (CruiseControl.NET-<version>-setup.exe)
• NAnt for running build scripts. Version 0.85-rc3 from:
http://nant.sourceforge.net
• NAntContrib for Visual SourceSafe support. Version 0.85-rc3 from:
http://sourceforge.net/projects/nantcontrib
• NUnit for adding test fixtures to Visual Studio .NET projects. Version 2.2.0
from: http://www.nunit.org

The more recent versions of CruiseControl.NET can dispense with the need for NAnt,
as they talk to SourceSafe directly for source retrieval and use Visual Studio .NET for
the builds. However I have not yet fully investigated this scenario, so we'll stick with
NAnt for this article.
I’m also assuming that you have an IIS web server on your build server correctly
configured to dish up ASP.NET pages.
Installation: Build Server
Your build server needs to have, at the least, a Visual SourceSafe client installation.
It does not, however, need a Visual Studio .NET installation as we will be using the
NAnt solution task to build our project.
Create a directory on your build server to hold all the bits and pieces. Mine is
C:\Projects\CI (short for Continuous Integration). In this directory:
• Unzip NAnt to C:\Projects\CI\
• Unzip NAntContrib to C:\Projects\CI\. Then copy the contents of the
nantcontrib-0.85-rc3\bin directory to the nant-0.85-rc3\bin directory. This will
enable NAnt to use the "vssget" task later on.

Now run the CruiseControl.NET installer on the build server and accept all the default
settings except for the destination folder. I like everything in the same place, so
enter "C:\Projects\CI\CruiseControl.NET".

You now have a single directory on the build server containing your continuous
integration environment.

Installation: Source Server


Your source control server needs a Visual SourceSafe server installation.

Installation: Client
Your client machine needs a Visual Studio .NET installation in order to create the
project in this example.

It also needs NUnit 2.2 installed so that we can write some tests.

You will find the installer for the CCTray application inside the webdashboard\cctray
folder in the build server installation. Run this on the client, installing it to any
destination folder you like.

Towards Our First Successful Build


We are going to run the continuous integration server from the command line at first
– it's easier to see what's going on. Open a command prompt window and navigate
to C:\Projects\CI\CruiseControl.NET\server. Run ccnet.exe. You will see this output
a bit like this:
[CruiseControl Server:Debug]: The trace level is currently set to
debug...
[CruiseControl Server:Info]: Reading configuration file
"C:\Projects\CI\CruiseControl.NET\server\ccnet.config"
[CruiseControl Server:Info]: No projects found
[CruiseControl Server:Info]: Registered channel: tcp
[CruiseControl Server:Info]: CruiseManager: Listening on url:
tcp://192.168.0.1:21234/CruiseManager.rem
[CruiseControl Server:Info]: Starting CruiseControl.NET Server

Nice and neat – a big improvement on previous versions. Starting in version 1.0 of
CruiseControl.NET debug tracing is turned on by default, so even more information is
displayed to help you get up and running. The application is reading an XML file
called ccnet.config, which at present has next to nothing in it. But it is very happily
starting and waiting for something to happen. Unfortunately nothing will happen
until we put something in ccnet.config.
We will modify ccnet.config file so that it contains two things:
• a source code repository to examine for changes
• a nant.exe to run and also a build file to run against

Once we’ve done this we’ll get our first successful build.
Modify ccnet.config
Leave ccnet.exe running and open ccnet.config in your favourite editor. Whenever
we change and save this file it will force a restart of the continuous integration server
so we'll get immediate feedback. Modify it to look like this:
<cruisecontrol>
<project>
<name>CITest</name>
<triggers>
<intervalTrigger seconds="60" buildCondition="ForceBuild" />
</triggers>
<sourcecontrol type="vss">
<project>$</project>
<username>ccnet</username>
<password>ccnet</password>
<ssdir>\\%sourceserver%\vss</ssdir>
</sourcecontrol>
<tasks>
<nant>
<executable>C:\Projects\CI\nant-0.85-
rc3\bin\nant.exe</executable>
<buildFile>cruise.build</buildFile>
<targetList>
<target>run</target>
</targetList>
</nant>
</tasks>
</project>
</cruisecontrol>

We have created a "project" element containing three other elements:


• triggers – which tells CruiseControl.NET what sort of source code repository
checks to perform. We have asked that a build be forced every 60 seconds.
Later on we'll modify this to check the source code repository for changes.
• sourcecontrol - which tells CruiseControl.NET which source code repository to
monitor for changes
• tasks - which describes the NAnt binary and build script to run when changes
are detected. We don’t have the NAnt build script yet, but that doesn’t matter
right now.
Let’s look at the file in detail.
Configure Visual SourceSafe
We have added a "sourcecontrol" element for Visual SourceSafe. This is often the
tricky part of CruiseControl.NET configuration. You should note the following:
• The "project" element is required. I have initially set its value to “$”
representing the root project in Visual SourceSafe. We don’t have our own
SourceSafe project yet. When we do, we'll change this value.
• The "username" and "password" elements are also required. If they don’t
refer to a valid SourceSafe user name with a valid password then
CruiseControl.NET will freeze while displaying output which looks something like
this:

...
...
[CITest:Debug]: Executing process D:\Program Files\Microsoft Visual
Studio\VSS\win32\ss.exe history $ -R
-Vd03/12/2005;22:00~03/12/2005;21:55 -Yccnet,badpassword -I-Y in
C:\Projects\CI\CruiseControl.NET\server\CITest\WorkingDirectory
...
...

This is because (unseen by you) the SourceSafe command line application


(ss.exe) is attempting to prompt you for a valid password! You will need to
break out of ccnet.exe and possibly kill ss.exe in task manager. So set up a build
user and password for CruiseControl.NET using SourceSafe Administrator, or
input your own login credentials.
• It’s a good idea to set the "ssdir" element, even though things should still
work correctly without it as ss.exe will extract the server details from your build
server’s copy of srcsafe.ini. It appears to be able to locate this file by stepping
up one directory in the hierarchy from where ss.exe is running! The content of
the element should be the name of the folder containing your database’s
srcsafe.ini file. Here the folder is shared as “vss” on the machine we’ve named
%sourceserver%.
• There is also an optional "executable" element which supplies ccnet with the
path to ss.exe. If you don’t supply one ccnet does some peeking in the registry
to extract the path to your ss.exe, so I don’t personally bother with it.

Save the ccnet.config file. Your console output should change immediately to
something which contains lines like this.

[CruiseControl Server:Debug]: Error loading buildfile.


[CruiseControl Server:Debug]: Could not find file
"C:\Projects\CI\CruiseControl.NET\server\CITest\WorkingDirectory\cruise
.build".
Let's view our failure in the Web Dashboard.
The Web Dashboard
There is no initial configuration required for the Web Dashboard. Simply navigate to
http://%buildserver%/ccnet in a browser. You’ll be able to see the CITest project
and view details of the failure.
Now let’s fix that build file problem.
First Successful Build
We told CruiseControl.NET that:
• there was a build file called “cruise.build”
• the build file contained a target called “run”

This was all lies. Let’s remedy the situation. Create the cruise.build file in
C:\Projects\CI\CruiseControl.NET\server\CITest\WorkingDirectory. Mine looks like
this:
<?xml version="1.0"?>
<project default="run">
<target name="run">
</target>
</project>

This should be enough to get us going. The next time ccnet goes through a cycle I
get this:
[CITest:Info]: No modifications detected.
[CITest:Info]: Build forced
[CITest:Info]: Building
[CITest:Info]: Build complete: Success
[CITest:Info]: Integration complete: 04/05/2005 22:44:14

Excellent! Refresh the Web Dashboard and you’ll be able to gloat over your success.
CCNet System Tray Application
In fact I’m so pleased that I want to view my successful build another way. Let’s set
up the CCTray application. CCTray uses .NET remoting to communicate with the
build server.
We will need to tell CruiseControl.NET the URL for our particular project so that
cctray can jump straight to that project's web page. To do this add a "webURL"
element to ccnet.config like this:
<cruisecontrol>
<project name="CITest">
<name>CITest</name>
<webURL>http://%buildserver%/ccnet/default.aspx?
_action_ViewProjectReport=true&amp;server=local&amp;project=CITest</web
URL>
...
...
I obtained this by viewing the CITest web page and pasting its URL into the config
file, using &amp; in place of ampersands.
Now run the cctray.exe application on your client machine. (Tip: we add cctray.exe
to our startup folder so that it is always running).
Double click on its system tray icon to bring up the application's main window then
select Settings... from the File menu.
Now click on the Add... button to add our build server. In the next dialog type the
name of the build server in the input box and click on the Add Server button. Select
CITest from the projects list on the right and click OK. You should now see our build
server in the list on the main settings dialog. Click OK.
Now the icon will go red if a failed build occurs. You can also force a build by
launching the main window and right clicking on the list entry for our project. Cool!
What’s Next
Let’s summarise what we have achieved so far and what the final steps will be. We
have:
• Configured the CruiseControl.NET continuous integration server (ccnet.exe)
using the ccnet.config file. This is nearly complete for the purposes of our
project. We are currently forcing a build every 60 seconds so that we don't have
to modify our code in order to kick off a build.
• Configured the CruiseControl.NET Web Dashboard so that it displays the
results of our builds.
• Configured the CruiseControl.NET system tray application (cctray.exe) so that
remote clients are informed of build results and can view the status of their
project in a browser.

Let's change the configuration so that CruiseControl.NET only runs a build when the
source code repository changes. It will then execute NAnt with our cruise.build file.
What we do inside the build file is up to us. We are going to:
• Clean the build directory for our project
• Get the latest source code from the repository and compile it
• Run tests

Remove Force Build


Modify the "triggers" element it so that it looks like this:
<cruisecontrol>
<project name="CITest">
<name>CITest</name>
<webURL>http://localhost/ccnet</webURL>
<triggers>
<intervalTrigger seconds="60" />
</triggers>
...
...

We have removed the force build. From now on we will rely on checkins to start the
integration process.
Add a Visual Studio .NET Project
Create a C# class library project with Visual Studio .NET called CITest. Place it in
your normal projects location – not under the CI directory. Don’t add any code –
just check that it builds. Now, using Visual Studio, add your project to source control
under the root SourceSafe project, so that we can reference its project path as
"$/CITest" in our ccnet.config file. (Tip: When the "Add to SourceSafe Project"
dialog pops up in Visual Studio, clear the "Project" text box completely, select the
"$/" node and press OK. A project named CITest will then be correctly added under
the root. If you type "CITest" in this box, you'll end up with a project called
$/CITest/CITest!)
Now modify the "project" element within the "sourcecontrol" element of our
ccnet.config file as follows:
<cruisecontrol>
<project name="CITest">
...
...
<sourcecontrol type="vss">
<project>$/CITest</project>
...
...

You should still have a successful build!


Now we are going to modify the cruise.build file to clean the build directory, get the
source and compile it.
Backtracking to NAnt
We are going to run NAnt directly against our cruise.build file for a while. Remember
that this is all that CruiseControl.NET does – so when we've got it all working
smoothly we can just plug it back again. In the meantime we will get a bit more
help with diagnostics from the output produced by NAnt.
It is very useful, whenever you're experiencing problems with CruiseControl.NET, to
run NAnt directly against your build file, so this technique may well save you a lot of
time somewhere down the line. if you're really having problems try running it with
the "–verbose" option.
Create a batch file called startnant.bat in the C:\Projects\CI directory and insert the
following line:
nant-0.85-rc3\bin\nant.exe
-buildfile:CruiseControl.NET\server\CITest\WorkingDirectory\cruise.build
This will execute NAnt against our cruise.build file. Stop CruiseControl.NET and
execute startnant.bat. You will see this:

NAnt 0.85 (Build 0.85.1932.0; rc3; 16/04/2005)


Copyright (C) 2001-2005 Gerry Shaw
http://nant.sourceforge.net

Buildfile:
file:///C:/Projects/CI/CruiseControl.NET/server/CITest/WorkingDirectory
/cruise.build
Target framework: Microsoft .NET Framework 1.1
Target(s) specified: run

run:
BUILD SUCCEEDED

Total time: 0.1 seconds.

Now modify cruise.build so that it deletes and creates a "Source" directory in which
we are going to get and build our source. It will now look like this:
<?xml version="1.0" ?>
<project default="run">
<target name="run" depends="clean">
</target>
<target name="clean">
<delete dir="Source" failonerror="false" />
<mkdir dir="Source" />
</target>
</project>

When you run startnant.bat it will delete the Source directory (if it exists) then
create it. Now add a target which uses the vssget task from NAntContrib to get the
source from SourceSafe:
<?xml version="1.0" ?>
<project default="run">
<target name="run" depends="get">
</target>
<target name="get" depends="clean">
<vssget username="ccnet"
password="ccnet"
localpath="Source"
recursive="true"
replace="true"
writable="false"
dbpath="\\%sourceserver%\vss\srcsafe.ini"
path="$/CITest"/>
</target>
<target name="clean">
....
</project>

The "srcsafe.ini" at the end of the "dbpath" attribute isn't required if you're using a
UNC path to access your source control server. Check that you're getting the source
correctly by running startnant.bat. At the end of the build the CITest directory
should contain a copy of the source.
Finally add a target to build the solution file:
<?xml version="1.0" ?>
<project default="run">
<target name="run" depends="build">
</target>
<target name="build" depends="get">
<solution configuration="debug" solutionfile="Source\CITest.sln" />
</target>
<target name="get" depends="clean">
...
...
</project>

Notice that each time we modify the file we change the dependencies. The targets
will be run by NAnt in the following order:
• clean
• get
• build

Later on we'll run a test target. Note that I haven't used any NAnt properties in my
build file. I leave this for you to play with later.
When you run startnant.bat you should now see:
NAnt 0.85 (Build 0.85.1932.0; rc3; 16/04/2005)
Copyright (C) 2001-2005 Gerry Shaw
http://nant.sourceforge.net

Buildfile:
file:///C:/Projects/CI/CruiseControl.NET/server/CITest/WorkingDirectory
/cruise.build
Target framework: Microsoft .NET Framework 1.1
Target(s) specified: run

clean:

[delete] Deleting directory


'C:\Projects\CI\CruiseControl.NET\server\CITest\WorkingDirectory\Source
'.
[mkdir] Creating directory
'C:\Projects\CI\CruiseControl.NET\server\CITest\WorkingDirectory\Source
'.

get:

[vssget] Getting '$/CITest' to


'C:\Projects\CI\CruiseControl.NET\server\CITest\WorkingDirectory\Source
'...

build:

[solution] Starting solution build.


[solution] Building 'CITest' [Debug] ...

run:

BUILD SUCCEEDED

Total time: 1.1 seconds.


Now restart ccnet and cctray. You should still have a successful build. If not, view
the NAnt log (by clicking on the “View Build Log” link in the Web Dashboard) to find
out the reason.
Once you have everything running smoothly, try breaking the build by checking out
a file from the CITest project, adding a compilation error and checking it in again.
CruiseControl.NET will detect the modification and the build will fail. Fix it!
NAnt and Assembly References
In previous versions of NAnt there were often problems when referencing third party
assemblies installed in the GAC. These seem to be resolved in the latest version (at
least in the case of NUnit) so the simplest thing for us to do in order to be able to
write some tests is:
• add a reference to the nunit.framework assembly in the CITest directory by
using the ".NET" tab "Add Reference" dialog.

Add a Test
Modify your Class1.cs to look something like this:
using System;
using NUnit.Framework;

namespace CITest
{
/// <summary>
/// Summary description for Class1.
/// </summary>
[TestFixture]
public class Class1
{
public Class1()
{
//
// TODO: Add constructor logic here
//
}

[Test]
public void ThisTestWillDefinitelyFail()
{
Assert.Fail("This test fails and I expect Cruise Control to
display this message");
}
}
}

Now modify the cruise.build file one last time to run any test fixtures present in your
dll. Here is the final version of cruise.build, with the modifications highlighted:
<?xml version="1.0" ?>
<project default="run">
<target name="run" depends="test">
</target>
<target name="test" depends="build">
<nunit2>
<formatter type="Xml" />
<test assemblyname="Source\bin\debug\CITest.dll" />
</nunit2>
</target>
<target name="build" depends="get">
<solution configuration="debug" solutionfile="Source\CITest.sln" />
</target>
<target name="get" depends="clean">
<vssget username="ccnet"
password="ccnet"
localpath="Source"
recursive="true"
replace="true"
writable="false"
dbpath="\\desktop\vss\srcsafe.ini"
path="$/CITest"/>
</target>
<target name="clean">
<delete dir="Source" failonerror="false" />
<mkdir dir="Source" />
</target>
</project>

I’ve added a target which runs NUnit against our assembly. I’ve specified the “Xml”
formatter, as CruiseControl.NET expects XML output.
Check in the code and run startnant.bat to check the cruise.build file. You should
see output ending like this:
...
...
[nunit2] <failure>
[nunit2] <message><![CDATA[This test fails and I expect Cruise
Control to display this message]]></message>
[nunit2] <stack-trace><![CDATA[ at
CITest.Class1.ThisTestWillDefinitelyFail() in
c:\Projects\CI\CruiseControl.NET\server\CITest\WorkingDirectory\Source\
Class1.cs:line 22
[nunit2] ]]></stack-trace>
[nunit2] </failure>
[nunit2] </test-case>
[nunit2] </results>
[nunit2] </test-suite>
[nunit2] </results>
[nunit2] </test-suite>
[nunit2] </results>
[nunit2] </test-suite>
[nunit2] </test-results>
[nunit2]

BUILD FAILED

C:\Projects\CI\CruiseControl.NET\server\CITest\WorkingDirectory\cruise.
build(6,6):
Tests Failed.

Total time: 1.7 seconds.


Exactly what we want. Your ccnet build should fail, and you will see the test failure
displayed in the web application. Now modify Class1 by adding the [Ignore]
attribute like this:
[Ignore("I wanna successful build")]
[Test]
public void ThisTestWillDefinitelyFail()
{
Assert.Fail("This test fails and I expect Cruise Control to
display this message");
}

Sit back and relax. Your next build will succeed. You will be able to see the ignored
test in the Web Dashboard output. We’re done!
Now that we have a working continuous integration system there are lots of possible
refinements. I won’t attempt to cover them all, but here are some candidates in no
particular order of priority.
Running the CruiseControl.NET Service
An easy one this – just stop running ccnet.exe and start the Windows Service
instead. The output that we have been viewing via the console is still logged to a file
called ccnet.log in the CruiseControl.NET\server directory.
Adding Visual Studio Projects
Don't forget that as you add more projects to your solution you will need to add a
new "test" element to cruise.build, otherwise your tests won't be run!
Also if you want to use application configuration files (as many people do when using
NUnit) you will need to reference them within your "test" element like this:
<test assemblyname="CITest\bin\debug\CITest.dll"
appconfig="CITest\bin\debug\CITest.dll.config" />

Adding Multiple CruiseControl Projects


Very handy this – multiple projects handled by the same build server – and/or
multiple build servers managed by a single Web Dashboard. See the
CruiseControl.NET documentation for more detail.
Merging NAnt Output
For more control over the output from your build script send it to files and combine
the files with the CruiseControl.NET log using a merge task. This involves changing
the "formatter" element in the cruise.build file so that it sends output to files:
<formatter type="Xml"
usefile="true"
extension=".xml" />

and adding a publishers element to the ccnet.config file so that it merges these files
into the log:
...
<publishers>
<merge>
<files>
<file>Source\bin\debug\*-results.xml</file>
</files>
</merge>
<xmllogger />
</publishers>
</project>
</cruisecontrol>

Note also that we have added a xmllogger publisher. This is the default publisher
used by CruiseControl.NET and is automatically used if you don't create a publishers
element in the ccnet.config. Once, however, you start to specify publishers you need
to include it. It also needs to be after the merge element.
Bootstrapping NAnt
In our team we maintain two NAnt build scripts. The first one just cleans the build
directory and gets the latest source from the repository. Included in the latest
source is the second build script, which is invoked from the first. The second script
compiles the source and runs the tests.
The advantage of this approach is that we can maintain the second build script as a
solution item and modify it (for example by adding a new "test" element) whenever
we add a new project to the solution. When the solution is checked in
CruiseControl.NET will automatically perform the modified build.
Summary
Any effort you invest in setting up CruiseControl.NET is repaid many times over, and
I hope that this article has helped you on your way.

You might also like