From e4957f223517cc634ec6d573a7cf5961e4f1966e Mon Sep 17 00:00:00 2001 From: "Darrin W. Cullop" Date: Sun, 20 Sep 2026 22:06:09 -0700 Subject: [PATCH] Generate release notes from the published commit --- .../scripts/Test-ReleaseNotesInvocation.ps1 | 46 +++++++++++++++++++ .github/workflows/ci-build.yml | 4 ++ .github/workflows/release.yml | 6 ++- 3 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 .github/scripts/Test-ReleaseNotesInvocation.ps1 diff --git a/.github/scripts/Test-ReleaseNotesInvocation.ps1 b/.github/scripts/Test-ReleaseNotesInvocation.ps1 new file mode 100644 index 000000000..73dc8d32a --- /dev/null +++ b/.github/scripts/Test-ReleaseNotesInvocation.ps1 @@ -0,0 +1,46 @@ +[CmdletBinding()] +param( + [string]$WorkflowPath = (Join-Path $PSScriptRoot '..\workflows\release.yml') +) + +$ErrorActionPreference = 'Stop' +$workflow = Get-Content -LiteralPath $WorkflowPath -Raw +$command = [regex]::Match($workflow, '(?m)^[ \t]+run:[ \t]+(git-release-notes[^\r\n]+)\r?$') +if (-not $command.Success) { + throw 'The release workflow must contain an inspectable release-notes invocation.' +} + +$expectedCommit = '1111111111111111111111111111111111111111' +$expectedVersion = '9.5.0' +$originalCommit = $env:GITHUB_SHA +$originalVersion = $env:RELEASE_VERSION +$env:GITHUB_SHA = $expectedCommit +$env:RELEASE_VERSION = $expectedVersion + +function git-release-notes { + $headIndex = [Array]::IndexOf($args, '--head-ref') + if ($headIndex -lt 0 -or $headIndex + 1 -ge $args.Count -or $args[$headIndex + 1] -ne $expectedCommit) { + throw 'Release notes must compare against the commit being published, not the repository default branch.' + } + + $versionIndex = [Array]::IndexOf($args, '--release-version') + if ($versionIndex -lt 0 -or $versionIndex + 1 -ge $args.Count -or $args[$versionIndex + 1] -ne $expectedVersion) { + throw 'Release notes must use the version being published.' + } + + $outputIndex = [Array]::IndexOf($args, '--output-file') + if ($outputIndex -lt 0 -or $outputIndex + 1 -ge $args.Count -or $args[$outputIndex + 1] -ne 'release-notes.md') { + throw 'Release notes must be written to the file consumed by the release step.' + } +} + +try { + & ([scriptblock]::Create($command.Groups[1].Value)) +} +finally { + $env:GITHUB_SHA = $originalCommit + $env:RELEASE_VERSION = $originalVersion + Remove-Item -LiteralPath Function:\git-release-notes +} + +Write-Output 'Release-notes invocation uses the published commit, version, and output file.' diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index e5f9f3baf..22eb7d59c 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -51,6 +51,10 @@ jobs: "SemVer2=$env:NBGV_SemVer2" >> $env:GITHUB_OUTPUT "PrereleaseVersion=$env:NBGV_PrereleaseVersion" >> $env:GITHUB_OUTPUT + - name: Verify release-note invocation + shell: pwsh + run: .\.github\scripts\Test-ReleaseNotesInvocation.ps1 + - name: NuGet Restore run: dotnet restore DynamicData.sln working-directory: src diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 50f8371d1..8ad7c9a12 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -108,6 +108,10 @@ jobs: } Write-Host "OK: no stable $major.$minor.* tag exists; '$env:SEMVER2' is safe to publish." + - name: Verify release-note invocation + shell: pwsh + run: .\.github\scripts\Test-ReleaseNotesInvocation.ps1 + - name: NuGet Restore run: dotnet restore DynamicData.sln working-directory: src @@ -142,7 +146,7 @@ jobs: GITHUB_TOKEN: ${{ github.token }} RELEASE_VERSION: ${{ steps.nbgv.outputs.SemVer2 }} shell: pwsh - run: git-release-notes --release-version "$env:RELEASE_VERSION" --output-file release-notes.md + run: git-release-notes --release-version "$env:RELEASE_VERSION" --head-ref "$env:GITHUB_SHA" --output-file release-notes.md - name: Create GitHub Release env: