Skip to content

fix(release): generate release notes against an explicit previous tag - #587

Closed
dcalhoun wants to merge 1 commit into
trunkfrom
fix/release-notes-previous-tag
Closed

fix(release): generate release notes against an explicit previous tag#587
dcalhoun wants to merge 1 commit into
trunkfrom
fix/release-notes-previous-tag

Conversation

@dcalhoun

@dcalhoun dcalhoun commented Aug 18, 2026

Copy link
Copy Markdown
Member

What?

Generated release notes restated every PR back to v0.16.0 on every release since v0.17.1. This makes the release lane pass an explicit previous tag instead of letting GitHub infer one.

The already-published notes have been regenerated separately (see below), so this PR is what stops the next release from reintroducing the problem.

Why?

Every GitHub Release from v0.17.1 through v0.20.0-alpha.0 listed months of already-shipped work — v0.20.0-alpha.0 showed 24 PRs where only 3 were new — and all of them reported compare/v0.16.0....

GitHub picks the notes base by walking tags newest-to-oldest and taking the first whose commit is an ancestor of the one being released. Our release tags never satisfy that. publish_release_to_github tags a Package.swift rewrite committed on a local release/vX.Y.Z branch that is never pushed, so no release tag is reachable from any other:

v0.16.0           -> ON trunk       ← lightweight tag predating this flow; the only candidate left
v0.17.1 … v0.19.0 -> NOT on trunk

GitHub falls back to v0.16.0 and re-lists everything since. Releases before v0.17.1 were unaffected — they predate this tagging flow, so the inference worked.

The off-trunk tag placement is deliberate and documented — it's what lets the tag carry a Package.swift pointing at the prebuilt XCFramework while trunk keeps .local. This PR leaves that design alone and fixes the notes instead.

How?

set_github_release can't express previous_tag_name (it only forwards a generate_release_notes boolean), so generated_release_notes calls the releases/generate-notes endpoint directly and passes the result through as description.

previous_release_tag resolves the base as the most recent stable release older than the version being published. Prereleases are skipped as candidates, matching GitHub's default — a stable release reports everything since the last stable release, including work already listed in its own alphas.

It reads the Releases API rather than local tags: CI checkouts may not have fetched every tag, the API reports prerelease authoritatively rather than us inferring it from the tag name, and it ignores stray tags never published as releases (vtest-s3-xcframework-*).

Resolution fails loudly if no base is found. A silently wrong base looks like a successful release and is only caught by someone reading the release page later.

validate also resolves and logs the base. That step runs before anything is published, so a wrong base surfaces while a re-run is still free. It recomputes rather than threading the value through, because Buildkite runs validate as a separate step — separate agent, separate process, no shared lane_context.

Testing Instructions

The release lanes hard-fail outside CI, so this can't be exercised end to end locally. The first real proof is the next release.

Confirm the cause still holds — release tags are unreachable from trunk, which is why the inference fails:

for t in v0.16.0 v0.17.1 v0.18.0 v0.19.0; do
    git merge-base --is-ancestor "$t^{commit}" origin/trunk \
        && echo "$t ON trunk" || echo "$t NOT on trunk"
done

Confirm every published release now chains to its immediate predecessor, with no gaps or repeats:

gh api 'repos/wordpress-mobile/GutenbergKit/releases?per_page=100' --jq '.[].tag_name' |
    while read -r t; do
        base=$(gh release view "$t" --json body --jq '.body' |
            grep 'Full Changelog' | sed 's|.*compare/||; s|\.\.\..*||')
        printf '%-24s base=%s\n' "$t" "${base:-<none>}"
    done

This is also the check worth re-running after the next release publishes — it is the invariant this PR is meant to preserve.

Review focus: previous_release_tag's filtering and semver comparison. Cases verified against the live release list — v0.20.0-alpha.0 → v0.19.0, v0.20.0 → v0.19.0, v0.19.0 → v0.18.1, v0.18.0 → v0.17.2, v0.17.1 → v0.16.0, v1.0.0 → v0.19.0, plus both nil paths (no prior releases, prereleases only).

Already-published notes

Regenerated ahead of this PR, oldest first, so the history reads correctly today rather than waiting on a merge:

Release Base was Base now PRs
v0.17.1 v0.16.0 unchanged 5 — was already correct
v0.17.2 v0.16.0 v0.17.1 7 → 2
v0.18.0 v0.16.0 v0.17.2 10 → 2
v0.18.1 v0.16.0 v0.18.0 10 → 0
v0.19.0-alpha.0 v0.16.0 v0.18.1 22 → 12
v0.19.0 v0.16.0 v0.18.1 22 → 12
v0.20.0-alpha.0 v0.16.0 v0.19.0 24 → 3

Only the release body changed — tags, assets, checksums, and prerelease flags are untouched, so nothing SPM consumers resolve is affected.

Two judgment calls worth surfacing:

Release notes have restated every PR back to v0.16.0 since v0.17.1.

GitHub infers the notes base by walking tags newest-to-oldest and taking
the first whose commit is an ancestor of the one being released. Our
release tags never satisfy that: each points at a Package.swift rewrite
committed on a local release/vX.Y.Z branch that is never pushed, so no
release tag is reachable from any other. GitHub falls back to v0.16.0 —
the last tag that does sit on trunk, created before this flow existed.

Resolve the base explicitly instead. set_github_release cannot express
previous_tag_name, so call the generate-notes endpoint directly and pass
the result through as `description`.

The base is the most recent stable release older than the version being
published; prereleases are skipped as candidates, matching GitHub's
default. Resolution is also run in `validate`, before anything is
published, so a wrong base surfaces while a re-run is still free.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added the [Type] Bug An existing feature does not function as intended label Aug 18, 2026
@dcalhoun dcalhoun closed this Aug 18, 2026
@dcalhoun
dcalhoun deleted the fix/release-notes-previous-tag branch August 18, 2026 11:56
@dcalhoun

Copy link
Copy Markdown
Member Author

Superseded by #588.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

[Type] Bug An existing feature does not function as intended

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant