ci: fix backfill-release-notes branch and channel detection#5319
Merged
Conversation
The first attempts to backfill v2.99.0-beta.1 surfaced two issues: - semantic-release uses env-ci to determine the current branch from GITHUB_REF / GITHUB_REF_NAME, not from `git rev-parse --abbrev-ref HEAD`. The backfill workflow is dispatched from whatever branch hosts it, which is not in the package.json release.branches config, so semantic-release silently emits no new_release_* outputs and the verify step fails with an empty SR_VERSION. Override GITHUB_REF / GITHUB_REF_NAME after the synthesised `git checkout -B develop|main`. - semantic-release reads `refs/notes/semantic-release-<tag>` to figure out each tag's channel, and actions/checkout doesn't fetch refs/notes/* by default. Without them, even reachable tags like v2.98.2 look like channels=[null] and get filtered out (see ci-beta-tags-investigation.md). Fetch refs/notes/* explicitly before invoking semantic-release. Also normalise the tag input so both "v2.99.0-beta.1" and "2.99.0-beta.1" work, with a clearer error message when the tag isn't found locally instead of git's exit code 128.
The previous commit added tolerance for tag inputs without the `v` prefix and a clearer error when the tag isn't found locally. The operator passes the GitHub tag verbatim, so the defensive handling is dead weight - drop it and let `git rev-list` surface the raw error if the input is wrong.
The bash steps in backfill-release-notes.yml worked but couldn't get the
"behind remote" / branch-config / channel-notes interactions right
without a lot of inline workarounds, and the rendered notes came out as
terminal-formatted text via marked-terminal instead of raw markdown the
GitHub Release body needs.
Move all of it into apps/cli/scripts/backfill-release-notes.ts. The
script does the same temp-clone setup (notes fetch, branch seeding,
channel-note backfill, package.json release-config patch, insteadOf URL
rewrite) and then calls semantic-release programmatically with
{dryRun: true, noCi: true} - that returns nextRelease.notes as raw
markdown, which is what `gh release edit --notes-file` wants. Same
script handles both the dry-run case (prints to stdout) and the apply
case (--apply, calls gh release edit).
The workflow now reduces to: checkout, setup, `bun apps/cli/scripts/
backfill-release-notes.ts --tag $TAG`, mirror the output to the job
summary, and conditionally re-run with --apply.
Adds semantic-release as a devDependency of apps/cli; it's only used
from the script, so knip's ignoreDependencies covers it.
Coly010
approved these changes
May 20, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the backfill-release-notes workflow so it actually produces the right notes for an arbitrary historical tag, and moves the engine out of inline bash into a reusable bun script.
Why the original workflow couldn't produce correct notes
Backfilling an old tag isn't symmetric with running semantic-release on a normal push — it trips on five separate things at once, each of which silently sends it down a wrong path:
cycjimmy/semantic-release-actionreads the branch viaenv-ci($GITHUB_REF/$GITHUB_REF_NAME), notgit rev-parse --abbrev-ref HEAD. Dispatching the workflow from any branch other thandevelop/mainleft semantic-release looking at an unconfigured branch and silently emitting nonew_release_*outputs.git ls-remote <repositoryUrl> <branch>and exits silently if the remote tip differs from local HEAD — which is always true when re-staging at an old tag.git log --notes=refs/notes/semantic-release*.actions/checkoutdoesn't fetchrefs/notes/*by default, and some historical tags (e.g.v2.99.0-beta.1) have no channel annotation at all — leaving them aschannels=[null], which the prerelease filter drops. semantic-release then walks past them andlastReleasedrifts back far enough to drag unrelated commits into the changelog.release.branchesconfig. Before commit2515885(May 11) thedevelopbranch had no explicit"channel": "beta", so semantic-release defaulted the channel to the branch name; before ci(release): add manual backfill-release-notes workflow #5316 the plugin chain didn't includerelease-notes-generator. A historical checkout therefore produces empty or misclassified notes.cycjimmy/semantic-release-action's stdout returns marked-terminal rendered ANSI/whitespace, not raw markdown — so even when the right notes were computed, the GH release body would render wrong.What this PR does
Moves the workflow's logic into
apps/cli/scripts/backfill-release-notes.tsand callssemantic-releaseprogrammatically so it can returnnextRelease.notesas raw markdown directly. The script's setup works around each of the issues above in a temp clone (so the original workspace stays clean):refs/notes/*from both the source repo and origin.develop/mainbranch at the tag's commit and seeds the other configured branch fromrefs/remotes/origin/<other>.v*-beta.*→beta,v*-alpha.*→alpha, elselatest).apps/cli/package.jsonwith the currentreleaseconfig so historical checkouts use today'schannel: "beta"and plugin chain.git config --local url.<local>.insteadOf <github>so semantic-release'sls-remotesilently targets the local clone (satisfying the "behind remote" check) whilerepositoryUrlstays the real GitHub URL — keeping commit/PR links correct in the rendered notes.semanticRelease({ dryRun: true, noCi: true, repositoryUrl }, { cwd: ... })and printsnextRelease.notesto stdout, or with--applycallsgh release edit --notes-file.The workflow itself reduces to: checkout, setup,
bun apps/cli/scripts/backfill-release-notes.ts --tag $TAG, mirror to the job summary, then conditionally re-run with--apply.Sample output (
v2.99.0-beta.2)Exactly the 5 commits between
v2.99.0-beta.1andv2.99.0-beta.2, with GitHub links rendered againsthttps://github.com/supabase/cliregardless of the local-clone redirection.Related
The same
release.repositoryUrl/ls-remoteinteraction and channel-notes drift will need to be addressed on the production publish path (.github/workflows/release-shared.yml) once Stage B of the changelog plumbing (claude/wire-release-notes-publish) lands.