refactor(cli): remove rawProjectConfig from ProjectContext#5281
Merged
Conversation
The Bun shell's projectContextLayer eagerly decoded supabase/config.toml through a strict Effect schema on every command boot, rejecting any env(VAR) literal on non-string fields with ProjectConfigParseError. This broke supabase db start (and every other legacy command that just proxies to the Go binary) for users whose config.toml references env() on numeric, boolean, or enum fields - despite the Go binary itself still resolving those references correctly. The decoded ProjectConfig was unused: cliConfigLayer only reads projectEnv, projectHomeLayer only reads paths, and no other consumer exists in apps/cli/src. Remove the load entirely and drop the rawProjectConfig field from the ProjectContext shape. functions dev keeps its own loadProjectConfig call - that surface remains tracked under the parent issue for a proper TS-side fix. Refs CLI-1489.
Boots a real supabase subprocess against a config.toml whose numeric port fields are written as env(VAR) references. Pre-fix, the Bun shell crashed at layer construction with ProjectConfigParseError: Expected number, got "env(SUPABASE_ANALYTICS_PORT)". Post-fix, the layer skips the strict decode and the command completes normally. RED/GREEN locally verified via the colocated unit test (same scenario, identical error message reproduced when the loadProjectConfig call is restored). The e2e variant covers the full subprocess + shim path that the customer hit through supabase/setup-cli@v1.
The Go CLI binary was renamed from `cli` to `supabase-go` during the monorepo cutover, but .gitignore was not updated to match. Cover the new artifact name so local builds don't produce an untracked binary.
Coverage Report for CI Build 26051249899Warning No base build found for commit Coverage: 63.744%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsRequires a base build to compare against. How to fix this → Coverage Stats
💛 - Coveralls |
The colocated e2e test in apps/cli only exercised the next shell at the apps/cli build artifact level. Moving the regression to apps/cli-e2e lets it run against every CLI target (go, ts-legacy, ts-next) via the existing CLI_HARNESS_TARGET matrix. The test writes a supabase/config.toml with env(VAR) references on numeric port fields, injects a 401 so no real API fixture is required, runs secrets list, and asserts neither ProjectConfigParseError nor the "Expected number, got \"env(...\"" message appears in output. Verified RED on the pre-fix layer for both ts-legacy and ts-next, GREEN on the fix. The Go target is unaffected by the bug and trivially passes.
Coly010
approved these changes
May 18, 2026
avallete
pushed a commit
that referenced
this pull request
May 20, 2026
…oval Resolve api.ts conflict (keep auto_expose_new_tables alongside the Effect.succeed default-key update from develop) and read the auto_expose_new_tables flag via loadProjectConfig now that ProjectContext no longer exposes rawProjectConfig (#5281).
Coly010
added a commit
that referenced
this pull request
May 22, 2026
Fixes the crash when `supabase/config.toml` uses `env(VAR)` on numeric
or boolean fields (e.g. `analytics.port =
"env(SUPABASE_ANALYTICS_PORT)"`). The strict Effect Schema decode ran
immediately after raw TOML parse, with `interpolateValue` in
`project.ts` only firing post-decode via `resolveProjectValue` — so it
never got the chance to substitute the string before `Schema.Number`
rejected it.
## What changed
- **Pre-decode env() interpolation in `packages/config/src/io.ts`** —
`loadProjectConfigFile` now loads the project environment
(`.env`/`.env.local`/ambient) and runs a schema-aware walker on the
parsed document before handing it to
`Schema.decodeUnknownSync(ProjectConfigSchema)`.
- **Schema-aware walker in `packages/config/src/lib/env.ts`** —
traverses both the parsed document and `ProjectConfigSchema.ast` in
parallel. For string leaves matching `env(VAR)`: substitutes against the
env, then coerces to Number/Boolean if the schema at that path expects
one. Mirrors Go's `LoadEnvHook` + mapstructure type chain
(`apps/cli-go/pkg/config/decode_hooks.go:14-21` → subsequent string→type
conversion hooks).
- **Verbatim-on-missing semantics** — `interpolateLeafValue` in
`project.ts` no longer throws `MissingProjectEnvVarError` when the
referenced env var is unset. It returns the literal `env(VAR)` string,
matching Go parity. The `MissingProjectEnvVarError` class and re-export
are removed; `resolveProjectValue` / `resolveProjectSubtree` no longer
have a failure channel.
- **Fields declared with the `env()` schema helper opt out** via the
`x-env-deferred` marker annotation. They still require the literal
`env(VAR)` format for post-decode resolution by `resolveProjectValue` —
the walker honors the marker and leaves those paths untouched.
## Reviewer-relevant context
- **No schema-file edits.** Coercion lives entirely in the walker, so
future fields added to any of the section schemas (`db.ts`,
`analytics.ts`, `auth/*.ts`, etc.) automatically work with `env()`
references — no risk of a contributor forgetting to use a coerced
primitive at declaration time.
- **The marker annotation lives on the check, not the outer AST.**
`env() = Schema.String.check(isPattern(envRegex)).annotate({...})`
attaches metadata to the resulting Filter rather than the base String
AST, so `isDeferredEnvField` inspects both `node.annotations` and
`node.checks[].annotations`. Caught by the `@supabase/stack`
`functions.unit.test.ts` regression for `functions.<name>.env` (env()
helper at a record value position).
- **Missing-env semantics in `project.ts` are now non-failing.** Two
existing "fails when missing env var" tests in
`project.unit.test.ts:223,255` are rewritten to assert verbatim
preservation. `redactValue` already skips redaction when the value is
still an env reference (`!isEnvReference(value)`), so unresolved
literals flow through as plain strings — no Redacted wrapping on missing
secrets.
- **The next/projectContextLayer workaround (PR #5281) is left in
place.** That layer dropped the `loadProjectConfig` call entirely to
avoid the crash; reintroducing it is a follow-up refactor since the
workaround still functions correctly (it only loads env, not config).
- The existing CLI-1489 regression test at
`apps/cli/src/next/config/project-context.layer.unit.test.ts:30` is not
modified — it asserts the workaround-era behavior of
`projectContextLayer`, which is unchanged. Direct upstream regression
coverage is added in `packages/config/src/io.unit.test.ts`: numeric
coercion, boolean coercion, verbatim preservation, ambient fallback, and
decode failure when an unset var is referenced from a numeric field.
Fixes CLI-1489
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.
Remove the unused
rawProjectConfigfield fromProjectContextand stop loading the raw project config in the project context layer.Changes
rawProjectConfig: Option.Option<ProjectConfig>fromProjectContextShapeinterfaceloadProjectConfigcall fromprojectContextLayer— onlyloadProjectEnvironmentis now usedmockProjectContexttest helper to remove therawProjectConfigoption parameterprojectContextLayercovering the scenario whereconfig.tomlusesenv()on numeric fields (CLI-1489) and the case where no Supabase project is foundImplementation Details
The
rawProjectConfigfield was not being used anywhere in the codebase. By removing it, we simplify theProjectContextservice and reduce unnecessary I/O during initialization. The project context now only loads and exposes the project environment configuration, which is what consumers actually need.https://claude.ai/code/session_01C7reiRogHr6WXQoKiEDQeW