Creating Fake builds in TFS Build 2010

It was brought to my attention that the code I posted here no longer works in TFS Build 2010. So, I promised I would update the code. In doing so, I realized there were some things I should probably explain:

1) In TFS 2010, you can no longer simply create a controller or agent by itself. You have to create a BuildServiceHost object which represents the build machine service. From that object you can create a controller or agent, but agents also require an existing controller.

2) Instead of creating ConfigurationSummaries and CompilationSummaries, you simply create BuildProjectNodes. These are basically the items that were built. In order to associate test results and the like, you have to create build project nodes with the fake build.

3) Setting the status of a build to Succeeded, Partially Succeeded, or Failed no longer sets the Finish Time of the build. What this means is that you need to call the method FinalizeStatus to actually "finish" the build. There are several reasons why we did this, but the most important is that we wanted the build itself to be able to set its own status BEFORE finishing. So, we added a new method to the build detail object that you call to tell the server that the build is complete.

4) The build definition object has remained mostly unchanged, except: ConfigurationFolderPath was removed and ProcessTemplate was added. The configuration folder path property became a required parameter of the Upgrade process template, but if you use the Default process template, you don't need a TfsBuild.proj file. That's right, we have eliminated the need for the TfsBuild.proj file. The ProcessTemplate is a Windows Workflow XAML file that tells the Controller and Agents how to build the sources. MSBuild is still called to do the actual building, but everything else is done through WF.

So, now you know (and knowing is half the battle ;)). Here's the 2010 code:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.TeamFoundation.Client;
using Common = Microsoft.TeamFoundation.Build.Common;
using Microsoft.TeamFoundation.Build.Client;

namespace AddFakeBuild
{
    class Program
    {
        static void Main(string[] args)
        {
            AddBuild("https://jpricket-test:8080/tfs/collection0", "ChristmasEveEve2009", "fakebuild3");
            Console.WriteLine("Build added.");
        }

        static void AddBuild(String serverName, String teamProject, String buildNumber)
        {
            // Get the TeamFoundation Server
            TfsTeamProjectCollection collection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(serverName));

            // Get the Build Server
            IBuildServer buildServer = (IBuildServer)collection.GetService(typeof(IBuildServer));

            // Create a fake definition
            IBuildDefinition definition = AddDefinition(buildServer, teamProject, "FakeDefinition2");

            // Create the build detail object
            IBuildDetail buildDetail = definition.CreateManualBuild(buildNumber);

            // Create platform/flavor information against which test
            // results can be published
            IBuildProjectNode buildProjectNode = InformationNodeConverters.AddBuildProjectNode(buildDetail.Information, DateTime.Now, "Debug", "Dummy.sln", "x86", @"$/tp/solutions/Dummy.sln", DateTime.Now, "default");
            buildProjectNode.Save();

            // Complete the build by setting the status to succeeded. This call also
            // sets the finish time of the build.
            buildDetail.FinalizeStatus(BuildStatus.Succeeded);
        }

        private static IBuildDefinition AddDefinition(IBuildServer buildServer, string teamProject, string definitionName)
        {
            try
            {
                // See if it already exists, if so return it
                return buildServer.GetBuildDefinition(teamProject, definitionName);
            }
            catch (BuildDefinitionNotFoundException)
            {
                // no definition was found so continue on and try to create one
            }

            // Use the first build controller as the controller for these builds
            IBuildController controller = AddBuildController(buildServer, "fakeMachine", "fakeController");

            // Get the Upgrade template to use as the process template
            IProcessTemplate processTemplate = buildServer.QueryProcessTemplates(teamProject, new ProcessTemplateType[] { ProcessTemplateType.Upgrade })[0];

            IBuildDefinition definition = buildServer.CreateBuildDefinition(teamProject);
            definition.Name = definitionName;
            definition.ContinuousIntegrationType = ContinuousIntegrationType.None;
            definition.BuildController = controller;
            definition.DefaultDropLocation = @"\\MySharedMachine\drops\";
            definition.Description = "Fake build definition used to create fake builds.";
            definition.Enabled = false;
            definition.Workspace.AddMapping("$/", "c:\\fake", WorkspaceMappingType.Map);
            definition.Process = processTemplate;
            definition.Save();

            return definition;
        }

        private static IBuildController AddBuildController(IBuildServer buildServer, string machineName, string controllerName)
        {
            IBuildServiceHost serviceHost = buildServer.CreateBuildServiceHost(machineName, new Uri("https://noservice:8888/"));
            serviceHost.Save();
            IBuildController controller = serviceHost.CreateBuildController(controllerName);
            controller.Save();
            return controller;
        }
    }
}