Build association with work Items in vNext

(Sriram Ravi) #1

Hi Team,

Good Morning.

I can able to release my project in Octopus Deploy. While trying to associating the build number to the tfs work item ‘Integrated in Build’ property after generating TFS build, I am unable to associate.

In my previous project however, we used TFS XAML Build, over there, the build number gets associated to TFS ‘integrated in build’ property automatically once the Build is generated.

I tried with the below code:

  1. I tried below link:

my script:

[string] $CollectionUri = “#pass your data#”
[string] $project = “#pass your data#”
[string] $BuildId = “$env:BUILD_BUILDID”
[string] $BuildNumber = “$env:BUILD_BUILDNUMBER”
[string] $AuthType = “Bearer”

function Get-Headers
{
$authorization = “$AuthType $env:SYSTEM_ACCESSTOKEN”

return @{
    Authorization = $authorization
}

}

function Get-BuildWorkItemIds([string] $collectionUri, [string] $projectName, [string] $buildId)
{
$headers = Get-Headers
$restUri = $collectionUri + $projectName + “/_apis/build/builds/” + $buildId + “/workitems?api-version=2.0”
[string[]] $buildWorkItemIds = @()

$response = Invoke-RestMethod -Uri $restUri -ContentType "application/json" -headers $headers -Method GET

Write-Debug (ConvertTo-Json $response -Depth 100)

$itemCount = $response.count

for($index = 0; $index -lt $itemCount ; $index++)
{
    $buildWorkItem = $response.value[$index]
    $workItemId = $buildWorkItem.id

    Write-Verbose "Found work item $workItemId linked to build $buildId"

    $buildWorkItemIds += $workItemId
}

return $buildWorkItemIds

}

function Get-BuildWorkItems([string] $collectionUri, [string] $projectName, [string] $buildId)
{
$buildWorkItemIds = @(Get-BuildWorkItemIds $collectionUri $projectName $buildId)

if ($buildWorkItemIds.Length -eq 0)
{
    return @()
}

return Get-WorkItems $collectionUri $buildWorkItemIds

}

function Get-WorkItems([string] $collectionUri, [string[]] $workItemIds)
{
$headers = Get-Headers
$ids = ($workItemIds -join “,”)
$restUri = $collectionUri + “_apis/wit/workitems?ids=” + $ids + “&api-version=1.0&`$expand=relations”

$response = Invoke-RestMethod -Uri $restUri -ContentType "application/json" -headers $headers -Method GET

Write-Debug (ConvertTo-Json $response -Depth 100)

if ($response.value -eq $null)
{
    return @()
}

return $response.value;

}

function Get-WorkItem([string] $collectionUri, [string] $workItemId)
{
Write-Debug “Getting work item $workItemId”

$headers = Get-Headers
$restUri = $collectionUri + "_apis/wit/workitems/" + $workItemId + "?api-version=1.0&`$expand=relations"

$response = Invoke-RestMethod -Uri $restUri -ContentType "application/json" -headers $headers -Method GET

Write-Debug (ConvertTo-Json $response -Depth 100)

return $response;

}

function Get-ParentWorkItemId([object] $workItem)
{
if ($workItem -eq $null)
{
return $null;
}

if ($workItem.relations -eq $null)
{
    Write-Debug "$($workItem.fields."System.WorkItemType") #$($workItem.id) does not have any relationships"

    return $null;
}

$relation = $workItem.relations | Where-Object {$_.rel -eq "System.LinkTypes.Hierarchy-Reverse" }

if ($relation -eq $null)
{
    Write-Debug "$($workItem.fields."System.WorkItemType") #$($workItem.id) does not have a parent work item"

    return $null
}

$workItemUri = $relation.url

$found = $workItemUri -match "\d+$"

if ($found) 
{
    $parentWorkItemId = $matches[0]

    Write-Verbose "Found work item $parentWorkItemId as a parent of $($workItem.fields."System.WorkItemType") #$($workItem.id)"
    
    return $parentWorkItemId
}

Write-Debug "No parent work item found for $($workItem.fields."System.WorkItemType") #$($workItem.id)"

return $null

}

function Get-WorkItemFound($workItems, [string] $workItemId)
{
foreach ($workItem in $workItems)
{
if ($workItem.id -eq $workItemId)
{
return $true
}
}

return $false

}

function Get-IsSupportedParent($workItem)
{
if ($workItem -eq $null)
{
return $false
}

$workItemType = $workItem.fields."System.WorkItemType"

Write-Debug "Work item type is $workItemType"

if ($workItemType -eq "Product Backlog Item")
{
    return $true
}

if ($workItemType -eq "Bug")
{
    return $true
}

return $false

}

