As a TFS administrator one of the questions I get asked frequently is why someone’s build seems to be stuck in queue. Our TFS deployment has quite a few machines that each have a single build agent and build controller installed on them. To complicate things further, we have a few dozen different team project collections in TFS.
Since you can only view builds queued up from a single team project, finding the source of these issues has always been a bit annoying. You would have have to iterate through each of the team projects, then view thequeuedbuilds in each team project until u found the culprit. Depending on the number of team projects and builds, this could be a very time consuming task.
Generally speaking, I would just write back and let them know I was keeping an eye on things and that they should just be patient for the build queue to clear itself up. But every now and then, something would hang indefinitely and the possibility of that always hung out in the back of my mind.
Thankfully, I found an article on Bart Wullem’s blog, The Art of Simplicity, that set me off on the right direction: a console application that got all of the queued build information from the TFS API and wrote it out to the screen. Even better, the source code to the console app was published too.
I made a few changes; instead of hard coding the TFS URL into the app I added a config file and put the URL in there so that you could easily change it, I prettied up the output a little bit by sorting the builds by the Build Controller it’s assigned to and then by the time it was queued, and lastly I added an elapsed time to the output.
In following Bart’s example, I thought I’d share this and save another TFS administrator the effort I went through:
Update (12/28/2011): Fixed an issue where queued builds caused the application to crash.
using System;
using System.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.Build.Client;
namespace TFSBuildQueue
{
class Program
{
static void Main(string[] args)
{
int BuildCount = 0;
string TFS_URL = ConfigurationManager.AppSettings["TFS_URL"];
Console.WriteLine("\nTFS Build Queue");
Console.WriteLine("===============\n");
Console.WriteLine("Connecting to: " + TFS_URL+ " and querying build controllers...");
TfsTeamProjectCollection tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(TFS_URL));
IBuildServer bs = tfs.GetService<IBuildServer>();
IQueuedBuildSpec qbSpec = bs.CreateBuildQueueSpec("*", "*");
IQueuedBuildQueryResult qbResults = bs.QueryQueuedBuilds(qbSpec);
// Define DataTable for storage and manipulation of currently queued builds.
DataTable QBTable = new DataTable();
QBTable.Columns.Add("Controller");
QBTable.Columns.Add("Project");
QBTable.Columns.Add("BuildDefinition");
QBTable.Columns.Add("BuildStatus");
QBTable.Columns.Add("Priority");
QBTable.Columns.Add("Date");
QBTable.Columns.Add("ElapsedTime");
QBTable.Columns.Add("User");
// Query TFS For Queued builds and write each build to QBTable
foreach (IQueuedBuild qb in qbResults.QueuedBuilds)
{
string RequestedBy = qb.RequestedBy.PadRight(18);
if (qb.RequestedBy != qb.RequestedFor)
{
RequestedBy = String.Concat(qb.RequestedBy," (for ",qb.RequestedFor,")").PadRight(18);
}
DateTime CurrentTime = DateTime.Now;
TimeSpan ElapsedTime = CurrentTime.Subtract(qb.QueueTime);
string ElapsedTimeString = ElapsedTime.ToString();
String TFSET = ElapsedTimeString;
String TFS_TEAMPROJECT;
if (qb.Status.ToString() == "Queued")
{
TFS_TEAMPROJECT = "-------";
}
else TFS_TEAMPROJECT = qb.Build.TeamProject;
QBTable.Rows.Add(
qb.BuildController.Name.Replace(" - Controller", "").PadRight(17).ToUpper() + "(" + qb.BuildServer.Name.PadRight(17).ToUpper() +")",
TFS_TEAMPROJECT.PadRight(22),
qb.BuildDefinition.Name.PadRight(55),
qb.Status.ToString().PadRight(15),
qb.Priority.ToString().PadRight(12),
qb.QueueTime.ToString().PadRight(23),
TFSET.PadRight(18),
RequestedBy.PadRight(18)
);
BuildCount++;
}
// Sorts QBTable on Build controller then by date
DataRow[] QBSorted = QBTable.Select("", "Controller ASC, Date ASC");
// Writes the headers
WriteHeaders();
foreach (DataRow dataRow in QBSorted)
{
WriteReportLine(
dataRow[0].ToString(),
dataRow[1].ToString(),
dataRow[2].ToString(),
dataRow[3].ToString(),
dataRow[4].ToString(),
dataRow[5].ToString(),
dataRow[6].ToString(),
dataRow[7].ToString());
}
Console.WriteLine("\n\nTotal Builds Queued: " + BuildCount + "\n\n");
}
static void WriteHeaders()
{
Console.WriteLine("\n\n");
Console.WriteLine("Controller (Agent)".PadRight(34) + " " +
"Project".PadRight(22) + " " +
"Build Definition".PadRight(55) + " " +
"Build Status".PadRight(12) + " " +
"Build Priority".PadRight(15) + " " +
"Date & Time Started".PadRight(23) + " " +
"Elapsed Time".PadRight(18) + " " +
"User".PadRight(18));
Console.WriteLine("=================".PadRight(34) + " " +
"=======".PadRight(22) + " " +
"================".PadRight(55) + " " +
"============".PadRight(12) + " " +
"==============".PadRight(15) + " " +
"====================".PadRight(23) + " " +
"==================".PadRight(18) + " " +
"============".PadRight(18));
}
static void WriteReportLine(string TFSBuildController, string TFSProject, string TFSBuildDefinition, string TFSBuildStatus, string TFSBuildPriority, string TFSBuildDateTime, string ElapsedTime, string TFSBuildUser)
{
Console.WriteLine("{0} {1} {2} {3} {4} {5} {6} {7}", TFSBuildController, TFSProject, TFSBuildDefinition, TFSBuildStatus, TFSBuildPriority, TFSBuildDateTime, ElapsedTime, TFSBuildUser);
}
}
}