Set Octopus release notes from TFS query

Octopus.Script exported 2023-02-16 by harrisonmeister belongs to ‘Octopus’ category.

Sets Octopus release notes from TFS query

Parameters

When steps based on the template are included in a project’s deployment process, the parameters below can be set.

TFS Instance

tfsInstance =

null

Collection

tfsCollection =

null

Project

tfsProject =

null

Personal access token

tfsPat =

Personal access token to retrieve from TFS

Octopus API key

octopusAPIKey =

null

Script body

Steps based on this template will execute the following PowerShell script.

#TFS
$instance = $OctopusParameters["tfsInstance"]
$collection = $OctopusParameters["tfsCollection"]
$project = $OctopusParameters["tfsProject"]
$PAT = $OctopusParameters["tfsPat"]
$pathquery = $OctopusParameters["tfsPathQuery"]

#Octopus
$octopusAPIKey = $OctopusParameters['octopusAPIKey']
$baseUri = $OctopusParameters['#{if Octopus.Web.ServerUri}Octopus.Web.ServerUri#{else}Octopus.Web.BaseUrl#{/if}']
$octopusProjectId = $OctopusParameters['Octopus.Project.Id']
$thisReleaseNumber = $OctopusParameters['Octopus.Release.Number']

write-host "Instance: $($instance)"
write-host "collection: $($collection)"
write-host "project: $($project)"
write-host "baseUri: $($baseUri)"
write-host "projectId: $($projectId)"
write-host "thisReleaseNumber: $($thisReleaseNumber)"
write-host "TFS path: $($pathquery)"

#Create HEADERS
$bytes = [System.Text.Encoding]::ASCII.GetBytes($PAT)
$base64 = [System.Convert]::ToBase64String($bytes)
$basicAuthValue = "Basic $base64"
$headers = @{ }
$headers.Add("Authorization", $basicAuthValue)
$headers.Add("Accept","application/json")
$headers.Add("Content-Type","application/json")

$reqheaders = @{"X-Octopus-ApiKey" = $octopusAPIKey }
$putReqHeaders = @{"X-HTTP-Method-Override" = "PUT"; "X-Octopus-ApiKey" = $octopusAPIKey }

function Test-SpacesApi {
	Write-Verbose "Checking API compatibility";
	$rootDocument = Invoke-WebRequest "$baseUri/api" -Headers $reqheaders -UseBasicParsing | ConvertFrom-Json;
    if($rootDocument.Links -ne $null -and $rootDocument.Links.Spaces -ne $null) {
    	Write-Verbose "Spaces API found"
    	return $true;
    }
    Write-Verbose "Pre-spaces API found"
    return $false;
}

if(Test-SpacesApi) {
	$spaceId = $OctopusParameters['Octopus.Space.Id'];
    if([string]::IsNullOrWhiteSpace($spaceId)) {
        throw "This step needs to be run in a context that provides a value for the 'Octopus.Space.Id' system variable. In this case, we received a blank value, which isn't expected - please reach out to our support team at https://help.octopus.com if you encounter this error.";
    }
	$baseApiUrl = "/api/$spaceId" ;
} else {
	$baseApiUrl = "/api" ;
}

# Get the current release
$releaseUri = "$baseUri$baseApiUrl/projects/$octopusProjectId/releases/$thisReleaseNumber"
write-host "Release uri $($releaseUri)"
try {
    $currentRelease = Invoke-RestMethod $releaseUri -Headers $reqheaders -UseBasicParsing 
} catch {
    if ($_.Exception.Response.StatusCode.Value__ -ne 404) {
        $result = $_.Exception.Response.GetResponseStream()
        $reader = New-Object System.Io.StreamReader($result);
        $responseBody = $reader.ReadToEnd();
        throw "Error occurred: $responseBody"
    }
}

if(![string]::IsNullOrWhiteSpace($currentRelease.ReleaseNotes)){
	write-host "Release notes already filled in. $($currentRelease.ReleaseNotes)"
    Set-OctopusVariable -name "ReleaseNotes" -value $releaseNotes
	exit;
}


#Get projectid
$url = "http://$($instance)/tfs/$($collection)/$($projectId)/_apis/projects/$($project)?&includeCapabilities=false&includeHistory=false&api-version=2.2"
write-host "Invoking url= $($url)"
$projectresponse = Invoke-RestMethod $url -Method GET -Headers $headers

