Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Script that downloads custom fields for release notes from Azure DevOps work items and outputs markdown.
param (
[string]$Pat = $env:AzureDevOpsReleaseNotesPat,
[string]$Organization = $env:AzureDevOpsReleaseNotesOrganization,
[string]$Project = $env:AzureDevOpsReleaseNotesProject,
[string]$Query = 'My Queries/Release Notes',
[string]$Header = 'Release Notes',
[switch]$IncludeUrls
)
$ErrorActionPreference = 'Stop'
function Invoke-AzureDevopsGetMethod {
param ($Uri)
$patAsBase64 = [convert]::ToBase64String([System.Text.ASCIIEncoding]::ASCII.GetBytes(":$Pat"))
$headers = @{ Authorization = "Basic $patAsBase64" }
return Invoke-RestMethod -Uri $Uri -Headers $headers
}
function Load-Dlls {
$html2MarkdownDllPath = Resolve-Path '.\Html2Markdown.*\lib\net45\Html2Markdown.dll'
if ($null -eq $html2MarkdownDllPath) {
nuget install Html2Markdown | Out-Null
$html2MarkdownDllPath = Resolve-Path '.\Html2Markdown.*\lib\net45\Html2Markdown.dll'
}
$htmlAgilityPackDllPath = Resolve-Path '.\HtmlAgilityPack.*\lib\Net45\HtmlAgilityPack.dll'
Add-Type -Path $htmlAgilityPackDllPath
Add-Type -Path $html2MarkdownDllPath
}
function Get-WorkItemUrls {
$query = Invoke-AzureDevopsGetMethod -Uri "https://dev.azure.com/$Organization/$Project/_apis/wit/queries/$($Query)?api-version=5.0"
$results = Invoke-AzureDevopsGetMethod -Uri $query._links.wiql.href
return $results.workItems | Select-Object -ExpandProperty url
}
function Get-HtmlInnerText {
param ($Html)
$doc = New-Object HtmlAgilityPack.HtmlDocument
$doc.LoadHtml($Html)
return $doc.DocumentNode.InnerText.Trim().Replace([char]160, [char]32) # Remove strange space char
}
function Get-PossiblyNotThereProperty {
param ($Wi, $Property, $Backup, $Default)
$value = $Wi.fields | Select-Object -ExpandProperty $Property -ErrorAction SilentlyContinue
if ($null -eq $value) {
$value = $value = $Wi.fields | Select-Object -ExpandProperty $Backup -ErrorAction SilentlyContinue
}
if ($null -eq $value) {
$value = $Default
}
return $value.Trim()
}
function Get-PropertiesFromWorkItem {
param ($Wi)
$title = Get-PossiblyNotThereProperty -Wi $Wi -Property Custom.PbiReleaseNotesTitle -Backup Custom.BugReleaseNotesTitle -Default '{Title not set}'
$type = Get-PossiblyNotThereProperty -Wi $Wi -Property Custom.PbiReleaseNotesType -Backup Custom.BugReleaseNotesType -Default 'Other'
$descriptionHtml = Get-PossiblyNotThereProperty -Wi $Wi -Property Custom.PbiReleaseNotesDescription -Backup Custom.BugReleaseNotesDescription '{Description is missing}'
$descriptionHtml = $descriptionHtml -replace '\n', ' ' -replace '<div>', '' -replace '</div>' # Remove new lines and divs
$converter = New-Object Html2Markdown.Converter
$descriptionMarkdown = Get-HtmlInnerText -Html $converter.Convert($descriptionHtml)
return New-Object psobject -Property @{Title = $title; Type = $type; Description = $descriptionMarkdown; Url = "https://dev.azure.com/$Organization/$Project/_workitems/edit/$($Wi.id)" }
}
if ($IncludeUrls) {
$sectionTemplate = @'
### [{0}]({2})
{1}
'@
}
else {
$sectionTemplate = @'
### {0}
{1}
'@
}
$index = 0
Load-Dlls
$workItemUrls = @(Get-WorkItemUrls)
$sectionItems = $workItemUrls | ForEach-Object {
$wi = Invoke-AzureDevopsGetMethod -Uri $_
Write-Progress -Activity "Collecting work items" -Status "Id: $($wi.id)" -PercentComplete ($index / $workItemUrls.Count * 100)
$index++
return Get-PropertiesFromWorkItem -Wi $wi
}
$sections = $sectionItems | Group-Object -Property Type | ForEach-Object {
$subTitle = $_.Name
$items = $_.Group
$subSections = $items | ForEach-Object {
$sectionTemplate -f $_.Title, $_.Description, $_.Url
}
$content = [string]::Join([System.Environment]::NewLine + [System.Environment]::NewLine, $subSections)
return @"
## $subTitle
$content
"@
}
$content = [string]::Join([System.Environment]::NewLine + [System.Environment]::NewLine, $sections)
# Return document
@"
# $Header
$content
"@
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.