http://www.developer.com/

Back to article

Getting Started with Continuous Integration


October 19, 2005

Imagine that you've just fixed a bug in your application's code and checked the fix into the project's source code repository. How much do you know now? If all you know is that the fix worked on your machine, you're playing with fire. If you've run the fix through a suite of integration tests and are confident that it didn't break anything else in the project's code, you're doing much better: your process is under control. But would you believe that you can do better than that? There are some teams that know that the new checkin passes the integration tests, that the tests cover the entire codebase, that the project's coding standards are being followed, and that there's no excessive duplication in code. And they know this after every checkin. Best of all, they know this without human intervention: a little icon in their system tray turns red if anything goes wrong, and stays happily green otherwise. At any time, they can visit a Web page on their local intranet to check up on the current state of the code.

The technique behind this seeming magic is called continuous integration, and it's not as difficult to set up as you might think. In this article I'll show you how the pieces fit together for a simple .NET continuous integration server that will help you monitor the quality of your own coding efforts in real time.

Setting Up the Server

The first step in continuous integration is to set up a build server. Ideally, this will be a dedicated computer that isn't used for anything else. This is the computer that will be the master source for final builds of your software: if all goes well, at any time you should be able to burn a working CD from this machine's hard drive and be ready to install it on customer boxes. You'll want a computer that is powerful enough to run all of the tools that you need to build your software. If you're doing .NET development, that means that it at least needs to be able to manage command-line builds using the csc or vbc compilers, though you shouldn't skimp here. The faster the build machine can turn out a fresh build of your software, the faster you get feedback on the code quality of new checkins.

The key piece of software you'll need is a continuous integration server. This is a master controller for the build process: it monitors your source code control repository for checkins, and every time that it spots one, it gets the latest version of the code. Then it kicks off a build by calling other tools, such as compilers and unit test tools. Finally, the continuous integration server notifies team members of the build status by publishing information in a variety of ways, from updated Web pages to e-mailed status messages, and goes back to watching for new checkins. There are several continuous integration servers for .NET available; I happen to use CruiseControl.NET, which is currently at the 1.0RC2 release level. Installing CruiseControl.NET is straightforward; you'll want to have the .NET Framework and IIS with ASP.NET installed first. Then just download and run the installers for CruiseControl.NET and the separate Web Dashboard (the applications are free) and you're off and running. CruiseControl.NET is open source, so if you prefer you can start with the source code and build it yourself.

Installing Your Tools

The next thing the server needs is everything required to actually build your software, starting from the source code. It's impossible to give a complete list here, because every project is different. What you need to do is observe your own manual practices, starting from source code checkin and ending with the deliverable. Certainly you'll need to install a source code control client, so the continuous integration server can get at the source code, and either the .NET SDK or Visual Studio .NET to build the software. From there, you may need a unit-testing tool, an obfuscator, a code-quality checker, a setup builder, and other tools.

Depending on your own particular set of tools, you'll also want to install a general-purpose build-scripting utility such as NAnt or MSBuild. While CruiseControl.NET can call many tools directly, it can't know about every development tool in the universe. Sometimes you may find that you need to have CruiseControl.NET call a NAnt task that in turn calls your development tool of choice.

It's important that you get the same versions of all your tools on the build server that you're using on your development computers. Otherwise, some day you'll spend far too much time trying to track down a subtle error caused by, say, the testing tool being at a different revision level on different machines. One way to handle this is to actually check the tools into your source code control repository and install them from there. Another is to use a central software distribution system such as Microsoft Systems Management Server to install the build tools.

A Simple Example

To make this all a bit more concrete, let's look at one of the projects on my own continuous integration server. My build tools stack for this project currently includes these tools:

  • Subversion for source code control.
  • Visual Studio .NET to handle the actual builds.
  • NUnit for unit testing.
  • NAnt for build automation.
  • FxCop for conformance checking.
  • NCover for code coverage analysis.
  • Simian for similarity analysis.
  • Vil for collecting code metrics.

One key to making the process work smoothly is the proper use of NAnt. I want the build process on the continuous integration server to be as close as possible to the build process on my local development computer. That way, if anything goes wrong, it's easy for me to replicate what happened. So rather than have CruiseControl.NET call out to the individual tools directly, I've set it up to call a NAnt build file that in turn calls the individual build tools. With this indirection in place, I can replicate what CruiseControl.NET is doing simply by invoking NAnt by had any time I need to check what's going on. The NAnt configuration file looks like this:


<?xml version="1.0" ?>
<project name="nant" default="compile"
  xmlns="http://nant.sf.net/schemas/nant.xsd">
<target name="compile">
  <solution solutionfile="RssDrop.sln" 
    configuration="AutomatedDebug" />
</target>
<target name="test" depends="compile, run-unit-tests" 
  description="Compile and Run Tests" />
<target name="ccnet" 
  depends="compile, fxcop, ncover, simian, vil" 
  description="Cruise Control.NET target" />
<target name="run-unit-tests">
  <nunit2>
    <formatter type="Xml" usefile="false" />
    <test assemblyname="RssDropLibUnitTests.dll" />
  </nunit2>
</target>
<!-- Run FxCop compliance checker -->
<target name="fxcop">
  <exec program="toolsFxCopfxcopcmd.exe" ... />
</target>
<!-- NOTE: ncover runs nunit tests -->
<target name="ncover">
  <exec program="toolsncoverNCover.Console.exe" ... />
</target>
<!-- Find duplicate code -->
<target name="simian" >
  <exec program="toolssimiansimian-2.2.7.exe"  ... /> 
</target>
<!-- Code metrics -->
<target name="vil">
  <exec program="toolsvilvil.exe" ... />
