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;
}
}
}
Comments
Anonymous
March 24, 2010
This is great. We have defined several metadata properties on our XAML that we would like to set each time we do a build. Through the Queue Build window, we can see those. Now we need to access the build definition in code (C#) and set each property that we've defined in the ProcessParameterMetadata collection, but definition.Process is a string and I see no other property, object, or interface that returns what we need. How can we get access to set the parameters easily to submit a build through code? Thanks, KevinAnonymous
March 25, 2010
Great question! It deserves its own blog post... Check out my latest post. http://blogs.msdn.com/jpricket/archive/2010/03/25/tfs2010-queuing-a-build-from-code-with-custom-process-parameter-values.aspxAnonymous
May 10, 2010
Thanks for the update. One question: How can I set a link to the logfile? buildProjectNode.Logfile.Url = new Uri("\serverbuild.log") does not work: ExceptionAnonymous
May 10, 2010
The LogFile property actually just returns the value of the first ExternalLink build information node under the BuildProjectNode. So, you should do something like this (untested code follows): List<IBuildInformationNode> links = buildProjectNode.Children.GetNodesByType(InformationTypes.ExternalLink); links[0].Fields[InformationFields.Url] = "\serverbuild.log"; Hope that helps, JasonAnonymous
May 11, 2010
thanks, but it didn't work. buildProjectNode.Children has no Nodes. I tried buildProjectNode.Node.Fields[InformationFields.Url] = "\serverbuild.log", but no link appeared. Everything else I tried, I get readonly-properties or NullReferences.Anonymous
May 11, 2010
In that case, you can add an IExternalLink node to the project node. But you should always check to see if one exists first. Adding the node should be as simple as... InformationNodeConverters.AddExternalLink(buildProjectNode, "Build Log", "\serverbuild.log"); buildProjectNode.Save();Anonymous
June 29, 2010
hi Exactly what I was looking for. But it does not update the global list with the buildnumber, so I'm unable to use in my WIT'sAnonymous
July 21, 2010
Hi, any idea how to get newlines in InformationNodeConverters.AddBuildError(BuildInformation, "this is a very long logfile rn and i want a formatted text with rn newlines", Datetime.Now);Anonymous
March 14, 2011
Hi! Thanks Jason for your post! However, I want to create a fake build even if build server is not installed in my Testing Center. Is it possible? Can I edit an XML file or a Config file so the list of available builds will be available? Thanks!Anonymous
April 13, 2011
Hi, When I use the script to create Fake Builds I get an exception on the below call, what could be the cause? buildServer = (IBuildServer)collection.GetService(typeof(IBuildServer)); (IBuildServer)collection.GetService(typeof(IBuildServer)):System.Net.WebException: The underlying connection was closed: An unexpected error occurred on a receive. ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host at System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags) at System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size) --- End of inner exception stack trace ---Anonymous
February 01, 2012
This is just what we need for our transition to TFS from our legacy build system. Thanks.Anonymous
June 20, 2013
Hi, Can we do the same procedure in Visual Studio 2012 version as well? Pls let me know whether the same procedure is applicable for 2012 as well?Anonymous
July 17, 2013
Yes, the same procedure should work in VS2012. Please let me know if it doesn't. Thanks, JasonAnonymous
March 20, 2014
Hello Jason, I was able to compile the program and get all .exe and other files in the Debug folder. I have several issues that I would like to get your help on: 1.) The problem that I have now is with the DropLocation. can you please give me an example of how to use it? 2.) when I look at VS under All Build Defnition, I noticed the the DailyBuild has an X next to it as it offline. I am sorry for these question since I am not really 100% expert on the build system. Thanks,Anonymous
May 20, 2014
All I get is a black command prompt. Sure would be nice to know why.Anonymous
May 20, 2014
The comment has been removedAnonymous
November 15, 2015
Hello, Does TFS 2015 have a built-in support for "fake" builds? Are there plans to update the code for 2015?