In my last post about creating a Fake build, I gave you the code to create a fake build in V1 and I promised to give you the code to do the same thing in Orcas. So, here it is. This code won't work in Beta1 or Beta2, but should work as is when you get an RC or RTM version of Orcas. The CreateManualBuild API was added to the code base post Beta2 cutoff.

So, here's the code:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.TeamFoundation.Build.Proxy;
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("http://jpricket-test:8080", "jpricket-070507", "fakebuild23");
        }

        static void AddBuild(String serverName, String teamProject, String buildNumber)
        {
            // Get the TeamFoundation Server
            TeamFoundationServer tfs = new TeamFoundationServer(serverName);

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

            // Create a fake definition (and agent)
            IBuildDefinition definition = AddDefinition(buildServer, teamProject, "FakeDefinition1");

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

            // Create platform/flavor information against which test 
            // results can be published
            IConfigurationSummary confSummary = InformationNodeConverters.AddConfigurationSummary(buildDetail, "Debug", "x86", "");
            ICompilationSummary compSummary = confSummary.AddCompilationSummary();
            compSummary.ProjectFile = "Dummy.sln";
            compSummary.Save();

            // Complete the build by setting the status to succeeded and setting the drop location.
            buildDetail.Status = BuildStatus.Succeeded;
            // The drop location is not copied properly from the definition so we have to copy it manually.
            buildDetail.DropLocation = definition.DefaultDropLocation;
            buildDetail.Save();
        }

        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
            }

            IBuildAgent agent = AddAgent(buildServer, teamProject, "FakeAgent1");

            IBuildDefinition definition = buildServer.CreateBuildDefinition(teamProject);
            definition.Name = definitionName;
            definition.ConfigurationFolderPath = "$/";
            definition.ContinuousIntegrationType = ContinuousIntegrationType.None;
            definition.DefaultBuildAgent = agent;
            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.Save();

            return definition;
        }

        private static IBuildAgent AddAgent(IBuildServer buildServer, String teamProject, String agentName)
        {
            IBuildAgent agent = buildServer.CreateBuildAgent(teamProject);
            agent.Name = agentName;
            agent.BuildDirectory = "c:\\nobuilddir";
            agent.Description = "Fake build agent used to create fake builds.";
            agent.MachineName = "NoBuildMachine";
            agent.Port = 9191;
            agent.Status = AgentStatus.Disabled;
            agent.Save();

            return agent;
        }
    }
}

There are a couple of things to take note of here. First, you may have noticed that I am creating a Build Definition and a Build Agent. In Orcas, if you try to create a Build without these things existing, you will get exceptions thrown from the server. However, if you were to use existing Definitions and Agents, the code gets a lot smaller. The next thing you should look closely at is the call to definition.CreateManualBuild. This new method encapsulates most of the code that we did in the V1 version.

CreateManualBuild is located off the IBuildDefinition interface. This allows you to get all the definition defaults for free. There are several overloads for this call. The shortest is used here. It takes in a build number and defaults all the other values. If you really had a manual build that you had copied to a drop location, you could specify the drop location as the next parameter to this call. The long version allows you to specify buildNumber, dropLocation, buildStatus, agent, and requestedFor.

In the above example code, the status of the build after calling CreateManualBuild is InProgress. I then add some project details to the build information and change the status to Succeeded. By setting the Build Status to a completed status myself, the BuildCompletion event is fired as if this was a real build. If the status was set to Succeeded in the CreateManualBuild call, the event would never be fired for this build. This gives you the ability to create historical builds (no events needed) or to create manual builds where you might want the events.

I know many customers will find this new API very useful.