</target>
</project>

I've omitted the details of the command lines and arguments for the various tools so as to make the structure of this file more clear. Even if you're not familiar with NAnt, you should be able to tell what's going on here. A NAnt configuration file is a collection of targets, each of which tells NAnt what to do in a particular circumstance; when you invoke NAnt, you tell it which target to carry out. The ccnet target is the composite target that CruiseControl.NET will execute; it in turn calls the targets that make up the various steps involved in building and testing my project.

Configuring the Continuous Integration Server

To hook all this up to the continuous integration server requires another XML configuration file, this one for CruiseControl.NET:


<cruisecontrol>
  <project name="RssDrop">
    <workingDirectory>c:ProjectsRssDrop</workingDirectory>
    <webURL>http://TITANIC/ccnet/</webURL>
    <triggers>
      <!-- Build every sixty seconds if any changes in SVN -->
      <intervalTrigger seconds="60" />
      <!-- Force a daily build at 1AM -->
      <scheduleTrigger time="01:00" buildCondition="ForceBuild" />
    </triggers>
    <modificationDelaySeconds>10</modificationDelaySeconds>
    <sourcecontrol type="svn">
      <executable>c:program filessubversionbinsvn.exe</executable>
      <trunkUrl>svn://BAKUNIN/Repos/Larkware/trunk/RssDrop</trunkUrl>
      <autoGetSource>true</autoGetSource>
      <username>CCNet</username>
      <password>XXXXXX</password>
      <workingDirectory>c:ProjectsRssDrop</workingDirectory>
    </sourcecontrol>
    <tasks>
      <nant>
        <executable>C:ProjectsRssDropToolsNAntnant.exe</executable>
        <nologo>false</nologo>
        <buildFile>RssDrop.build</buildFile>
        <targetList>
          <target>ccnet</target>
        </targetList>
        <buildTimeoutSeconds>600</buildTimeoutSeconds>
      </nant>
    </tasks>
    <publishers>
      <merge>
        <files>
          <file>AutomatedBuildsFxCop.xml</file>
          <file>AutomatedBuildsRssDropLibUnitTests.dll-results.xml</file>
          <file>AutomatedBuildsCoverage.xml</file>
          <file>AutomatedBuildssimian.xml</file>
          <file>AutomatedBuildsresults-vil.xml</file>
        </files>
      </merge>
      <xmllogger />
    </publishers>
  </project>
</cruisecontrol>

Let's pick this apart one piece at a time. First comes some basic setup: the working directory on the build server where CruiseControl.NET will do its work and the URL on my intranet where it will publish results. Next comes the triggers section. This specifies the events that will trigger a build. I've set up two of these. The first checks the source code repository once every minute for checkins, and starts a build if anything has changed. The second builds the code every morning at 1AM when I am (hopefully) in bed, so I get a daily record of progress.

The next section of the file sets up the source code control hookup - in this case, to my Subversion repository, which lives on the BAKUNIN server. I've supplied all the necessary information for CruiseControl.NET to log in to the server using its own account. Note that it automatically gets any changes to the code when it checks the repository, so it's always building from the most recent sources. The modificationDelaySeconds element puts a ten-second delay into things so that multiple checkins in a short span of time don't trigger multiple builds; it waits until things are quiet for a little bit before getting underway.

Next comes the tasks section, which tells CruiseControl.NET just what to do when it does decide it's time to rebuild. In this case, I've added a single task, which calls the NAnt build file that I set up earlier. More complex situations will call for multiple tasks here. CruiseControl.NET can do many things with tasks, including call NAnt, call MSBuild, call any command-line executable, invoke Visual Studio .NET directly, and so on.

The publishers section of the configuration file tells the server what to do after the build. In this case, it collects all the various XML output files from the different build tools and merges them together into a single XML log file. If you're working with a team, you'll probably also want to take advantage of the server's ability to send success or failure e-mail here.

That's about all there is to setting things up. What about keeping an eye on them?

Monitoring the Results

CruiseControl.NET maintains its own IIS-based Web Dashboard for easy Web-based reporting on its activities. Figure 1 shows a sample Web report for the project that I've been examining in this article.

CruiseControl.NET Web Dashboard

The Web Dashboard lets you easily move between projects on your server, see the builds for any project, and drill into the results from each tool on any build you wish. XSL transforms are used to format the tools' XML output files for pretty display. You can also force a build from the Web Dashboard, telling the server to build immediately without waiting for the triggers to activate.

The Web Dashboard is useful when you need to see the details of a particular build, but for convenient monitoring of the current build status you can't beat the CCTray application. As you might guess from the name, this little tool normally lives in the tray notification area, where it presents build status through simple color codes: green to show the last build was good, red to show it failed, and yellow to show that a build is underway. As long as the icon stays green, you know that your most recent checkin didn't break anything - and neither did a checkin from any of your co-workers.

It's All About Good Build Hygiene

Continuous integration by itself doesn't do any good, of course. You still need to write good unit tests, check in code on a regular basis, think about the architecture of your code, and so on. But coupled with other good build and development processes, it can be a powerful way to know that your code quality is staying high - or to help you immediately pinpoint problem areas when things slip. You'll need to devote a bit of time and a dedicated computer to setting things up, but you'll probably find that the rewards are worth it. With the tools being free, you've got very little to lose by trying it out!

About the Author

Mike Gunderloy is the author of over 20 books and numerous articles on development topics, and the lead developer for Larkware. Check out his latest books, Coder to Developer and Developer to Designer, both from Sybex. When he's not writing code, Mike putters in the garden on his farm in eastern Washington state.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date