Skip to content

Commit 3bb09d7

Browse files
committed
docs(release): prepare first public changelog
1 parent b7842fe commit 3bb09d7

6 files changed

Lines changed: 136 additions & 370 deletions

File tree

.github/release-template/links.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
- **Wiki:** <https://github.com/{full-repo}/wiki>
55
- **Trust Gateway docs:** <https://github.com/{full-repo}/wiki/Trust-Gateway>
66
- **Source:** commit `{commit-sha-short}` on `main`
7-
- **License:** [MIT](https://github.com/{full-repo}/blob/main/LICENSE)
7+
- **License:** [GPL-3.0](https://github.com/{full-repo}/blob/main/LICENSE)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
## What you need to do
22

3-
If you are already on a prior release: run `WKVRCProxy.Updater.exe` from the install folder. It downloads this release, verifies the zip against the SHA256 in this body, atomically swaps the files, and relaunches.
3+
Fresh install: follow the install steps above. Existing manual or prerelease installs can be replaced with this zip, or updated with `WKVRCProxy.Updater.exe` if that executable is already present in the install folder. The updater downloads this release, verifies the zip against the SHA256 in this body, atomically swaps the files, and relaunches.
44

55
If you hit a playback bug after this release: re-paste the URL with the watchdog window visible, copy the watchdog log starting from the failed request, and include both in the bug report.

.github/scripts/Generate-ReleaseNotes.ps1

Lines changed: 92 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@
2828
2929
Prev-tag resolution is layered for resilience against history rewrites that
3030
orphan the prior tag (rebase + force-push of main): describe + sanity gate,
31-
then subject-match against the most recent published GitHub release, then
32-
root-walk fallback. See Resolve-PrevTagForSlice for details.
31+
then subject-match against the most recent published GitHub release. If no
32+
prior tag or release exists, the body uses the curated CHANGELOG first-release
33+
section instead of walking the repository's full pre-release history.
3334
3435
Templates and the optional extras file run through the same scrub gates as
3536
commit subjects: ASCII normalisation pass, then non-ASCII fail, then a
@@ -133,7 +134,7 @@ if (-not $TemplateDir) {
133134
}
134135

135136
# Resolve previous tag. Layered fallback so a history rewrite that orphans the
136-
# prior tag does not produce a giant slice walking from root:
137+
# prior tag does not produce a giant slice from pre-release history:
137138
# 1. `git describe --tags --abbrev=0 <tag>^` finds the most recent tag
138139
# reachable from $Tag^ along the current line of history. Sanity gate:
139140
# if the slice is more than 50 commits, treat the describe result as
@@ -147,8 +148,9 @@ if (-not $TemplateDir) {
147148
# 3. Date anchoring is NOT used: a force-push rebase rewrites every
148149
# commit's committer-date, so --since against the prev release's
149150
# publishedAt walks the full rewritten history instead of the slice.
150-
# 4. If layers 1+2 yield nothing, walk from the repo's root commit.
151-
# First-release fallback.
151+
# 4. If layers 1+2 yield nothing, treat the tag as the first release and
152+
# use the curated CHANGELOG section instead of walking the full repository
153+
# history.
152154
# Surfaces a ::warning:: when layer 2 or 4 fires so the operator sees in
153155
# workflow logs that a fallback was used.
154156
function Resolve-PrevTagForSlice([string]$Tag, [string]$Repo) {
@@ -219,23 +221,24 @@ function Resolve-PrevTagForSlice([string]$Tag, [string]$Repo) {
219221
Source = 'subject-match'
220222
}
221223
}
222-
Write-Host "::warning::Prev tag $candidatePrevTag subject '$orphanSubject' not found in current $Tag history. Falling back to root walk."
224+
Write-Host "::warning::Prev tag $candidatePrevTag subject '$orphanSubject' not found in current $Tag history. Falling back to first-release changelog notes."
223225
}
224226
}
225227
}
226228
} else {
227-
Write-Host "::warning::'gh release list' produced no usable output (gh not authed or no releases yet). Falling back to root walk."
229+
Write-Host "::warning::'gh release list' produced no usable output (gh not authed or no releases yet). Falling back to first-release changelog notes."
228230
}
229231
}
230232

