Wednesday, April 4, 2018

Customize TFS XAML build template to create a Bug WI

With the release of TFS 2013, many new features have been introduced. And one of those is the script hooks which lets you add pre- and post-build as well as pre- and post-test hooks. These make customizing your build process a breeze.

Recently, I have been assigned a task to customize the build process so that it can create a BUG work item in case of build failure. Now you'd think that this a standard request and also available OOB in TFS. However, this time client wants to create the work items in different TFS project. They have a clear separation between ALM and VCS projects. There are obviously pros and cons of this separation, however, that’s another post in itself. And they were using TFS 2012 build templates. So I have to implement the custom hooks logic in a 2012 build – unfortunately, there is no straight forward way. Obviously, that's the reason they called me.

I came up with a less popular approach for 2012 builds. This post will walk you through the steps of customizing the default build template for a post-build script.

Step 1 - Customize Build template


Prepare a solution to customize your build process template:


I created a new VS 2013 solution and that solution includes this customized build process template. 
  1. Create a new solution named BuildProcessSource that contains a new Visual Basic workflow activity library code project called Templates. 

  2. Take a copy of standard build template which you want to customize. I used DefaultTemplate.11.1.xaml.
    Connect to your TFS project via Team Explorer. On the Builds page, click "New Build Definition" link.
    Download a copy of the default template - DefaultTemplate.11.1.xaml. Save your new template in the same folder that contains the code project you created earlier in this procedure as DefaultTemplate.11.1-CreateWI.xaml.
  3. In solution explorer, add the DefaultTemplate.11.1-CreateWI template to your Templates project using Existing Item context menu option. You don’t need the Activity1.xaml file, so you can delete it if you want.
  4. Set the Build Action property of your template to Content.
  5. Add the following references to your Templates code project:
    1. Microsoft.TeamFoundation.Build.Activities
    2. Microsoft.TeamFoundation.Build.Client
    3. Microsoft.TeamFoundation.Build.Workflow
    4. Microsoft.TeamFoundation.VersionControl.Client
  6. Save your project and upload your new solution. Check in pending changes.
  7. After you have uploaded a custom build process template to your team project as explained above, you can use the template from your build definitions. On the builds page, create or edit a build definition. On the build definition Process tab, choose Show details, and then choose New. After you choose New: Type or browse to the path to the template (DefaultTemplate.11.1-CreateWI) on your Team Foundation Server. After you specify the path to the template, you can select it from the list. Save your build definition.
  8. Above process is documented here 

Perform customizations on build process template:

  1. Double-click the DefaultTemplate.11.1-CreateWI.xaml template from your solution explorer after opening the VS 2013 project as created above.
  2. Add Arguments as:
    1. CreateCustomBugWI (Input, Boolean with default as False)
    2. TfsProjectForCustomBugWI (Input, String)
    3. PostBuildScriptPath (Input, String)
  3. Add Variables as:
    1. PostBuildScriptLocalPath (String, Scope: Handle Exception)
  4. Scroll down beneath the TryCatch activity called “Try Compile, Test, and Associate Changesets and Work Items”
  5. Look for Sequence activity titled as "If Create WorkItem". You'll find this under Catches>Handle Exception which is nested under "Try to Compile the Project" under "Compile and Test"
  6. Create a new Sequence activity under Else section block of "If CreateWorkItem" activity
  7. Add a new "If" activity under new Sequence activity and set the condition to CreateCustomBugWI to ensure it will only run when the argument is passed.
  8. Add in a Sequence activity in the Then block of new "If" activity with Display name as "Create Bug in case of build failure"
  9. Add a "ConvertWorkspaceItem" activity inside "Create Bug in case of build failure" activity with details as:
    1. Display name: Convert post-build script file name
    2. Input: PostBuildScriptPath
    3. Result: PostBuildScriptLocalPath
    4. Workspace: Workspace
  10. Add an "InvokeProcess" activity and join it with "Convert post-build script file name" with details as:
    1. Display name: Execute Post-build script
    2. File name: "PowerShell"
    3. Output Encoding: System.Text.Encoding.GetEncoding(System.Globalization.CultureInfo.InstalledUICulture.TextInfo.OEMCodePage)
    4. Arguments: String.Format(" ""& '{0}' '{1}' '{2}' '{3}' "" ", PostBuildScriptLocalPath, TfsProjectForCustomBugWI, BuildDetail.BuildNumber, BuildDetail.RequestedBy)
    5. To see results from the powershell command drop a "WriteBuildMessage" activity on the "Handle Standard Output" and pass the stdOutput variable to the Message property.
    6. Drop a "WriteBuildError" activity on the "Handle Error Output" and pass the errOutput variable to the Message property.
  11. This leads to the following result

  12. To publish it, check in the Build Process Template

