Skip to content

Commit ed426e5

Browse files
authored
feat(config): publish @supabase/config to npm (CLI-2169) (#6423)
## What kind of change does this PR introduce? Feature — flips `packages/config` to `private: false`, activating the publish half of the release pipeline that landed in #6381, and drops the README's "not yet published" caveat. ## What is the current behavior? `@supabase/config` is `private: true`: every push touching `packages/config/**` rehearses the plan half of the Release Config workflow (version computation, build, pack, type-surface gate), but `should_release` stays false and nothing publishes. npm currently hosts only a `0.0.0` placeholder stub. ## What is the new behavior? Merging this PR is the package's first real release. The squash commit (`feat(config)`) is the first releasable commit since the `config-v0.0.0` baseline tag, so the Release Config workflow will: 1. plan **0.1.0** (minor over the 0.0.0 stub baseline), 2. pack the tarball and generate the approval evidence — since the stub contains no `.d.ts`, the type-surface gate renders the **entire public surface as additions** for the approver, 3. wait on `config-release` environment approval (required reviewers configured), 4. publish that exact tarball via OIDC trusted publishing, push `config-v0.1.0`, and create the GitHub release (never repo-"latest"). Go-live preconditions, all in place: npm trusted publisher configured and bootstrap token revoked (verified with infra/security), `config-release` environment armed with required reviewers, `config-v0.0.0` baseline tag pushed at ed81a1c and verified green (plan run reports "0 commits: no release" pre-merge). Part of CLI-2169.
1 parent f50e083 commit ed426e5

6 files changed

Lines changed: 38 additions & 33 deletions

File tree

.github/workflows/release-config.yml

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,9 @@ jobs:
102102
fi
103103
104104
# The build, gate, and pack steps also run on private-blocked pushes
105-
# (should_release=false, version set) — every config push rehearses the
106-
# plan half of the release train while CLI-2169 hasn't flipped `private`
107-
# yet. The publish half stays unexercised until then.
105+
# (should_release=false, version set): if `private` were ever flipped
106+
# back on, every config push would still rehearse the plan half of the
107+
# release train while the publish half stays parked.
108108
- name: Build @supabase/config
109109
if: steps.plan.outputs.version != ''
110110
run: pnpm exec turbo run @supabase/config#build
@@ -159,8 +159,7 @@ jobs:
159159
# would run straight through unreviewed. Fail closed here — before a
160160
# real (non-dry) release can reach the publish job — if the rule is
161161
# missing or unreadable. Private-blocked rehearsals (should_release
162-
# false) are unaffected, so this only bites once CLI-2169 flips
163-
# `private`, which is exactly when it must.
162+
# false) are unaffected — this only gates real releases.
164163
- name: Assert the release approval gate is armed
165164
if: steps.plan.outputs.should_release == 'true' && steps.plan.outputs.dry_run != 'true'
166165
env:
@@ -264,7 +263,7 @@ jobs:
264263
[[ "$(jq -r .name package/package.json)" == "@supabase/config" ]]
265264
[[ "$(jq -r .version package/package.json)" == "${VERSION}" ]]
266265
if [[ "$(jq -r .private package/package.json)" == "true" ]]; then
267-
echo "packages/config is still private: true — flip it under CLI-2169 before publishing." >&2
266+
echo "packages/config is private: true — refusing to publish a private manifest (was it flipped back deliberately?)." >&2
268267
exit 1
269268
fi
270269

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ Generic linting (`oxlint`), formatting (`oxfmt`), and unused-code analysis (`kni
3434
Expected exceptions:
3535

3636
- `apps/cli` is published, so it is not `private`
37+
- `packages/config` is published (on its own release train — see `packages/config/AGENTS.md`), so
38+
it is not `private`
3739
- `apps/docs` is a Next.js app and does not follow the standard package template
3840
- `packages/cli-*` are binary wrapper packages and do not follow the standard TypeScript workspace template
3941

packages/config/AGENTS.md

Lines changed: 26 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -156,24 +156,29 @@ elsewhere in the monorepo never releases `@supabase/config`, and vice versa.
156156
- **Local dry runs:** `scripts/release-plan.ts` runs the plan locally without publishing;
157157
`tools/config-release-gate.ts --tarball` rehearses the type-surface gate locally.
158158

159-
### One-time setup (tracked under CLI-2169)
160-
161-
Four things must be settled before the first real publish:
162-
163-
1. The `config-release` GitHub environment needs required reviewers configured in repo settings. An
164-
environment referenced by a workflow is auto-created WITHOUT protection rules — the plan job
165-
asserts the rule exists and refuses to plan a real release until it does, so the first release
166-
attempt fails closed rather than publishing unreviewed.
167-
2. npm trusted publishing must be configured for the package, which requires the package to exist
168-
first. The very first publish is a manual bootstrap — use a granular, single-package,
169-
short-expiry token and revoke it as soon as the trusted publisher is configured (repo
170-
`supabase/cli`, workflow `release-config.yml`, environment `config-release`).
171-
3. Push a baseline `config-v*` tag (e.g. `config-v0.1.0`) on a `develop` commit. This is required,
172-
not optional: with no baseline, semantic-release would cut `1.0.0` with release notes generated
173-
from the entire monorepo history — a whole-history changelog as both the approval artifact and
174-
the public GH release body. `scripts/release-plan.ts` refuses to plan without a baseline tag
175-
(escape hatch: `CONFIG_RELEASE_ALLOW_NO_BASELINE=1`). This is the single exception to the
176-
"never hand-push a `config-v*` tag" rule above.
177-
4. Add a repository tag ruleset protecting `config-v*` (alongside `v*`), restricted to the release
178-
App. The last `config-v*` tag is the version oracle: a stray hand-pushed tag permanently skews
179-
versioning, and a deleted tag makes the next plan re-cut an already-published version.
159+
### Standing release configuration (set up under CLI-2169)
160+
161+
The one-time go-live setup is complete. These are the standing invariants — verify them if a
162+
release fails unexpectedly, and restore them if repo or npm settings are ever rebuilt:
163+
164+
1. **The `config-release` GitHub environment has required reviewers.** An environment referenced
165+
by a workflow is auto-created WITHOUT protection rules — the plan job asserts the rule exists
166+
and refuses to plan a real release without it, so a stripped environment fails closed rather
167+
than publishing unreviewed.
168+
2. **npm trusted publishing is configured** for the package (repo `supabase/cli`, workflow
169+
`release-config.yml`, environment `config-release`); no `NPM_TOKEN` exists anywhere. Trusted
170+
publishing can only be configured on a package that already exists, so the package was seeded
171+
with a manually published `0.0.0` placeholder (no `dist/`), and the bootstrap token was
172+
revoked immediately after.
173+
3. **The baseline tag `config-v0.0.0` matches that placeholder** — the tag oracle and the registry
174+
must always agree on the last released version. With no baseline tag, semantic-release would
175+
cut `1.0.0` with release notes generated from the entire monorepo history — a whole-history
176+
changelog as both the approval artifact and the public GH release body — so
177+
`scripts/release-plan.ts` refuses to plan in that state (escape hatch:
178+
`CONFIG_RELEASE_ALLOW_NO_BASELINE=1`). Seeding it was the single exception to the "never
179+
hand-push a `config-v*` tag" rule above.
180+
4. **The "Protect `config-v*` release tags" ruleset** restricts creating, moving, and deleting
181+
`config-v*` tags to the `supabase-cli-releaser` App (the same App the release workflows mint
182+
tokens from). The last `config-v*` tag is the version oracle: a stray hand-pushed tag
183+
permanently skews versioning, and a deleted tag wedges the next plan on an already-published
184+
version.

packages/config/README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ import { getDefaultCliConfig, toProjectConfig } from "@supabase/config";
3030
const projectConfig = toProjectConfig({ cliConfig: getDefaultCliConfig() });
3131
```
3232

33-
This package is not yet published (`private: true`; publishing is tracked separately). Once it
34-
is, install it alongside the peers your runtime needs.
33+
Install it alongside the peers your runtime needs.
3534

3635
This package requires Effect 4.x, currently only published under the `rc` dist-tag — `effect@latest`
3736
still resolves to 3.x, which will not satisfy this package's peer range.

packages/config/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@supabase/config",
33
"version": "0.1.0",
4-
"private": true,
4+
"private": false,
55
"description": "Supabase project configuration schema, parsing, and validation, built on Effect Schema.",
66
"keywords": [
77
"config",

packages/config/scripts/release-plan.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -174,8 +174,8 @@ export function renderStepSummary(plan: ReleasePlan): string {
174174
if (plan.isPrivate) {
175175
lines.push(
176176
"> [!WARNING]",
177-
"> `packages/config` is still `private: true`, so publishing is blocked — flip it under " +
178-
"CLI-2169. This run validated the release pipeline only; nothing will be published.",
177+
"> `packages/config` is `private: true`, so publishing is blocked. This run validated the " +
178+
"release pipeline only; nothing will be published.",
179179
"",
180180
);
181181
}
@@ -204,7 +204,7 @@ function renderLocalPlan(plan: ReleasePlan): string {
204204
`${PACKAGE_PATH_PREFIX} since the last config-v* tag).`
205205
);
206206
}
207-
const privateNote = plan.isPrivate ? " (blocked: packages/config is still private: true)" : "";
207+
const privateNote = plan.isPrivate ? " (blocked: packages/config is private: true)" : "";
208208
return `[release-plan] @supabase/config would release ${plan.version} (${plan.bumpType})${privateNote}.`;
209209
}
210210

0 commit comments

Comments
 (0)