TFS API Find out who deleted your build?

I am sure you have been in the situation where you start following a change through the list of builds and suddenly realize a lot of the builds have been deleted by some one! The good news is, when the build is deleted from the build explorer in Visual Studio it is only soft deleted in the database.

!

Are you wondering who moved your cheese?

In this blog post, I'll give you a walkthrough on how to write a simple utility to get the list of deleted builds and their details using the TFS API.

Download the sample application – wpf,.net 4, you need to have vs sdk installed on your machine to run it.

image 

1. Connect to TFS using the API

If you are new to the TFS API, refer to this blog post on getting started with the Visual Studio SDK.

The below snippet will help present a connect to tfs pop up to the user

private TfsTeamProjectCollection _tfs; private string _selectedTeamProject;

public bool ConnectToTfs { bool isSelected = false; TeamProjectPicker tfsPP = new TeamProjectPicker(TeamProjectPickerMode.SingleProject, false); tfsPP.ShowDialog; this._tfs = tfsPP.SelectedTeamProjectCollection;

if (tfsPP.SelectedProjects.Count > 0) { this._selectedTeamProject = tfsPP.SelectedProjects[0].Name; isSelected = true; } return isSelected; }

2. Get All the build definitions for the selected Team Project using the TFS API

By using the IBuildService you can get access to the QueryBuildDefinition method which takes team project and returns the build definitions associated to the team project.

private IBuildDefinition[] GetAllBuildDefinitionsFromTheTeamProject(_selectTeamProject) { _bs = _tfs.GetService; return _bs.QueryBuildDefinitions(_selectedTeamProject); }

3. Get all the deleted builds for the chosen build definition using the TFS API

By using the QueryBuilds methods it is possible to construct a query to confine the search to builds for the selected build definition that have been marked as deleted.

// bdef is the selected build definition var bdef = (((ComboBox)sender).SelectedItem) as IBuildDefinition;

