Search This Blog

Wednesday, October 20, 2010

Extending TFS Builds - Specifically working with the Drop Location

I've seen a few different posts regarding getting to the Drop Location when working with MSBuild. In short, the DropLocation property isn't set when PropertyGroup variables are evaluated.

A workaround to this is to use the CreateProperty task and the GetBuildProperties task in a target just before you call other targets that need the folder location.

For starters, our script adds the AfterDropBuild target so we get into the workflow right after the build has been dropped to its drop location. An example drop location might look like "d:\builddrop\MyProject_Staging\Build_20101001.1".

Once you have the folder, you can then target specific published projects and do meaningful tasks like changing configuration to match the target environment.

Take a moment to look at builds that have been dropped and you should see beneath the build flavor (Debug, Release) a folder called "_PublishedWebSites". Each of the web sites defined (or services) in your solution will exist beneath that folder.

To get the folder, you need to configure a dependency target that is called through the "DependsOnTargets" attribute for the AfterDropBuild target.  The target calls the same task that is used by the Microsoft targets specifically "GetBuildProperties".  If you traverse the targets stored in the Microsoft folder under the MSbuild directory you'll see how it is used.  What you want to do is acquire just the DropLocation value.

Next, use the CreateProperty task to [re]populate a property variable with the DropLocation value.

You can accomplish this without having to wrap the TFSBuild.proj targets at all (as I've seen in other posts).

The following is an example:

<xmp>
  <PropertyGroup>
    <CurrentDropLocation></CurrentDropLocation>
  </PropertyGroup>
  <Target Name="AfterDropBuild" DependsOnTargets="ReinitializeBuildProperties;ConfigureUnityMappingFile;ConfigureWebConfigFile;CopyAndConfigureService">
    <!-- do your other stuff here such as configuring your .config files. -->
  </Target>

  <Target Name="ReinitializeBuildProperties">
      <GetBuildProperties TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
                          BuildUri="$(BuildUri)">
        <Output TaskParameter="DropLocation" PropertyName="MyDropLocation" />
      </GetBuildProperties>
       
    <CreateProperty Value="$(MyDropLocation)\%(ConfigurationToBuild.FlavorToBuild)">
      <Output PropertyName="CurrentDropLocation" TaskParameter="Value" />
    </CreateProperty>
</xmp>

Spring.NET and the power of the Dictionary

I stumbled on a technique for simplifying factories (which if you think about, is exactly what Spring.NET's IOC container is) that are context-based.  This is not a new concept but it's worth repeating due to its simplicity and its elegance.

As an example scenario, we have an application that processes about 20 different types of notifications that send out targeted content to subscribers (yes, it's an email application aka SPAM engine).  Each notification can be configured independently of the others in the areas of transformation (the creation of formatted content to send) and transfer (SMTP, DB, MSMQ, etc). 

We arrived at a design that uses Composite, Strategy and Command patterns.  The Composite pattern is applied for Notification Commands (example, a notification that has many sections) and each Notification Command implements the Command Pattern (Execute, etc).  Likewise, Transfers are configured as Strategies (Strategy Pattern) and Composites ( a Transfer Strategy may be 3 physical strategies ie., Send to SMTP, Send to DB and Send to MSMQ). 

To add context, each type of notification has a unique value that traces back to the underlying persistent store.  Notification Type 1 for example, is a "Sales Flyer".

We have a very simple Job controller that is handed a Job (context) and is then asked to process it.  What used to be about 5,000 lines of code (for 20 notifications) has effectively been reduced to about 150 lines of code. 

In our Spring.NET XML registry, we defined our commands and strategies but were then taken aback out HOW we would choose the appropriate command to use to process the Job.  What we arrived at is both simple and rather elegant.

Spring.NET registries can store much more than templates for objects.  They can also store dictionaries.  In our case, we chose to store a dictionary of commands.  The dictionary's key refers to the unique identifier for each type of job and the value is the id of the command to use.  When the controller is activated and the job context passed, we reach into the dictionary (served up as a dependency property to the controller) and then ask the repository for the ICommand by Id which is retrieved from the dictionary. 

Code can be shared if anyone is interested although I'm fairly positive this is not an original idea and to some may be more along the lines of  "we already do this".

Wearing two saddles: Scrum and the multiple project Affect

Back in April, I agreed to be a team member on 2 projects split equally 50/50.  This was an experiment I agreed to in order to experience first-hand why team members need to be dedicated to a single project. 

It started out fine on planning day.  Half the day was dedicated to planning for Project A and the other half for Project B.  Half-way through the first sprint, it was apparent what was going to happen.  My 3 hours daily allocation per project was working out to be more like 2 effective hours per project and as I was just about ready to implement something cool for Project A, I had to stop and work on Project B.

On the day when QA needed to have the last bits to test, I had incomplete stories on both projects.  And of course, the next bad thing that happened was Project A wanted to be more valuable than Project B.  So what did I do?  Of course!  I worked long hours on Project A. 

In short, both sprints' goals were not met and I had very few completed stories.  Each project became the other's impediment and I "thrashed" between the two of them getting very little done.

This experiment reminds me of a line from Patrick Lencioni's "The 5 Dysfunctions of a Team" where the CEO character had to tell her team they were to pick a #1 priority goal.  A member asked why not 2 goals and her reply was, "If every goal is a priority, then none of them are". 

In my case, this was exactly what happened.  Both projects wanted to be #1 and it's just not realistic.  One project needed to be killed (there were no other resources to work on it).

As Scrum practitioners, it's important to learn why certain things don't work well and why others do (such as XP practices).  By all means, don't take my word for it.  Experience is the best teacher and in this case, my experience was quite the eye opener.  We have since moved to strictly dedicated team members.  We still have shared resources but none of them own stories and they certainly don't sit at the table.