231-
# Layer 3: root walk.
232-
$root = (& git rev-list --max-parents=0 HEAD | Select-Object -First 1).Trim()
233-
Write-Host "::warning::No prior tag matched; walking from root $root."
233+
# Layer 3: first release. The repository may have a long pre-release commit
234+
# history whose subjects are not suitable for public release notes. Use the
235+
# curated CHANGELOG section for this case.
236+
Write-Host "::warning::No prior tag or published release matched; treating $Tag as the first release."
234237
return @{
235238
Tag = $null
236-
LogArgs = @("$root..$Tag")
237-
Display = "$root..$Tag (root walk)"
238-
Source = 'root'
239+
LogArgs = @()
240+
Display = "$Tag (first release)"
241+
Source = 'first-release'
239242
}
240243
}
241244

@@ -244,11 +247,54 @@ $prevTag = $prevInfo.Tag
244247
$logArgs = $prevInfo.LogArgs
245248
$range = $prevInfo.Display
246249

250+
function Read-ChangelogNotes([string]$Version) {
251+
$path = Join-Path -Path (Get-Location) -ChildPath 'CHANGELOG.md'
252+
if (-not (Test-Path -LiteralPath $path)) { return $null }
253+
254+
$content = Get-Content -LiteralPath $path -Raw -Encoding UTF8
255+
if ($Version) {
256+
$escaped = [regex]::Escape($Version)
257+
$versionPattern = "(?ms)^##\s+\[$escaped\][^\n]*\n(.*?)(?=^---\s*$|^##\s+|\z)"
258+
$versionMatch = [regex]::Match($content, $versionPattern)
259+
if ($versionMatch.Success) {
260+
$notes = $versionMatch.Groups[1].Value.Trim()
261+
if ($notes) { return $notes }
262+
}
263+
}
264+
265+
$unreleasedPattern = '(?ms)^##\s+Unreleased\s*\n(.*?)(?=^---\s*$|^##\s+|\z)'
266+
$unreleasedMatch = [regex]::Match($content, $unreleasedPattern)
267+
if ($unreleasedMatch.Success) {
268+
$notes = $unreleasedMatch.Groups[1].Value.Trim()
269+
if ($notes -and $notes -notmatch '^_No notable changes since the last release\._$') {
270+
return $notes
271+
}
272+
}
273+
274+
return $null
275+
}
276+
277+
$changelogNotes = $null
278+
if ($prevInfo.Source -eq 'first-release') {
279+
$changelogNotes = Read-ChangelogNotes -Version $Tag
280+
if (-not $changelogNotes) {
281+
if ($AllowEmpty) {
282+
$changelogNotes = '_First release; see commit log for details._'
283+
} else {
284+
throw "No prior tag or release exists, and CHANGELOG.md has no notes for $Tag or Unreleased. " +
285+
"For the first release, write curated public notes under ## Unreleased before tagging."
286+
}
287+
}
288+
}
289+
247290
# %H = full sha, %h = short sha, %an = author name, %s = subject. Tabs as field
248291
# separator are safe -- git rejects literal tabs in author names and subjects
249292
# don't contain them in any of these repos.
250-
$raw = & git log @logArgs --no-merges --pretty=format:"%H`t%h`t%an`t%s" 2>$null
251-
if ($LASTEXITCODE -ne 0) { $raw = @() }
293+
$raw = @()
294+
if (-not $changelogNotes) {
295+
$raw = & git log @logArgs --no-merges --pretty=format:"%H`t%h`t%an`t%s" 2>$null
296+
if ($LASTEXITCODE -ne 0) { $raw = @() }
297+
}
252298