$projectid = $projectresponse.id
write-host "projectid $($projectid)"

#Get the ID of the query to execute
$queryResult = Invoke-RestMethod "http://$($instance)/tfs/$($collection)/$($projectId)/_apis/$($pathquery)?$depth=1&api-version=2.2" -Method GET -Headers $headers
write-host "queryResult $($queryResult)"

#https://{instance}/DefaultCollection/[{project}/]_apis/wit/wiql/{id}?api-version={version}
$queryResult = Invoke-RestMethod "http://$($instance)/tfs/$($collection)/$($projectId)/_apis/wit/wiql/$($queryResult.Id)?api-version=2.2" -Method GET -Headers $headers

Write-Host "Found $($queryResult.workItems.Count) number of workitems for query: ReleaseNotes$($releaseTag)"

$releaseNotes = "**Work Items:**"


if($queryResult.workItems.Count -eq 0)
{
	Write-Host "No work items for release"
	$releaseNotes = "`n no new work items"
}
else
{
	#Create a list of ids
	$ids = [string]::Join("%2C", ($queryResult.workItems.id))

	#Get all the work items
	$workItems = Invoke-RestMethod  "http://$($instance)/tfs/$($collection)/_apis/wit/workItems?ids=$($ids)&fields=System.Title" -Method GET -Headers $headers

	foreach($workItem in $workItems.value)
	{
		#Add line for each work item
		$releaseNotes = $releaseNotes + "`n* [$($workItem.id)] (http://$($instance)/tfs/$($collection)/9981e67f-b27c-4628-b5cf-fba1d327aa07/_workitems/edit/$($workItem.id)) : $($workItem.fields.'System.Title')"
	}

}



# Update the release notes for the current release
$currentRelease.ReleaseNotes = $releaseNotes 
write-host "Release notes $($currentRelease.ReleaseNotes)"
Write-Host "Updating release notes for $thisReleaseNumber`:`n`n"
try {
    $releaseUri = "$baseUri$baseApiUrl/releases/$($currentRelease.Id)"
    write-host "Release uri $($releaseUri)"
    $currentReleaseBody = $currentRelease | ConvertTo-Json
    write-host "Current release body $($currentReleaseBody)"
    $result = Invoke-RestMethod $releaseUri -Method Post -Headers $putReqHeaders -Body $currentReleaseBody -UseBasicParsing
	write-host "result $($result)"
} catch {
    $result = $_.Exception.Response.GetResponseStream()
    $reader = New-Object System.Io.StreamReader($result);
    $responseBody = $reader.ReadToEnd();
    Write-Host "error $($responseBody)"
    throw "Error occurred: $responseBody"
}

Set-OctopusVariable -name "ReleaseNotes" -value $releaseNotes

Provided under the Apache License version 2.0.

Report an issue

To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.

