From b9ca48feb02d37ef560cdcb9482738c77265bbf1 Mon Sep 17 00:00:00 2001 From: David Refoua Date: Wed, 1 Jul 2026 06:04:57 +0330 Subject: [PATCH] Retarget vendor badges to release status --- .github/workflows/vendor.yml | 127 ++++++++++++++++++++++++++++++++--- 1 file changed, 119 insertions(+), 8 deletions(-) diff --git a/.github/workflows/vendor.yml b/.github/workflows/vendor.yml index 7fcbc1938..34996140f 100644 --- a/.github/workflows/vendor.yml +++ b/.github/workflows/vendor.yml @@ -183,12 +183,112 @@ jobs: return Invoke-RestMethod @params } + function Set-UrlQueryParameter { + param( + [Parameter(Mandatory = $true)] + [string]$Url, + + [Parameter(Mandatory = $true)] + [string]$Name, + + [AllowEmptyString()] + [string]$Value + ) + + $encodedName = [System.Uri]::EscapeDataString($Name) + $encodedValue = [System.Uri]::EscapeDataString($Value) + $pattern = "([?&])$([regex]::Escape($encodedName))=[^&]*" + $match = [regex]::Match($Url, $pattern) + if ($match.Success) { + return $Url.Substring(0, $match.Index) + $match.Groups[1].Value + "$encodedName=$encodedValue" + $Url.Substring($match.Index + $match.Length) + } + + $separator = if ($Url.Contains('?')) { '&' } else { '?' } + return "$Url$separator$encodedName=$encodedValue" + } + + function ConvertTo-ReleaseBuildStatusBadge { + param( + [Parameter(Mandatory = $true)] + [string]$BadgeMarkdown, + + [Parameter(Mandatory = $true)] + [hashtable]$ReleaseInfo + ) + + $match = [regex]::Match($BadgeMarkdown, '\[!\[(?[^\]]*)\]\((?[^)]*)\)\]\((?[^)]*)\)') + if (-not $match.Success) { + return 'โ€”' + } + + $image = $match.Groups['image'].Value + $target = $match.Groups['target'].Value + $tagName = $ReleaseInfo.TagName + $retargeted = $false + + if ($image -match '(?i)^https://dev\.azure\.com/.+/_apis/build/status' -or $target -match '(?i)^https://dev\.azure\.com/.+/_build/') { + if ($image -match '(?i)^https://dev\.azure\.com/.+/_apis/build/status') { + $image = Set-UrlQueryParameter -Url $image -Name 'branchName' -Value $tagName + $retargeted = $true + } + if ($target -match '(?i)^https://dev\.azure\.com/.+/_build/') { + $target = Set-UrlQueryParameter -Url $target -Name 'branchName' -Value $tagName + } + } elseif ($image -match '(?i)^https://github\.com/[^/]+/[^/]+/actions/workflows/[^?#]+/badge\.svg') { + $image = Set-UrlQueryParameter -Url $image -Name 'branch' -Value $tagName + if ($target -match '(?i)^https://github\.com/[^/]+/[^/]+/actions/workflows/[^?#]+') { + $target = Set-UrlQueryParameter -Url $target -Name 'query' -Value "branch:$tagName" + } else { + $target = Set-UrlQueryParameter -Url "$($ReleaseInfo.RepoUrl)/actions" -Name 'query' -Value "branch:$tagName" + } + $retargeted = $true + } elseif ($image -match '(?i)^https://github\.com/[^/]+/[^/]+/workflows/[^/]+/badge\.svg') { + $image = Set-UrlQueryParameter -Url $image -Name 'branch' -Value $tagName + if ($target -match '(?i)^https://github\.com/[^/]+/[^/]+/actions') { + $target = Set-UrlQueryParameter -Url $target -Name 'query' -Value "branch:$tagName" + } else { + $target = Set-UrlQueryParameter -Url "$($ReleaseInfo.RepoUrl)/actions" -Name 'query' -Value "branch:$tagName" + } + $retargeted = $true + } + + if (-not $retargeted) { + return 'โ€”' + } + + $alt = "Build status for release $tagName" + return "[![$alt]($image)]($target)" + } + + function Test-BuildStatusBadgeHasReleaseStatus { + param( + [Parameter(Mandatory = $true)] + [string]$BadgeMarkdown + ) + + $match = [regex]::Match($BadgeMarkdown, '\[!\[(?[^\]]*)\]\((?[^)]*)\)\]\((?[^)]*)\)') + if (-not $match.Success) { + return $false + } + + try { + $response = Invoke-WebRequest -Uri $match.Groups['image'].Value -UseBasicParsing -TimeoutSec 20 + $badgeText = $response.Content.ToLowerInvariant() + return $badgeText -notmatch 'no status|never built|not found|unknown|invalid' + } catch { + Write-Verbose "Unable to verify release build badge $($match.Groups['image'].Value): $($_.Exception.Message)" -Verbose + return $false + } + } + function Get-ReadmeBuildStatusBadge { param( [Parameter(Mandatory = $true)] - [string]$RepoPath + [hashtable]$ReleaseInfo ) + $RepoPath = $ReleaseInfo.RepoPath + try { $readme = Invoke-GitHubApi -Uri "https://api.github.com/repos/$RepoPath/readme" if ([string]::IsNullOrWhiteSpace($readme.content)) { @@ -228,7 +328,10 @@ jobs: } if ($bestScore -gt 0 -and -not [string]::IsNullOrWhiteSpace($bestBadge)) { - return $bestBadge + $releaseBadge = ConvertTo-ReleaseBuildStatusBadge -BadgeMarkdown $bestBadge -ReleaseInfo $ReleaseInfo + if ($releaseBadge -ne 'โ€”' -and (Test-BuildStatusBadgeHasReleaseStatus -BadgeMarkdown $releaseBadge)) { + return $releaseBadge + } } } catch { Write-Verbose "Unable to fetch README build badge for ${RepoPath}: $($_.Exception.Message)" -Verbose @@ -306,7 +409,7 @@ jobs: } else { $releaseBody = $release.body.Trim() if ($releaseBody.Length -gt 1200) { - $releaseBody = $releaseBody.Substring(0, 1200).TrimEnd() + "...`n`n_Release notes truncated; open the release link for the full text._" + $releaseBody = $releaseBody.Substring(0, 1200).TrimEnd() + "...`n`n---`n`n[!TIP]`nRelease notes truncated; [open the release notes]($releaseUrl) for the full text." } $notes += $releaseBody + "`n" @@ -314,7 +417,7 @@ jobs: } if ($omittedReleaseCount -gt 0) { - $notes += "`n...and $omittedReleaseCount more releases. Open the compare link for the full range.`n" + $notes += "`n...and $omittedReleaseCount more releases. [Compare changes]($($ReleaseInfo.CompareUrl)) for the full range.`n" } return $notes.Trim() @@ -342,7 +445,9 @@ jobs: [AllowEmptyString()] [string]$Markdown, - [int]$MaxLength = 2500 + [int]$MaxLength = 2500, + + [string]$FullTextUrl ) $text = if ([string]::IsNullOrWhiteSpace($Markdown)) { @@ -352,7 +457,13 @@ jobs: } if ($text.Length -gt $MaxLength) { - $text = $text.Substring(0, $MaxLength).TrimEnd() + "...`n`n_Release notes truncated; open the release links for the full text._" + $linkedText = if ([string]::IsNullOrWhiteSpace($FullTextUrl)) { + "Release notes truncated." + } else { + "Release notes truncated; [open the release notes]($FullTextUrl) for the full text." + } + + $text = $text.Substring(0, $MaxLength).TrimEnd() + "...`n`n---`n`n[!TIP]`n$linkedText" } return (($text -split "`r?`n") | ForEach-Object { "> $_" }) -join "`n" @@ -403,7 +514,7 @@ jobs: if ($null -ne $releaseInfo) { $repoUrl = $releaseInfo.ReleasesUrl $releaseUrl = $releaseInfo.ReleaseUrl - $statusBadge = Get-ReadmeBuildStatusBadge -RepoPath $releaseInfo.RepoPath + $statusBadge = Get-ReadmeBuildStatusBadge -ReleaseInfo $releaseInfo } else { $repoUrl = ($repoUrl = $s.Url.Replace("/archive/", "/releases/")).Substring(0, $repoUrl.IndexOf("/releases/")) + "/releases" $releaseUrl = $repoUrl @@ -444,7 +555,7 @@ jobs: $changelogSection += "
`n" $changelogSection += "$($s.name)`n`n" $changelogSection += "[Release notes]($($releaseInfo.ReleaseUrl)) ยท [Compare changes]($($releaseInfo.CompareUrl))`n`n" - $changelogSection += (ConvertTo-BlockQuote -Markdown $releaseNotes -MaxLength 6000) + "`n`n" + $changelogSection += (ConvertTo-BlockQuote -Markdown $releaseNotes -MaxLength 6000 -FullTextUrl $releaseInfo.ReleaseUrl) + "`n`n" $changelogSection += "
`n`n" }