Skip to content

Commit 1dbbc41

Browse files
vaindclaude
andauthored
Add git commit fallback for repositories without changelog files (#116)
* feat: add git commit fallback for repositories without changelog files - Adds fallback to generate changelog from git commits when no changelog.md exists - Refactors code into separate functions for better maintainability: - Get-ChangelogFromCommits: Generate changelog from git log - Get-ChangelogFromDiff: Generate changelog from file diff - Format-ChangelogContent: Apply consistent formatting and sanitization - Filters out version tag commits to focus on meaningful changes - Applies same link formatting to prevent GitHub notifications - Supports repositories like Catch2, React, Vue.js that use GitHub releases - Maintains backward compatibility with existing changelog.md workflow - Adds comprehensive tests for new functionality 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: improve tests and remove redundant code - Update git commit fallback tests to match exact expected output like other tests - Remove redundant $prefix variable definition (use global scope) - Add changelog entry for the new git commit fallback feature 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: address review feedback on error handling and robustness - Add comprehensive error handling for all git operations to prevent script termination - Implement progressive fallback: shallow clone → deep clone → full clone - Add proper cleanup in exception scenarios using try/catch/finally blocks - Make tests more robust to reduce external dependency brittleness - Add test for invalid repository error handling - Improve error messages with specific exit codes - Ensure temporary repository directories are always cleaned up Addresses review comments: - Fix PSNativeCommandErrorActionPreference termination issue - Handle git command failures gracefully - Improve git clone depth handling - Add better directory cleanup - Make tests less dependent on external repository state 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: reorder features section in changelog for clarity * refactor: simplify error handling by changing PSNativeCommandErrorActionPreference Instead of wrapping every git command with complex error handling, simply set PSNativeCommandErrorActionPreference to false and handle $LASTEXITCODE explicitly. This is much cleaner and more maintainable: - Removes 50+ lines of complex error handling wrappers - Makes the code more readable and easier to understand - Still maintains all the same error handling behavior - All tests continue to pass Changes: - Set PSNativeCommandErrorActionPreference = $false globally - Simplified git clone/fetch/log operations to check $LASTEXITCODE directly - Removed complex try/catch/finally wrappers from Get-ChangelogFromCommits - Simplified Get-ChangelogFromDiff git diff operation - Maintained all progressive fallback and cleanup logic 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: remove unused variable for test cases in update-changelog tests * fix: improve cloning process by removing unnecessary depth adjustments * test: improve test cases to use single expected multiline strings Refactored git commit fallback test cases to follow the same pattern as 'supports cross-repo links' test using single expected multiline strings instead of multiple Should-Match assertions. Changes: - 'falls back to git commits when no changelog files exist': Now uses exact expected output - 'git commit fallback handles PR references correctly': Uses exact expected output - 'git commit fallback filters out version tag commits': Uses exact expected output with full commit list Benefits: - More consistent test style across the test suite - Easier to see what the expected output should be - Better failure messages when tests fail - All 16 tests continue to pass 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: move changelog fetching into Get-ChangelogFromDiff function Cleaned up the main logic by encapsulating changelog file fetching within the Get-ChangelogFromDiff function itself. Changes: - Get-ChangelogFromDiff now takes (oldTag, newTag, tmpDir) instead of file paths - Function handles its own changelog file fetching and returns null if files don't exist - Main logic is simplified to just call both functions and use whichever succeeds - Removes duplicate code and makes the interface cleaner - All 16 tests continue to pass Benefits: - Cleaner separation of concerns - Simpler main logic flow - Each function is more self-contained - Easier to understand and maintain 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: streamline cloning process by removing fallback for shallow clone * test: update changelog tests to use new repository and tag references * perf: optimize missing versions test by using existing repo Change test from sentry-javascript to github-workflows repo to reduce git clone timeout from 26s to 4s. The github-workflows repo is already used in other tests and clones much faster while still testing the same error handling functionality for invalid tags. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: remove unnecessary redirection in git log command for commit messages --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 6af5c2d commit 1dbbc41

File tree

4 files changed

+309
-74
lines changed

4 files changed

+309
-74
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ To update your existing Danger workflows:
4444
4545
### Features
4646
47+
- Updater now supports dependencies without changelog files by falling back to git commit messages ([#116](https://github.com/getsentry/github-workflows/pull/116))
4748
- Danger - Improve conventional commit scope handling, and non-conventional PR title support ([#105](https://github.com/getsentry/github-workflows/pull/105))
4849
- Add Proguard artifact endpoint for Android builds in sentry-server ([#100](https://github.com/getsentry/github-workflows/pull/100))
4950
- Updater - Add CMake FetchContent support for automated dependency updates ([#104](https://github.com/getsentry/github-workflows/pull/104))

updater/scripts/get-changelog.ps1

Lines changed: 184 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ param(
55
)
66

77
Set-StrictMode -Version latest
8-
$PSNativeCommandErrorActionPreference = $true
8+
$PSNativeCommandErrorActionPreference = $false
99
$ErrorActionPreference = 'Stop'
1010

1111
$prefix = 'https?://(www\.)?github.com/'
@@ -45,104 +45,217 @@ function Get-ChangelogContent {
4545
return $false
4646
}
4747

48-
try {
49-
Write-Host 'Fetching CHANGELOG files for comparison...'
48+
# Function to generate changelog from git commits
49+
function Get-ChangelogFromCommits {
50+
param($repoUrl, $oldTag, $newTag, $tmpDir)
5051

51-
# Fetch old changelog
52-
$oldChangelogPath = Join-Path $tmpDir 'old-changelog.md'
53-
if (-not (Get-ChangelogContent $OldTag $oldChangelogPath)) {
54-
Write-Warning "Could not find changelog at $OldTag"
55-
return
52+
# Clone the repository
53+
$repoDir = Join-Path $tmpDir 'repo'
54+
Write-Host "Cloning repository to generate changelog from commits..."
55+
git clone --no-single-branch --quiet $repoUrl $repoDir
56+
if ($LASTEXITCODE -ne 0) {
57+
Write-Warning "Could not clone repository $repoUrl"
58+
return $null
59+
}
60+
61+
if (-not (Test-Path $repoDir)) {
62+
Write-Warning "Repository directory was not created successfully"
63+
return $null
64+
}
65+
66+
Push-Location $repoDir
67+
try {
68+
# Ensure we have both tags
69+
git fetch --tags --quiet
70+
if ($LASTEXITCODE -ne 0) {
71+
Write-Warning "Could not fetch tags from repository"
72+
return $null
73+
}
74+
75+
# Get commit messages between tags
76+
Write-Host "Getting commits between $oldTag and $newTag..."
77+
$commitMessages = git log "$oldTag..$newTag" --pretty=format:'%s'
78+
if ($LASTEXITCODE -ne 0) {
79+
Write-Warning "Could not get commits between $oldTag and $newTag (exit code: $LASTEXITCODE)"
80+
return $null
81+
}
82+
83+
if ([string]::IsNullOrEmpty($commitMessages)) {
84+
Write-Host "No commits found between $oldTag and $newTag"
85+
return $null
86+
}
87+
88+
# Filter out version tag commits and format as list
89+
$commits = $commitMessages -split "`n" |
90+
Where-Object {
91+
$_ -and
92+
$_ -notmatch '^\s*v?\d+\.\d+\.\d+' -and # Skip version commits
93+
$_.Trim().Length -gt 0
94+
} |
95+
ForEach-Object { "- $_" }
96+
97+
if ($commits.Count -eq 0) {
98+
Write-Host "No meaningful commits found between $oldTag and $newTag"
99+
return $null
100+
}
101+
102+
# Create changelog from commits
103+
$changelog = "## Changelog`n`n"
104+
$changelog += "### Commits between $oldTag and $newTag`n`n"
105+
$changelog += $commits -join "`n"
106+
107+
Write-Host "Generated changelog from $($commits.Count) commits"
108+
return $changelog
109+
}
110+
catch {
111+
Write-Warning "Error generating changelog from commits: $($_.Exception.Message)"
112+
return $null
113+
}
114+
finally {
115+
Pop-Location
116+
# Ensure repository directory is cleaned up
117+
if (Test-Path $repoDir) {
118+
try {
119+
Remove-Item -Recurse -Force $repoDir -ErrorAction SilentlyContinue
120+
Write-Host "Cleaned up temporary repository directory"
121+
}
122+
catch {
123+
Write-Warning "Could not clean up temporary repository directory: $repoDir"
124+
}
125+
}
56126
}
127+
}
128+
129+
# Function to generate changelog from diff between changelog files
130+
function Get-ChangelogFromDiff {
131+
param($oldTag, $newTag, $tmpDir)
132+
133+
# Try to fetch changelog files for both tags
134+
$oldChangelogPath = Join-Path $tmpDir 'old-changelog.md'
135+
$hasOldChangelog = Get-ChangelogContent $oldTag $oldChangelogPath
57136

58-
# Fetch new changelog
59137
$newChangelogPath = Join-Path $tmpDir 'new-changelog.md'
60-
if (-not (Get-ChangelogContent $NewTag $newChangelogPath)) {
61-
Write-Warning "Could not find changelog at $NewTag"
62-
return
138+
$hasNewChangelog = Get-ChangelogContent $newTag $newChangelogPath
139+
140+
# Return null if we don't have both changelog files
141+
if (-not $hasOldChangelog -or -not $hasNewChangelog) {
142+
return $null
63143
}
64144

65-
Write-Host "Generating changelog diff between $OldTag and $NewTag..."
145+
Write-Host "Generating changelog diff between $oldTag and $newTag..."
66146

67147
# Generate diff using git diff --no-index
68148
# git diff returns exit code 1 when differences are found, which is expected behavior
69-
# We need to handle this properly when PSNativeCommandErrorActionPreference is enabled
70-
$fullDiff = & {
71-
$oldErrorActionPreference = $ErrorActionPreference
72-
$ErrorActionPreference = 'Continue'
73-
try {
74-
git diff --no-index $oldChangelogPath $newChangelogPath
75-
} finally {
76-
$ErrorActionPreference = $oldErrorActionPreference
77-
}
78-
}
149+
$fullDiff = git diff --no-index $oldChangelogPath $newChangelogPath
79150

80151
# The first lines are diff metadata, skip them
81152
$fullDiff = $fullDiff -split "`n" | Select-Object -Skip 4
82153
if ([string]::IsNullOrEmpty("$fullDiff")) {
83-
Write-Host "No differences found between $OldTag and $NewTag"
84-
return
154+
Write-Host "No differences found between $oldTag and $newTag"
155+
return $null
85156
} else {
86157
Write-Host "Successfully created a changelog diff - $($fullDiff.Count) lines"
87158
}
88159

89160
# Extract only the added lines (lines starting with + but not ++)
90161
$addedLines = $fullDiff | Where-Object { $_ -match '^[+][^+]*' } | ForEach-Object { $_.Substring(1) }
91162

92-
if ($addedLines.Count -gt 0) {
93-
# Create clean changelog from added lines
94-
$changelog = ($addedLines -join "`n").Trim()
163+
if ($addedLines.Count -eq 0) {
164+
Write-Host "No changelog additions found between $oldTag and $newTag"
165+
return $null
166+
}
95167

96-
# Apply formatting to clean changelog
97-
if ($changelog.Length -gt 0) {
98-
# Add header
99-
if (-not ($changelog -match '^(##|#) Changelog')) {
100-
$changelog = "## Changelog`n`n$changelog"
101-
}
168+
# Create clean changelog from added lines
169+
$changelog = ($addedLines -join "`n").Trim()
102170

103-
# Increase header level by one for content (not the main header)
104-
$changelog = $changelog -replace '(^|\n)(#+) ', '$1$2# ' -replace '^### Changelog', '## Changelog'
171+
if ($changelog.Length -eq 0) {
172+
return $null
173+
}
105174

106-
# Only add details section if there are deletions or modifications (not just additions)
107-
$hasModifications = $fullDiff | Where-Object { $_ -match '^[-]' -and $_ -notmatch '^[-]{3}' }
108-
if ($hasModifications) {
109-
$changelog += "`n`n<details>`n<summary>Full CHANGELOG.md diff</summary>`n`n"
110-
$changelog += '```diff' + "`n"
111-
$changelog += $fullDiff -join "`n"
112-
$changelog += "`n" + '```' + "`n`n</details>"
113-
}
175+
# Add header if needed
176+
if (-not ($changelog -match '^(##|#) Changelog')) {
177+
$changelog = "## Changelog`n`n$changelog"
178+
}
114179

115-
# Apply standard formatting
116-
# Remove at-mentions.
117-
$changelog = $changelog -replace '@', ''
118-
# Make PR/issue references into links to the original repository (unless they already are links).
119-
$changelog = $changelog -replace '(?<!\[)#([0-9]+)(?![\]0-9])', ('[#$1](' + $RepoUrl + '/issues/$1)')
120-
# Replace any links pointing to github.com so that the target PRs/Issues don't get na notification.
121-
$changelog = $changelog -replace ('\(' + $prefix), '(https://github-redirect.dependabot.com/'
122-
123-
# Limit the changelog length to ~60k to allow for other text in the PR body (total PR limit is 65536 characters).
124-
$limit = 60000
125-
if ($changelog.Length -gt $limit) {
126-
$oldLength = $changelog.Length
127-
Write-Warning "Truncating changelog because it's $($changelog.Length - $limit) characters longer than the limit $limit."
128-
while ($changelog.Length -gt $limit) {
129-
$lastNewlineIndex = $changelog.LastIndexOf("`n")
130-
if ($lastNewlineIndex -eq -1) {
131-
# No newlines found, just truncate to limit
132-
$changelog = $changelog.Substring(0, $limit)
133-
break
134-
}
135-
$changelog = $changelog.Substring(0, $lastNewlineIndex)
136-
}
137-
$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.**"
138-
}
180+
# Increase header level by one for content (not the main header)
181+
$changelog = $changelog -replace '(^|\n)(#+) ', '$1$2# ' -replace '^### Changelog', '## Changelog'
182+
183+
# Only add details section if there are deletions or modifications (not just additions)
184+
$hasModifications = $fullDiff | Where-Object { $_ -match '^[-]' -and $_ -notmatch '^[-]{3}' }
185+
if ($hasModifications) {
186+
$changelog += "`n`n<details>`n<summary>Full CHANGELOG.md diff</summary>`n`n"
187+
$changelog += '```diff' + "`n"
188+
$changelog += $fullDiff -join "`n"
189+
$changelog += "`n" + '```' + "`n`n</details>"
190+
}
191+
192+
return $changelog
193+
}
194+
195+
# Function to sanitize and format changelog content
196+
function Format-ChangelogContent {
197+
param($changelog, $repoUrl)
198+
199+
if ([string]::IsNullOrEmpty($changelog)) {
200+
return $null
201+
}
202+
203+
# Apply standard formatting
204+
# Remove at-mentions
205+
$changelog = $changelog -replace '@', ''
206+
207+
# Make PR/issue references into links to the original repository (unless they already are links)
208+
$changelog = $changelog -replace '(?<!\[)#([0-9]+)(?![\]0-9])', ('[#$1](' + $repoUrl + '/issues/$1)')
139209

140-
Write-Host "Final changelog length: $($changelog.Length) characters"
141-
Write-Output $changelog
210+
# Replace any links pointing to github.com so that the target PRs/Issues don't get notification
211+
$changelog = $changelog -replace ('\(' + $prefix), '(https://github-redirect.dependabot.com/'
212+
213+
# Limit the changelog length to ~60k to allow for other text in the PR body (total PR limit is 65536 characters)
214+
$limit = 60000
215+
if ($changelog.Length -gt $limit) {
216+
$oldLength = $changelog.Length
217+
Write-Warning "Truncating changelog because it's $($changelog.Length - $limit) characters longer than the limit $limit."
218+
while ($changelog.Length -gt $limit) {
219+
$lastNewlineIndex = $changelog.LastIndexOf("`n")
220+
if ($lastNewlineIndex -eq -1) {
221+
# No newlines found, just truncate to limit
222+
$changelog = $changelog.Substring(0, $limit)
223+
break
224+
}
225+
$changelog = $changelog.Substring(0, $lastNewlineIndex)
142226
}
227+
$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.**"
228+
}
229+
230+
Write-Host "Final changelog length: $($changelog.Length) characters"
231+
return $changelog
232+
}
233+
234+
try {
235+
Write-Host 'Fetching CHANGELOG files for comparison...'
236+
237+
$changelog = $null
238+
239+
# Try changelog file diff first, fall back to git commits if not available
240+
$changelog = Get-ChangelogFromDiff $OldTag $NewTag $tmpDir
241+
242+
# Fall back to git commits if no changelog files or no diff found
243+
if (-not $changelog) {
244+
Write-Host "No changelog files found or no changes detected, falling back to git commits..."
245+
$changelog = Get-ChangelogFromCommits $RepoUrl $OldTag $NewTag $tmpDir
143246
}
144247

145-
Write-Host "No changelog additions found between $OldTag and $NewTag"
248+
# Apply formatting and output result
249+
if ($changelog) {
250+
$formattedChangelog = Format-ChangelogContent $changelog $RepoUrl
251+
if ($formattedChangelog) {
252+
Write-Output $formattedChangelog
253+
} else {
254+
Write-Host "No changelog content to display after formatting"
255+
}
256+
} else {
257+
Write-Host "No changelog found between $OldTag and $NewTag"
258+
}
146259
} catch {
147260
Write-Warning "Failed to get changelog: $($_.Exception.Message)"
148261
} finally {

0 commit comments

Comments
 (0)