Skip to content

ci: fix backfill-release-notes branch and channel detection#5319

Merged
avallete merged 4 commits into
developfrom
claude/fix-backfill-release-notes
May 20, 2026
Merged

ci: fix backfill-release-notes branch and channel detection#5319
avallete merged 4 commits into
developfrom
claude/fix-backfill-release-notes

Conversation

@avallete
Copy link
Copy Markdown
Member

@avallete avallete commented May 20, 2026

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:

  • Branch detection. cycjimmy/semantic-release-action reads the branch via env-ci ($GITHUB_REF / $GITHUB_REF_NAME), not git rev-parse --abbrev-ref HEAD. Dispatching the workflow from any branch other than develop/main left semantic-release looking at an unconfigured branch and silently emitting no new_release_* outputs.
  • "Behind remote" check. semantic-release runs 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.
  • Channel notes for historical tags. semantic-release reads git log --notes=refs/notes/semantic-release*. actions/checkout doesn't fetch refs/notes/* by default, and some historical tags (e.g. v2.99.0-beta.1) have no channel annotation at all — leaving them as channels=[null], which the prerelease filter drops. semantic-release then walks past them and lastRelease drifts back far enough to drag unrelated commits into the changelog.
  • Drift in release.branches config. Before commit 2515885 (May 11) the develop branch 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 include release-notes-generator. A historical checkout therefore produces empty or misclassified notes.
  • Output format. Parsing 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.ts and calls semantic-release programmatically so it can return nextRelease.notes as raw markdown directly. The script's setup works around each of the issues above in a temp clone (so the original workspace stays clean):

  1. Clones the repo to a temp directory and fetches refs/notes/* from both the source repo and origin.
  2. Synthesises a develop/main branch at the tag's commit and seeds the other configured branch from refs/remotes/origin/<other>.
  3. Backfills missing channel notes on every reachable tag (v*-beta.*beta, v*-alpha.*alpha, else latest).
  4. Patches the temp clone's apps/cli/package.json with the current release config so historical checkouts use today's channel: "beta" and plugin chain.
  5. Uses git config --local url.<local>.insteadOf <github> so semantic-release's ls-remote silently targets the local clone (satisfying the "behind remote" check) while repositoryUrl stays the real GitHub URL — keeping commit/PR links correct in the rendered notes.
  6. Calls semanticRelease({ dryRun: true, noCi: true, repositoryUrl }, { cwd: ... }) and prints nextRelease.notes to stdout, or with --apply calls gh 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)

$ bun apps/cli/scripts/backfill-release-notes.ts --tag v2.99.0-beta.2
# [2.99.0-beta.2](https://github.com/supabase/cli/compare/v2.99.0-beta.1...v2.99.0-beta.2) (2026-05-20)


### Bug Fixes

* **ci:** make release re-cut reliable after stale-bytes runs ([#5209](https://github.com/supabase/cli/issues/5209)) ([ef1b13a](https://github.com/supabase/cli/commit/ef1b13a70d6a06087aa7fd58e336999e6d1e0fba)), closes [#5205](https://github.com/supabase/cli/issues/5205) [#5207](https://github.com/supabase/cli/issues/5207) [#5205](https://github.com/supabase/cli/issues/5205) [#5207](https://github.com/supabase/cli/issues/5207) [#App](https://github.com/supabase/cli/issues/App)
* **cli:** make npm publish idempotent for partial-failure re-runs ([#5207](https://github.com/supabase/cli/issues/5207)) ([bbeaec7](https://github.com/supabase/cli/commit/bbeaec7799fce3580ec3747ff097853a717a669a)), closes [#release-creation](https://github.com/supabase/cli/issues/release-creation)
* **cli:** mark platform binaries executable in pnpm publish tarball ([#5201](https://github.com/supabase/cli/issues/5201)) ([b2b397a](https://github.com/supabase/cli/commit/b2b397af205f772fb3e333e559378402e69bdd91)), closes [#5199](https://github.com/supabase/cli/issues/5199)
* **cli:** smoke test now actually verifies our local build ([#5205](https://github.com/supabase/cli/issues/5205)) ([9109056](https://github.com/supabase/cli/commit/910905698cc393e296ddf73076eb43ecb48ca6cf)), closes [#5199](https://github.com/supabase/cli/issues/5199) [#5200](https://github.com/supabase/cli/issues/5200)

Exactly the 5 commits between v2.99.0-beta.1 and v2.99.0-beta.2, with GitHub links rendered against https://github.com/supabase/cli regardless of the local-clone redirection.

Related

The same release.repositoryUrl / ls-remote interaction 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.

claude added 2 commits May 20, 2026 11:31
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.
@avallete avallete requested a review from a team as a code owner May 20, 2026 11:35
claude and others added 2 commits May 20, 2026 13:28
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.
@avallete avallete enabled auto-merge (squash) May 20, 2026 13:49
@avallete avallete merged commit 6f283f7 into develop May 20, 2026
9 checks passed
@avallete avallete deleted the claude/fix-backfill-release-notes branch May 20, 2026 14:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants