Set Octopus release notes from TFS query
Octopus.Script exported 02/16/2023 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
Collection
tfsCollection
Project
tfsProject
Personal access token
tfsPat
Personal access token to retrieve from TFS
Octopus API key
octopusAPIKey
Script body
Steps based on this template will execute the following PowerShell script.
Show 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
To use this template in Octopus Deploy, copy the JSON below and paste it into the Library → Step templates → Import dialog.
Show JSON{
"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/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"
}
}Provided under the Apache License version 2.0.