Sunday, November 28, 2010

Anatomy Of A Continuous Integration System Part One

I occasionally give my eleven year old a particular piece of advice - "keep your eye on the prize" - and that's a good way to approach continuous integration. The architecture of a well-functioning msbuild script consists of milestones (significant events) tied together in a linear fashion with a clearly defined "prize" result. That's singular. If you find you need something more complex, you should be cutting your builds up into smaller builds and setting up logical relationships between them.
As I said in part zero I wanted to stay away from philosophy, but I want to talk about that last sentence a little more. Trying to address multiple needed outcomes/deliverables in a single container approaches a worst practice. You will need to check for and track multiple fatal failure conditions and conversely, multiple success conditions, and you're going to fail trying. It's not worth it. You can't effectively serve two masters at the same time - listen to what Yoda said in the movies. A successful msbuild has just one required output, everything else is frosting. Take your uber-complex build monster script, break down the requirements until you have a list of the prizes you need and silo each one into its own build. Capisce?

Back to our build script. Remember wikipedia's definition of CI from part zero of this series?
"small pieces of effort, applied frequently" 
Identify your prize, break it down to the smallest piece of discrete effort, and write it so you can apply it frequently. That's the magic formula. In fact, 99% of the build scripts I write can be boiled down to the same skeleton over and over.  Cleanup, build (clean) and deploy (to Test). Step 1, Step 2 and Step 3. Everything else in your build is window dressing and any failure of those three steps is fatal.
If other factors come into play, such as multiple environmental targets in a single build (deploy to Test, QA, Staging, offsite tape and burn to a DVD gold master), while I may pragmatically add an extra piece or two  at first, it doesn't take very much to push me into cutting one build into multiple builds and chaining them together in some fashion.

Here's a molecular build script - under normal circumstances you don't want to break it down any further than this, and conversely you don't want to get too much more complicated than this. If you're deep in a thorny issue, you happen to step back and take a look at your creation and you don't recognize your msbuild script having a direct, familial relationship with the Clean, Compile, Deploy pseudo-code skeleton below, then you probably made a left turn at Albuquerque along the way and some refactoring needs to occur, perhaps along the lines of creating multiple builds out of your problem build.

 Starter stub skeleton of 99% of my build scripts:


<?xml version="1.0" encoding="utf-8" ?>
<Project DefaultTargets="Publish" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="MasterClean">
<Cleanup Actions/>
</Target>

<Target Name="MasterBuild" DependsOnTargets="MasterClean">
<MSBuild (compile) Actions/>
</Target>

<Target Name="Publish" DependsOnTargets="MasterBuild">
<Publish Actions/>
</Target>
</Project>

For troubleshooting purposes, I'll leave three manual cmd files committed into source control in the root of each project; one to kick off the cleanup, one to kick off the compile (which has a prereq of cleanup) and one to kick off publish (which has a prereq of compile, which has a prereq of cleanup - you get the idea). Here's the meat of the Publish.cmd I use in a .Net 4.0 project for instance -

%SYSTEMROOT%\Microsoft.NET\Framework\v4.0.30319\msbuild.exe master.build %*  /t:Publish %*

Very occasionally developers get some use out of them, but they are really for me when I'm initially setting up a build and troubleshooting build issues, build server/CI system issues or when I'm doing maintenance on a build.

-Kelly Schoenhofen

No comments:

Post a Comment