651.288.7000 info@intertech.com

TFS Tutorial (Part 3) – TFS Plugin

by | Mar 1, 2017

If you would like a little background on this TFS Tutorial, in the series.

In the final article of my TFS Tutorial, I will walk you through how to create a TFS plugin that will listen for CheckinNotification’s. When an event happens, it will call the code we created in the previous two articles to create build and release definitions.

The first part of creating a TFS plugin is to implement the ISubscriber interface.  This interface has two properties and two methods that need to be implemented.  The first method is SubscriberTypes. This defines what events you are going to be watched for.

public Type[] SubscribedTypes() {    
var eventsToWatchFor = new List < Type > {    
return eventsToWatchFor.ToArray();    

As you can see, I am listening for CheckinNotification but you can listen for multiple types of events in one plugin.  The next method is ProcessEvent.  This method does all the work of your plugin.  I will walk through this method further in this TFS Tutorial.

public EventNotificationStatus ProcessEvent(IVssRequestContext requestContext, NotificationType notificationType,   
object notificationEventArgs,   
out int statusCode,   
out string statusMessage,   
out ExceptionPropertyCollection properties)  

The two properties are Name and Priority.  Name is the name of your plugin and Priority is what priority your plugin will run under.

publicstring Name => typeof(BranchCreated).FullName;  
publicSubscriberPriority Priority => SubscriberPriority.Normal; 

Before I dig into ProcessEvents, I should bring up one class that I haven’t talked about yet. This is the TfsHelper class.  This class is used to interrogate the changeset and gather information about it.

“Now let’s dig into the ProcessEVents method. The first thing I do is initialize the out parameters and my event response. Since I am only processing notification’s, I can’t cancel the check in. This process of creating the build and release definitions will happen after the check in has happened. I bring this up since there is a possibility that either the creating of the build or release definition could fail but the branch is still created. I felt that it was far more important to allow the check in to happen and figure out why the definitions were not built than to stop the check in completely.”

varresponse = EventNotificationStatus.ActionPermitted;  
properties = newExceptionPropertyCollection();  
statusMessage = String.Empty;  
statusCode = 0;

The next step is to make sure that the event type of my CheckinNotification is a Notification and not a DecisionPoint.  If I was looking for a DecisionPoint, I could actually cancel the check, but again that was not the intention of this plugin.

var notification = notificationEventArgs as CheckinNotification;
if (notification != null && notificationType == NotificationType.Notification)

Now that I am sure I have the correct type of Notification, I initialize my TfsHelper class with request context.  This context will allow me to get a reference to the correct service to gather all my change set information.

using(var tfsHelper = new TfsHelper(requestContext, notification))

The first thing I do is use my helper class to get the change set changes.  In the GetChangeSetChanges method I return a collection of ChangeSetItemInfo model objects.  From this collection I will check to see if any of the items are a Branch. If so, I will look deeper if I need to create or delete my definitions. If not, then the plugin is done processing.

var changeSetItemInfos = tfsHelper.GetChangeSetChanges(notification.Changeset);  
if (changeSetItemInfos.Exists(csii => csii.IsBranch)) 

Now, once we have found a branch, we will make sure that this branch is configured to create a build and release definition by looking into our Configuration section.  I am not going to go into this section since it is just checking if your team project is enabled to create the definitions.

Once we know that we can process this changeset, we check to see if if the ChangeType is either Add or Branch.  When that is true, we will create our build and release definitions. When not true, we are assuming it is a delete and we will delete our definitions.

if (!filterByTeamProject || allowTeamProjectThrough) {  
var tfsTemplateList = new TfsServiceCalls();  
var configurationTemplateInfo = tfsTemplateList.FindProgramTypeConfigurationInformation(changeSetItem);  
if (changeSetItem.ChangeType == ChangeType.Add || changeSetItem.ChangeType == ChangeType.Branch)
var buildDefinition = tfsTemplateList.CreateBuildDefinition(changeSetItem, configurationTemplateInfo);  
tfsTemplateList.CreateReleaseDefinition(buildDefinition, changeSetItem, configurationTemplateInfo);  

The last thing in this TFS Tutorial I should talk about, if you have never deployed a TFS plugin, is where to deploy your assemblies and how to debug your code.  Your assemblies should be deployed to the following directory: C:\Program Files\Microsoft Team Foundation Server 14.0\Application Tier\Web Services\bin\Plugins.  I am assuming you installed on your c: drive. If not, you will need to change that part of the path.  After I deploy my plugin I always do an iisreset (though I’m not sure if it’s required).   Once your assembly is deployed, you can debug your plugin by attaching to the correct iis process. Once you have attached to the process place a break point in your ProcessEvent method, now your code is waiting for a Notification. CheckinNotification’s occur when something is committed to source control so to debug your code either check some code into source control or create a new branch once that is done you should hit your break point.

You can get the source code for the complete plugin here.

We hope you enjoyed this TFS Tutorial series!

Check out the other posts in this series:

TFS Tutorial (Part 1) – REST Service Build Definition Creation

TFS Tutorial (Part 2) – REST Service Release Definition Creation

Intertech both consults and instructs TFS. Check out our TFS Training courses and our services.

[et_bloom_locked optin_id=optin_4]