ci(config): add independent release automation for @supabase/config (CLI-2233) - #6381
Conversation
…review (CLI-2233) The publish job now publishes the exact tarball the approver reviewed (artifact handoff; no install or build in the job holding id-token), the plan job asserts the config-release environment has required reviewers before a real release, commit-derived text is fenced/bracketed out of the approval evidence, registry tarballs are integrity- and origin-checked, the commit filter is -z clean for non-ASCII paths, a missing baseline config-v* tag fails the plan loudly, and a dist-less published tarball degrades to a warning instead of blocking every release.
Coly010
force-pushed
the
columferry/cli-2233-set-up-independent-release-automation-for-supabaseconfig
branch
from
August 30, 2026 13:35
45aef83 to
dfdfdeb
Compare
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.
Closes CLI-2233
Stacked on #6366 (compiled build + trimmed surface) — review only the top three commits until that merges.
What changed
@supabase/configgets its own release train, fully independent of the CLI's semantic-release flow:.github/workflows/release-config.yml, triggered only by pushes todeveloptouchingpackages/config/**.Version computation (plan job)
packages/config/scripts/release-plan.ts) withtagFormat: config-v${version}— semantic-release's tag matching is anchored, so theconfig-v*and CLIv*trains can't see each other's tags (verified in both directions against the installed v25 source).packages/config/**by an in-repo plugin (scripts/semantic-release-path-filter.ts): one batchedgit diff-tree --stdin -r --root --name-only -zcall, then delegation to the real@semantic-release/commit-analyzer/release-notes-generator.semantic-release-monorepowas considered and rejected (last published Feb 2024, unproven against v25's ESM internals); the wrapper is small and unit-tested, including the case that justifies its existence — a breaking change outsidepackages/config/must not produce a major — plus merge-commit, root-commit (--root), and non-ASCII-path (-zvscore.quotePath) edges.config-v*tag: with none, semantic-release would cut1.0.0with notes generated from the entire monorepo history — a whole-history changelog as the approval artifact. Escape hatch:CONFIG_RELEASE_ALLOW_NO_BASELINE=1.private: true(until CLI-2169 flips it), the plan job still runs everything — version, gate, pack — and reportsblocked_on_private; every config push to develop rehearses the plan half of the train while publishing nothing.The release-time hard gate (deferred here from #6366)
tools/config-release-gate.tsdiffs the freshly builtdist/**/*.d.tsagainst the previously published npm tarball's declarations and writes the diff to the job summary, with semver-sanity warnings (surface changed on a patch bump; a.d.tsfile removed, or declaration lines removed from an existing one, on a non-major bump). It shares its diff machinery with the PR-time advisory compare via a newtools/lib/dts-diff.ts(behavior-preserving extraction fromtools/config-api-compare.ts, verified byte-identical output). The gate itself never fails on a diff — the human approving theconfig-releaseenvironment, with the plan job's summary in front of them, IS the gate. Handled explicitly: first publish (E404, parsed fromnpm view --json, not substring-matched), a published tarball with nodist/(warns instead of blocking every subsequent release), an unparseable published version (bump class degrades to "unknown" instead of exit 2). Downloaded tarballs are verified against the registry'sdist.integrity, refused if their URL points off-registry, and extracted with--no-same-owner --no-same-permissions.Publish job — the approved bytes are the published bytes
The plan job packs the tarball the gate evidence describes and uploads it as a workflow artifact; the publish job downloads that exact tarball, verifies it (
dist/index.jspresent — the.gitignore/packlist trap, name/version match,privatenot true), and publishes its extracted content withpnpm publish --provenance --tag latest --no-git-checks. Nopnpm installand no build run in the job that holdsid-token: write— the only repo code it executes is the workflow file itself. The rest mirrorsrelease-shared.yml's hardened posture: github-hosted runner (npm provenance rejects self-hosted), OIDC trusted publishing (no NPM_TOKEN), idempotent registry probe and idempotentconfig-v*tag push, GitHub Release from the semantic-release notes withmake_latest: "false"so a config release can never hijack thereleases/latest/download/...URLs the CLI install scripts and setup-cli resolve.Approval-evidence integrity: the plan job asserts the
config-releaseenvironment actually has a required-reviewers rule before a real release can reach the publish job (auto-created environments have no protection rules); semantic-release's commit-derived stdout is bracketed with::stop-commands::so a crafted commit message can't issue workflow commands (e.g.::add-mask::over the gate output); and everything commit- or registry-derived that lands in the step summary is rendered inside dynamically-sized fences with HTML-escaped paths, so a fenced code block in a JSDoc comment or PR title can't forge the approver's evidence.package.json'sversionis never committed; it's set at pack time from the computed version, same convention as the CLI's own train.Decisions (flagging for override)
develop, npm dist-taglatest, no beta/alpha and no git-notes channel machinery. The CLI's three channels exist for binary distribution rings; a library doesn't need them, and adding a prerelease branch later is additive.versioninput on workflow_dispatch — the registry probe makes re-runs idempotent, and with no binary artifacts plus a human approval in the loop, the CLI's cut-forward escape hatch isn't worth a second code path; recovery is "land a new releasable commit".One-time setup before the first real publish (CLI-2169)
Documented in
packages/config/AGENTS.md→ "Releases":config-releaseenvironment (the plan job fails closed until this exists).supabase/cli, workflowrelease-config.yml, environmentconfig-release.config-v0.1.0) — required, see above.config-v*(the last tag is the version oracle).Also here: npm-page README pass (CLI-2169, second commit)
Quickstart moved to the top with a pure-entrypoint example, the remaining relative links made absolute, the semver section now describes the live release pipeline instead of "planned under CLI-2233", Development section labeled contributor-facing, License section added, no bare issue IDs left.
Review
Engineer and security reviews both ran before this PR; every blocking and medium finding is fixed in the third commit (artifact handoff, gate-armed assertion, markdown-fence injection,
-zpath quoting, dist-less tolerance, baseline guard, tarball integrity/origin checks, stop-commands bracketing, version-shape validation, notes trailing-newline). Noted but deliberately not done:feat(config):bumps the CLI version too, as it always has) — the "vice versa" half of the filtering would change the CLI's established release behavior and deserves its own issue.privateflips — the first real publish is also the first execution of the OIDC path, which is why the bootstrap in AGENTS.md is manual and deliberate.~/.releasercleakage into localrelease-plan.tsruns (cosmiconfig searches up to$HOME): harmless in CI, where no such file exists.