Oct 172011

Web.config Transformations

A great feature in Web Deploy (Microsoft’s latest website publishing technology) is the ability to transform your default web.config to a customised version, driven by the configuration being built. So you can have different configurations for Test, Staging and Production etc. with transformation rules that change only the settings that differ in each of those environments.

To add web.config transformations in Visual Studio, simply right-click your web.config and select Add Config Transforms. If the option is disabled, you probably need to add a new configuration to the solution using the Configuration Manager. The transformation language is XML Document Transform (XDT) which has been covered very well by Scott Hanselman and the syntax is fully documented.

You may be surprised to learn that TFS build does not automatically apply your web.config transformations when it generates the _PublishedWebsites folder containing your compiled web application. This is an inconvenience if you aren’t using the Web Deployment Tool to deploy the application, and just want a package of files customised to the right environment. The fix for this is fairly straightforward: to get TFS build to run the transformations you need to pass some additional arguments to MSBuild telling it to use the new Web Publishing Pipeline. This can be done using the build definition’s MSBuild Arguments parameter (if you are using the default build template or a custom template derived from it). Simply add /p:UseWPP_CopyWebApplication=true /p:PipelineDependsOnBuild=false as shown below.

* * * * *

App.config Transformations

Web.config transformations are great, but you can also transform app.config files. You need to make some changes to the MSBuild project file to associate the configs and import a transformation MSBuild script, but that’s all. Here’s how to do it using the neat solution by Sayed Ibrahim Hashimi:

First, if your project doesn’t have an app.config file, add one using the provided template (under the General category when you choose to add a new item to a project). Next, add further app.*.config files using the same template – one for each configuration in the project except Debug.

Add transforms to the configuration-specific files. For testing, you could use something simple like:

<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<add key="MyNewKey" value="MyNewValue" xdt:Transform="Insert"/>

Next, associate the config files by editing the project file (.csproj or .vbproj) either in a text editor or in Visual Studio (but you will need to unload the project first). Locate each app.*.config reference like…

<None Include="app.Release.config" />

…and change it to…

<None Include="app.Release.config">

Next, add the following just above the final Project closing tag:

<Import Project="AppConfig.Transformation.targets" />

Finally, download the script from Sayed Ibrahim Hashimi’s blog and save it as AppConfig.Transformation.targets to the same location as your project file. Build the project and you should see the transformed app.config in the output folder.

We use this file in several projects so deploy it to C:\Program Files\MSBuild on each development and build machine and reference that folder using $(MSBuildExtensionsPath32), instead of adding it to every project.

Build Logging: Importance and Verbosity

 Comments Off on Build Logging: Importance and Verbosity
Oct 092011

TFS writes to the build log if the “Importance” level of the entry to be written is sufficiently high, according to the build definition’s verbosity setting.

So far, so good – all you need to know is how importance and verbosity relate to each other. That’s covered in the ALM Rangers Build Customisation Guide, which comprehensively explains the relationship and has some great guidance. However, there are some oddities which are worth special attention:

  1. The importance-to-verbosity relationship is different for build messages (BuildMessageImportance) than other build activities (BuildTrackingParticipant.Importance)
  2. Build messages (i.e. from the WriteBuildMessage activity) are not visible at Normal verbosity if their importance is set to Normal, it needs to be High – which you can change using the designer
  3. Other build activities are visible at Normal verbosity if their importance is set to Normal (the default) – but you can only change that in the XAML

This can lead to two common problems:

  1. Build messages don’t appear in the log when you think they will
  2. Control flow activity DisplayNames do appear in the log, but you don’t want them to

You can avoid these problems if you remember a couple of guidelines:

  1. For build messages set the importance to High
  2. For control flow activities set the importance to Low

For build messages set the importance to High

If you add a WriteBuildMessage activity to a template and want to see the message at Normal verbosity make sure you set the importance to High. (You could use this activity to write detailed debugging information that you only want to see at higher verbosity levels, in which case you would use a lower importance such as Normal – the default.)

