Inside the Microsoft® Build Engine: Using MSBuild and Team Foundation Build

I’m proud to say that after many months of work Sayed Ibrahim Hashimi and I have completed work on Inside the Microsoft® Build Engine: Using MSBuild and Team Foundation Build (PRO-Developer). It is being published by Microsoft Press and is due out on 7th January 2009.

Preserving Labels When Builds Are Deleted

When builds are deleted in TFS 2008, either manually or via retention policies, everything about the build is deleted, including the label. This completely removes the ability to reproduce the build in the future if the need arises.

In Service Pack 1 the capability was added to have labels preserved when the build is deleted and Buck explains how to enable this in his blog post.

Team Foundation Build Load Balancer Released

I’ve released a new open-source project that we’ve been using internally for a number of months called the Team Foundation Build Load Balancer (http://www.codeplex.com/teambuildloadbalance/). It’s a very simple command-line application (which is designed to be run in a scheduled job) to balance queued builds between multiple build agents irrespective of which build agent it was queued on.

Allowing Users To Edit Destination Email Address in Reporting Services

When users subscribe to a report via email in Reporting Services the destination email address field defaults to their username and is read-only. If the SMTP server that you’re using won’t accept emails for plain usernames then you will have to allow users to enter their full email address when subscribing to a report.

To do this:

  1. Edit %ProgramFiles%\Microsoft SQL Server\MSSQL.3\Reporting Services\ReportServer\rsreportserver.config.
  2. Locate <SendEmailToUserAlias>True</SendEmailToUserAlias> and change True to False.
  3. Save the file and restart the ReportServer service.

Tracking Time Against TFS Work Items Using TFS Working On

On Friday at the Queensland VSTS User Group Matthew Rowan mentioned an open-source system tray utility he’d written called TFS Working On. This utility allows developers to select the work item they are currently working on and it will record the time spent, add it to the work item’s history, as well as updating the work completed and work remaining fields appropriately.

Matthew will be presenting at the December user group meeting about this and how they leveraged the Team System OLAP cube for reporting time and doing evidence-based scheduling.

Biggest Yet Power Tools Release Coming Up

Brian Harry has blogged about the upcoming power tools release and this is without a doubt the biggest yet, here’s a couple of the highlights to whet your appetite:

  • Team Members displayed in Team Explorer, so what? Well…
    • IM integration allows you to message team members from within Visual Studio
    • View their shelvesets
    • View their pending changes
  • Improved deployment for check-in policies and custom work-item controls.
  • Integration into Windows Explorer and PowerShell.

Receiving TFS Events Using WCF

There are numerous examples around that explain how to receive TFS events using an ASMX web service. In this post we’ll look at how you can create a WCF service, hosted in IIS or self-hosted, to receive these events.

Prerequisites

  • Visual Studio 2008 with Service Pack 1
  • Team Foundation Server 2008

Create a WCF Service

The first step is of course to create a WCF service project. In Visual Studio 2008 create a new WCF Service Library using the language of your choice but in this example we’ll use Visual Basic .NET. If you do not see the WCF option select either .NET Framework 3.0 or .NET Framework 3.5 from the dropdown list in the top right-hand corner of the New Project dialog.

 image

This will create a project containing an app.config (which stores the WCF configuration), IService1.vb (which stores the service’s interface), and Service1.vb (which stores the service’s implementation). The new project should look like this:

 image

Renaming the Service Interface and Service Implementation

IService1 and Service1 aren’t the most descriptive names, so we’ll rename them to TfsEventSubscriber and remove the sample service implementation. To do this:

  1. Rename the file IService1.vb to ITfsEventSubscriber.vb.
  2. Open the file ITfsEventSubscriber.vb and rename the interface from IService1 to ITfsEventSubscriber.
  3. Remove the GetData and GetDataUsingContract functions.
  4. Remove the CompositeType class.
  5. Open the file app.config and replace any references to IService1 with ITfsEventSubscriber.
  6. Rename the file Service1.vb to TfsEventSubscriber.
  7. Open the file TfsEventSubscriber.vb and rename the class from Service1 to TfsEventSubscriber.
  8. Change the interface the class inherits from IService1 to ITfsEventSubscriber.
  9. Remove the GetData and GetDataUsingContract functions.
  10. Open the file app.config and replace any references to IService1 with ITfsEventSubscriber.
  11. Save all of the files and project and build the project to make sure we haven’t broken anything.

The resulting project should look like this:

 image

Define the Method to Receive TFS Events

The next step is to define a method that will receive the TFS Events. The name of this method is up to you but the convention is to call it Notify and it must accept two string parameters called eventXml and tfsIdentityXml. The eventXml argument will contain the XML about the event occur and this will conform to the schema for that particular event. The tfsIdentityXml argument will contain XML identifying the application tier that raised the event.

We now add this method to the ITfsEventSubscriber and apply the OperationContract attribute. We also need to specify the namespace and action named used by Team Foundation Server when calling this operation. This results in the interface looking like this:

<ServiceContract(Namespace:="http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03")> _

Public Interface ITfsEventSubscriber

    <OperationContract(Action:="http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03/Notify")> _

    Sub Notify(ByVal eventXml As String, ByVal tfsIdentityXml As String)End Interface

For the project to compile we must implement this new method in the TfsEventSubscriber which would then look like this:

Public Class TfsEventSubscriber
    Implements ITfsEventSubscriber

    Public Sub Notify(ByVal eventXml As String, ByVal tfsIdentityXml As String) Implements ITfsEventSubscriber.Notify

    End Sub
End Class

Writing the Implementation

You can now extend the Notify method to provide your service’s implementation. In our example we simply dump each request out as a uniquely named XML file:

Imports System.IO

Public Class TfsEventSubscriber
    Implements ITfsEventSubscriber

    Public Sub Notify(ByVal eventXml As String, ByVal tfsIdentityXml As String) Implements ITfsEventSubscriber.Notify
        File.WriteAllText(Path.Combine(Path.GetTempPath(), Guid.NewGuid.ToString() & ".xml"), eventXml)
    End Sub
End Class

Configure the WCF Service

There are a number of changes that need to be made to the WCF Service’s configuration, these can be made by editing the app.config file directly or by using the WCF Configuration Editor shown here.

 image

We need to change the binding from wsHttpBinding to basicHttpBinding.

 image

Because Team Foundation Server will never access the metadata service we can safely remove it from our configuration. Firstly we delete the endpoint that implements the IMetadataExchange contract.

 image

Then we remove the serviceMetadata behaviour:

 image

Testing and Debugging Your Service

If you run the project using Visual Studio it will automatically host your service using the WcfSvcHost that ships with Visual Studio 2008. While the project is running in Visual Studio then all of the usually debugging tools (such as breakpoints) will be available.

Once the service is running you can use BisSubscribe.exe from the Visual Studio 2008 SDK to add a subscription to Team Foundation Server so that your service is called when events are raised. In this example we add a SOAP subscription to the BuildStatusChangedEvent using the URL that Visual Studio hosted our service:

BisSubscribe.exe /eventType BuildStatusChangedEvent 

    /address http://DEVWORKSTATION:8731/Design_Time_Addresses/ReceiveTfsEventsUsingWcf/TfsEventSubscriber/

    /server http://TFSRTM08:8080/ 

    /deliveryType Soap

If you have a firewall, including the one that ships with Windows, you may need to disable it or add an exception for the port that the service has been hosted on.

Deploying Your Service

Once you’ve built and tested and your service you can host it using any of the hosting methods supported by WCF (such as using IIS or self-hosting) and then use BisSubscribe to add the necessary subscriptions.

TFS 2008 Service Pack 1 Bug Fixes

Brian Harry has published a list of bug fixes that shipped in TFS 2008 Service Pack 1. What I also found interesting from this list is the breakdown of where different bugs were detected or reported.

Best Practices for Workspaces

One Team System concept that a number of VSS users struggle with is workspaces because there doesn’t appear to be a comparable concept under VSS.

This isn’t entirely true because by setting working folders in VSS you are effectively establishing a “default” workspace and you can create multiple workspaces by performing gets into alternate directories. However, unlike Team System these workspaces aren’t named or managed.

So how can you get the most value out of Team System workspaces?

Read the Workspaces section of High-level Best Practices in Software Configuration Management by Laura Wingerd & Christopher Seiwald from Perforce Software. In this SCM tool agnostic article Laura and Christopher outline five course-grained best practices for workspaces:

  1. Don’t share workspaces. A workspace should have a single purpose, … for a single developer. Each developer owns their own workspaces and they should not be stored in a shared location where other developers can access (or worse modify it).
  2. Don’t work outside of managed workspaces. Your SCM system can only track work in progress when it takes place within managed workspaces. Under Team System this really means that all changes should be performed using Team System (either through Source Control Explorer, the tf command-line, or custom applications that use the TFS API) rather than using Windows Explorer or other file system commands.
  3. Don’t use jello views. A file in your workspace should not change unless you explicitly cause the change. Although Windows supports symbolic links (of a sort) they aren’t often used so this is less of an issue, but conceptually it is still important that developers are in control of and understand the current state of their workspace.
  4. Stay in sync with the codeline. As a developer, the quality of your work depends on how well it meshes with other peoples’ work. Staying in sync with the codeline can be a trade-off between productivity and “correctness”. What’s important is that the developer is aware of how often the codeline that their workspace maps to is changing and the risk of including those changes in their workspace and schedules their synchronisations accordingly. The ideal time to synchronise is when the codeline is in a known good state and so is the developer’s workspace and it would be advisable not to go more than a week without synchronising except in exceptional circumstances. Developers should always synchronise their workspace with the codeline before checking their changes in, this allows them to resolve any conflicts in their workspace without compromising the codeline.
  5. Check in often. Integrating your development with other peoples’ work also requires you to check in your changes as soon as they are ready. As a rule as soon as a developer has completed a task and it has passed any quality gates defined by the codeline’s policies their changes should be checked-in to the codeline so that other developers have access to the change and integrate their changes with it.

The next question is should you have one workspace or multiple and what should the granularity of each workspace be? I take the following factors into account whenever I create a workspace:

  • Each workspace should have the simplest set of working folders possible. Overly complicated working folder mappings (such as large numbers of working folders or mixtures of active and cloaked working folders) can be confusing and cumbersome to manage.
  • Each workspace has an overhead (e.g. staying in sync with the codeline). You need to evaluate the cost-benefit balance for each workspace you create. For example, creating a separate workspace for each task would probably not be worth the overhead it adds.
  • Each workspace should have a single defined purpose (e.g. development, QA, release management, or testing). For example, don’t do development and QA using the same workspace or don’t do QA and release management using the same workspace. These purposes have different codeline synchronisation, backup, and integration requirements, keeping them separate allows you to manage these activities more easily.
  • Each workspace should contain a single product or a related set of products. This will reduce the scope of any activities you perform which will both improve performance as well as avoid you having to deal with synchronisation or integration conflicts that aren’t relevant to your current context.
  • Each task you perform should start with an empty list of pending changes. Your contents of your pending changes tool window should always reflect your current task because managing pending changes that are related to a multitude of different tasks is cumbersome and prone to error.

Here are some examples on how these can be applied:

Joe is a developer for the WidgetManager product and performs QA when required. To do this Joe would create two workspaces, one called “WidgetManager Development” and one called “WidgetManager QA”.

Joe would use the “WidgetManager Development” workspace to fix bugs and implement enhancements to the product. Because of the development nature of this workspace he would ensure that it is synchronised with the codeline when both the codeline and the workspace are in known good states. Once he has completed his work and met all of the quality gates the changes in the workspace would be checked-in. If Joe had to switch development tasks he would shelve the pending changes (without preserving the local copy) and work on the other development task.

Halfway through the development cycle Joe is asked to perform some QA on work integrated into the codeline by one of his colleagues. He switches to the “WidgetManager QA” workspace and synchronises it with the codeline and then performs QA on the necessary files.

The advantage of having a separate workspace is that Joe doesn’t need to worry about shelving his pending development changes (or even worse losing them) when switching contexts. He also doesn’t need to worry about his in progress development contaminating the files being QA’d.

Let’s look at another example:

Niles is a build engineer on two separate products and is responsible for building and packaging the products for distribution. The products don’t have any dependencies and ship on entirely separate schedules. To do this Niles would create two workspaces, one called “WidgetManager Release” and one called “FooPlus Release”.

Each workspace would map the folders for the development and maintenance branches as well as any tools or scripts needed to build and release the software. Niles can synchronise each workspace separately which improves performance and reduces the scope of any conflicts.

Hopefully this has given you some ideas on how you can better use workspaces in your development process.

TF215085 Error When Building Using TFS 2008 Workgroup Edition

I came across a TF215085 error today while trying to build on a TFS 2008 Workgroup Edition and it turned out that the solution was to add the user that the Build Service was running as to the Team Foundation Server’s Licensed Users group.

http://www.bartonline.be/2008/03/22/TF215085+An+Error+Occurred+While+Connecting+To+Agent.aspx