Skip to content

Commit 5d37fd7

Browse files
authored
Merge pull request #3098 from DRSDavidSoft/codex/vendor-pr-body
Improve vendor update PR details
2 parents 7e06c48 + 38931d9 commit 5d37fd7

1 file changed

Lines changed: 252 additions & 33 deletions

File tree

.github/workflows/vendor.yml

Lines changed: 252 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -77,16 +77,219 @@ jobs:
7777
# Source utility functions
7878
. scripts/utils.ps1
7979
80+
function Add-GitHubEnvMultiline {
81+
param(
82+
[Parameter(Mandatory = $true)]
83+
[string]$Name,
84+
85+
[AllowEmptyString()]
86+
[string]$Value
87+
)
88+
89+
$delimiter = [System.Guid]::NewGuid().ToString('N')
90+
Add-Content -Path $env:GITHUB_ENV -Value "$Name<<$delimiter"
91+
Add-Content -Path $env:GITHUB_ENV -Value $Value
92+
Add-Content -Path $env:GITHUB_ENV -Value $delimiter
93+
}
94+
95+
function Get-GitHubReleaseInfo {
96+
param(
97+
[Parameter(Mandatory = $true)]
98+
[string]$DownloadUrl,
99+
100+
[Parameter(Mandatory = $true)]
101+
[string]$OldVersion,
102+
103+
[Parameter(Mandatory = $true)]
104+
[string]$NewVersion
105+
)
106+
107+
$uri = [uri]$DownloadUrl
108+
$segments = $uri.AbsolutePath.Trim('/').Split('/')
109+
if ($uri.Host -ne 'github.com' -or $segments.Count -lt 2) {
110+
return $null
111+
}
112+
113+
$owner = $segments[0]
114+
$repo = $segments[1]
115+
$repoPath = "$owner/$repo"
116+
$repoUrl = "https://github.com/$repoPath"
117+
$tagName = $null
118+
119+
for ($i = 0; $i -lt $segments.Count; $i++) {
120+
if ($segments[$i] -eq 'download' -and $i + 1 -lt $segments.Count) {
121+
$tagName = $segments[$i + 1]
122+
break
123+
}
124+
125+
if ($segments[$i] -eq 'archive' -and $i + 1 -lt $segments.Count) {
126+
$tagName = $segments[$i + 1] -replace '\.tar\.gz$', '' -replace '\.zip$', ''
127+
break
128+
}
129+
}
130+
131+
if ([string]::IsNullOrWhiteSpace($tagName)) {
132+
$tagName = "v$NewVersion"
133+
}
134+
135+
if ($tagName -match [regex]::Escape($NewVersion)) {
136+
$oldTagName = $tagName -replace [regex]::Escape($NewVersion), $OldVersion
137+
} else {
138+
$oldTagName = "v$OldVersion"
139+
}
140+
141+
return @{
142+
RepoPath = $repoPath
143+
RepoUrl = $repoUrl
144+
ReleasesUrl = "$repoUrl/releases"
145+
ReleaseUrl = "$repoUrl/releases/tag/$tagName"
146+
CompareUrl = "$repoUrl/compare/$oldTagName...$tagName"
147+
OldTagName = $oldTagName
148+
TagName = $tagName
149+
}
150+
}
151+
152+
function Invoke-GitHubApi {
153+
param(
154+
[Parameter(Mandatory = $true)]
155+
[string]$Uri,
156+
157+
[string]$Method = 'Get',
158+
159+
[object]$Body
160+
)
161+
162+
$headers = @{
163+
Accept = 'application/vnd.github+json'
164+
'X-GitHub-Api-Version' = '2022-11-28'
165+
}
166+
167+
if (-not [string]::IsNullOrWhiteSpace($env:GITHUB_TOKEN)) {
168+
$headers.Authorization = "Bearer $env:GITHUB_TOKEN"
169+
}
170+
171+
$params = @{
172+
Uri = $Uri
173+
Method = $Method
174+
Headers = $headers
175+
ErrorAction = 'Stop'
176+
}
177+
178+
if ($null -ne $Body) {
179+
$params.Body = ($Body | ConvertTo-Json -Depth 8)
180+
$params.ContentType = 'application/json'
181+
}
182+
183+
return Invoke-RestMethod @params
184+
}
185+
186+
function Get-ReleaseNotes {
187+
param(
188+
[Parameter(Mandatory = $true)]
189+
[hashtable]$ReleaseInfo
190+
)
191+
192+
try {
193+
$release = Invoke-GitHubApi -Uri "https://api.github.com/repos/$($ReleaseInfo.RepoPath)/releases/tags/$($ReleaseInfo.TagName)"
194+
if (-not [string]::IsNullOrWhiteSpace($release.body)) {
195+
return $release.body.Trim()
196+
}
197+
} catch {
198+
Write-Verbose "Unable to fetch release notes for $($ReleaseInfo.RepoPath) $($ReleaseInfo.TagName): $($_.Exception.Message)" -Verbose
199+
}
200+
201+
try {
202+
$comparison = Invoke-GitHubApi -Uri "https://api.github.com/repos/$($ReleaseInfo.RepoPath)/compare/$($ReleaseInfo.OldTagName)...$($ReleaseInfo.TagName)"
203+
if ($comparison.commits.Count -gt 0) {
204+
$summary = "No release notes were found. Recent commits in the compare range:`n"
205+
foreach ($commit in ($comparison.commits | Select-Object -First 12)) {
206+
$subject = ($commit.commit.message -split "`r?`n" | Select-Object -First 1)
207+
$summary += "- [$($commit.sha.Substring(0, 7))]($($commit.html_url)) $subject`n"
208+
}
209+
210+
if ($comparison.commits.Count -gt 12) {
211+
$summary += "- ...and $($comparison.commits.Count - 12) more commits.`n"
212+
}
213+
214+
return $summary.Trim()
215+
}
216+
} catch {
217+
Write-Verbose "Unable to fetch compare details for $($ReleaseInfo.RepoPath) $($ReleaseInfo.OldTagName)...$($ReleaseInfo.TagName): $($_.Exception.Message)" -Verbose
218+
}
219+
220+
return "No release notes were found. See the release and compare links for details."
221+
}
222+
223+
function ConvertTo-BlockQuote {
224+
param(
225+
[AllowEmptyString()]
226+
[string]$Markdown,
227+
228+
[int]$MaxLength = 2500
229+
)
230+
231+
$text = if ([string]::IsNullOrWhiteSpace($Markdown)) {
232+
"No release notes were found."
233+
} else {
234+
$Markdown.Trim()
235+
}
236+
237+
if ($text.Length -gt $MaxLength) {
238+
$text = $text.Substring(0, $MaxLength).TrimEnd() + "`n`n_Release notes truncated; open the release link for the full text._"
239+
}
240+
241+
return (($text -split "`r?`n") | ForEach-Object { "> $_" }) -join "`n"
242+
}
243+
244+
function Get-VersionFromSources {
245+
param(
246+
[object[]]$Sources,
247+
248+
[Parameter(Mandatory = $true)]
249+
[string]$Name
250+
)
251+
252+
return ($Sources | Where-Object { $_.name -eq $Name } | Select-Object -First 1).version
253+
}
254+
255+
$previousPrVersion = $null
256+
$fetchPreviousPrBranchOutput = & git fetch origin update-vendor 2>&1
257+
if ($LASTEXITCODE -eq 0) {
258+
$previousPrSourcesJson = & git show FETCH_HEAD:vendor/sources.json 2>$null
259+
if ($LASTEXITCODE -eq 0 -and -not [string]::IsNullOrWhiteSpace($previousPrSourcesJson)) {
260+
$previousPrVersion = $previousPrSourcesJson | ConvertFrom-Json
261+
}
262+
} else {
263+
Write-Verbose "No existing update-vendor branch was found to use as notification baseline: $fetchPreviousPrBranchOutput" -Verbose
264+
}
265+
266+
$notificationBaselineVersion = if ($null -ne $previousPrVersion) { $previousPrVersion } else { $currentVersion }
80267
$listUpdated = ""
81268
$updateMessage = "| Name | Old Version | New Version |`n| :--- | :---: | :---: |`n"
82-
$majorUpdates = @()
269+
$changelogSection = ""
270+
$hasBreakingChanges = $false
271+
$hasMinorOrMajorSinceLastPrUpdate = $false
272+
$usedEmojis = [System.Collections.Generic.List[string]]::new()
273+
$emojiDescriptions = [ordered]@{
274+
'🔥' = 'Major version update.'
275+
'🚀' = 'Minor version update.'
276+
'⬆️' = 'Patch version update.'
277+
'🔄' = 'Version change could not be classified.'
278+
}
83279
$singleDepName = ""
84280
$singleDepOldVersion = ""
85281
$singleDepNewVersion = ""
86282
foreach ($s in $newVersion) {
87283
$oldVersion = ($currentVersion | Where-Object {$_.name -eq $s.name}).version
88284
if ($s.version -ne $oldVersion) {
89-
$repoUrl = ($repoUrl = $s.Url.Replace("/archive/", "/releases/")).Substring(0, $repoUrl.IndexOf("/releases/")) + "/releases"
285+
$releaseInfo = Get-GitHubReleaseInfo -DownloadUrl $s.url -OldVersion $oldVersion -NewVersion $s.version
286+
if ($null -ne $releaseInfo) {
287+
$repoUrl = $releaseInfo.ReleasesUrl
288+
$releaseUrl = $releaseInfo.ReleaseUrl
289+
} else {
290+
$repoUrl = ($repoUrl = $s.Url.Replace("/archive/", "/releases/")).Substring(0, $repoUrl.IndexOf("/releases/")) + "/releases"
291+
$releaseUrl = $repoUrl
292+
}
90293
91294
# Store single dependency info for messages (only if this is the only update)
92295
if ($count -eq 1) {
@@ -101,20 +304,31 @@ jobs:
101304
$emoji = $result.Emoji
102305
$isMajor = $result.IsMajor
103306
104-
# Track major updates for changelog section
105-
if ($isMajor) {
106-
$compareUrl = "$repoUrl/compare/v$oldVersion...v$($s.version)"
107-
$majorUpdates += @{
108-
name = $s.name
109-
oldVersion = $oldVersion
110-
newVersion = $s.version
111-
compareUrl = $compareUrl
112-
repoUrl = $repoUrl
307+
if (-not $usedEmojis.Contains($emoji)) {
308+
$usedEmojis.Add($emoji)
309+
}
310+
311+
if ($changeType -in @('major', 'downgrade', 'unknown')) {
312+
$hasBreakingChanges = $true
313+
}
314+
315+
$previousPrUpdateVersion = Get-VersionFromSources -Sources $notificationBaselineVersion -Name $s.name
316+
if (-not [string]::IsNullOrWhiteSpace($previousPrUpdateVersion) -and $previousPrUpdateVersion -ne $s.version) {
317+
$notificationChangeType = Get-VersionChangeType -OldVersion $previousPrUpdateVersion -NewVersion $s.version
318+
if ($notificationChangeType.ChangeType -in @('major', 'minor')) {
319+
$hasMinorOrMajorSinceLastPrUpdate = $true
113320
}
114321
}
115322
323+
if ($null -ne $releaseInfo) {
324+
$releaseNotes = Get-ReleaseNotes -ReleaseInfo $releaseInfo
325+
$changelogSection += "### $($s.name) $oldVersion → $($s.version)`n`n"
326+
$changelogSection += "[Release notes]($($releaseInfo.ReleaseUrl)) · [Compare changes]($($releaseInfo.CompareUrl))`n`n"
327+
$changelogSection += (ConvertTo-BlockQuote -Markdown $releaseNotes) + "`n`n"
328+
}
329+
116330
$listUpdated += "$($s.name) v$($s.version), "
117-
$updateMessage += "| $emoji **[$($s.name)]($repoUrl)** | ``$oldVersion`` | **``$($s.version)``** |`n"
331+
$updateMessage += "| $emoji **[$($s.name)]($repoUrl)** | ``$oldVersion`` | **[``$($s.version)``]($releaseUrl)** |`n"
118332
}
119333
}
120334
@@ -140,31 +354,33 @@ jobs:
140354
Set-GHVariable -Name SINGLE_DEP_OLD_VERSION -Value $singleDepOldVersion
141355
Set-GHVariable -Name SINGLE_DEP_NEW_VERSION -Value $singleDepNewVersion
142356
143-
# Write multiline UPDATE_MESSAGE to GITHUB_ENV
144-
## echo "UPDATE_MESSAGE<<EOF`n$updateMessage`nEOF" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8
145-
Add-Content -Path $env:GITHUB_ENV -Value "UPDATE_MESSAGE<<EOF"
146-
Add-Content -Path $env:GITHUB_ENV -Value $updateMessage
147-
Add-Content -Path $env:GITHUB_ENV -Value "EOF"
148-
149-
# Generate major updates changelog section and export
150-
if ($majorUpdates.Count -gt 0) {
151-
$changelogSection = "`n<details>`n<summary>🔥 Major version updates - View changelog</summary>`n`n"
152-
foreach ($update in $majorUpdates) {
153-
$changelogSection += "### [$($update.name)]($($update.repoUrl))`n"
154-
$changelogSection += "**$($update.oldVersion)** → **$($update.newVersion)**`n`n"
155-
$changelogSection += "- [View full changelog]($($update.compareUrl))`n"
156-
$changelogSection += "- [Release notes]($($update.repoUrl)/tag/v$($update.newVersion))`n`n"
157-
}
158-
$changelogSection += "</details>`n"
357+
$emojiLegend = "**Legend**`n`n"
358+
foreach ($usedEmoji in $usedEmojis) {
359+
$emojiLegend += "$usedEmoji $($emojiDescriptions[$usedEmoji])`n"
360+
}
361+
362+
if ([string]::IsNullOrWhiteSpace($changelogSection)) {
363+
$changelogSection = ""
364+
} else {
365+
$changelogSection = "`n<details>`n<summary>Release notes for updated vendors</summary>`n`n$changelogSection</details>`n"
366+
}
367+
368+
$reviewTeam = ""
369+
if ($hasMinorOrMajorSinceLastPrUpdate -and $env:GITHUB_REPOSITORY_OWNER -eq 'cmderdev') {
370+
$reviewTeam = "trusted-contributors"
371+
}
159372
160-
Add-Content -Path $env:GITHUB_ENV -Value "CHANGELOG_SECTION<<EOF"
161-
Add-Content -Path $env:GITHUB_ENV -Value $changelogSection
162-
Add-Content -Path $env:GITHUB_ENV -Value "EOF"
373+
Add-GitHubEnvMultiline -Name UPDATE_MESSAGE -Value $updateMessage
374+
Add-GitHubEnvMultiline -Name CHANGELOG_SECTION -Value $changelogSection
375+
Add-GitHubEnvMultiline -Name EMOJI_LEGEND -Value $emojiLegend
376+
377+
if ($hasBreakingChanges) {
163378
Add-Content -Path $env:GITHUB_ENV -Value "HAS_BREAKING_CHANGES=True"
164379
} else {
165-
Add-Content -Path $env:GITHUB_ENV -Value "CHANGELOG_SECTION="
166380
Add-Content -Path $env:GITHUB_ENV -Value "HAS_BREAKING_CHANGES=False"
167381
}
382+
Add-Content -Path $env:GITHUB_ENV -Value "HAS_MINOR_OR_MAJOR_SINCE_LAST_PR_UPDATE=$hasMinorOrMajorSinceLastPrUpdate"
383+
Add-Content -Path $env:GITHUB_ENV -Value "REVIEW_TEAM=$reviewTeam"
168384
169385
- name: Summary - Update check results
170386
shell: pwsh
@@ -301,12 +517,15 @@ jobs:
301517
302518
---
303519
304-
${{ env.HAS_BREAKING_CHANGES == 'True' && '⚠️ **This update contains major version changes that may include breaking changes.**' || 'ℹ️ This update only contains minor or patch changes.' }}
520+
${{ env.EMOJI_LEGEND }}
521+
522+
${{ env.HAS_BREAKING_CHANGES == 'True' && '**Review note:** This update contains version changes that may require manual review.' || '**Review note:** This update only contains minor or patch changes.' }}
305523
306524
Please verify and then **Merge** the pull request to apply the updates.
307525
commit-message: '⬆️ Update dependencies (${{ env.LIST_UPDATED }})'
308526
branch: update-vendor
309527
base: master
528+
team-reviewers: ${{ env.REVIEW_TEAM }}
310529

311530
- name: Summary - Pull request result
312531
if: env.COUNT_UPDATED > 0 && env.AUTO_MERGED != 'true'

0 commit comments

Comments
 (0)