How to execute a special build on TFS using Team Foundation Object Model (TFS APIs) – Part 1

Posted by: Subodh Sohoni , on 6/30/2009, in Category VSTS & TFS (Azure DevOps)
Views: 27002
Abstract: Recently I came across a customer requirement which was quite peculiar. Due to certain compulsion, customer has only one branch in which everyone keeps on checking the code in. Code being simultaneously developed is for bug fixes as well as new feature additions in the product.
How to execute a special build on TFS using Team Foundation Object Model (TFS APIs) – Part 1
 
Recently I came across a customer requirement which was quite peculiar. Due to certain compulsion, customer has only one branch in which everyone keeps on checking the code in. Code being simultaneously developed is for bug fixes as well as new feature additions in the product. Quite often the customers of the customer need the product which may not have the new features but need the code which is bug fixed to the latest known bug. A standard build will consider all the code which is checked in after the last build. It cannot allow us to pick and choose specific changesets which were created after that build. I created that application using Team Foundation Object Model and found it a very good learning experience. I was working with objects for Team Build, Version Control and WorkItem Tracking at the same time. Although that customer needed the solution for targeting TFS 2005, after that project was created, I migrated it to target TFS 2008 these articles cover the code for TFS 2008.
It is an application that provides the configuration of special build which overrides the default
behaviour of the Team Build. It is a common requirement of many organizations to configure and execute a build which will allow flexibility to the Build and Release Managers to cherry pick the changesets for build. Through this tool, those Build and Managers will be able to:
* Select a previous build
* View changesets which are created after that build, with their comments 
* Select all or few changesets from the list to be included in the next build
* Label them with a temporary label
* Execute that build
* Finally, associate the selected changesets with the executed build while overriding the default behaviour of the Team Build.
In the set of two articles, let me explain some important points of this application. We will create a class library to encapsulate the logic which will be exposed by a windows forms application. I will not be providing code for client since it does not contain any TFOM code. We will discuss only the class library. What we will cover in this article are how to find the build definitions for a team project, builds of specific definition which were executed in the past, changesets which were considered for that build, latest changeset in the repository, label the files in a list of changesets, execute the build which does not get the latest version but a labelled version and associate certain changesets with the executing build.
We start by creating a project and give references to
·         Microsoft.TeamFoundation.Client.dll
·         Microsoft.TeamFoundation.Build.Client.dll
·         Microsoft.TeamFoundation.VersionControl.Client.dll
·         Microsoft.TeamFoundation.WorkItemTracking.Client.dll
We also add the namespaces:
·         Microsoft.TeamFoundation.Client
·         Microsoft.TeamFoundation.Build.Client
·         Microsoft.TeamFoundation.VersionControl.Client
·         Microsoft.TeamFoundation.WorkItemTracking.Client
We will first declare some variables which we will be using in number of methods.
 
C#
 
public class TfsDataClient
{
    TeamFoundationServer tfs;
    WorkItemStore ws;
    VersionControlServer tfvc;
    IBuildServer bs;
    string project;
    IBuildDetail[] builds;
    string SelectedBuildType;
    IQueuedBuild QueuedBuild;
    List<int> changeSets = null;
}
 
VB.NET
Public Class TfsDataClient
            Private tfs As TeamFoundationServer
            Private ws As WorkItemStore
            Private tfvc As VersionControlServer
            Private bs As IBuildServer
            Private project As String
            Private builds() As IBuildDetail
            Private SelectedBuildType As String
            Private QueuedBuild As IQueuedBuild
            Private changeSets As List(Of Integer) = Nothing
End Class
 
First method that we will write is to create instance of the Team Foundation Server, Team Project and Team Foundation Version Control.
 
C#
    
