Repository navigation
chore: production deploy - #7004
Merged
Merged
Conversation
This bumps realtime from v2.140.3-r0 to v2.140.6-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.140.6-r0 Planned after supabase/slim-services published v2.140.5-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…hotfixes (#6926) Follows up the non-blocking review notes on #6883. - **Required fetchers.** `RevisionIo.fetchManifest` and `fetchProvenance` are now required, and `refreshCatalogPin` always resolves `upstreamImage`. The serializer only accepts a pin type where that field is required, so a rewritten `Artifacts.ts` always typechecks. - **One update per release line.** When a line upgrades, `planSlimUpdates` skips that line's hotfix. It emits a warning that `--service <svc> --release <U>-r<N>` can pin the hotfix alone. Otherwise the `slim-hotfix/<svc>[-<line>]` PR would conflict once the upgrade merges. Separate lines, such as postgres 17 and 15, stay independent. - **Docs over comments.** The sync script's mode docs and the `slim-release-published.yml` header move into ADR 0026: - a new "Invocations" section; - "Hotfix and upgrade pickup" now covers payload trust, the per-line rule, line assignment and app-token handling. Provenance, narration and over-long comments are trimmed across the files #6883 changed. - **Listing tests.** `services.shared.unit.test.ts` covers two `supabase services` cases against the real catalog again: - the default slim listing; - a catalog-pinned postgres 15 override. The auth-only fixture stays the default for the other tests.
## Current Behavior The Live E2E notifier works out the previous suite result by walking the workflow run history when it runs. That lookup misfires at event time: some failures are silently deduplicated against a failure that was never reported, and "✅ Live E2E recovered" gets posted after runs whose predecessor had passed. ## Expected Behavior The notifier records the newest suite result it has applied for each branch in a small state artifact, and decides from that record instead of the run history: - A failure is posted once. Repeated failures and reruns stay quiet until a pass re-arms the alert. - A pass posts "✅ Live E2E recovered" only when the recorded state is a reported failure, so each alert gets at most one recovery. Other passes only update the recorded state. - Each record carries its source run's creation time, and results older than the recorded one are ignored, so late or rerun events can't overwrite newer state. - If the recorded state can't be read, failures are still posted. - The state is keyed by a digest of the full branch name, and only artifacts from same-repository runs are trusted, so forks can't spoof it. The lookup pages through the artifact listing until it finds a trusted record. - Suite classification runs in its own job, so only real suite results queue on the per-branch lock that serializes state updates. The lock group can replace a pending run; if any notifier run was cancelled since the recorded failure, the next failure is posted instead of deduplicated. Each run logs the recorded state, the current result and the action taken. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This bumps edge-runtime from v1.77.1-r0 to v1.77.2-r0. https://github.com/supabase/slim-services/releases/tag/edge-runtime-v1.77.2-r0 Planned after supabase/slim-services published v1.77.2-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## TL;DR fixes two flaky tests in the slim images e2e and the stack functions integration suite - `start.slim-images` served the scaffolded `functions new` template, which pulls `jsr:` and `npm:` packages when the worker boots, so a fresh `@supabase/server` release that was still propagating failed the boot. the function is now dependency free and `new.stack.e2e` keeps covering the template imports - `serves a standalone Functions bootstrap over HTTP` ran on the 30s default even though it can pull the edge-runtime image first. it now gets the same 120s every other real image service test uses ## ref - spotted in: [slim images](https://github.com/supabase/cli/actions/runs/36699192281/job/109834573341) [functions bootstrap](https://github.com/supabase/cli/actions/runs/36713073189/job/109879274052)
## Summary
**TL;DR:** `cli_command_executed` now records which local backend a
command ran on (`stack_backend`: `legacy` or `stack`), which runtime its
stack uses (`stack_runtime`: `docker`, `podman`, or `native`), and
whether the project selects an OrioleDB image (`orioledb`). With these,
error rates can be compared per command between the legacy and new stack
backends (and per container or native runtime), and between OrioleDB and
standard Postgres projects, before the new stack becomes the default.
### Before
```mermaid
flowchart LR
A["supabase <command>"] --> B["resolve backend<br/>(legacy for unrouted)"]
B --> C["handler runs"]
C --> D["cli_command_executed<br/>no backend, no engine"]
```
### After
```mermaid
flowchart LR
A["supabase <command>"] --> B{"routed by<br/>experimental.stack?"}
B -->|yes| C["StackBackendContext<br/>legacy or stack"]
B -->|no| D["no backend"]
C --> E["handler runs"]
D --> E
E --> F["config read records<br/>orioledb true/false"]
E --> R["stack lookup records<br/>stack_runtime"]
F --> G["cli_command_executed<br/>+ stack_backend<br/>+ orioledb<br/>+ stack_runtime"]
R --> G
```
## Why
Command telemetry had no way to tell whether a command ran on the new
stack runtime or the legacy one, so we couldn't measure the new
runtime's adoption or failure rate. It also couldn't separate OrioleDB
projects from standard Postgres ones.
## What changed
- `resolveStackBackend` returns no backend for commands that
`experimental.stack` does not route (and for `--version`). The root
command provides `StackBackendContext` only when a backend was selected.
Handlers still default to legacy, so routing behavior is unchanged.
- `withCommandTelemetry` adds `stack_backend` when a backend was
selected. It also provides a command-scoped attribute sink, so code
below the handler can add properties to the command event.
- `orioledb` is recorded wherever local database config is resolved: the
legacy config reader, Postgres image resolution, and stack config
loading. The stack records it before rejecting OrioleDB, so failed stack
starts on OrioleDB projects are attributed. The value describes local
config only; it doesn't describe remote targets such as `--linked` or
`--db-url`.
- `stack_runtime` is recorded where a stack-backend command learns its
stack's runtime: runtime selection for new and shadow stacks, the
project stack lookup used by local database commands, and the stack
target resolver used by `stack` commands. Commands without a single
stack (stack-mode `services`, `stack stop --all`) omit it.
- Removed the unused shared `withCommandInstrumentation` wrapper and the
unused `flags_used`/`flag_values` context fields. `docs/analytics.md`
now describes the current wrapper, event shape, flag redaction and base
properties.
## Linked issue
Closes CLI-2589
Closes CLI-2590
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ss-throughs (#6953) ## Summary A shallow-module audit of `apps/cli` found two kinds of cleanup. Each candidate was checked with the deletion test: remove the module and paste its body into every caller. If the callers end up no longer, the module was only passing work through. **Dead code.** Knip did not flag these because each file's only importer was its own colocated unit test. - The superseded auth layer under `apps/cli/src/shared/auth/` (api, crypto, credentials, platform-api, token, errors). Live replacements are `src/auth/command-*.ts` and `src/command-internal/login-*.layer.ts`. - `shared/config/cli-project-local-service-versions.{service,layer}.ts`, plus the two matching die-placeholders in `run.ts` and the mock in `tests/helpers/mocks.ts`. - `shared/output/table.ts` and `shared/output/time.ts`. **Pass-throughs inlined or merged.** - `db-connection.layer.ts` was a two-line alias; the sql-pg layer now exports `dbConnectionLayer` directly. - `OutputFormatFlag` moves into `command-internal/global-flags.ts` with the other globals. - The region display table is hoisted once to `command-internal/region.format.ts` and shared by backups and projects. - `changedLinkedLocalFlags` is called directly by seed and storage instead of through per-command aliases; its coverage moves to the db-target-flags unit test. - `tracing.service.ts`, `db-bootstrap/messages.ts`, `standalone-analytics-config.layer.ts`, `supabaseHome()`, `output/bold.ts`, `go-string.ts`, `report.layers.ts`, and `prepare.options.ts` are inlined at their single caller. - `packages/cli-test-helpers` exports `harness.ts` directly instead of through a barrel. **Follow-up cleanups in later commits.** - The error-tag stability snapshot (`src/shared/telemetry/__fixtures__/error-tags.txt`) drops the four tags that belonged to the deleted modules: `ApiError`, `InvalidLocalServiceVersionsStateError`, `InvalidTokenError`, and `PlatformAuthRequiredError`. None of them reached production telemetry. - Docs that referenced the removed code are updated: the `apps/cli/README.md` architecture list now points at `src/auth/` and describes what remains in `src/shared/auth/`; `apps/cli/docs/tracing-monitoring.md` no longer names a `Tracing` service; `apps/cli/docs/code-structure.md` uses live symbols in its naming examples; and the `db start` side-effects note points at `start-database.ts` for the startup messages. - `docs/self-documenting-cli.md` was an orphaned copy still describing the long-removed `--usage` and `--skill` flags and guide templates. It now covers only the `generate-docs.ts` script and the Fumadocs site that exist today. No behaviour change. Span names and telemetry command paths are unchanged. Linear: [CLI-2606](https://linear.app/supabase/issue/CLI-2606/delete-dead-sharedauth-layer-and-inline-single-caller-pass-throughs-in) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This bumps realtime from v2.140.6-r0 to v2.140.7-r0. https://github.com/supabase/slim-services/releases/tag/realtime-v2.140.7-r0 Planned after supabase/slim-services published v2.140.7-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
…#6923) Docker-backed stack tests left a named `supabase-db-*` volume behind on every run. Docker database storage keeps one shared volume per state root and daemon. Stack destroy deliberately clears only the stack's namespace inside it, because the volume is shared with other stacks and holds the snapshot cache. Tests that run a Docker database under a temporary state root destroyed the stack and then deleted the root. After that, nothing could find the volume again. This PR cleans up those volumes in one place per test run instead of in each test: - When `SUPABASE_STACK_TEST_RUN` is set, `DockerDatabaseStorage` adds a `com.supabase.stack-test-run=<id>` label to the volumes it creates, and the container runtime adds it to the containers it creates. Nothing else changes, and nothing changes when the variable is unset. - `packages/stack/tests/docker-volume-run.ts` is a vitest `globalSetup` for the stack and CLI integration and E2E projects. It generates a random id unless an outer invocation already owns one. Its teardown removes exactly the stack-managed volumes carrying that id, without force, and fails the run if a removal fails. It skips when Docker is unavailable. - Vitest workers, CLI subprocesses and detached stack owners inherit the id. `processEnvLayer` keeps it when it replaces the environment. - Docker test state roots must stay private to a run. The `Commands.integration` Docker variants passed a bare temporary directory as the database root, which made them share one never-removed volume keyed to the parent of `$TMPDIR`. They now use `makeDockerDatabaseRoot` like the other database service tests. - The stack-cache E2E's own marker-based volume cleanup is removed. - Under Bun, vitest exits on SIGINT and SIGTERM without running globalSetup teardown, so an interrupted run used to leave its volumes behind and its containers running. The owning setup now records a per-user, host-local marker with its pid, and deletes it at teardown. Each new run's setup finds markers whose process is dead and removes that run's labelled containers, then its labelled volumes, then the marker. Live runs, other machines sharing the daemon, and unlabelled resources are never touched. Volumes created before this change carry no run label and are not cleaned up automatically.
This bumps edge-runtime from v1.77.2-r0 to v1.77.4-r0. https://github.com/supabase/slim-services/releases/tag/edge-runtime-v1.77.4-r0 Planned after supabase/slim-services published v1.77.4-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Summary
**TL;DR:** Every CLI run can now produce one OpenTelemetry trace that
shows where time goes, down to individual migrations, containers, health
waits, API operations, and SQL statements. Export is opt-in per run
(`SUPABASE_TRACE_FILE` for a local OTLP/JSON file, or
`SUPABASE_OTLP_ENDPOINT` for Tempo/Jaeger/Grafana), costs nothing when
unset, and never exports free-form text or secrets. Two new scripts turn
traces into answers: `trace-report` (critical path, self time, repeated
spans) and `bench-cli` (fair A/B timing between two binaries, optionally
per span).
### Before
```mermaid
flowchart LR
A["CLI run"] --> B["ad-hoc spans<br/>(uneven coverage)"]
B --> C{"telemetry consent?"}
C -->|yes| D["~/.supabase/traces/*.ndjson<br/>no parent ids, never read"]
C -->|no| E["dropped"]
B -->|--debug| F["one stderr line per span"]
```
### After
```mermaid
flowchart LR
A["CLI run"] --> R{"SUPABASE_TRACE_FILE<br/>or SUPABASE_OTLP_ENDPOINT?"}
R -->|neither| N["tracer disabled<br/>no-op spans, no I/O"]
R -->|set| T["cli.run span tree<br/>commands, phases, I/O, processes"]
T --> S["sanitizer<br/>no free text, no secrets"]
S --> F["OTLP/JSON file"] --> TR["trace-report / bench-cli"]
S --> O["OTLP collector<br/>Tempo, Jaeger, Grafana"]
```
### Why
Benchmarking or debugging the CLI meant adding temporary instrumentation
for each investigation. The existing tracer wrote consent-gated NDJSON
without parent span ids, so no span tree could be rebuilt, and nothing
read those files. Agents and maintainers need one consistent trace per
run that standard tools can open.
### What changed
- **Export pipeline** (`apps/cli/src/shared/telemetry/`): one sink per
run (file xor OTLP/HTTP, both is a config error); CLI-namespaced
variables so a global `OTEL_*` setup never receives CLI internals;
`TRACEPARENT` is adopted as the parent and passed to child processes;
the trace scope flushes within a bounded window before
`processControl.exit`.
- **Tracing off by default**: without a sink the root runs with
`TracerEnabled = false` and the exporter module is never loaded. Startup
and command wall time match `develop` within noise.
- **Sanitizer**: exported batches keep only structured data — exception
events keep `exception.type` (plus a structured SQLSTATE), status and
log messages are dropped, SQL text becomes `db.operation.name` and a
per-run salted hash, URLs lose query strings and storage object keys,
headers are allowlisted, resource attributes are allowlisted, trace
files are `0600`.
- **Instrumentation sweep** across commands, `command-internal`,
`shared`, `packages/stack`, and `packages/api`, following the new
conventions ADR: named spans at I/O and service boundaries, handler
phase spans, outcome annotations (counts, cache hit/miss, attempts, exit
codes), process spans with the docker verb, API spans named by operation
id, no spans per poll attempt or loop item.
- **`SUPABASE_DEBUG=1` console** keeps its behavior (spans up to depth 2
plus failures on stderr) with sanitized, length-capped attributes;
`--debug` alone still prints no spans.
- **Scripts**: `apps/cli/scripts/trace-report.ts` and
`apps/cli/scripts/bench-cli.ts` (alternating launches, isolated
environment, warm-up, untimed traced/profiled launches, `--cpu-prof`).
- **Removed** the NDJSON sink and the dead `command-instrumentation.ts`
copy of the command span.
- **Docs**: [ADR 0027](docs/adr/0027-cli-tracing-conventions.md) for
conventions and
[tracing-monitoring.md](apps/cli/docs/tracing-monitoring.md) for usage;
telemetry docs updated for the removed NDJSON files (old
`~/.supabase/traces` files are no longer written and are safe to
delete).
### Terminal captures
Recorded from a fresh corpus project against a local `grafana/otel-lgtm`
and with `vhs`; both builds are local binaries of `develop` and this
branch.
**Tempo: CLI runs, `supabase start` waterfall, cold `db diff` breakdown,
a sanitized process span, two commands grouped by `TRACEPARENT`**





**Terminal: debug console, traced command, trace-report, A/B bench,
per-span bench**





## Linked issue
None.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…t mode (#6890) ## Problem Nothing limits test growth, and agents write a test for nearly every change. The rules against low-value tests live as prose in the "Test quality" section of `AGENTS.md`, which agents skim past when they add or review tests. ## Change - Adds `.agents/skills/test-audit/SKILL.md`, which loads whenever a test is written, changed or reviewed. It includes: - **Authoring gate:** four questions a new test must answer before it's added. - **Level rule:** one primary test per contract, at the strongest boundary that can reach it. - **Junk-pattern checklist:** fitted to our Effect layers, handler integration tests and `runSupabase()` e2e, including real-time waits on production timeouts. - **Retention bar and regression-test rule.** - **Read-only audit mode:** a retain / fix / consolidate / delete ledger with one evidence line per mark. - "Test quality" in `AGENTS.md` points to the skill and drops the redundant-coverage bullet, which the skill now covers. - `.claude/skills/` holds one symlink per repo skill (`effect`, `test-audit`) into `.agents/skills/`, so Claude Code discovers them automatically and Codex keeps reading `.agents/skills`. `.gitignore` now ignores `.claude/*` except `.claude/skills`, and local state such as worktrees and settings stays ignored. A new skill in `.agents/skills` needs a matching symlink.
`types.typegen-host.integration.test.ts` › "survives a tool that exits before reading a large stdin" intermittently hit its 5s timeout when the machine was busy. The typegen host writes the request to the tool's stdin and waits for that write to finish. When the write hits backpressure, it waits for a `drain` that never comes once the reader has exited. The only way out is the pipe's `error` (EPIPE) event. Under load, that event can be delivered before the listener for it is attached, so the feed hangs even though the tool's exit code, stdout and stderr have all resolved. The stdin feed is now raced against the child's exit, which is always observable once the tool is gone, so the wait no longer depends on winning that scheduling race. Under the same CPU load, the test failed 2 times in 50 before the change and 0 times in 50 after. The stack's `CommandRunner` stdin path already races its feed against process exit, and it stays settled when several MB of stdin are fed to a psql that exits immediately.
`InstanceRoot.integration.test.ts` › "lets concurrent first claims of a
fresh root all succeed" failed intermittently.
`ensureOwnedInstanceRoot` claimed a fresh root by writing the owner
marker with `writeFile(…, { flag: "wx" })`. Node implements that as an
exclusive `open` followed by a separate `write`. In that window a
concurrent claimer could see the marker file already created but still
empty, compare it against its own marker, and fail with "belongs to
another instance".
The marker is now written in full to a unique temporary file beside the
root and hard-linked into place. `link` is atomic and fails with
`EEXIST` when the marker already exists, so it keeps the exclusivity of
the old exclusive create without ever exposing a partial file. Every
claimer then validates the marker that landed: concurrent claims for the
same instance succeed, and conflicting claims still fail. The temporary
file is always removed.
A new integration test covers conflicting concurrent first claims, where
exactly one may win.
…topped (#6937) When a native service ignores its graceful stop signal past `gracefulStopTimeout`, `NativeProcess.killProcess` fell back to SIGKILLing the launcher and its process group. It returned without confirming that either had exited, so `stop` could report a service stopped while its workload still held its port or files, and a replacement started right after could collide with it. On macOS and Linux the fallback now kills the workload's process group first, while the launcher is still alive to reap its direct child, then waits for the launcher to exit. If the launcher hasn't exited within the graceful-stop timeout, it is force-killed and its exit is awaited, so stop always settles. Windows behaviour is unchanged. The tests drive the timeout with `TestClock`. They prove `kill` stays pending until the launcher's exit is confirmed, and that a launcher that never reaps is force-killed. Waiting on deeper detached descendants of the workload would need a launcher protocol change and is out of scope.
…orkloads (#6952) The Docker whole-stack E2E "eager lifecycle and reopen" failed intermittently on its identity check (`identity.endsWith(databaseMarker)` in `tests/whole-stack/scenarios.ts`). `tests/whole-stack/workloads.ts` listed Docker workloads by the `com.supabase.stack` label alone. The container runtime's host-gateway probe (`readProbeHosts` in `Container.ts`) is a short-lived `docker run --rm` container that carries the stack labels but deliberately no `com.supabase.instance` label, since it isn't an instance. When the listing ran while the probe was still exiting, the probe's bare container ID was taken as a workload identity. Running the probe command in a loop while polling the same `docker ps` filter catches it mid-flight on most iterations. The listing now only counts containers carrying `com.supabase.instance` or `com.supabase.stack-managed=true`, which are the labels that identify stack workloads. The probe still removes itself, so the check that every workload is gone at stop keeps its coverage. Product code is unchanged.
…6939) ## Summary **TL;DR:** The upgrade notice no longer calls the GitHub releases API on every run outside a project or on every `supabase --version`. Outside a project the latest tag is now cached in `<SUPABASE_HOME>/cli-latest` for ten hours, and root `--version` uses the cache like every other command. The notice text, the `SUPABASE_NO_UPDATE_NOTIFIER` opt-out, and project-dotenv precedence are unchanged. ### Before ```mermaid flowchart LR A["Command succeeds"] --> B{"real supabase/ dir?"} B -- yes --> C{"--version?"} C -- no --> D{"cache fresh?"} D -- yes --> E["read supabase/.temp/cli-latest"] D -- no --> F["fetch GitHub, write cache"] C -- yes --> F B -- no --> G["fetch GitHub every run, no cache"] ``` ### After ```mermaid flowchart LR A["Command succeeds"] --> B{"real supabase/ dir?"} B -- yes --> C["supabase/.temp/cli-latest"] B -- no --> D["SUPABASE_HOME/cli-latest"] C --> E{"fresh and no symlink?"} D --> E E -- yes --> F["read cached tag"] E -- no --> G["fetch GitHub, write tag or empty backoff"] ``` ### Why The cache lived only in `supabase/.temp/cli-latest` and was written only when `supabase/` existed, so any command run outside a project fetched `api.github.com/repos/supabase/cli/releases/latest` before exiting. Root `--version` always bypassed the cache. Each fetch blocked exit for up to a 3s timeout on slow or offline networks, and unauthenticated calls count against GitHub's 60 requests per hour per IP; repeated runs exhaust that quota quickly. ### Performance Median wall time over alternating `develop` / PR launches on one machine, each build with its own `SUPABASE_HOME` (10–15 runs per row, 5–6 for the hung-network and first-run rows, one warmup). "Hung network" is a local proxy that accepts the connection and never answers. Outside a project, `develop` never caches, so it calls GitHub on every run; this PR reads `<SUPABASE_HOME>/cli-latest` after the first run. **Improved:** runs where `develop` calls GitHub and this PR reads a cache or backs off. | Scenario | `develop` | This PR | Change | |---|---|---|---| | Outside a project, `--version` | 209 ms | 133 ms | −37% | | Outside a project, `completion zsh` | 221 ms | 142 ms | −36% | | Inside a project, `--version` | 206 ms | 130 ms | −37% | | Outside a project, hung network, `--version` | 3174 ms | 154 ms | −95% | | Outside a project, hung network, `completion zsh` | 3168 ms | 159 ms | −95% | | First run, hung network, `--version` | 3367 ms | 1107 ms | −67% (800ms timeout) | **Controls (expected unchanged):** both builds do the same work. | Scenario | `develop` | This PR | Why | |---|---|---|---| | First run with an empty cache, `--version` | 241 ms | 245 ms | both fetch once | | Inside a project, `completion zsh` | 137 ms | 140 ms | both read the project cache | #### Trace Median `UpgradeNotice.check` span duration from `SUPABASE_TRACE_FILE` traces of alternating launches (10 runs, 5 for the first run). `develop` has no child fetch span, so its check duration is its GitHub request. | Scenario | `develop` check | This PR check | This PR `UpgradeNotice.fetch` | This PR attributes | |---|---|---|---|---| | Improved: outside a project, `completion zsh` | 79.9 ms | 0.6 ms | none (0/10) | `cache.location=user`, `cache.hit=true` | | Improved: inside a project, `--version` | 76.3 ms | 0.5 ms | none (0/10) | `cache.location=project`, `cache.hit=true` | | Control: first run with an empty cache, `--version` | 77.4 ms | 78.4 ms | 77.1 ms (5/5) | `cache.location=user`, `cache.hit=false` | | Control: inside a project, `completion zsh` | 1.1 ms | 1.7 ms | none (0/10) | `cache.location=project`, `cache.hit=true` | ### What changed - A real `<project>/supabase/` directory keeps using `supabase/.temp/cli-latest` exactly as before. - Otherwise the tag is cached in `<SUPABASE_HOME>/cli-latest` (`SUPABASE_HOME` from the shell env, default `~/.supabase`), with the same ten-hour TTL and empty-file offline backoff. - The symlink protections apply to the new location: a symlinked `SUPABASE_HOME` (including spellings with a trailing `/` or `/.`) or cache file disables the cache, and the write still uses `O_NOFOLLOW`. - Root `--version` no longer forces a fetch, so a release published within the last ten hours may surface on a later run instead of immediately. - The release fetch times out after 800ms instead of 3s. A round trip to `api.github.com` normally takes well under that; a slower fetch falls through to the empty-file backoff, so on such networks the notice is skipped until the next check. - The `UpgradeNotice.check` span records `cache.location` (`project`, `user`, or `disabled`) and `cache.hit`, and a cache miss runs the GitHub request as an `UpgradeNotice.fetch` child span, so traces and `bench-cli --trace` show when the check pays for a network round trip. - `resolveSupabaseHome` accepts any `{ join }` implementation so the hook can reuse it with `node:path`. - The new state file is documented in `apps/cli/docs/supabase-home.md`. ## Linked issue None. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…6957) `slim-release-published.yml` force-pushes one branch per service release line. When that branch's PR sits in the merge queue, GitHub rejects the push (GH006) and the job used to fail, dropping the planned pin until the next upstream release. On 2026-10-01 this lost edge-runtime v1.77.4-r0: the v1.77.3-r0 and v1.77.4-r0 dispatches both hit `slim-bump/edge-runtime` while #6930 was queued (restored by #6955). When a push is rejected because the branch is queued for merging, the Apply step now: - waits for the queue to merge or drop that PR, polling `isInMergeQueue` for up to 30 minutes and within the app token's lifetime; - stops processing the current plan, so no remaining item is built from the pre-merge base; - re-sends the same dispatch so a fresh run re-plans from the updated default branch, at most three times in a row (tracked by a validated `client_payload.replay` counter). The concurrency group now keeps every pending run (`queue: max`) instead of replacing the pending one, so a replay can never cancel a newer dispatch and its release-visibility wait. A failed queue lookup or dispatch, a queue that holds the branch past the deadline, an exhausted replay budget, and any other push rejection still fail the run with the manual recovery command. ADR 0026 documents the behavior.
…ects a config change (#6944) When `supabase start` (with `[experimental] stack`) refuses a change to a saved stack, the error now says which setting changed and what to do about it. It used to report internal paths such as `endpoints.http` with a generic suggestion. - Each rejected change is reported as the `config.toml` key, or the `SUPABASE_*` environment variable that set it, with the saved and requested values, after a lead-in saying the saved stack cannot adopt them. For example: `[db] major_version: saved 17, requested 15`, or `[api] port: saved automatic, requested 54999`. - Changes across all affected services are reported together in one error. A shared `[api] port` appears once. - The suggestion offers two ways out: revert the setting to keep the stack and its data, or run `supabase stack destroy --stack-id <id>` to recreate it. It warns that recreating deletes the local database data. The ID is used so the command targets this stack regardless of the current directory. When a change can't be reverted, such as a catalog-pinned artifact or Postgres build from a CLI upgrade, the suggestion offers only the destroy command. - JSON and stream-json errors carry the same details in structured form (`stack_changes` with service, path, key, saved and requested values, plus `recreate_command`), so agents don't have to parse prose. `stack_changes` keeps one entry per affected service, and `recreate_command` omits `--yes`, which a JSON-mode destroy requires. Port settings are now defined once in `stack-config.ts`, shared by config translation and error attribution, so a new port can't be added to one and missed in the other. The tests use the real composition planner.
…6941) On the native runtime, a reopened stack intermittently failed to start Logflare with `port 46477 already in use` / `eaddrinuse`. This showed up in the native "eager lifecycle and reopen" stack E2E on Linux CI. `ProcessRecipe` reserved each native backend port by binding port 0 and releasing it before spawning the service. That port comes from the OS ephemeral range (Linux: 32768–60999). In the gap, an unrelated outgoing connection can take it as its source port and leave it in TIME_WAIT, and the service's own bind then fails. Backend ports are now reserved by `Ports.ts`, with the same below-ephemeral scan that public auto allocation uses (ADR 0017): - Ports claimed by any saved stack are skipped, so a backend can't take a sticky public port that another stack will reclaim on its next start. Backend ports are never saved as claims. - Occupancy uses the same `accepts`-based check as public ports. A loopback-only probe bind can coexist with a wildcard listener on macOS, BSD and Windows, which hid ports already published by Docker. - When an attempt loses its bind to another listener, that port is excluded from the retry, so retries try a different port. The window between releasing the probe and the child binding, and the retry count, are unchanged.
…6986) ## TL;DR fixes `status` and the `start` summary dropping `API_URL`, `FUNCTIONS_URL` and `MCP_URL` when `[api] enabled = false`. ## what's missing? `start` runs Kong whatever `[api] enabled` says, since that setting only gates PostgREST. `status` still read it as Kong's switch, so with `[api] enabled = false` or `SUPABASE_API_ENABLED=false` it hid `API_URL`, `FUNCTIONS_URL` and `MCP_URL` even though Kong was serving auth there. - same class as #5332, reached through the api setting instead of `-x postgrest`. ## now fixed by: gating the gateway urls on Kong's own exclusion, the same way `start` does only `REST_URL` and `GRAPHQL_URL` still follow `api.enabled`. ## ref: - closes: #6976
## TL;DR fixes `stop --no-backup` leaving the `supabase_edge_runtime_<project_id>` volume behind after a migra `db diff` or `db pull` ## what got missed? since the native `db diff` and `db pull` port in v2.108.0, `docker run -v` created the migra Deno cache volume with no labels `stop --no-backup` prunes volumes by project label, so it skipped this one and still exited 0 ## sorted by: creating the volume w the `com.supabase.cli.project` and `com.docker.compose.project` labels before the migra run, like `start` and the old Go CLI did, so `stop --no-backup` removes it it is skipped under Bitbucket Pipelines, which drops the mount > [!NOTE] > tho volumes already left unlabeled by older versions still stay that way and need a one time `docker volume rm` to clear them ## ref: - closes: #6973 - broken in: #5620
) ## Summary **TL;DR:** `supabase stack list` prints an 8-character stack ID, but every `--stack-id` flag rejected it and required the full 64-character id. Every stack command now accepts the short ID from the list, or any unique prefix of at least four characters, the same convention git and docker use. The list table keeps its compact column. ### Before ```mermaid flowchart LR L["stack list"] --> S["ID column: 8 chars"] S --> D["stack destroy --stack-id abcd1234"] D --> E["error: must be a lowercase SHA-256 stack id"] ``` ### After ```mermaid flowchart LR A["--stack-id value"] --> B{"4-64 lowercase hex?"} B -->|no| E["flags error + hint"] B -->|64 chars| F["exact lookup"] B -->|4-63 chars| D["match saved stacks by prefix"] D -->|none| N["not found: run stack list"] D -->|several| G["ambiguous: lists full ids"] D -->|one unreadable| I["read error surfaced"] D -->|one| OK["full id used"] F --> OK ``` ### Why Copying the ID column from `stack list` into `stack destroy --stack-id` (or `stop`, `status`, ...) failed with `--stack-id must be a lowercase SHA-256 stack id`, so users had to rerun the list with `--output-format json` to find the real id. ### What changed - The shared stack target resolver accepts a full id (unchanged exact lookup) or a unique lowercase-hex prefix of 4 or more characters. This covers `destroy`, `stop`, `start`, `restart`, `status`, `logs`, and `prepare`. - A prefix matching several stacks fails with a flags error listing every matching full id. A prefix matching a stack whose saved state is unreadable surfaces the read error instead of reporting it missing. - "Not found" and malformed-id errors now point to `supabase stack list`. - The `status --env` pointer printed by `stack start` repeats the resolved full id rather than the typed prefix. - Flag help, command `SIDE_EFFECTS.md` files, and `docs/stack-commands.md` describe prefix selection. - The stack package's `discover` takes an `idPrefix`, so a prefix lookup only probes the owners of matching stacks. A full id keeps the direct lookup. ### Terminal captures Recorded with `vhs` from source on the base commit and on this branch, against two native demo stacks (`default`, `feature-a`) in a throwaway `SUPABASE_HOME`. Each capture runs `stack list`, destroys `feature-a` with the 8-character ID from the list, then lists again. **Before:** the listed ID is rejected and both stacks remain.  **After:** the listed ID resolves to the full id, `feature-a` is destroyed, and `default` remains.  ## Linked issue Closes [CLI-2609](https://linear.app/supabase/issue/CLI-2609/stack-text-stack-list-shows-an-8-character-id-that-stack-id-rejects) 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…6949) ## TL;DR fixes disabled data api detection in the shared mapping used by `config diff`, `config push` and `config pull` ## what's hurting the users? the v2 project config returns `pg_pgrst_no_exposed_schemas` when the data api is disabled, but the mapping treated any nonempty value as enabled `config push` started hitting this in stable v2.117.0 when it switched from the v1 `/postgrest` read to the shared v2 mapping ## fixed now by: `remoteDataApiDisabled` recognizes both `""` and the exact marker. `api.enabled` now uses that same check as `schemas`, `extra_search_path` and `max_rows` <details> <summary>before/after:</summary> with local `api.enabled = false`, the remote marker, and no unrelated drift: | invocation | before | now | | --- | --- | --- | | `config diff --exit-code` | reports false api drift; exits 2 | no differences; exits 0 | | `config push --yes` | sends `{"db_schema":""}` again | api config is up to date | | `config pull --yes` | proposes enabling the api and importing the marker as a schema | proposes no api changes | </details> explicit `api.enabled = true` now re-enables the api with default schemas custom schemas still work. schema-only changes with enablement undeclared require an explicit enable request... <details> <summary>rep:</summary> ```mermaid flowchart TD off["data api disabled"] off --> v1["v1: empty db_schema"] off --> v2["v2: platform marker"] v1 --> legacy["v2.116.0 push"] legacy --> disabled["reads disabled"] v2 --> shared["push from stable v2.117.0"] shared --> before["before: nonempty means enabled"] before --> wrong["reads enabled"] shared --> after["now: empty or exact marker means disabled"] after --> disabled ``` </details> ## ref: - closes #6932 - regression introduced in #6454
Slim pickup PRs (`chore(stack): bump …` / `chore(stack): pin …`) are opened by the release app, which cannot approve its own PRs, and they touch files owned by `@supabase/cli`, so each one currently waits for a manual review. - `slim-release-published.yml` now approves each opened or rewritten PR with `AUTO_APPROVE_PR_PAT` (skipped when the PR is already approved, so rewrites don't stack reviews) and enables auto-merge, so a green PR enters the merge queue on its own. The new token is unset for the sync, format and render steps, like the app token. - `CODEOWNERS` adds `@supabase-oss` as a co-owner of the generated `packages/stack/src/Artifacts.ts`, `apps/cli-go/pkg/config/templates/Dockerfile`, and `apps/cli-go/api/v1-openapi.yaml`, so its approval satisfies code-owner review on the slim and API-types sync PRs. Human PRs touching these files still need a `@supabase/cli` review. - Both workflows that approve with `AUTO_APPROVE_PR_PAT` now run in the `auto-approve` environment, which only deploys from `develop`, so a workflow pushed on another branch cannot approve with it. - ADR 0026 documents the auto-approval.
…h effect lint (CLI-2593) (#6947) ## TL;DR brings the `cli` area and `stack-constants.ts` under the effect lint ## whats introduced? effect lint applied to `shared/cli`, `shared/stack-constants.ts` and their tests: - `shared/cli/**` and `shared/stack-constants.ts` allow list entries, with the plain node npm launcher `cli/bin.ts` kept out - `runCli` is an effect that `main.ts` runs once, instead of an async function chaining three `Effect.runPromise` calls - the three `Effect.provide` chains in `run.ts` are named `Layer.provideMerge` chains in the same order, so layers build, share and release exactly as before - the analytics layer option lists the services it needs instead of `any`, and `beforeParse` keeps its own error type instead of `unknown` - `seed` applies its `--linked`/`--local` global flags after its subcommands so they no longer leak into the root command's requirements, and `root.ts` checks the root with `satisfies CliRootCommand` so it keeps its real error type - tests use `it.effect`, `runSupabaseEffect` with scoped temp dirs, `FileSystem`, `Path` and `ChildProcess` instead of async helpers, spies and `node:fs`, with every `as Effect.Effect` cast gone - the hidden `db test` alias test now proves dispatch through the handler's typed mutual exclusivity failure in a pinned env, instead of a missing service crash ## ref: - closes: CLI-2593
…-2592) (#6960) ## TL;DR brings the `functions` download, delete, config and shared helpers under the effect lint ## whats introduced? - the `download`, `delete`, `functions-api`, `functions.shared` and `functions-config` modules join the allow list file by file, so the rest of `shared/functions` waits for the later PRs in this stack - `download` runs on the `FileSystem` and `Path` services instead of `node:fs` and `node:path`, with a posix `Path` for container and API paths - the temp file still opens with `wx`, renames into place and is removed when the rename fails - host failures keep their native error text, and the four plain `Error` failures on the eszip path become an untagged `FunctionEszipDownloadError` with the same telemetry - the functions list and metadata responses decode through `Schema`, so a malformed response now reads `Expected a valid JSON string` instead of the parser message - `downloadFunctions` and `deleteFunction` become `Effect.fn` with the same span names - integration tests cover an empty file, a malformed list or metadata response, a mistyped list, a failed rename with temp cleanup, a failed temp dir and the native host errors on the eszip path ## ref - related: CLI-2592
…6968) ## TL;DR brings `functions deploy` under the effect lint ## whats introduced? - `deploy.ts`, `deploy.errors.ts` and the new errors unit test join the allow list, and the `deploy` unit tests follow on the next branch - the import map walk, source collection and docker binds run as effects on the `FileSystem` and `Path` services, shared with `functions serve` and `supabase start`, and `onWarning` is now an effect that `serve.ts` passes - source collection and the binds walk run in detached fibers that are joined, so an interrupt still lets them finish in the background - host failures keep their develop shape, plain `Error` failures become an untagged `FunctionDeployError` and the import map parse error keeps the `SyntaxError` name, so telemetry and traces stay the same - the user's `deno.json` or import map and the list, deploy and function responses decode through `Schema`, so a malformed document now reads `Expected a valid JSON string` - `deployFunctions` becomes `Effect.fn` with the same span name, `DEBUG` reads through `Config` and dated rate limit headers read the time through `Clock` - tests cover the decode text and error identity, an invalid static glob, a failed progress write, a NUL byte in an import, an uncreatable bundle dir, both detached walks and `DEBUG` driving `--verbose` ## ref - related: CLI-2592
…2592) (#6970) ## TL;DR brings the `deploy` unit tests under the effect lint ## whats introduced? - `deploy.unit.test.ts` joins the allow list - the tests keep every existing case and run on `@effect/vitest` with `BunServices`, building fixtures through `FileSystem` and `Path` in scoped temp dirs and encoding JSON through `Schema` - failure assertions read the exit cause instead of catching a rejection, and pin both import walk failures as defects, the `FunctionImportNotDirectoryError` and the native `ENOENT` host error - a new test pins which entries `discoverFunctionSlugs` counts as functions, keeping symlinked directories and skipping files, links to files, broken links and directories without an entrypoint ## ref - related: CLI-2592
…LI-2592) (#6971) ## TL;DR brings the serve main bundler and its tests under the effect lint ## whats introduced? - `serve-main-bundler.ts` and its unit and integration tests join the allow list - `bundleServeMainTemplate` now returns an effect, which `serve.ts` yields and the build scripts and offline e2e run with `Effect.runPromise` - the template bundles in a detached fiber that `serve.ts` joins, so an interrupted startup still fills the template cache, and an esbuild failure stays a defect - the bundler integration tests keep all eighteen cases and coordinate through `Deferred` and scoped abort signals instead of manual promises, with the fake `Deno.errors` classes as `Data.Error` ## ref - related: CLI-2592
This bumps storage from v1.79.33-r0 to v1.79.34-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.79.34-r0 Planned after supabase/slim-services published v1.79.34-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps pgmeta from v0.99.0-r0 to v0.100.0-r0. https://github.com/supabase/slim-services/releases/tag/pgmeta-v0.100.0-r0 Planned after supabase/slim-services published v0.100.0-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This PR was automatically created to sync the generated `@supabase/api` package with the latest Management API OpenAPI document. Changes were detected in the upstream OpenAPI documents exposed by `https://api.supabase.com/api/v1-json` and `https://api.supabase.com/api/v2-json`. Co-authored-by: jgoux <1443499+jgoux@users.noreply.github.com>
This bumps storage from v1.79.34-r0 to v1.79.35-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.79.35-r0 Planned after supabase/slim-services published v1.79.35-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This bumps storage from v1.79.35-r0 to v1.79.36-r0. https://github.com/supabase/slim-services/releases/tag/storage-v1.79.36-r0 Planned after supabase/slim-services published v1.79.36-r0. This branch is rewritten from develop whenever a newer relevant release arrives. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
## Summary **TL;DR:** The CLI now bundles `@supabase/pg-delta@1.0.0-alpha.57` and `@supabase/pg-topo@1.0.0-alpha.8`. An unchanged declarative export no longer makes `db` declarative sync revoke column grants. ```mermaid flowchart LR exp["declarative export (unchanged)"] --> old["alpha.56: column grants dropped from grouped export"] --> rev["sync plans REVOKE"] exp --> new["alpha.57: column grants kept"] --> ok["sync is a no-op"] ``` ## Why alpha.57 keeps column grants in the grouped declarative export. Without it, re-running declarative sync against an export that did not change emits `REVOKE` statements for column-level grants. The release also relicenses both packages under the PostgreSQL License. ## What changed - Bump `@supabase/pg-delta` from `1.0.0-alpha.56` to `1.0.0-alpha.57`. - Bump peer `@supabase/pg-topo` from `1.0.0-alpha.7` to `1.0.0-alpha.8`, which alpha.57 requires. - Point `minimumReleaseAgeExclude` at those two versions. ## Linked issue Supabase maintainer change; no public issue to close. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Every PR a bot opens against `develop` (Dependabot, the release app's sync and stack pickup PRs) is now assigned to and approved by `supabase-oss` and set to auto-merge by a single `bot-pr-auto-merge.yml` workflow, so green required checks and the merge queue are the only gate. - `bot-pr-auto-merge.yml` runs on `pull_request_target` without checking out PR code, and acts only when both the PR author and the event sender are Dependabot or the release app (other apps that open PRs here, such as coding agents, still need a maintainer), the head branch is in this repository, the PR is not a draft, and it is not a `release-notes/` PR (those are published by a maintainer approval and must never merge). The `supabase-oss` token stays in the develop-only `auto-approve` environment. - It replaces the Dependabot-only `automerge.yml` and the approve/auto-merge steps in `api-package-sync.yml`, `cli-go-api-sync.yml`, and `slim-release-published.yml`, none of which need the approval token anymore. - CODEOWNERS lists `supabase-oss` as a co-owner of only the files bots update (workflow files, dependency manifests and lockfiles, Go modules, service Dockerfiles, the artifact catalog, and the generated API clients and their OpenAPI snapshots), replacing the ownerless rules there. Human PRs touching those paths need CLI team review again, and human commits pushed onto a bot PR that touch anything else still need it too. The AI review and infra workflows stay maintainer-only.
jgoux
approved these changes
Oct 6, 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.
api.enabledis false (CLI-2616) (fix(cli): print gateway urls whenapi.enabledis false (CLI-2616) #6986)stop --no-backup(CLI-2613) (fix(cli): prune migra cache onstop --no-backup(CLI-2613) #6982)shared/cliandshared/stack-constants.tswith effect lint (CLI-2593) (refactor(cli): covershared/cliandshared/stack-constants.tswith effect lint (CLI-2593) #6947)shared/functionshelpers with effect lint (CLI-2592) (refactor(cli): covershared/functionshelpers with effect lint (CLI-2592) #6960)deploywith effect lint (CLI-2592) (refactor(cli): cover functionsdeploywith effect lint (CLI-2592) #6968)deployunit tests with effect lint (CLI-2592) (test(cli): cover functionsdeployunit tests with effect lint (CLI-2592) #6970)serve-main-bundlerwith effect lint (CLI-2592) (refactor(cli): cover functionsserve-main-bundlerwith effect lint (CLI-2592) #6971)sharedwith effect lint (CLI-2458) (refactor(cli): cover all ofsharedwith effect lint (CLI-2458) #7002)