Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@ To update your existing Danger workflows:

- Updater - Prevent script injection vulnerabilities through workflow inputs ([#98](https://github.com/getsentry/github-workflows/pull/98))

### Fixes

- Improve changelog generation for non-tagged commits and edge cases ([#115](https://github.com/getsentry/github-workflows/pull/115))

## 2.14.1

### Fixes
Expand Down
214 changes: 123 additions & 91 deletions updater/scripts/get-changelog.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -5,122 +5,154 @@ param(
)

Set-StrictMode -Version latest
$PSNativeCommandErrorActionPreference = $true
$ErrorActionPreference = 'Stop'

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

$repoOwner = $matches[2]
$repoName = $matches[3]
$apiRepo = "$repoOwner/$repoName"

# Create temporary directory for changelog files
$tmpDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.Guid]::NewGuid())
New-Item -ItemType Directory $tmpDir | Out-Null

try
{
git clone --depth 1 $RepoUrl $tmpDir
# Function to try different changelog filenames
function Get-ChangelogContent {
param($ref, $filePath)

$changelogNames = @('CHANGELOG.md', 'changelog.md', 'CHANGELOG.txt', 'changelog.txt', 'CHANGELOG')

foreach ($name in $changelogNames) {
try {
# Try fetching directly from raw.githubusercontent.com
$rawUrl = "https://raw.githubusercontent.com/$apiRepo/$ref/$name"
$content = Invoke-RestMethod -Uri $rawUrl -Method Get -ErrorAction SilentlyContinue
if ($content) {
Set-Content -Path $filePath -Value $content -Encoding UTF8
Write-Host "Found $name for ref $ref"
return $true
}
} catch {
# Continue to next filename
}
}
return $false
}

try {
Write-Host 'Fetching CHANGELOG files for comparison...'

$file = $(Get-ChildItem -Path $tmpDir | Where-Object { $_.Name -match '^changelog(\.md|\.txt|)$' } )
if ("$file" -eq '')
{
Write-Warning "Couldn't find a changelog"
# Fetch old changelog
$oldChangelogPath = Join-Path $tmpDir 'old-changelog.md'
if (-not (Get-ChangelogContent $OldTag $oldChangelogPath)) {
Write-Warning "Could not find changelog at $OldTag"
return
}
elseif ($file -is [Array])
{
Write-Warning "Multiple changelogs found: $file"

# Fetch new changelog
$newChangelogPath = Join-Path $tmpDir 'new-changelog.md'
if (-not (Get-ChangelogContent $NewTag $newChangelogPath)) {
Write-Warning "Could not find changelog at $NewTag"
return
}
Write-Host "Found changelog: $file"
[string[]]$lines = Get-Content $file
}
finally
{
Write-Host "Removing $tmpDir"
Remove-Item -Recurse -Force -ErrorAction Continue -Path $tmpDir
}

$startIndex = -1
$endIndex = -1
$changelog = ''
for ($i = 0; $i -lt $lines.Count; $i++)
{
$line = $lines[$i]

if ($startIndex -lt 0)
{
if ($line -match "^#+ +v?$NewTag\b")
{
$startIndex = $i
Write-Host "Generating changelog diff between $OldTag and $NewTag..."

# Generate diff using git diff --no-index
# git diff returns exit code 1 when differences are found, which is expected behavior
# We need to handle this properly when PSNativeCommandErrorActionPreference is enabled
$fullDiff = & {
$oldErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = 'Continue'
try {
git diff --no-index $oldChangelogPath $newChangelogPath
} finally {
$ErrorActionPreference = $oldErrorActionPreference
}
}
elseif ($line -match "^#+ +v?$OldTag\b")
{
$endIndex = $i - 1
break

# The first lines are diff metadata, skip them
$fullDiff = $fullDiff -split "`n" | Select-Object -Skip 4
if ([string]::IsNullOrEmpty("$fullDiff")) {
Write-Host "No differences found between $OldTag and $NewTag"
return
} else {
Write-Host "Successfully created a changelog diff - $($fullDiff.Count) lines"
}
}

# If the changelog doesn't have a section for the oldTag, stop at the first SemVer that's lower than oldTag.
if ($endIndex -lt 0)
{
$endIndex = $lines.Count - 1 # fallback, may be overwritten below
try
{
$semverOldTag = [System.Management.Automation.SemanticVersion]::Parse($OldTag)
for ($i = $startIndex; $i -lt $lines.Count; $i++)
{
$line = $lines[$i]
if ($line -match '^#+ +v?([0-9]+.*)$')
{
try
{
if ($semverOldTag -ge [System.Management.Automation.SemanticVersion]::Parse($matches[1]))
{
$endIndex = $i - 1
# Extract only the added lines (lines starting with + but not ++)
$addedLines = $fullDiff | Where-Object { $_ -match '^[+][^+]*' } | ForEach-Object { $_.Substring(1) }

if ($addedLines.Count -gt 0) {
# Create clean changelog from added lines
$changelog = ($addedLines -join "`n").Trim()

# Apply formatting to clean changelog
if ($changelog.Length -gt 0) {
# Add header
if (-not ($changelog -match '^(##|#) Changelog')) {
$changelog = "## Changelog`n`n$changelog"
}

# Increase header level by one for content (not the main header)
$changelog = $changelog -replace '(^|\n)(#+) ', '$1$2# ' -replace '^### Changelog', '## Changelog'

# Only add details section if there are deletions or modifications (not just additions)
$hasModifications = $fullDiff | Where-Object { $_ -match '^[-]' -and $_ -notmatch '^[-]{3}' }
if ($hasModifications) {
$changelog += "`n`n<details>`n<summary>Full CHANGELOG.md diff</summary>`n`n"
$changelog += '```diff' + "`n"
$changelog += $fullDiff -join "`n"
$changelog += "`n" + '```' + "`n`n</details>"
}

# Apply standard formatting
# Remove at-mentions.
$changelog = $changelog -replace '@', ''
# Make PR/issue references into links to the original repository (unless they already are links).
$changelog = $changelog -replace '(?<!\[)#([0-9]+)(?![\]0-9])', ('[#$1](' + $RepoUrl + '/issues/$1)')
# Replace any links pointing to github.com so that the target PRs/Issues don't get na notification.
$changelog = $changelog -replace ('\(' + $prefix), '(https://github-redirect.dependabot.com/'

# Limit the changelog length to ~60k to allow for other text in the PR body (total PR limit is 65536 characters).
$limit = 60000
if ($changelog.Length -gt $limit) {
$oldLength = $changelog.Length
Write-Warning "Truncating changelog because it's $($changelog.Length - $limit) characters longer than the limit $limit."
while ($changelog.Length -gt $limit) {
$lastNewlineIndex = $changelog.LastIndexOf("`n")
if ($lastNewlineIndex -eq -1) {
# No newlines found, just truncate to limit
$changelog = $changelog.Substring(0, $limit)
break
}
$changelog = $changelog.Substring(0, $lastNewlineIndex)
}
catch {}
$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.**"
}

Write-Host "Final changelog length: $($changelog.Length) characters"
Write-Output $changelog
}
}
catch {}
}

# Slice changelog lines from startIndex to endIndex.
if ($startIndex -ge 0)
{
$changelog = ($lines[$startIndex..$endIndex] -join "`n").Trim()
}
else
{
$changelog = ''
}
if ($changelog.Length -gt 1)
{
$changelog = "# Changelog`n$changelog"
# Increase header level by one.
$changelog = $changelog -replace '(^|\n)(#+) ', '$1$2# '
# Remove at-mentions.
$changelog = $changelog -replace '@', ''
# Make PR/issue references into links to the original repository (unless they already are links).
$changelog = $changelog -replace '(?<!\[)#([0-9]+)(?![\]0-9])', ('[#$1](' + $RepoUrl + '/issues/$1)')
# Replace any links pointing to github.com so that the target PRs/Issues don't get na notification.
$changelog = $changelog -replace ('\(' + $prefix), '(https://github-redirect.dependabot.com/'
}

# Limit the changelog length to ~60k to allow for other text in the PR body (total PR limit is 65536 characters).
$limit = 60000
if ($changelog.Length -gt $limit)
{
$oldLength = $changelog.Length
Write-Warning "Truncating changelog because it's $($changelog.Length - $limit) characters longer than the limit $limit."
while ($changelog.Length -gt $limit)
{
$changelog = $changelog.Substring(0, $changelog.LastIndexOf("`n"))
Write-Host "No changelog additions found between $OldTag and $NewTag"
} catch {
Write-Warning "Failed to get changelog: $($_.Exception.Message)"
} finally {
if (Test-Path $tmpDir) {
Write-Host 'Cleaning up temporary files...'
Remove-Item -Recurse -Force -ErrorAction Continue $tmpDir
Write-Host 'Cleanup complete.'
}
$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.**"
}

$changelog
# This resets the $LASTEXITCODE set by git diff above.
# Note that this only runs in the successful path.
exit 0
Loading
Loading