Professional Documents
Culture Documents
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
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: 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.
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>
...
...
[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
...
...
Save the ccnet.config file. Your console output should change immediately to
something which contains lines like this.
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&server=local&project=CITest</web
URL>
...
...
I obtained this by viewing the CITest web page and pasting its URL into the config
file, using & 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
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>
...
...
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
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:
get:
build:
run:
BUILD SUCCEEDED
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.
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" />
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.