function Get-RelatedWorkItems([string] $collectionUri, [string] $projectName, [string] $buildId)
{
$workItems = @(Get-BuildWorkItems $collectionUri $projectName $buildId)

Write-Verbose "Found $($workItems.length) work items directly related to build $buildId"

foreach ($workItem in $workItems)
{
    $parentWorkItemId = Get-ParentWorkItemId $workItem

    if ($parentWorkItemId -eq $null)
    {
        continue
    }

    if (Get-WorkItemFound $workItems $parentWorkItemId -eq $true)
    {
        Write-Debug "Skipping $parentWorkItemId because it has already been included"

        continue
    }

    $parentWorkItem = Get-WorkItem $collectionUri $parentWorkItemId
    $isParentWorkItemSupported = Get-IsSupportedParent $parentWorkItem

    if ($isParentWorkItemSupported -eq $false)
    {
        Write-Debug "Skipping $($parentWorkItem.fields."System.WorkItemType") #$($parentWorkItem.id) because it is not a PBI or Bug"

        continue
    }
}

if ($workItems.length -gt 0)
{
    Write-Host "Found the following work items related to build $buildId"

    foreach ($workItem in $workItems)
    {
        Write-Host "`t$($workItem.fields."System.WorkItemType") #$($workItem.id)"
    }
}

return $workItems

}

function Set-FieldOperation([string[]] $operations, [string] $buildNumber, [PSObject] $workItem, [string] $fieldName, [boolean] $setReleaseTypeOnly)
{
if ([string]::IsNullOrEmpty($fieldName))
{
return $operations
}

$fieldValue = $workItem.fields.$fieldName
    
if ($setReleaseTypeOnly -eq $true)
{
    Write-Verbose "Skipping $($workItem.fields."System.WorkItemType") #$($workItem.id) $fieldName as it is currently ($fieldVersion) $fieldValue and the field only supports Release versions"

    return $operations
}

$operations += "{`"op`": `"add`", `"path`": `"/fields/$fieldName`", `"value`": `"$buildNumber`"}"
Write-Host "Setting $($workItem.fields."System.WorkItemType") #$($workItem.id) $fieldName $buildNumber"
    
return $operations

}

function Set-BuildVersionInfo
{
$headers = Get-Headers
$buildWorkItems = Get-RelatedWorkItems $CollectionUri $project $BuildId
$integratedInField = “Microsoft.VSTS.Build.IntegrationBuild”

foreach ($workItem in $buildWorkItems)
{
    [string[]] $operations = @()

    $operations = Set-FieldOperation $operations $BuildNumber $workItem $integratedInField $false
    
    $isValidParentWorkItem = Get-IsSupportedParent $workItem
    
    if ($operations.Length -eq 0)
    {
        Write-Host "No changes are being made to $($workItem.fields."System.WorkItemType") #$($workItem.id)"

        continue
    }

    $body = "[" + ($operations -join ",") + "]"
    $restUri = $workItem.url + "?api-version=1.0"
    
    Write-Debug "Updating $($workItem.fields."System.WorkItemType") #$($workItem.id) [$restUri] with the following operations: $body"
    
    try
    {
        $response = Invoke-RestMethod -Uri $restUri -Body $body -ContentType "application/json-patch+json" -headers $headers -Method Patch
        
        Write-Debug (ConvertTo-Json $response -Depth 100)
    }
    catch
    {    
        Write-Error "StatusCode: $_.Exception.Response.StatusCode.value__"
        Write-Error "StatusDescription: $_.Exception.Response.StatusDescription"
        Write-Error $_
    }
}

}

#________________________________________

Run the script

#________________________________________

#$VerbosePreference = “Continue”
#$DebugPreference = “SilentlyContinue”

Set-BuildVersionInfo

Output: Getting build error.

Is there any way to associate build without writing the powershell script in vNext TFS Build definition?:
if there is any tfs parameter like ‘integrated in build’ in vNext, which we can set in the build definition instead of writing ps scriptin, it will be very nice.

Could you please assist with this issue.

Thanks in Advance.

Regards,
Sriram

Windows Service Project Setup in Octopus Server
(David Young) #3

Hi Sriram.

Thanks for getting in touch with us. At Octopus we don’t use the “Integrated in Build” property, and we’re not in a position to advise you on that, but I can tell you that we associate work items with builds by adding links, which is quite well supported in the UI in newer versions of TFS/Azure DevOps, and removes the need for scripting.

