Tuesday, 11 September 2007

Little TFS Team Build Customisation

A colleague (James) asked if I knew of a way to stop TFS builds for creating a work item if the build in question fails. The answer took a few minutes to dig up but in a nutshell the answer is that you have to create a CreateWorkItem target (see update at bottom for better answer) in your MSBuild   file.

Ok why is that?

MSBuild scripts , like NAnt, are defined using a set of targets. Each target (defined with <Target /> tag) and is an outcome which MSBuild  must assert as true for the build to succeed (or conversely not assert as true for the build to fail). This is not meant as a tutorial on MSBuild, as there's hundred's out there, but it does help to know the basics of what MSBuild is doing before you start customising the build.

If you think of the build process as of essentially asserting one target (e.g. <Target Name='MakeItSoTarget' />). This in itself may not do very much, but what a target can do it assert other targets, so MakeItSo may assert that it will be true if PowerEngines and SetCourse also assert true. 

In this way a hierarchy of dependencies can be created to define the entire build process from start to finish.  In fact VS2005 .csproj files are essentially MSBuild build files that Visual Studio passes to the MSBuild to create your lovingly crafted code masterpiece. But I think a real strength of MSBuild is the pre-existing targets (and thus build processes) that MS has defined, which can be used to create a standard build process such as for Visual Studio or say TFS.

Anyway back to the question, why do I need to override CreateWorkItem. Well for team build there is a targets file which MS uses to control the build process. This does number of things including, preparing for a build, getting the source code from TFS, compiling, testing, and packaging (I'll put a more detailed description of this in another post) . You can see the structure yourself in the file but essentially the main target of concern is OnBuildBreak. This is called (surprisingly) when a build breaks (i.e. some other target is not asserted).


   1:       <PropertyGroup>
   2:                <OnBuildBreakDependsOn>
   3:                       BeforeOnBuildBreak;
   4:                       GetChangeSetsOnBuildBreak;
   5:                       DropBuild;
   6:                       CreateWorkItem;
   7:                       AfterOnBuildBreak;
   8:                </OnBuildBreakDependsOn>
   9:         </PropertyGroup>
  10:         <Target Name="OnBuildBreak" 
  11:                       Condition=" '$(IsDesktopBuild)'!='true' "
  12:                       DependsOnTargets="$(OnBuildBreakDependsOn)" />


Here we can see that the OnBuildBreak asserts true if the targets defined in property group OnBuildBreakDependsOn also assert (line 12). This means that we  have a number of options for customisation. We can

  1. Change OnBuildBreak to not depend on this precise property group
  2. Alter the property group
  3. Create a custom version CreateWorkItem

Since MS recommends that we don't actually alter the supplied .targets files, we can do each of these in our build file as any targets we define with the same name as the MS ones will be used by MSBuild instead. The path of least resistance suggests that we should endeavour to keep true the MSBuild as possible as this leads to a smaller ongoing maintenance cost (thanks for that Howard!) so to me the simplest thing is to define a new target:


<Target Name='CreateWorkItem' >
<Message Text='Not creating a work item !!!.' />

Now a new work item will not be created!

UPDATE: looking again I see an even simpler way - define the property SkipWorkItemCreation=true in your script, then team build will not do the task. I hope the exercise in understanding was still of value.