253299
$lines = @()
254300
if ($raw) { $lines = $raw -split "`r?`n" | Where-Object { $_ } }
@@ -288,7 +334,7 @@ $entries = foreach ($line in $lines) {
288334
# means either the prev-tag detection is wrong, every commit was skipped,
289335
# or the tag was pushed from an empty branch. All three are operator
290336
# mistakes worth catching before publish.
291-
if (-not $entries -or $entries.Count -eq 0) {
337+
if (-not $changelogNotes -and (-not $entries -or $entries.Count -eq 0)) {
292338
if ($AllowEmpty) {
293339
# First-release escape hatch. Emit a stub body the workflow can still
294340
# publish; downstream consumers can reformat or replace.
@@ -319,21 +365,26 @@ function Get-Category([string] $subject) {
319365
# Conventional-commit coverage warning. Don't fail -- 'Other Changes' is the
320366
# documented bucket for non-conforming subjects -- but log to stderr so the
321367
# operator sees them in the workflow output and can amend if desired.
322-
$nonConforming = @($entries | Where-Object {
323-
$_.Subject -notmatch '^(feat|fix|perf|refactor|revert|docs|style|test|ci|build|chore)(\(.+?\))?!?:'
324-
})
325-
if ($nonConforming.Count -gt 0) {
326-
Write-Host "::warning::$($nonConforming.Count) commit(s) in range $range do not follow conventional-commit prefixes; bucketed under 'Other Changes':"
327-
foreach ($e in $nonConforming) {
328-
Write-Host "::warning:: $($e.Short) $($e.Subject)"
368+
$nonConforming = @()
369+
if (-not $changelogNotes) {
370+
$nonConforming = @($entries | Where-Object {
371+
$_.Subject -notmatch '^(feat|fix|perf|refactor|revert|docs|style|test|ci|build|chore)(\(.+?\))?!?:'
372+
})
373+
if ($nonConforming.Count -gt 0) {
374+
Write-Host "::warning::$($nonConforming.Count) commit(s) in range $range do not follow conventional-commit prefixes; bucketed under 'Other Changes':"
375+
foreach ($e in $nonConforming) {
376+
Write-Host "::warning:: $($e.Short) $($e.Subject)"
377+
}
329378
}
330379
}
331380

332381
$useGroups = $false
333-
foreach ($e in $entries) {
334-
if ($e.Subject -match '^(feat|fix|perf|refactor|revert|docs|style|test|ci|build|chore)(\(.+?\))?!?:') {
335-
$useGroups = $true
336-
break
382+
if (-not $changelogNotes) {
383+
foreach ($e in $entries) {
384+
if ($e.Subject -match '^(feat|fix|perf|refactor|revert|docs|style|test|ci|build|chore)(\(.+?\))?!?:') {
385+
$useGroups = $true
386+
break
387+
}
337388
}
338389
}
339390

@@ -352,10 +403,16 @@ if ($Repo -and ($Repo -match '/')) {
352403
}
353404
$tagCommitSha = ''
354405
$tagCommitShort = ''
355-
$tagSha = & git rev-parse "$Tag^{}" 2>$null
356-
if ($LASTEXITCODE -eq 0 -and $tagSha) {
357-
$tagCommitSha = $tagSha.Trim()
358-
if ($tagCommitSha.Length -ge 12) { $tagCommitShort = $tagCommitSha.Substring(0, 12) }
406+
$prevErrorActionPreference = $ErrorActionPreference
407+
$ErrorActionPreference = 'Continue'
408+
try {
409+
$tagSha = & git rev-parse "$Tag^{}" 2>$null
410+
if ($LASTEXITCODE -eq 0 -and $tagSha) {
411+
$tagCommitSha = $tagSha.Trim()
412+
if ($tagCommitSha.Length -ge 12) { $tagCommitShort = $tagCommitSha.Substring(0, 12) }
413+
}
414+
} finally {
415+
$ErrorActionPreference = $prevErrorActionPreference
359416
}
360417
$priorTagToken = if ($prevTag) { $prevTag } else { '' }
361418
$zipNameToken = if ($ZipName) { $ZipName } elseif ($ZipPath) { (Split-Path -Leaf $ZipPath) } else { '' }
@@ -406,7 +463,10 @@ if ($repoShort) {
406463
[void]$sb.AppendLine("## What's Changed")
407464
[void]$sb.AppendLine()
408465

409-
if ($useGroups) {
466+
if ($changelogNotes) {
467+
[void]$sb.AppendLine($changelogNotes)
468+
[void]$sb.AppendLine()
469+
} elseif ($useGroups) {
410470
$tagged = foreach ($e in $entries) {
411471
$cat = Get-Category $e.Subject
412472
[pscustomobject]@{ Order = $cat.Order; Name = $cat.Name; Entry = $e }

.github/workflows/release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ jobs:
110110
TAG_NAME: ${{ github.ref_name }}
111111
GITHUB_REPOSITORY: ${{ github.repository }}
112112
# gh release list is used by the prev-tag resolver's subject-match
113-
# fallback. Without it the resolver silently falls through to the
114-
# root walk, which produces a giant slice on rebased histories.
113+
# fallback. Without it the resolver may treat the tag as the first
114+
# release and use the curated changelog section.
115115
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
116116
run: |
117117
# Composes the full release body: title + auto-changelog slice +

0 commit comments

Comments
 (0)