public void init(string Tfs, string Project)
        {
            tfs = this.GetTFS(Tfs);
            project = this.GetTeamProject(Project);
        }
        private TeamFoundationServer GetTFS(string Tfs)
        {
            TeamFoundationServer _tfs = TeamFoundationServerFactory.GetServer(Tfs);
            tfvc = (VersionControlServer)_tfs.GetService(typeof(VersionControlServer));
            return _tfs;
        }
        private string GetTeamProject(string teamProject)
        {
            ws = (WorkItemStore)tfs.GetService(typeof(WorkItemStore));
            Project _project = ws.Projects[teamProject];
            return _project.Name;
        }
 
VB.NET
 
Public Sub init(ByVal Tfs As String, ByVal Project As String)
                  tfs = Me.GetTFS(Tfs)
                  project = Me.GetTeamProject(Project)
 End Sub
            Private Function GetTFS(ByVal Tfs As String) As TeamFoundationServer
                  Dim _tfs As TeamFoundationServer = TeamFoundationServerFactory.GetServer(Tfs)
                  tfvc = CType(_tfs.GetService(GetType(VersionControlServer)), VersionControlServer)
                  Return _tfs
            End Function
            Private Function GetTeamProject(ByVal teamProject As String) As String
                  ws = CType(tfs.GetService(GetType(WorkItemStore)), WorkItemStore)
                  Dim _project As Project = ws.Projects(teamProject)
                  Return _project.Name
End Function
 
Let us now check which Build Definitions exist for this team project and get a list names of those in an array. We can off course return it as a list of strings too. What we want to avoid is sending those as list of objects which will require the referencing the team foundation object model class libraries in the client. In this way we will keep the client loosely coupled with the TFS objects.
 
C#
public string[] GetBuildTypes()
        {
            bs = (IBuildServer)tfs.GetService(typeof(IBuildServer));
            IBuildDefinition[] BuildDefinitions = bs.QueryBuildDefinitions(project);
            List<string> buildDefNames = new List<string>();
            buildDefNames.Add("Select Build Definition");
            foreach (IBuildDefinition BuildDefinition in BuildDefinitions)
            {
                buildDefNames.Add(BuildDefinition.Name);
            }
           return buildDefNames.ToArray();
        }
 
VB.NET
 
Public Function GetBuildTypes() As String()
                  bs = CType(tfs.GetService(GetType(IBuildServer)), IBuildServer)
                  Dim BuildDefinitions() As IBuildDefinition = bs.QueryBuildDefinitions(project)
                  Dim buildDefNames As New List(Of String)()
                  buildDefNames.Add("Select Build Definition")
                  For Each BuildDefinition As IBuildDefinition In BuildDefinitions
                        buildDefNames.Add(BuildDefinition.Name)
                  Next BuildDefinition
               Return buildDefNames.ToArray()
 End Function
We now can get the builds which were executed for a selected build definition.
 
C#
public string[] GetBuilds(string BuildType)
        {
            SelectedBuildType = BuildType;
            builds = bs.QueryBuilds(project, BuildType);
            List<string> buildLabelName = new List<string>();
            for (int i=0;i<builds.Length;i++)
            {
                buildLabelName.Add(builds[i].BuildNumber);
            }
            return buildLabelName.ToArray();
        }
 
VB.NET
 
Public Function GetBuilds(ByVal BuildType As String) As String()
                  SelectedBuildType = BuildType
                  builds = bs.QueryBuilds(project, BuildType)
                  Dim buildLabelName As New List(Of String)()
                  For i As Integer = 0 To builds.Length - 1
                        buildLabelName.Add(builds(i).BuildNumber)
                  Next i
                  Return buildLabelName.ToArray()
End Function
 
User can select a specific build as the base build. Now that we have a build number we can find out which changesets were considered for that build.
 
C#
 
public List<int> GetChangesetNumbers(string buildNumber)
        {
            foreach (IBuildDetail buildDetail in builds)
            {
                if (buildDetail.BuildNumber == buildNumber)
                {
                    build = buildDetail;
                    break;
                }
            }
            List<IChangesetSummary> AssociatedChangesets =
informationNodeConverters.GetAssociatedChangesets(build);
            if (AssociatedChangesets.Count == 0)
            {
                throw new Exception("This build does not have any changesets and workitems associated");
            }
            List<int> ChangeSets = new List<int>();
            foreach (IChangesetSummary changeSet in AssociatedChangesets)
            {              
                ChangeSets.Add(changeSet.ChangesetId);               
            }
            return ChangeSets;
        }
 