Step 2 - Prepare PowerShell script


Do ensure that ExecutionPolicy defined on build server is set to Remotesigned.
You can set the policy by executing below command in PowerShell console:

set-executionpolicy remotesigned

The script itself is pretty straight-forward. It expects 3 script parameters:
  1. ProjectName
    It's a mandatory string parameter which is set from build definition editor.
  2. BuildNumber
    It's a mandatory string parameter which is set from customized build template.
  3. BuildRequestedBy
    It's a mandatory string parameter which is set from customized build template.
It then invokes TFS REST api to create a work item of type BUG in specified TFS project. It sets following fields for the new WI:
  1. Title (/fields/System.Title)
  2. Assigned To (/fields/System.AssignedTo)
  3. Repro steps (/fields/Microsoft.VSTS.TCM.ReproSteps)
  4. Severity (/fields/Microsoft.VSTS.Common.Severity)
  5. Reason (/fields/System.Reason)
  6. Comments (/fields/System.History)
You can find more technical details here and here 

I have enclosed some snippets from the script below:

param
(
    [Parameter(Mandatory)]
[string]
$ProjectName,        #Coming from XAML build definition argument
    [Parameter(Mandatory)]
[string]
$BuildNumber,        #Coming from Customized TFS XAML build
   
    [Parameter(Mandatory)]
[string]
$BuildRequestedBy    #Coming from Customized TFS XAML build
)
$bodyUpdate = "
[
{`"op`":`"add`",`"path`":`"/fields/System.Title`",`"value`":`"Build Failure in Build: $BuildNumber`"},
{`"op`":`"add`",`"path`":`"/fields/System.AssignedTo`",`"value`":`"$BuildRequestedBy`"},
{`"op`":`"add`",`"path`":`"/fields/Microsoft.VSTS.TCM.ReproSteps`",`"value`":`"Start the build using TFS Build`"},
{`"op`":`"add`",`"path`":`"/fields/Microsoft.VSTS.Common.Severity`",`"value`":`"1 - Critical`"},
{`"op`":`"add`",`"path`":`"/fields/System.Reason`",`"value`":`"Build Failure`"},
{`"op`":`"add`",`"path`":`"/fields/System.History`",`"value`":`"This work item was created by TFS Build on a build failure.`"},
]"

$url = "$ProjectName/_apis/wit/workitems/`$Bug?api-version=2.2"
Invoke-RestMethod $url -Method Patch -Body $bodyupdate -UseDefaultCredentials -ContentType "application/json-patch+json" -Verbose

Please note, you can add more robust error handling in the script depending upon your needs.

Step 3 - Configure build definition


Now create a new build definition, and make sure to:
  1. Add the folder containing your PowerShell script as a folder mapping in the Source Settings tab of your build definition.

  2. Specify the build script arguments properly. For instance, set "CreateCustomBugWI" to True. And specify the TFS project url for "TfsProjectForCustomBugWI" argument like "http://tfsServer:8080/tfs/Collection/Project"


Open your build definition to edit and specify the TFS version control path to the post-build script and also the TFS project url where you want the BUG WI to be created in case of build failure.

Step 4 - Verification


Now try to run your build, which you expect to fail. I deliberately introduced compile time errors and checked-in to make the TFS build fail. Once the build fails, you must see a new Bug created in your specified TFS project which can be verified from your browser.