{
  "Id": "c6faf6be-296c-44ee-abf6-ce87331b2557",
  "Name": "Set Octopus release notes from TFS query",
  "Description": "Sets Octopus release notes from TFS query",
  "Version": 6,
  "ExportedAt": "2023-02-16T15:38:44.043Z",
  "ActionType": "Octopus.Script",
  "Author": "harrisonmeister",
  "Parameters": [
    {
      "Id": "08ce7e1a-7251-4b9f-bd07-f63cbfbc7f20",
      "Name": "tfsInstance",
      "Label": "TFS Instance",
      "HelpText": null,
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      },
      "Links": {}
    },
    {
      "Id": "85eff544-5275-420f-81d8-cf01ea42d903",
      "Name": "tfsCollection",
      "Label": "Collection",
      "HelpText": null,
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      },
      "Links": {}
    },
    {
      "Id": "a423bf6c-52be-4994-8bab-2d64f05f167f",
      "Name": "tfsProject",
      "Label": "Project",
      "HelpText": null,
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      },
      "Links": {}
    },
    {
      "Id": "1db49bf8-d38a-4ae7-b858-693c584272d5",
      "Name": "tfsPat",
      "Label": "Personal access token",
      "HelpText": "Personal access token to retrieve from TFS",
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      },
      "Links": {}
    },
    {
      "Id": "0427c73a-2925-4c55-a2ce-5584cd24b1dd",
      "Name": "octopusAPIKey",
      "Label": "Octopus API key",
      "HelpText": null,
      "DefaultValue": "",
      "DisplaySettings": {
        "Octopus.ControlType": "SingleLineText"
      },
      "Links": {}
    }
  ],
  "Properties": {
    "Octopus.Action.Script.ScriptSource": "Inline",
    "Octopus.Action.Script.Syntax": "PowerShell",
    "Octopus.Action.Script.ScriptBody": "#TFS\n$instance = $OctopusParameters[\"tfsInstance\"]\n$collection = $OctopusParameters[\"tfsCollection\"]\n$project = $OctopusParameters[\"tfsProject\"]\n$PAT = $OctopusParameters[\"tfsPat\"]\n$pathquery = $OctopusParameters[\"tfsPathQuery\"]\n\n#Octopus\n$octopusAPIKey = $OctopusParameters['octopusAPIKey']\n$baseUri = $OctopusParameters['#{if Octopus.Web.ServerUri}Octopus.Web.ServerUri#{else}Octopus.Web.BaseUrl#{/if}']\n$octopusProjectId = $OctopusParameters['Octopus.Project.Id']\n$thisReleaseNumber = $OctopusParameters['Octopus.Release.Number']\n\nwrite-host \"Instance: $($instance)\"\nwrite-host \"collection: $($collection)\"\nwrite-host \"project: $($project)\"\nwrite-host \"baseUri: $($baseUri)\"\nwrite-host \"projectId: $($projectId)\"\nwrite-host \"thisReleaseNumber: $($thisReleaseNumber)\"\nwrite-host \"TFS path: $($pathquery)\"\n\n#Create HEADERS\n$bytes = [System.Text.Encoding]::ASCII.GetBytes($PAT)\n$base64 = [System.Convert]::ToBase64String($bytes)\n$basicAuthValue = \"Basic $base64\"\n$headers = @{ }\n$headers.Add(\"Authorization\", $basicAuthValue)\n$headers.Add(\"Accept\",\"application/json\")\n$headers.Add(\"Content-Type\",\"application/json\")\n\n$reqheaders = @{\"X-Octopus-ApiKey\" = $octopusAPIKey }\n$putReqHeaders = @{\"X-HTTP-Method-Override\" = \"PUT\"; \"X-Octopus-ApiKey\" = $octopusAPIKey }\n\nfunction Test-SpacesApi {\n\tWrite-Verbose \"Checking API compatibility\";\n\t$rootDocument = Invoke-WebRequest \"$baseUri/api\" -Headers $reqheaders -UseBasicParsing | ConvertFrom-Json;\n    if($rootDocument.Links -ne $null -and $rootDocument.Links.Spaces -ne $null) {\n    \tWrite-Verbose \"Spaces API found\"\n    \treturn $true;\n    }\n    Write-Verbose \"Pre-spaces API found\"\n    return $false;\n}\n\nif(Test-SpacesApi) {\n\t$spaceId = $OctopusParameters['Octopus.Space.Id'];\n    if([string]::IsNullOrWhiteSpace($spaceId)) {\n        throw \"This step needs to be run in a context that provides a value for the 'Octopus.Space.Id' system variable. In this case, we received a blank value, which isn't expected - please reach out to our support team at https://help.octopus.com if you encounter this error.\";\n    }\n\t$baseApiUrl = \"/api/$spaceId\" ;\n} else {\n\t$baseApiUrl = \"/api\" ;\n}\n\n# Get the current release\n$releaseUri = \"$baseUri$baseApiUrl/projects/$octopusProjectId/releases/$thisReleaseNumber\"\nwrite-host \"Release uri $($releaseUri)\"\ntry {\n    $currentRelease = Invoke-RestMethod $releaseUri -Headers $reqheaders -UseBasicParsing \n} catch {\n    if ($_.Exception.Response.StatusCode.Value__ -ne 404) {\n        $result = $_.Exception.Response.GetResponseStream()\n        $reader = New-Object System.Io.StreamReader($result);\n        $responseBody = $reader.ReadToEnd();\n        throw \"Error occurred: $responseBody\"\n    }\n}\n\nif(![string]::IsNullOrWhiteSpace($currentRelease.ReleaseNotes)){\n\twrite-host \"Release notes already filled in. $($currentRelease.ReleaseNotes)\"\n    Set-OctopusVariable -name \"ReleaseNotes\" -value $releaseNotes\n\texit;\n}\n\n\n#Get projectid\n$url = \"http://$($instance)/tfs/$($collection)/$($projectId)/_apis/projects/$($project)?&includeCapabilities=false&includeHistory=false&api-version=2.2\"\nwrite-host \"Invoking url= $($url)\"\n$projectresponse = Invoke-RestMethod $url -Method GET -Headers $headers\n\n$projectid = $projectresponse.id\nwrite-host \"projectid $($projectid)\"\n\n#Get the ID of the query to execute\n$queryResult = Invoke-RestMethod \"http://$($instance)/tfs/$($collection)/$($projectId)/_apis/$($pathquery)?$depth=1&api-version=2.2\" -Method GET -Headers $headers\nwrite-host \"queryResult $($queryResult)\"\n\n#https://{instance}/DefaultCollection/[{project}/]_apis/wit/wiql/{id}?api-version={version}\n$queryResult = Invoke-RestMethod \"http://$($instance)/tfs/$($collection)/$($projectId)/_apis/wit/wiql/$($queryResult.Id)?api-version=2.2\" -Method GET -Headers $headers\n\nWrite-Host \"Found $($queryResult.workItems.Count) number of workitems for query: ReleaseNotes$($releaseTag)\"\n\n$releaseNotes = \"**Work Items:**\"\n\n\nif($queryResult.workItems.Count -eq 0)\n{\n\tWrite-Host \"No work items for release\"\n\t$releaseNotes = \"`n no new work items\"\n}\nelse\n{\n\t#Create a list of ids\n\t$ids = [string]::Join(\"%2C\", ($queryResult.workItems.id))\n\n\t#Get all the work items\n\t$workItems = Invoke-RestMethod  \"http://$($instance)/tfs/$($collection)/_apis/wit/workItems?ids=$($ids)&fields=System.Title\" -Method GET -Headers $headers\n\n\tforeach($workItem in $workItems.value)\n\t{\n\t\t#Add line for each work item\n\t\t$releaseNotes = $releaseNotes + \"`n* [$($workItem.id)] (http://$($instance)/tfs/$($collection)/9981e67f-b27c-4628-b5cf-fba1d327aa07/_workitems/edit/$($workItem.id)) : $($workItem.fields.'System.Title')\"\n\t}\n\n}\n\n\n\n# Update the release notes for the current release\n$currentRelease.ReleaseNotes = $releaseNotes \nwrite-host \"Release notes $($currentRelease.ReleaseNotes)\"\nWrite-Host \"Updating release notes for $thisReleaseNumber`:`n`n\"\ntry {\n    $releaseUri = \"$baseUri$baseApiUrl/releases/$($currentRelease.Id)\"\n    write-host \"Release uri $($releaseUri)\"\n    $currentReleaseBody = $currentRelease | ConvertTo-Json\n    write-host \"Current release body $($currentReleaseBody)\"\n    $result = Invoke-RestMethod $releaseUri -Method Post -Headers $putReqHeaders -Body $currentReleaseBody -UseBasicParsing\n\twrite-host \"result $($result)\"\n} catch {\n    $result = $_.Exception.Response.GetResponseStream()\n    $reader = New-Object System.Io.StreamReader($result);\n    $responseBody = $reader.ReadToEnd();\n    Write-Host \"error $($responseBody)\"\n    throw \"Error occurred: $responseBody\"\n}\n\nSet-OctopusVariable -name \"ReleaseNotes\" -value $releaseNotes\n"
  },
  "Category": "Octopus",
  "HistoryUrl": "https://github.com/OctopusDeploy/Library/commits/master/step-templates//opt/buildagent/work/75443764cd38076d/step-templates/octopus-set-Octopus-releaese-notes-from-TFS-query.json",
  "Website": "/step-templates/c6faf6be-296c-44ee-abf6-ce87331b2557",
  "Logo": "iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAMAAACahl6sAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAC1QTFRFT6Tl////L5Pg8vj9Y67omsvwPJrisdfzfbzs5fL7y+T32Ov5isLucLXqvt31CJPHWwAABMJJREFUeNrs3deW4jAMAFDF3U75/89dlp0ZhiU4blJEjvQ8hYubLJsA00UCBCIQgQhEIAIRiEAEIhCBCEQgAhGIQAQiEIEIhD8kJm+t+QprfdKfB9HbYpx6CWfspj8HMi+gMgHL/AmQA8W3JTKH+ALFvzCeL0RbpyoCPE9IJeNOSQwh5Z3qd6yRGWQ2qi2cZQWxqj1WzQYSjeoJmJlAklOd4VlArOqPhQEkqBERToeMcfRJBkC0Uep8CfBpjz4JsHJ0zF3dkEWNje0kiB/sUC6eApndaIiCMyAa1PiwJ0AWhRGJHJJQHG2dC7h1rNbO1QOxSA7lNCkkKrQIpJCAB1GREILYIC1NAiwbpKFJgGWDNExcwGstfExcZBCHC6nOglshHtmhViLIig1RNBCN7qjtW8C0Z1UvJcC1Z9XmwMBzzvobmgAyEzgq91dtEEsBsQSQQAFZCSBAATEEEApHZbrVBIkkEIUPSVeB+KtALA0kXQUSrwKZBCIQBnk8Y4i5CsReBeKvkqLM+BCSDWJlrZFvGk9SRTHshkgjZCGAaArIxm3H3grhVzFlW2msfl1ca79UJ1bofYvsDHHlNdTZnlh5MghuPd5NdBDUNZHyCkfktIh03XzALGRPlBDPac7qgWjHZzWcmF5zmmkhidMQ6boKiDXcDTUEaylZqCGJ0Vjvu/fLJtHqhSANEvqb2OYqkOUqEHuVMbJcZdZCGiPhKhC4yjqiIjEE7XThMp8fAWII3mY3kUIQD+AMKQTzPiBhgQ63HlT/KSvgtoi0dq5mCPah1UIE0eh3sT0NhOByvKeAkFzi8PgQomumFhsyOxpIzZN4gLOj5plVwNpR0b2AuePWKBEHQu24pSsJA+LVCeHHQxZ1SiyDIdqok8IOhSSnTottHEQTdyt4ettAj4KkzA4dMikk2Dht2S5ptm1vswnPDxn0YyDZ5oDM3iToo2T5voWaYe+Q+vdjH80QyAzZhCgcDtLMI1Tmtz9w++XHgziHQHJJu/OZ3bs9Xn8gQ72NcP3dKqEfkp10F51xhoIi2I91R+LurXV/5q7pH+wx061CzO16oSQleMyr8fXvwMA0Pro8432DPD/ySx8XrHfSuDAM8n6UhnjQabaiXf5Bq/lREHvEeNtn1rJ08+C/uXkQZHeguxAPC3UvtcJYUogLzZX5hhZZvS6onG5lxXtzWGaygwb79vT/IXhdlNibwlKYOR6T8xjI7W8n+xV7T+GH4tMzWwR+lZhRkJYSsC0thpmCYqyngOz3rN2FLBZ2wZflBCggUHF0Vnp88JKienzIXLSEZCZqU7IKr/gQW9yx3pzV7Y9kvWZWTRRIqDmTtRUnU7b2lLcTYmoqHqnmiO1poER0SPkAeZMAZxaJx0Y3TCdAclsIqDz03ALcyxfTCZBsthoGXWmigGyVhWPLFJJfuuKQWycoEFdXbH4dJJoJxNR1eD/kshz6yn48cF8yW8sFoitflB1w6Q8n+/15Za7oA17/pYNmYgP5fmWm8L1NOHPWgK8kuFew1/JXtOA0yJCv7ah7X8ObUuT5kObU30+fDZm8+zqP+HTIpK0xQ796b5Kv2hSIQAQiEIEIRCACEYhABCIQgQhEIAIRiEAEIpBf8UeAAQAEjtYmlDTcCgAAAABJRU5ErkJggg==",
  "$Meta": {
    "Type": "ActionTemplate"
  }
}

History

Page updated on Thursday, February 16, 2023