The workflow designer exposes an “Importance” property for the WriteBuildMessage activity so you can change it easily in the Visual Studio Properties window. Alternatively, if you prefer to edit the XAML directly, it looks like this:

<mtbwa:WriteBuildMessage Importance="[Microsoft.TeamFoundation.Build.Client.BuildMessageImportance.High]" Message="..." mva:VisualBasic.Settings="Assembly references and imported namespaces serialized as XML namespaces" />

For control flow activities set the importance to Low

For most other activities the importance defaults to Normal, but this is BuildTrackingParticipant.Importance, which behaves differently to BuildMessageImportance. At Normal verbosity, activities with Normal importance do appear in the build log. In the case of control flow activities such as If, ForEach and Sequence you will often want to omit them from the log, because the activities they contain are more useful for describing what the build is doing.

Unlike the WriteBuildMessage activity, you cannot set the importance for control flow activities in the designer, you must edit the underlying XAML. To set the importance to low you need to add a BuildTrackingParticipant.Importance attribute as in this example:

<If Condition="..." DisplayName="If..." mtbwt:BuildTrackingParticipant.Importance="Low">

Follow these guidelines and you should be doing the right thing nine times out of ten for TFS build logging.

Oct 042011

The default TFS build template has a great feature where you can include in the build summary a list of changesets and work items that relate to the build. This is very useful, but under certain circumstances the list gets longer and longer – the build process correctly associates new changes with the build, but does it cumulatively – associating the same changes and work items with many successive builds. This erodes the usefulness of the section in the build summary, and creates a long history of work item updates saying “The Fixed In field was updated as part of associating work items with the build” making the history a mess and confusing your users.

To fix this we first have to understand how TFS is finding which changesets and work items to associate with the build. This is how it works:

  1. The build labels the source code. You can see this in the detailed build log under the message “Create Label”, followed by “Label {BUILDNAME}@$/{TEAMPROJECT} ….. was successfully created.”
  2. The build calls a Team Foundation Build Activity called AssociateChangesetsAndWorkItems. You can see where this happens in the build log at the message “Associate Changesets and Work Items”.
  3. The AssociateChangesetsAndWorkItems activity seems like a black box but it writes a key piece of information to the build log: “Analyzing labels {PREVIOUSBUILDNAME} and {CURRENTBUILDNAME}”. This shows that it is comparing source labels to figure out which changesets have been checked in between the two builds.
  4. When the changesets have been identified it’s easy for TFS to find the work items, using changeset links. When you choose a work item on check-in a changeset link is created linking changeset and work item together. You can see this link from either side – in the work items channel of the changeset details dialog (accessible from the source history) and on the All Links tab of work items.

If you see your list of associated changesets and work items getting longer and longer then the build log is the first place to look. The “Analyzing labels…” entry will make it clear which build it is comparing to. If that “baseline” build is way back – and not changing between successive builds – that’s why the list is forever growing.

So how does TFS decide which build to choose as the baseline? The default behaviour is to use the last successful build of the same definition – meaning fully successful builds only, not partially successful builds (i.e. with one or more test failures). So you may have many consecutive partially successful builds but TFS will ignore them for the purpose of associating changesets and work items.

Now we understand the cause we can design a fix. Handily, the AssociateChangesetsAndWorkItems activity comes with some useful input arguments: CurrentLabel, LastLabel, and UpdateWorkItems. In the default template, both the label arguments are blank so the activity will work out which to use, but we can provide our own values to override that behaviour. If we determine the label for the last build which was successful or partially successful we will fix the ever-growing changesets and work items problem. (We are putting aside here the concern we should have over recurrent test failures – making this change shouldn’t be an excuse for not fixing test failures).

First (assuming you have set up a solution for creating custom build activities) you need to create an activity which gets the label we will pass in via the LastLabel argument. Here’s an example:

