Continuous Integration Demo Code from Doug Neumann’s TLN301 PDC Talk


Doug Neumann’s TLN301 presentation, VSTS: Behind the Scenes of Visual Studio 2005 Team Foundation Server (slides), featured a demonstration of how to use the server’s check-in event notification to kick off a build for a continuous integration build system using Team Build.  A number of people asked for it, so we’ve decided to post it here.


Doug’s demonstration used the July CTP, but since beta 3 will hopefully be released this week, the code has been modified to run on beta 3.  You’ll need to create a web service and put the following code into it.  Here are the assemblies you’ll need to reference.



Microsoft.TeamFoundation.Build.Client.dll
Microsoft.TeamFoundation.Build.Common.dll
Microsoft.TeamFoundation.Client.dll
Microsoft.TeamFoundation.VersionControl.Client.dll
Microsoft.TeamFoundation.VersionControl.Common.dll
Microsoft.TeamFoundation.WorkItemTracking.Client.dll


The code is intentionally simplified to meet the needs of a demo (e.g., a real continuous integration system would need more intelligence in handling check-in events that come in while the current build is running, etc.), but it’s a good example of how to hook into the Team Foundation server’s events and build useful extensions.


Once you’ve built and deployed your web service using VS 2005, you’ll need to add a subscription for your web service.  The check-in event is sent to your continuous integration web service via SOAP.  The following command, with changes as necessary for the name of your machine (here, I use localhost), must be run on the application tier to add an event subscription for your service.



bissubscribe /eventType CheckinEvent /userId mydomain\myusername /address http://localhost:8080/ContinuousBuild/Service.asmx /deliveryType Soap /domain localhost


Now when you check code into your server, your continuous integration service will kick off a build.


The Team Build team plans to post a more elaborate continuous integration example.


[UPDATE 2/20/06]  I updated the version numbers in the SoapDocumentMethod attribute to work with RC and RTM releases.

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Configuration;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.Win32;
using Microsoft.TeamFoundation.Build.Common;
using Proxy = Microsoft.TeamFoundation.Build.Proxy;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Common;

[WebService(Namespace = “http://tempuri.org/”)]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class Service : System.Web.Services.WebService
{
public Service ()
{
}

[SoapDocumentMethod(“http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03/Notify”,
RequestNamespace = “http://schemas.microsoft.com/TeamFoundation/2005/06/Services/Notification/03”)]
[WebMethod]
public void Notify(string eventXml) // Do not change the name of this argument.
{
ThreadPool.QueueUserWorkItem(CallBuild, eventXml);
}

public void CallBuild(object state)
{
string eventXml = (string)state;

// De-serializing the event
XmlDocument Xmldoc = new XmlDocument();
Xmldoc.LoadXml(eventXml);

string teamProject = Xmldoc.DocumentElement[“TeamProject”].InnerText;
string owner = Xmldoc.DocumentElement[“Owner”].InnerText;

// NOTE: hard-code info for demo
string teamFoundationServer = “http://localhost:8080”;
string buildType = “Continuous Integration Build”;
string buildMachine = “localhost”;
string buildDirectoryPath = “c:\\builds”;

Proxy.BuildController controller = Proxy.BuildProxyUtilities.GetBuildControllerProxy(teamFoundationServer);
Proxy.BuildStore store = Proxy.BuildProxyUtilities.GetBuildStoreProxy(teamFoundationServer);
Proxy.BuildParameters buildParams = new Proxy.BuildParameters();
buildParams.TeamFoundationServer = teamFoundationServer;
buildParams.TeamProject = teamProject;
buildParams.BuildType = buildType;
buildParams.BuildDirectory = buildDirectoryPath;
buildParams.BuildMachine = buildMachine;

string buildUri = controller.StartBuild(buildParams);

// wait until the build completes
BuildConstants.BuildStatusIconID status;
bool buildComplete = false;
do
{
Proxy.BuildData bd = store.GetBuildDetails(buildUri);
status = (BuildConstants.BuildStatusIconID)bd.BuildStatusId;
buildComplete = (status == BuildConstants.BuildStatusIconID.BuildSucceeded ||
status == BuildConstants.BuildStatusIconID.BuildFailed ||
status == BuildConstants.BuildStatusIconID.BuildStopped);
} while (!buildComplete);

if (status == BuildConstants.BuildStatusIconID.BuildFailed)
{
// create a workitem for the developer who checked in
CreateWorkItem(teamFoundationServer, teamProject, owner);
}
}

public void CreateWorkItem(string server, string projectName, string owner)
{
TeamFoundationServer tfs = TeamFoundationServerFactory.GetServer(server);
WorkItemStore store = (WorkItemStore) tfs.GetService(typeof(WorkItemStore));

WorkItemTypeCollection workItemTypes = store.Projects[projectName].WorkItemTypes;

// Enter the work item as a bug
WorkItemType wit = workItemTypes[“bug”];
WorkItem workItem = new WorkItem(wit);

workItem.Title = “The changes submitted have caused a build break – please investigate”;
string[] ownerSplit = owner.Split(‘\\’);
owner = ownerSplit[ownerSplit.GetLength(0) – 1];
workItem.Fields[“System.AssignedTo”].Value = owner;
workItem.Fields[“Microsoft.VSTS.Common.Priority”].Value = 1;
workItem.Save();
}
}

Comments (7)

  1. dion says:

    Hi Buck,

    Which bissubscribe parameters should I use to kick off a MSBuild script instead of calling a Webservice? (because I think a MSBuild project is better configurable)

  2. buckh says:

    Bissubscribe is only going to register this for the web service notification. If you want to run MSBuild instead, you’d need to change the code above to call MSBuild rather than Team Build in response to the event.

  3. Sam says:

    Buck-

    Thanks for this. I have two questions for you. First, is there a command line way to unsubscribe from an Event?

    Second, I am getting the following error when I run the StartBuild method from your example:

    "The build service could not validate the caller. Please ensure that the network connection between Build machine and Team Foundation Server is proper, and the caller is valid."

    Any idea what could cause that error. The build server and foundation server are on the same box.

    Thanks

  4. dion says:

    Hi Sam,

    I’ve got the same problems over here. Seems that the ‘Internet Connection Sharing service’ needs to be running before installing TFS. Otherwise, the build service will not respond.

    This problem exists since beta 2, see http://blogs.msdn.com/team_foundation/archive/2005/04/26/412118.aspx

    About the unsubscribe: Only way I could get this done is by removing the record in the subscriptions table.

    Also, I’m working on a CI webservice that kicks of an MSBuild project (and sends parameters). When done, I’ll post this on my weblog.

  5. Just finished reading Bill’s excellent post on his frustrations with Continuous Integration (CI) in the

Skip to main content