Skip to content

Instantly share code, notes, and snippets.

@johanclasson
Last active November 9, 2019 19:31
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save johanclasson/027f49ef0bf9e924927554b4926266bd to your computer and use it in GitHub Desktop.
Save johanclasson/027f49ef0bf9e924927554b4926266bd to your computer and use it in GitHub Desktop.
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