using System;
using System.Activities;
using Microsoft.TeamFoundation.Build.Client;
using Microsoft.TeamFoundation.Build.Workflow.Activities;

namespace BuildTasks.Activities
/// The default AssociateChangesetsAndWorkItems activity compares the current build with
/// the last completely successful build. If builds have been consistently partially successful rather
/// than fully successful the list of changesets and work items will accumulate items ad infinitum.
/// This activity returns the label for the last fully OR partially successful build, which can then
/// be used when calling the AssociateChangesetsAndWorkItems activity, so only changesets and work
/// items since that build are associated.
/// </summary>

public sealed class GetLastSuccessfulBuildLabel : CodeActivity<string>
/// </summary>

/// <param name="context"></param>
/// <returns>Label applied for last successful (full or partial) build of this definition</returns>

protected override string Execute(CodeActivityContext context)
string label = "";

IBuildDetail buildDetail = context.GetExtension<IBuildDetail>();
IBuildServer buildServer = buildDetail.BuildServer;
IBuildDefinition buildDefinition = buildDetail.BuildDefinition;

IBuildDetailSpec spec = buildServer.CreateBuildDetailSpec(buildDefinition);
spec.MaxBuildsPerDefinition = 1;
spec.Status = BuildStatus.Succeeded | BuildStatus.PartiallySucceeded;
spec.QueryOrder = BuildQueryOrder.FinishTimeDescending;

IBuildQueryResult queryResult = buildServer.QueryBuilds(spec);

if (queryResult.Builds.Length > 0)
// label can be null if build was run without "Label Sources" option
label = queryResult.Builds[0].LabelName ?? "";

// labels are returned in the format {LABELNAME}@$/{TEAMPROJECT},
// but we just want the label before the @ sign
if (label.Contains("@"))
label = label.Substring(0, label.IndexOf("@"));

return label;

The activity needs to be added to your library of custom build activities, building and deploying. Next, you need to modify the build template (best to use a copy of the default template) to make use of the new activity and pass its result into the LastLabel argument of AssociateChangesetsAndWorkItems. The most logical place to put this is just before the AssociateChangesetsAndWorkItems activity itself:


<If Condition="[AssociateChangesetsAndWorkItems]" DisplayName="If AssociateChangesetsAndWorkItems" sap:VirtualizedContainerService.HintSize="464,201" mtbwt:BuildTrackingParticipant.Importance="Low">
<mtbwa:InvokeForReason DisplayName="Associate Changesets and Work Items for non-Shelveset Builds" sap:VirtualizedContainerService.HintSize="222,208" Reason="Manual, IndividualCI, BatchedCI, Schedule, ScheduleForced, UserCreated">
<Variable x:TypeArguments="x:String" Name="LastSuccessfulBuildLabel" />
<ba:GetLastSuccessfulBuildLabel DisplayName="Get Last Successful Build Label" sap:VirtualizedContainerService.HintSize="200,22" mtbwt:BuildTrackingParticipant.Importance="Low" Result="[LastSuccessfulBuildLabel]" />
<mtbwa:AssociateChangesetsAndWorkItems DisplayName="Associate Changesets and Work Items" sap:VirtualizedContainerService.HintSize="200,22" LastLabel="[LastSuccessfulBuildLabel]" Result="[associatedChangesets]" UpdateWorkItems="[UpdateWorkItems]" />


Note that the and elements are unchanged from the default template – I have included them to indicate where the change has been made. I create a variable called LastSuccessfulBuildLabel to hold the result and pass that into the LastLabel argument. Note that I also parameterized the UpdateWorkItems argument, but you don’t have to do that to fix this problem (I find it useful to associate changesets but leave work items alone when I have multiple build definitions running against the same branch of code – I don’t want to update work items more than necessary). Of course, you may well find it easiest to make this change using the workflow designer. Here’s a snippet of what it should look like:

So there’s the fix! I hope someone finds that useful.

Note: labelling the source code and associating changesets and work items can be toggled off in the build definition – both need to be turned on for the feature to work.