if (bdef!= null) { // _bs = IBuildServer service, create a new query var def = _bs.CreateBuildDetailSpec(_selectedTeamProject); // only bring back the last 100 deleted builds def.MaxBuildsPerDefinition = 100; // query for only deleted builds def.QueryDeletedOption = QueryDeletedOption.OnlyDeleted; // Last deleted should be returned 1st def.QueryOrder = BuildQueryOrder.FinishTimeDescending; // Only look for deleted builds in the chosen build definition def.DefinitionSpec.Name = bdef.Name; // Bring back deleted builds from any state def.Status = BuildStatus.All; // Pass this query for processing to the build service var builds = _bs.QueryBuilds(def).Builds;

// Add each deleted build to a local buildDetail entity, // You could switch this with Console.WriteLine if you // would like to see the output instead. foreach (var build in builds) { dgBuildDetails.Items.Add(new BuildDetail { BuildStatus = build.Status.ToString, BuildNumber = build.BuildNumber, BuildDef = build.BuildDefinition.Name, TeamProject = build.TeamProject, RequestedBy = build.RequestedBy, RequestedOn = build.FinishTime.ToString, DeletedBy = build.LastChangedBy, DeletedOn = build.LastChangedOn.ToString }); } }

4. Putting it all together

Download the sample application – wpf,.net 4, you need to have vs sdk installed on your machine to run it.

using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using Microsoft.TeamFoundation.Build.Client; using Microsoft.TeamFoundation.Client; using Microsoft.TeamFoundation.TestManagement.Client; using Microsoft.TeamFoundation.VersionControl.Client;

namespace TfsApiGetDeletedBuildDetails { ///

/// Interaction logic for MainWindow.xaml /// public partial class MainWindow: Window { public MainWindow { InitializeComponent; }

private TfsTeamProjectCollection _tfs; private string _selectedTeamProject; private IBuildServer _bs; private VersionControlServer _vcs;

public bool ConnectToTfs { bool isSelected = false; TeamProjectPicker tfsPP = new TeamProjectPicker(TeamProjectPickerMode.SingleProject, false); tfsPP.ShowDialog; this._tfs = tfsPP.SelectedTeamProjectCollection;

if (tfsPP.SelectedProjects.Count > 0) { this._selectedTeamProject = tfsPP.SelectedProjects[0].Name; isSelected = true; } return isSelected; }

private void btnConnect_Click(object sender, RoutedEventArgs e) { if (ConnectToTfs) { cbBuildDef.IsEnabled = true; _vcs = _tfs.GetService; cbBuildDef.ItemsSource = GetAllBuildDefinitionsFromTheTeamProject; cbBuildDef.DisplayMemberPath = "Name"; cbBuildDef.SelectedValuePath = "Uri"; cbBuildDef.SelectedIndex = 0; } }

// Grab all build definitions private IBuildDefinition[] GetAllBuildDefinitionsFromTheTeamProject { _bs = _tfs.GetService; return _bs.QueryBuildDefinitions(_selectedTeamProject); }

public class BuildDetail { public string BuildStatus { get; set; } public string BuildNumber { get; set; } public string BuildDef { get; set; } public string TeamProject { get; set; } public string RequestedBy { get; set; } public string RequestedOn { get; set; } public string DeletedBy { get; set; } public string DeletedOn { get; set; } }

// Get All Deleted Builds for the selected build definition private void cbBuildDef_SelectionChanged(object sender, SelectionChangedEventArgs e) { dgBuildDetails.Items.Clear;

List buildDetails = new List;

// bdef is the selected build definition var bdef = (((ComboBox)sender).SelectedItem) as IBuildDefinition;

if (bdef!= null) { // _bs = IBuildServer service, create a new query var def = _bs.CreateBuildDetailSpec(_selectedTeamProject); // only bring back the last 100 deleted builds def.MaxBuildsPerDefinition = 100; // query for only deleted builds def.QueryDeletedOption = QueryDeletedOption.OnlyDeleted; // Last deleted should be returned 1st def.QueryOrder = BuildQueryOrder.FinishTimeDescending; // Only look for deleted builds in the chosen build definition def.DefinitionSpec.Name = bdef.Name; // Bring back deleted builds from any state def.Status = BuildStatus.All; // Pass this query for processing to the build service var builds = _bs.QueryBuilds(def).Builds;

// Add each deleted build to a local buildDetail entity, // You could switch this with Console.WriteLine if you // would like to see the output instead. foreach (var build in builds) { dgBuildDetails.Items.Add(new BuildDetail { BuildStatus = build.Status.ToString, BuildNumber = build.BuildNumber, BuildDef = build.BuildDefinition.Name, TeamProject = build.TeamProject, RequestedBy = build.RequestedBy, RequestedOn = build.FinishTime.ToString, DeletedBy = build.LastChangedBy, DeletedOn = build.LastChangedOn.ToString }); } } }

private void Hyperlink_RequestNavigate(object sender, RequestNavigateEventArgs e) { Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri)); e.Handled = true;

} } }

Thank you for taking the time out and reading this blog post. If you enjoyed the post, remember to subscribe to http://feeds.feedburner.com/TarunArora.

Check out the other posts on cool tools using TFS API.In love

This article is part of the GWB Archives. Original Author: Tarun Arora [Microsoft MVP]

New on Geeks with Blogs

  • We Won The One Award I Actually Care About

    Full Scale made the Inc. 5000 for the fifth year straight, the 12th listing across my three companies. Here is why the one award you cannot buy is worth stopping for.

  • Your Customers Build the Features Now

    I let a tool I liked sit dead for a year rather than build the features I wanted. An MCP server meant I never had to, and your customers can do the same to your product.

  • Get the Size of a Directory in Linux the Easy Way

    du -sh for the quick answer, ncdu for the cleanup, df for the disk itself: every command for checking directory size in Linux, plus why du and df never agree.

  • Vim Search and Replace: The Ultimate Guide

    One :%s command replaces every match in a file before a find dialog would even open. The Vim substitute patterns worth the muscle memory: flags, ranges, capture groups, and multi-file edits.