VB.NET
 
Public Function GetChangesetNumbers(ByVal buildNumber As String) As List(Of Integer)
                  For Each buildDetail As IBuildDetail In builds
                        If buildDetail.BuildNumber = buildNumber Then
                              build = buildDetail
                              Exit For
                        End If
                  Next buildDetail
                  Dim AssociatedChangesets As List(Of IChangesetSummary) = informationNodeConverters.GetAssociatedChangesets(build)
                  If AssociatedChangesets.Count = 0 Then
                        Throw New Exception("This build does not have any changesets and workitems associated")
                  End If
                  Dim ChangeSets As New List(Of Integer)()
                  For Each changeSet As IChangesetSummary In AssociatedChangesets
                        ChangeSets.Add(changeSet.ChangesetId)
                  Next changeSet
                  Return ChangeSets
End Function
 
We also need the list of changesets which were created after the selected build was executed and the comments related to those changesets.
 
C#
 
public List<int> GetChangeSetsAfterBuild(string buildNumber)
        {
            List<int> AssociatedChangeSets = null;
            try
            {
                AssociatedChangeSets = removeDuplicates(this.GetChangesetNumbers(buildNumber));
                //GetChangeSetNumbers return all the changesets considered for that build
                AssociatedChangeSets.Sort();
            }
            catch(Exception ex)
            {
                throw new Exception(ex.Message);
            }
            int LastChangeSet = 0;
            foreach (int changeSet in AssociatedChangeSets)
            {
                if (changeSet > LastChangeSet)
                {
                    LastChangeSet = changeSet;
                }
            }
            List<int> ChangeSetsAfterSelectedBuild = new List<int>();
            int LastChangeSetInTFVC = tfvc.GetLatestChangesetId();
            for (int i = LastChangeSet + 1; i <= LastChangeSetInTFVC; i++)
            {
                try
                {
                    Changeset changeset = tfvc.GetChangeset(i);
                    if (changeset.Comment != "Label Change")
                    {
                        ChangeSetsAfterSelectedBuild.Add(changeset.ChangesetId);
                    }
                }
                catch
                {
                }
            }
            return ChangeSetsAfterSelectedBuild;
        }
     
       public List<string> GetChangeSetComments(List<int> ChangeSetIds)
        {
            List<string> ChangeSetComments = new List<string>();
            foreach (int ChangeSetId in ChangeSetIds)
            {
                ChangeSetComments.Add(tfvc.GetChangeset(ChangeSetId).Comment);
            }
            return ChangeSetComments;
        }
 
VB.NET
 
Public Function GetChangeSetsAfterBuild(ByVal buildNumber As String) As List(Of Integer)
                  Dim AssociatedChangeSets As List(Of Integer) = Nothing
                  Try
                        AssociatedChangeSets = removeDuplicates(Me.GetChangesetNumbers(buildNumber))
                        'GetChangeSetNumbers return all the changesets considered for that build
                        AssociatedChangeSets.Sort()
                  Catch ex As Exception
                        Throw New Exception(ex.Message)
                  End Try
                  Dim LastChangeSet As Integer = 0
                  For Each changeSet As Integer In AssociatedChangeSets
                        If changeSet > LastChangeSet Then
                              LastChangeSet = changeSet
                        End If
                  Next changeSet
                  Dim ChangeSetsAfterSelectedBuild As New List(Of Integer)()
                  Dim LastChangeSetInTFVC As Integer = tfvc.GetLatestChangesetId()
                  For i As Integer = LastChangeSet + 1 To LastChangeSetInTFVC
                        Try
                              Dim changeset As Changeset = tfvc.GetChangeset(i)
                              If changeset.Comment <> "Label Change" Then
                                    ChangeSetsAfterSelectedBuild.Add(changeset.ChangesetId)
                              End If
                        Catch
                        End Try
                  Next i
                  Return ChangeSetsAfterSelectedBuild
