Before I start, I want to turn your attention for not well know product named CI Factory. It's a continuous integration tool (or server) created by Jay Flower. It's quite interesting alternative for those who don't want to set up and configure everything on his own. The CI Factory is using CC.NET and Nant and number of packages. I've never try that tool myself, so if there is somebody who did, I will appreciate some extended comment or review to publish.
When I introduced this cycle I wrote a few words about the target and assumptions. Generally speaking, I'm not going to write about installation of CruiseControl.NET or Subversion, as there are a lot of resources and help for those tools. So let us assume, that we are starting at the moment, when we have Subversion and CC.NET installed on the server along with the IIS and .NET 2.0, everything is configured and works properly, source control is accessible from the network and the same is with CC.NET web interface. From other, non standard tools, we will need a set of MSBuild tasks names MSBuild Community Tasks which should be installed and MSBuild itself which is part of .NET 2.0.
As I mention in introductory post, I'm using MSBuild as a solution for a build script, but there is no reason why you shouldn't use NAnt, however I will not be able to help with details here. If you want to know why I have selected MSBuild there is a few reasons. First of all I want to be able easily extend my build script by adding new tasks which is fairly simple with MSBuild. Of course, MSBuild itself will be much worse option than NAnt, but with a number of additional tasks is, in my opinion, competent alternative.
Last thing I want to remember is that everything I will be doing during the cycle is regarding web application project (not web site, you will see later in the cycle why). Nevertheless all this can be easily adapted to other types of application. Only what you need is to remove site deployment parts.
For the very first steps we are going to create very simple build server that will take our project from the repository and build it. Some kind of "Hello World" form CI (Continuous Integration) where I want to show generic concept how I server and scripts are organised.
Whole configuration is stored by set of two scripts:
- ccnet.config – CC.NET configuration script located in by default in C:\Program Files\CruiseControl.NET\server\
- project.proj – MSBuild script for our project.
The first file, ccnet.cfg configures CC.NET and defines what to do and when. One of my biggest concerns when I've started my first CI server was if I should use ccnet.cfg or MSBuild to do all work. There is some functionality, which exists in both solutions, such as subversion checkout or NUnit tests. Finally I've decide to use MSBuild for all building process including checkout, unit testing, code analysis and other. In that situation CC.NET has been used only as a wrapper for my project file. As I saw on the web, using CC.NET to do more work is very popular, but I will to try to convince you that it's a bit easier to use it in the way I have done. One of reasons, and most important I suppose, was that I'm using a number of variables, such as release number retrieved from Subversion. Unfortunately CC.NET does not support something like custom properties or variables unlikely to MSBuild (or I just can't find them). Finally I simply want to have everything in one single file which I can easily edit without checking what is done where. First of all, I'm lazy.
Last thing from setup part is directory structure. Probably there are as many opinions as developers on that subject so I will describe what structure I'm currently using. Please note that I'm not talking about directory structure for the project itself as it is not important from our point of view.
Basically structure is simple. Root directory named build contains separated directories for every project we are going to include into CI. In presented example it is myproject directory. Each project directory has three subfolders:
- myproject\drop – This is storage for drops, compiled and ready for download releases of our project with separation to debug and release builds. This directory can be shared over the network using ftp or IIS to allow users download the latest release.
- myproject\working – This is the most important directory where all CI working cycle will be located and all temporary files will be stored. This folder will be cleaned before and after every build. In this directory .proj file will be placed.
- myproject\working\checkout – This directory will be used for source files checked out from source control.
- myproject\working\outputs – This directory will be used for all outputs generated during the build process such as logs, xml reports and so on.
- myproject\testsite – This is folder where latest release of site will be published for test (or normal) users. This folder is intended to be accessible via IIS as a default website or virtual folder.
The First Build Script
Having all setup work done we can start working on things that are really important. As I mention at the beginning we are going to do:
- Checkout sources from subversion
- Build the project (solution)
As a project I will use very simple ASP.NET application with one Class Library project so our solution will contains two projects at the beginning. You can download this project from the attachments.
We will start from the MSBuild script by creating empty file \build\myproject\myproject.proj. We can use the Visual Studio 2005 to edit that file with all intellisense benefits. First thing to do after creating empty file is adding root tag and importing additional tasks we will be using.
$(MSBuildExtensionsPath) is the property built in MSBuild that points to default MSBuild directory. Community tasks I mention before are installed by default under that directory.
Next thing we will need is a declaration of the properties that will make our further work a bit easier. Using PropertyGroup section we can declare properties for use it later. To learn more about properties, item groups and other basic concepts of MSBuild you can refer to MSBuild documentation or wiki on Channel9.
In code below we are creating first property ProjectDir that points to project directory. Then, in the next group we are introducing next two properties that define paths to checkout and outputs folders under working directory. To define that paths we are using property declared before which allow us make change in project location with single line change.
Having all properties ready we can start with writing tasks. First work to do will be cleaning of the working directory and preparing it for the next CI cycle. Tasks are organised in groups inside targets. Each target has name that allow calling only selected target and can also depend on other targets.
In this pretty simple snippet above we are using two standard tasks RemoveDir and MakeDir. The easiest way to clean directory is to remove it and create back again. At the end we are displaying message using Message task.
When we are sure that working directory will be clean and ready we can get source files from the source control. So let us create new target as follows:
This target, named Checkout, depend on previous one, so if we call MSBuild script with parameter /targets:Checkout we are sure that cleaning will be performed at the first place. Task SvnCheckout is included in MSBuildComminutyTasks installed before. Parameters of this task are pretty obvious and do not require any special comment. At the end we are displaying message as previously.
After execution of that target we will have latest source files located in our checkout directory ready to build, so let's do it. Target to do this is also pretty simple in fact:
I named this target Build and set that it depends on "Checkout" target, which is pretty obvious. Because dependency between targets are cascade, co this guarantee us that when we will run MSBuild on out project it will do cleaning, checkout and building. Please note that we are using $(Configuration) parameter in MSBuild task. Because we don't want to hardcode type of build, that will allow us option to later separate build types, we have to pass correct parameter to MSBuild. We can define parameters in the command line which call MSBuild and this parameter will be then available in the script. Note that we don't have to declare that parameter as other parameters before.
To complete our script we have to clean up after build. To do so we will reuse "Clean" target by calling it again after build. We can't set dependency on Build for that target because it will cause circular dependency which is not allowed. There are two ways to achieve that. First is creation the next target which will be duplicated of the cleaning. We can think about that solution if we want to do something more than initial cleaning. In that case possible is creation of the new target, which will execute the initial cleaning and then do some work. Other way is creation of special target, which will execute other targets in required order. For our needs the second way is better. That target is presented on snippet below.
That target will call other targets in the specified order. Moreover, allow us to simply call one target in MSBuild command line to run all of them as we want.
We can test our build script if we want by calling
msbuild.exe myproject.proj /targets:All /p:Configuration=Debug
That command line will execute target "All" and will pass parameter "Configuration" wit value "Debug" which will execute debug build.
Server Configuration Script
To accomplish our job in this part we have to create configuration script of CC.NET. Detailed syntax and structure of that script is quite well described in CC.NET documentation. As a first step we need to create project section in the script. Project tag allows us to specify project name which will be visible on the web dashboard.
Then, we should specify url of project dashboard. This setting is not required, however is used by CCTray, small program that reside in tray and monitor build status on server by showing nice green ball or bad red when build is broken.
Next comes one of the most important things. There are different ways to set up when CC.NET should execute our build. We can set number of triggers that allow scheduling build for many conditions. However in our case we want to initiate build shortly after every source commit, so we will not need triggers at the moment. Instead of that we will use the special feature of CC.NET that is connected with source control block described later and allows initiating build after defined time after last check in.
Thereafter we have to add source control block thereafter to make above working.
If you are still focused after reading such long and boring text, you will probably shout right now: "Wait a moment. We have source control in MSBuild already!". Yes, that's true, we have two checkouts. It may not be clear now, because we are doing nothing complicated in our script, but later we will gather some information from source control during checkout and reuse them in further tasks. In theory we can checkout sources using CC.NET and then use that in MSBuild, but as far I've checked it's not possible to do the same what we finally will do in that way. This explanation seems to be a bit lousy, but everything becomes clear in the next part of the cycle. I hope that. So far you have to accept my explanation and deal with double checkout.
Right, next element is the task, which will call MSBuild and will build our project.
All parameters are pretty clear. We are passing Configuration parameter, targets to build are set to "All" and timeout is set to 1200 seconds. A bit attention needs the last tag <logger>. To be able to get MSBuild log and display it in CC.NET dashboard we have to change the way how build log is displayed using custom loggers. In this case we are using XmlLogger which generate XML file with build log.
In order to use log producer by MSBuild we have to add our final block that will publish log to form that is acceptable by CC.NET web dashboard.
In that way we are done. Below you can find all two scripts in one snippet. You can also find them in file attached to post.
At the moment our server will build our project 30 seconds after check in. Results of the build will be displayed on web dashboard.
This part becomes far longer than I was expecting, but I decide that I have to cover basic topics and post with configuration only would be in my opinion boring. In the next part, in much shorter post, we will add automatic versioning to our script and all explanations about doubled checkout shall become clear.
Below you can find a list of resources I refer in this post. Instead of adding links everywhere in the text, I've decide to group them all together.
03-10-2007 12:45 AM