Skip to content

Commit 7afb0ea

Browse files
vaindclaude
andcommitted
fix: improve changelog generation for non-tagged commits and edge cases
- Switch from git clone to GitHub raw API for better performance - Support commit SHAs as OldTag/NewTag parameters - Use diff-based approach for accurate changelog extraction - Handle repositories without existing changelog files - Add proper error handling and cleanup - Improve test coverage for edge cases 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 054504c commit 7afb0ea

File tree

2 files changed

+250
-127
lines changed

2 files changed

+250
-127
lines changed

updater/scripts/get-changelog.ps1

Lines changed: 104 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -7,120 +7,128 @@ param(
77
Set-StrictMode -Version latest
88

99
$prefix = 'https?://(www\.)?github.com/'
10-
if (-not ($RepoUrl -match "^$prefix"))
11-
{
12-
Write-Warning "Only github.com repositories are currently supported. Given RepoUrl doesn't look like one: $RepoUrl"
10+
if (-not ($RepoUrl -match "^$prefix([^/]+)/([^/]+?)(?:\.git)?/?$")) {
11+
Write-Warning "Only https://github.com repositories are currently supported. Could not parse repository from URL: $RepoUrl"
1312
return
1413
}
1514

15+
$repoOwner = $matches[2]
16+
$repoName = $matches[3]
17+
$apiRepo = "$repoOwner/$repoName"
18+
19+
# Create temporary directory for changelog files
1620
$tmpDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid())
1721
New-Item -ItemType Directory $tmpDir | Out-Null
1822

19-
try
20-
{
21-
git clone --depth 1 $RepoUrl $tmpDir
23+
# Function to try different changelog filenames
24+
function Get-ChangelogContent {
25+
param($ref, $filePath)
2226

23-
$file = $(Get-ChildItem -Path $tmpDir | Where-Object { $_.Name -match '^changelog(\.md|\.txt|)$' } )
24-
if ("$file" -eq '')
25-
{
26-
Write-Warning "Couldn't find a changelog"
27-
return
27+
$changelogNames = @('CHANGELOG.md', 'changelog.md', 'CHANGELOG.txt', 'changelog.txt', 'CHANGELOG')
28+
29+
foreach ($name in $changelogNames) {
30+
try {
31+
# Try fetching directly from raw.githubusercontent.com
32+
$rawUrl = "https://raw.githubusercontent.com/$apiRepo/$ref/$name"
33+
$content = Invoke-RestMethod -Uri $rawUrl -Method Get -ErrorAction SilentlyContinue
34+
if ($content) {
35+
Set-Content -Path $filePath -Value $content -Encoding UTF8
36+
Write-Host "Found changelog for $ref as: $name"
37+
return $true
38+
}
39+
} catch {
40+
# Continue to next filename
41+
}
2842
}
29-
elseif ($file -is [Array])
30-
{
31-
Write-Warning "Multiple changelogs found: $file"
43+
return $false
44+
}
45+
46+
try {
47+
Write-Host 'Fetching CHANGELOG files for comparison...'
48+
49+
# Fetch old changelog
50+
$oldChangelogPath = Join-Path $tmpDir 'old-changelog.md'
51+
if (-not (Get-ChangelogContent $OldTag $oldChangelogPath)) {
52+
Write-Warning "Could not find changelog at $OldTag"
3253
return
3354
}
34-
Write-Host "Found changelog: $file"
35-
[string[]]$lines = Get-Content $file
36-
}
37-
finally
38-
{
39-
Write-Host "Removing $tmpDir"
40-
Remove-Item -Recurse -Force -ErrorAction Continue -Path $tmpDir
41-
}
4255

43-
$startIndex = -1
44-
$endIndex = -1
45-
$changelog = ''
46-
for ($i = 0; $i -lt $lines.Count; $i++)
47-
{
48-
$line = $lines[$i]
49-
50-
if ($startIndex -lt 0)
51-
{
52-
if ($line -match "^#+ +v?$NewTag\b")
53-
{
54-
$startIndex = $i
55-
}
56+
# Fetch new changelog
57+
$newChangelogPath = Join-Path $tmpDir 'new-changelog.md'
58+
if (-not (Get-ChangelogContent $NewTag $newChangelogPath)) {
59+
Write-Warning "Could not find changelog at $NewTag"
60+
return
5661
}
57-
elseif ($line -match "^#+ +v?$OldTag\b")
58-
{
59-
$endIndex = $i - 1
60-
break
62+
63+
Write-Host "Generating changelog diff between $OldTag and $NewTag..."
64+
65+
# Generate diff using git diff --no-index
66+
$fullDiff = git diff --no-index $oldChangelogPath $newChangelogPath
67+
68+
# The first lines are diff metadata, skip them
69+
$fullDiff = $fullDiff -split "`n" | Select-Object -Skip 4
70+
71+
if ([string]::IsNullOrEmpty($fullDiff)) {
72+
Write-Host "No differences found between $OldTag and $NewTag"
73+
return ''
6174
}
62-
}
6375

64-
# If the changelog doesn't have a section for the oldTag, stop at the first SemVer that's lower than oldTag.
65-
if ($endIndex -lt 0)
66-
{
67-
$endIndex = $lines.Count - 1 # fallback, may be overwritten below
68-
try
69-
{
70-
$semverOldTag = [System.Management.Automation.SemanticVersion]::Parse($OldTag)
71-
for ($i = $startIndex; $i -lt $lines.Count; $i++)
72-
{
73-
$line = $lines[$i]
74-
if ($line -match '^#+ +v?([0-9]+.*)$')
75-
{
76-
try
77-
{
78-
if ($semverOldTag -ge [System.Management.Automation.SemanticVersion]::Parse($matches[1]))
79-
{
80-
$endIndex = $i - 1
81-
break
82-
}
76+
# Extract only the added lines (lines starting with + but not ++)
77+
$addedLines = $fullDiff | Where-Object { $_ -match '^[+][^+]*' } | ForEach-Object { $_.Substring(1) }
78+
79+
if ($addedLines.Count -gt 0) {
80+
# Create clean changelog from added lines
81+
$changelog = ($addedLines -join "`n").Trim()
82+
83+
# Apply formatting to clean changelog
84+
if ($changelog.Length -gt 0) {
85+
# Add header
86+
if (-not ($changelog -match '^(##|#) Changelog')) {
87+
$changelog = "## Changelog`n`n$changelog"
88+
}
89+
90+
# Increase header level by one for content (not the main header)
91+
$changelog = $changelog -replace '(^|\n)(#+) ', '$1$2# ' -replace '^### Changelog', '## Changelog'
92+
93+
# Only add details section if there are deletions or modifications (not just additions)
94+
$hasModifications = $fullDiff | Where-Object { $_ -match '^[-]' -and $_ -notmatch '^[-]{3}' }
95+
if ($hasModifications) {
96+
$changelog += "`n`n<details>`n<summary>Full CHANGELOG.md diff</summary>`n`n"
97+
$changelog += '```diff' + "`n"
98+
$changelog += $fullDiff -join "`n"
99+
$changelog += "`n" + '```' + "`n`n</details>"
100+
}
101+
102+
# Apply standard formatting
103+
# Remove at-mentions.
104+
$changelog = $changelog -replace '@', ''
105+
# Make PR/issue references into links to the original repository (unless they already are links).
106+
$changelog = $changelog -replace '(?<!\[)#([0-9]+)(?![\]0-9])', ('[#$1](' + $RepoUrl + '/issues/$1)')
107+
# Replace any links pointing to github.com so that the target PRs/Issues don't get na notification.
108+
$changelog = $changelog -replace ('\(' + $prefix), '(https://github-redirect.dependabot.com/'
109+
110+
# Limit the changelog length to ~60k to allow for other text in the PR body (total PR limit is 65536 characters).
111+
$limit = 60000
112+
if ($changelog.Length -gt $limit) {
113+
$oldLength = $changelog.Length
114+
Write-Warning "Truncating changelog because it's $($changelog.Length - $limit) characters longer than the limit $limit."
115+
while ($changelog.Length -gt $limit) {
116+
$changelog = $changelog.Substring(0, $changelog.LastIndexOf("`n"))
83117
}
84-
catch {}
118+
$changelog += "`n`n> :warning: **Changelog content truncated by $($oldLength - $changelog.Length) characters because it was over the limit ($limit) and wouldn't fit into PR description.**"
85119
}
120+
121+
return $changelog
86122
}
87123
}
88-
catch {}
89-
}
90-
91-
# Slice changelog lines from startIndex to endIndex.
92-
if ($startIndex -ge 0)
93-
{
94-
$changelog = ($lines[$startIndex..$endIndex] -join "`n").Trim()
95-
}
96-
else
97-
{
98-
$changelog = ''
99-
}
100-
if ($changelog.Length -gt 1)
101-
{
102-
$changelog = "# Changelog`n$changelog"
103-
# Increase header level by one.
104-
$changelog = $changelog -replace '(^|\n)(#+) ', '$1$2# '
105-
# Remove at-mentions.
106-
$changelog = $changelog -replace '@', ''
107-
# Make PR/issue references into links to the original repository (unless they already are links).
108-
$changelog = $changelog -replace '(?<!\[)#([0-9]+)(?![\]0-9])', ('[#$1](' + $RepoUrl + '/issues/$1)')
109-
# Replace any links pointing to github.com so that the target PRs/Issues don't get na notification.
110-
$changelog = $changelog -replace ('\(' + $prefix), '(https://github-redirect.dependabot.com/'
111-
}
112124

113-
# Limit the changelog length to ~60k to allow for other text in the PR body (total PR limit is 65536 characters).
114-
$limit = 60000
115-
if ($changelog.Length -gt $limit)
116-
{
117-
$oldLength = $changelog.Length
118-
Write-Warning "Truncating changelog because it's $($changelog.Length - $limit) characters longer than the limit $limit."
119-
while ($changelog.Length -gt $limit)
120-
{
121-
$changelog = $changelog.Substring(0, $changelog.LastIndexOf("`n"))
125+
Write-Host "No changelog additions found between $OldTag and $NewTag"
126+
return ''
127+
} catch {
128+
Write-Warning "Failed to get changelog: $($_.Exception.Message)"
129+
} finally {
130+
if (Test-Path $tmpDir) {
131+
Write-Host 'Cleaning up temporary files...'
132+
Remove-Item -Recurse -Force -ErrorAction Continue $tmpDir
122133
}
123-
$changelog += "`n`n> :warning: **Changelog content truncated by $($oldLength - $changelog.Length) characters because it was over the limit ($limit) and wouldn't fit into PR description.**"
124134
}
125-
126-
$changelog

0 commit comments

Comments
 (0)