End Function
 
         Public Function GetChangeSetComments(ByVal ChangeSetIds As List(Of Integer)) As List(Of String)
                  Dim ChangeSetComments As New List(Of String)()
                  For Each ChangeSetId As Integer In ChangeSetIds
                        ChangeSetComments.Add(tfvc.GetChangeset(ChangeSetId).Comment)
                  Next ChangeSetId
                  Return ChangeSetComments
End Function
 
Method removeDuplicates which was used is as follows:
 
C#
 
static List<int> removeDuplicates(List<int> inputList)
        {
            Dictionary<int, int> uniqueStore = new Dictionary<int, int>();
            List<int> finalList = new List<int>();
            foreach (int currValue in inputList)
            {
                if (!uniqueStore.ContainsKey(currValue))
                {
                    uniqueStore.Add(currValue, 0);
                    finalList.Add(currValue);
                }
            }
            return finalList;
        }
 
VB.NET
 
Shared Function removeDuplicates(ByVal inputList As List(Of Integer)) As List(Of Integer)
                  Dim uniqueStore As New Dictionary(Of Integer, Integer)()
                  Dim finalList As New List(Of Integer)()
                  For Each currValue As Integer In inputList
                        If (Not uniqueStore.ContainsKey(currValue)) Then
                              uniqueStore.Add(currValue, 0)
                              finalList.Add(currValue)
                        End If
                  Next currValue
                  Return finalList
End Function
 
Now we come to the heart of the program where we will set the label to the files which are either in the base build or are added later on. We will complete that part of the application and also write the code to execute the build programmatically in the Part 2 of this article.
The entire source code of this article can be downloaded from here.
If you liked the article,  Subscribe to the RSS Feed or Subscribe Via Email
  
Subodh Sohoni is MCTS - Microsoft Team Foundation Server - Configuration and Development and also is a Microsoft Certified Trainer(MCT) since 2004. Subodh works as VP, Technology with SEED Infotech Ltd

This article has been editorially reviewed by Suprotim Agarwal.

Absolutely Awesome Book on C# and .NET

C# and .NET have been around for a very long time, but their constant growth means there’s always more to learn.

We at DotNetCurry are very excited to announce the The Absolutely Awesome Book on C# and .NET. This is a 500 pages concise technical eBook available in PDF, ePub (iPad), and Mobi (Kindle).

Organized around concepts, this eBook aims to provide a concise, yet solid foundation in C# and .NET, covering C# 6.0, C# 7.0 and .NET Core, with chapters on .NET Standard and the upcoming C# 8.0 too. Use these concepts to deepen your existing knowledge of C# and .NET, to have a solid grasp of the latest in C# and .NET OR to crack your next .NET Interview.

Click here to Explore the Table of Contents or Download Sample Chapters!

What Others Are Reading!
Was this article worth reading? Share it with fellow developers too. Thanks!
Share on LinkedIn
Share on Google+

Author
Subodh is a consultant and corporate trainer. He has overall 28+ years of experience. His specialization is Application Lifecycle Management and Team Foundation Server. He is Microsoft MVP – VS ALM, MCSD – ALM and MCT. He has conducted more than 300 corporate trainings and consulting assignments. He is also a Professional SCRUM Master. He guides teams to become Agile and implement SCRUM. Subodh is authorized by Microsoft to do ALM Assessments on behalf of Microsoft. Follow him on twitter @subodhsohoni


Page copy protected against web site content infringement 	by Copyscape




Feedback - Leave us some adulation, criticism and everything in between!

Categories

JOIN OUR COMMUNITY

POPULAR ARTICLES

C# .NET BOOK

C# Book for Building Concepts and Interviews

Tags

JQUERY COOKBOOK

jQuery CookBook