This is also the kind of association that Octopus will be able to detect, if you want to use our work item tracking and automatic release notes generation features.

You can link a build with work items in any of the following ways:

  • Edit a pull request in Azure DevOps, and use the Work Items panel to select a work item.
  • Edit a work item in Azure DevOps, and use the Development panel to add a pull request link (before build), or a commit link, or a build link.
  • When you commit code. If you enable the repository setting: Automatically create links for work items mentioned in a commit comment under Project Settings (Repositories), you can include # followed by a valid work item ID in the commit message. For example, git commit -a -m "Fixing bug #42 in the web client" .

These instructions were written with Azure DevOps and TFS 2018 in mind, but I’m hoping they is reasonably applicable to the version you are using.

More information on our approach can be found here: https://octopus.com/docs/deployment-process/issue-tracking/azure-devops

I hope that’s of some use to you. Please let me know if I can assist further with integrating Octopus into your workflow.

Warm regards,

David.

(Sriram Ravi) #4

Hello David,

Thanks for your timely response.

I will try with your approach and let you know if we face any issues.

Regards,
Sriram

(Sriram Ravi) #7

Hello David,

Thanks for the update.

Iam not using Azure Devops.

We are using VSTF version 14 with vnext along with Octopus Deploy.

(David Young) #8

Hi Sriram,

TFS and VSTS were renamed to Azure DevOps, but the approach should be similar.

What Octopus functionality are you trying to get working? If you are trying to see work items in Octopus, I don’t think it will be necessary to copy the association to another field using a script, because Octopus retrieves it the same way as your Get-BuildWorkItemIds() function did.

Can you describe what happens when you use one of the approaches I outlined, and how the result differs from what you were expecting?

Regards,

David.

(Sriram Ravi) #9

Hi David,

Thanks for your help.

Its better if we can have a call, so that, I can explain the issue clearly.

Is it possible to have a zoom call(I will create and share the zoom link), else please let me know the other possibilitie. I will share you the link?

(David Young) #10

Hi Sriram,

Before we can prepare for a more detailed conversation, can you provide an overview of what Octopus feature you are trying to use, what you want it to accomplish, and what problem you are hitting? We can only support TFS to the extent of helping you integrate it with Octopus. Octopus does not require the field Microsoft.VSTS.Build.IntegrationBuild, because it uses the following API directly:

/_apis/build/builds/???/workitems

Regards,

David.

(Sriram Ravi) #11

Sure David,

I will draft in detailed manner and come back today.

Thanks for the help :slight_smile:

(Sriram Ravi) #12

Hi Team,

We have a requirement of associating work items with TFS Builds. Generally, this is possible (or we can say ‘natural’) while using XAML Build Templates in TFS (via Integration Build field of WorkItem. In the below screenshot it was mentioned as ‘Integrated In’). We are currently trying to migrate from XAML Build Templates to vNext version of VSTFS 2015 and further to integrate with Octopus Deploy for CI/CD practices.

We are now half the way in the migration because we are stuck up making this vNext templates to associate TFS WorkItems with their Build numbers. Without doing this step, if we complete this migration, it will be difficult to get associated build numbers for each Work Items delivered in that build.

https://social.msdn.microsoft.com/Forums/getfile/1508855

When we searched MSDN blogs, we got a post (link given below) similar to our requirement but I tried that and ended up with no luck.

https://blogs.msdn.microsoft.com/tfssetup/2016/05/09/build-association-with-work-items-in-vnext/

So, please suggest the ways which can resolve our requirement.

Thank you,

Sriram Ravi

(Sriram Ravi) #13

Hi David,

We posted the same in msdn site today.

Can you have a look and let us know your thought.

Regards,
Sriram

(David Young) #14

Hi Sriram,

This is a bit outside the scope of support we provide, and although I’m happy to take a look, we don’t really have experience in setting that field because we don’t use it. For support with issues internal to TFS, you might need to talk to Microsoft or somebody more specialized in that area.

I did notice the script on that blog post also contains a call to this API: "/_apis/build/builds/" + $BuildId + "/workitems?api-version=2.0"

For that to return anything, you need to have created a linkage in the UI between a work item and something that goes into the build, so the advice I gave on Nov 13 applies here too.

If you are getting some kind of error back from the script, you’ll need to take note of the error and trace what it means.

Sorry I can’t be of more help. Please reach out if there is anything we can do to assist with integrating Octopus, though.

Regards,

David.

(Sriram Ravi) #15

Thanks for the update David

Regards,
Sriram