views:

572

answers:

1

We are using continuous integration as part of our build automation. For every check in, the tfs build server builds the project and deploys to our web servers on success.

When the build fails, it automatically creates a new Bug with the details of the build failure.

Due to CI and the activity on the server, this might result in 10 or 20 failure work items before the build starts succeeding again.

So, I have two options. I'd like to either have the build process see if an open work item already exists for a build failure and just add details to that; OR, I'd like the build server to close all of the build failure items automatically when it starts working again.

Any ideas?

+2  A: 

You can create a MSBuild Task to do either of these options. Here is a similar piece of code I use to get you started but since I don't know the details of your work item or process you will have to change it.

This code takes all of the work items associated with a build and updates their status.

If you select your first option you can just change the UpdateWorkItemStatus method and update any existing WIs. For the Second method you will need to do a bit more work as you need to look up the prior build rather than take it as a input.

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Build.Utilities;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Microsoft.Build.Framework;
using Microsoft.TeamFoundation.Build;
using Microsoft.TeamFoundation.Build.Client;

namespace Nowcom.TeamBuild.Tasks
{
    public class UpdateWorkItemState: Task
    {
        private IBuildDetail _Build;

        private void test()
        {
            TeamFoundationServerUrl = "Teamserver";
            BuildUri = "vstfs:///Build/Build/1741";
            Execute();
        }

        public override bool Execute()
        {
            bool result = true;

            try
            {
                TeamFoundationServer tfs = TeamFoundationServerFactory.GetServer(TeamFoundationServerUrl, new UICredentialsProvider());
                tfs.EnsureAuthenticated();

                WorkItemStore store = (WorkItemStore)tfs.GetService(typeof(WorkItemStore));
                IBuildServer buildServer = (IBuildServer)tfs.GetService(typeof(IBuildServer));
                _Build = buildServer.GetAllBuildDetails(new Uri(BuildUri));
                //add build step
                IBuildStep buildStep = InformationNodeConverters.AddBuildStep(_Build, "UpdateWorkItemStatus", "Updating Work Item Status");


                try
                {
                    Log.LogMessageFromText(string.Format("Build Number: {0}", _Build.BuildNumber), MessageImportance.Normal);

                    List<IWorkItemSummary> assocWorkItems = InformationNodeConverters.GetAssociatedWorkItems(_Build);

                        // update work item status 
                        UpdateWorkItemStatus(store, assocWorkItems, "Open", "Resolved");

                    SaveWorkItems(store, assocWorkItems);
                }
                catch (Exception)
                {
                    UpdateBuildStep(buildStep, false);
                    throw;
                }

                UpdateBuildStep(buildStep, result);
             }
            catch (Exception e)
            {
                result = false;

                BuildErrorEventArgs eventArgs;
                eventArgs = new BuildErrorEventArgs("", "", BuildEngine.ProjectFileOfTaskNode, BuildEngine.LineNumberOfTaskNode, BuildEngine.ColumnNumberOfTaskNode, 0, 0, string.Format("UpdateWorkItemState failed: {0}", e.Message), "", "");

                BuildEngine.LogErrorEvent(eventArgs);

                throw;
            }

            return result;
        }

        private static void SaveWorkItems(WorkItemStore store, List<IWorkItemSummary> assocWorkItems)
        {
            foreach (IWorkItemSummary w in assocWorkItems)
            {
                WorkItem wi = store.GetWorkItem(w.WorkItemId);

                if (wi.IsDirty)
                {
                    wi.Save();
                }
            }
        }

        // check in this routine if the workitem is a bug created by your CI process. Check by title or assigned to or description depending on your process.
        private void UpdateWorkItemStatus(WorkItemStore store, List<IWorkItemSummary> assocWorkItems, string oldState, string newState)
        {
            foreach (IWorkItemSummary w in assocWorkItems)
            {
                Log.LogMessageFromText(string.Format("Updating Workitem Id {0}", w.WorkItemId), MessageImportance.Normal);
                WorkItem wi = store.GetWorkItem(w.WorkItemId);
                if (wi.Fields.Contains("Microsoft.VSTS.Build.IntegrationBuild") && wi.State != "Resolved")
                {
                    wi.Fields["Microsoft.VSTS.Build.IntegrationBuild"].Value =_Build.BuildNumber;
                }
                if (wi.State == oldState)
                {
                    wi.State = newState;
                    foreach (Field field in wi.Fields)
                    {
                        if (!field.IsValid)
                        {
                            break;
                        }
                    }
                }

                if (wi.IsDirty)
                {
                    wi.Save();
                }
            }
        }

        private void UpdateBuildStep(IBuildStep step, bool result)
        {
            step.Status = result ? BuildStepStatus.Succeeded : BuildStepStatus.Failed;
            step.FinishTime = DateTime.Now;
            step.Save();
        }

        [Required]
        public string BuildUri { get; set; }

        [Required]
        public string TeamFoundationServerUrl {get; set;}
    }
}



 < UpdateWorkItemState
TeamFoundationServerUrl="$(TeamFoundationServerUrl)"
BuildUri="$(BuildUri)"
ContinueOnError="false"/>
Robert Kozak