diff --git a/apps/cli/src/command-internal/db-bootstrap/slim-runtime.ts b/apps/cli/src/command-internal/db-bootstrap/slim-runtime.ts index bc65badb39..c9e65587eb 100644 --- a/apps/cli/src/command-internal/db-bootstrap/slim-runtime.ts +++ b/apps/cli/src/command-internal/db-bootstrap/slim-runtime.ts @@ -1,7 +1,7 @@ /** * Slim-image runtime contracts that still differ from docker.io. Spec builders * switch on {@link usesSlimImageRuntime} so flag-off stays byte-identical even - * if a caller passes a ghcr-shaped override. + * if a caller passes a slim-shaped override. * * Slim auth, storage, Vector, and the Elixir images (realtime, analytics, * pooler) ship BusyBox wget — not GNU wget and not curl. BusyBox documents diff --git a/apps/cli/src/command-internal/docker-image-resolve.unit.test.ts b/apps/cli/src/command-internal/docker-image-resolve.unit.test.ts index 10eefc1256..3615514ab6 100644 --- a/apps/cli/src/command-internal/docker-image-resolve.unit.test.ts +++ b/apps/cli/src/command-internal/docker-image-resolve.unit.test.ts @@ -195,6 +195,36 @@ describe("makeDockerImageResolver", () => { }), ); + it.effect("pulls a slim image from ECR Public once GHCR keeps rate-limiting it", () => + Effect.gen(function* () { + const originalWrite = globalThis.process.stderr.write.bind(globalThis.process.stderr); + globalThis.process.stderr.write = (() => true) as typeof globalThis.process.stderr.write; + + try { + const digest = "sha256:8fb7ae7cd0d8121c460c8756d7f2791ce9a19c3331b46987456dfa4f8b45d701"; + const slim = `ghcr.io/supabase/cli/postgres:17.11.0.004-r1@${digest}`; + const mirror = `public.ecr.aws/supabase/cli/postgres:17.11.0.004-r1@${digest}`; + const throttled = { + exitCode: 1, + stderr: "Error response from daemon: toomanyrequests: retry-after: 173.687µs", + }; + const mock = mockSpawner([throttled, throttled, throttled, { exitCode: 0 }]); + const fiber = yield* makeDockerImageResolver(mock.spawner)(slim).pipe( + Effect.forkChild({ startImmediately: true }), + ); + + yield* TestClock.adjust("4 seconds"); + yield* TestClock.adjust("8 seconds"); + const image = yield* Fiber.join(fiber); + + expect(mock.pulls).toEqual([slim, slim, slim, mirror]); + expect(image).toBe(mirror); + } finally { + globalThis.process.stderr.write = originalWrite; + } + }), + ); + it.effect( "does not inject a blank line before the banner when the child error is newline-terminated", () => diff --git a/apps/cli/src/command-internal/docker-registry.ts b/apps/cli/src/command-internal/docker-registry.ts index 186b825de3..47df502b4f 100644 --- a/apps/cli/src/command-internal/docker-registry.ts +++ b/apps/cli/src/command-internal/docker-registry.ts @@ -3,9 +3,10 @@ * overrides the default ECR mirror; `docker.io` returns the image unchanged, and any other value * rewrites it to `/supabase/`. Callers that can retry pulls should * use {@link getRegistryImageUrlCandidates} instead, which falls back through GHCR and the - * source image. Slim images ({@link isSlimImageRef}) skip every rewrite — there's no mirror to - * redirect them to. + * source image. Slim images ({@link isSlimImageRef}) skip these rewrites; their only fallback is + * the ECR Public mirror at the same tag and digest. */ +import { slimImageMirrors } from "@supabase/stack/internal/artifacts"; import { Config, ConfigProvider, Effect, Option } from "effect"; import { isSlimImageRef } from "../shared/services/slim-images.ts"; @@ -67,7 +68,7 @@ export function getRegistryImageUrlCandidates( projectEnvValues?: Readonly>, ): Effect.Effect, Config.ConfigError> { if (isSlimImageRef(imageName)) { - return Effect.succeed([imageName]); + return Effect.succeed([imageName, ...slimImageMirrors(imageName)]); } return registryOverride(projectEnvValues).pipe( diff --git a/apps/cli/src/command-internal/docker-registry.unit.test.ts b/apps/cli/src/command-internal/docker-registry.unit.test.ts index a1b857206a..8489afdcbd 100644 --- a/apps/cli/src/command-internal/docker-registry.unit.test.ts +++ b/apps/cli/src/command-internal/docker-registry.unit.test.ts @@ -102,12 +102,14 @@ describe("getRegistryImageUrl", () => { ).toBe("merged.example/supabase/pg_prove:3.36"); }); - // Published only under ghcr.io/supabase/cli; rewriting by last path segment would misroute it. + // Published under ghcr.io/supabase/cli; rewriting by last path segment would misroute it. const SLIM_IMAGE = "ghcr.io/supabase/cli/postgres:17.6.1.165"; + const SLIM_IMAGE_MIRROR = "public.ecr.aws/supabase/cli/postgres:17.6.1.165"; it("leaves a slim image unrewritten, whatever the registry override says", () => { for (const registry of [undefined, "public.ecr.aws", "docker.io", "my.mirror.example"]) { expect(resolveImage(registry, SLIM_IMAGE)).toBe(SLIM_IMAGE); + expect(resolveImage(registry, SLIM_IMAGE_MIRROR)).toBe(SLIM_IMAGE_MIRROR); } expect( resolveImage(undefined, SLIM_IMAGE, { @@ -116,15 +118,15 @@ describe("getRegistryImageUrl", () => { ).toBe(SLIM_IMAGE); }); - it("plans a single pull candidate for a slim image", () => { + it("plans the slim image, then its ECR Public mirror, whatever the registry override says", () => { for (const registry of [undefined, "public.ecr.aws", "docker.io", "my.mirror.example"]) { - expect(resolveCandidates(registry, SLIM_IMAGE)).toEqual([SLIM_IMAGE]); + expect(resolveCandidates(registry, SLIM_IMAGE)).toEqual([SLIM_IMAGE, SLIM_IMAGE_MIRROR]); } expect( resolveCandidates(undefined, SLIM_IMAGE, { SUPABASE_INTERNAL_IMAGE_REGISTRY: "my.mirror.example", }), - ).toEqual([SLIM_IMAGE]); + ).toEqual([SLIM_IMAGE, SLIM_IMAGE_MIRROR]); }); it("still rewrites the non-slim ghcr.io/supabase namespace", () => { diff --git a/apps/cli/src/commands/db/dump/SIDE_EFFECTS.md b/apps/cli/src/commands/db/dump/SIDE_EFFECTS.md index a35d069505..e63963126a 100644 --- a/apps/cli/src/commands/db/dump/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/db/dump/SIDE_EFFECTS.md @@ -35,19 +35,19 @@ image), to stdout or `--file`. ## Environment Variables -| Variable | Purpose | -| ----------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `SUPABASE_DB_PASSWORD` (`DB_PASSWORD` viper key; `--password`/`-p` overrides) | remote DB password | -| `SUPABASE_ACCESS_TOKEN` | `--linked` auth | -| `SUPABASE_DB_ORIOLEDB_VERSION` | overrides `db.orioledb_version`, selecting the OrioleDB `pg_dump` image | -| `SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION` | deprecated alias of `SUPABASE_DB_ORIOLEDB_VERSION`, used only while that is unset; when set, stderr gets `WARN: SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION is deprecated and will be removed in v3, use SUPABASE_DB_ORIOLEDB_VERSION instead.` | -| `BITBUCKET_CLONE_DIR` | clears the `label:disable` security option that mounted TLS files add | -| `SUPABASE_INTERNAL_IMAGE_REGISTRY` | rewrite the pg image registry for compose dumps; stack dumps use the catalog image pin unchanged | -| `SUPABASE_USE_SLIM_IMAGES` | resolve the current Postgres pin from the slim `ghcr.io/supabase/cli` builds (`true`/`1` enable); majors 13/15 use `15.14.1.167` when the flag is on; historical pins, PG14, OrioleDB tags the catalog does not pin, and flag-off `15.8.1.085` stay on docker.io | -| `DOCKER_HOST` | docker daemon endpoint | -| `MSYSTEM`, `TERM_PROGRAM` | suppress the piped-stdout non-ASCII warning in MSYS/mintty sessions | -| `PG*` libpq | linked-project TLS, timeout, service; see `db push`. An invalid value fails before `pg_dump` runs; a remote target also hands them to `pg_dump` (see Notes) | -| `SSL_CERT_FILE`, `SSL_CERT_DIR` | a native `pg_dump` on libpq 16+ keeps `sslrootcert=system` when either is set; libpq 15 uses only `SSL_CERT_FILE` | +| Variable | Purpose | +| ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `SUPABASE_DB_PASSWORD` (`DB_PASSWORD` viper key; `--password`/`-p` overrides) | remote DB password | +| `SUPABASE_ACCESS_TOKEN` | `--linked` auth | +| `SUPABASE_DB_ORIOLEDB_VERSION` | overrides `db.orioledb_version`, selecting the OrioleDB `pg_dump` image | +| `SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION` | deprecated alias of `SUPABASE_DB_ORIOLEDB_VERSION`, used only while that is unset; when set, stderr gets `WARN: SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION is deprecated and will be removed in v3, use SUPABASE_DB_ORIOLEDB_VERSION instead.` | +| `BITBUCKET_CLONE_DIR` | clears the `label:disable` security option that mounted TLS files add | +| `SUPABASE_INTERNAL_IMAGE_REGISTRY` | rewrite the pg image registry for compose dumps; stack dumps use the catalog image pin unchanged | +| `SUPABASE_USE_SLIM_IMAGES` | resolve the current Postgres pin from the slim `ghcr.io/supabase/cli` builds (`true`/`1` enable); majors 13/15 use `15.14.1.167` when the flag is on; historical pins, PG14, OrioleDB tags the catalog does not pin, and flag-off `15.8.1.085` stay on docker.io; a failed GHCR pull falls back to the same tag and digest on `public.ecr.aws/supabase/cli` | +| `DOCKER_HOST` | docker daemon endpoint | +| `MSYSTEM`, `TERM_PROGRAM` | suppress the piped-stdout non-ASCII warning in MSYS/mintty sessions | +| `PG*` libpq | linked-project TLS, timeout, service; see `db push`. An invalid value fails before `pg_dump` runs; a remote target also hands them to `pg_dump` (see Notes) | +| `SSL_CERT_FILE`, `SSL_CERT_DIR` | a native `pg_dump` on libpq 16+ keeps `sslrootcert=system` when either is set; libpq 15 uses only `SSL_CERT_FILE` | ## Exit Codes diff --git a/apps/cli/src/commands/db/pull/SIDE_EFFECTS.md b/apps/cli/src/commands/db/pull/SIDE_EFFECTS.md index 9155ce69a8..a49112ebbf 100644 --- a/apps/cli/src/commands/db/pull/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/db/pull/SIDE_EFFECTS.md @@ -128,22 +128,22 @@ at all, so nothing is cached for it. ## Environment Variables -| Variable | Purpose | Required? | -| ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -| `SUPABASE_ACCESS_TOKEN` | auth for the linked target | no | -| `SUPABASE_DB_PASSWORD` | remote DB password (overridden by `-p`) | no | -| `SUPABASE_DB_SHADOW_PORT` | shadow container's host port (`db.shadow_port`) — NOT `SUPABASE_DB_PORT`, which the shadow never reads | no | -| `SUPABASE_DB_MAJOR_VERSION` / `SUPABASE_DB_HEALTH_TIMEOUT` / `SUPABASE_DB_SETTINGS_*` | shadow container-config overrides, same as `db start`/`db reset` | no | -| `SUPABASE_DB_ORIOLEDB_VERSION` | overrides `db.orioledb_version`, selecting the OrioleDB shadow and initial-migra `pg_dump` images | no | -| `SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION` | deprecated alias of `SUPABASE_DB_ORIOLEDB_VERSION`, used only while that is unset; when set, stderr gets `WARN: SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION is deprecated and will be removed in v3, use SUPABASE_DB_ORIOLEDB_VERSION instead.` | no | -| `SUPABASE_PROJECT_ID` | overrides the shadow container's project id/labels, same as `db start`/`db reset` (`utils.DbId`); ALSO the linked-ref resolution fallback `--project-ref` supersedes — see Notes for the narrower scope of the flag | no | -| `SUPABASE_NETWORK_ID` (`--network-id`) | forces the shadow and initial-migra `pg_dump` containers onto an existing Docker network | no | -| `SUPABASE_USE_SLIM_IMAGES` | resolves the current-pin shadow Postgres, `pg_dump`, PG15+ realtime/storage/auth migrate-job images (migration-style cold shadow), and (for migra) the edge-runtime image from the slim `ghcr.io/supabase/cli` builds (`true`/`1` enable); majors 13/15 use `15.14.1.167` when the flag is on; historical pins, PG14, OrioleDB tags the catalog does not pin, flag-off `15.8.1.085`, and `deno_version = 1` stay on docker.io | no | -| `SUPABASE_HOME` | overrides the `~/.supabase` root used for the shadow baseline cache (and other CLI state) | no | -| `SUPABASE_SHADOW_CACHE` | shadow baseline cache; on by default, opt-out (`0`/`false`); the shadow's post-baseline state is saved under a managed snapshot key and restored into the next run's fresh stack database (see Notes) | no | -| `SUPABASE_EXPERIMENTAL_PG_DELTA` | force pg-delta diff engine | no | -| `SUPABASE_EXPERIMENTAL` | selects the deprecated in-process structured-dump export (same as `--declarative`) when `--declarative` is not set | no | -| `PG*` libpq | linked-project TLS, timeout, service; see `db push`. The `pg_dump` seed gets them (see `db dump`); the migra container gets none of these `PG*` TLS settings (migra gets the CLI's own bundled CA outside its OOM fallback) | no | +| Variable | Purpose | Required? | +| ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------- | +| `SUPABASE_ACCESS_TOKEN` | auth for the linked target | no | +| `SUPABASE_DB_PASSWORD` | remote DB password (overridden by `-p`) | no | +| `SUPABASE_DB_SHADOW_PORT` | shadow container's host port (`db.shadow_port`) — NOT `SUPABASE_DB_PORT`, which the shadow never reads | no | +| `SUPABASE_DB_MAJOR_VERSION` / `SUPABASE_DB_HEALTH_TIMEOUT` / `SUPABASE_DB_SETTINGS_*` | shadow container-config overrides, same as `db start`/`db reset` | no | +| `SUPABASE_DB_ORIOLEDB_VERSION` | overrides `db.orioledb_version`, selecting the OrioleDB shadow and initial-migra `pg_dump` images | no | +| `SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION` | deprecated alias of `SUPABASE_DB_ORIOLEDB_VERSION`, used only while that is unset; when set, stderr gets `WARN: SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION is deprecated and will be removed in v3, use SUPABASE_DB_ORIOLEDB_VERSION instead.` | no | +| `SUPABASE_PROJECT_ID` | overrides the shadow container's project id/labels, same as `db start`/`db reset` (`utils.DbId`); ALSO the linked-ref resolution fallback `--project-ref` supersedes — see Notes for the narrower scope of the flag | no | +| `SUPABASE_NETWORK_ID` (`--network-id`) | forces the shadow and initial-migra `pg_dump` containers onto an existing Docker network | no | +| `SUPABASE_USE_SLIM_IMAGES` | resolves the current-pin shadow Postgres, `pg_dump`, PG15+ realtime/storage/auth migrate-job images (migration-style cold shadow), and (for migra) the edge-runtime image from the slim `ghcr.io/supabase/cli` builds (`true`/`1` enable); majors 13/15 use `15.14.1.167` when the flag is on; historical pins, PG14, OrioleDB tags the catalog does not pin, flag-off `15.8.1.085`, and `deno_version = 1` stay on docker.io; a failed GHCR pull falls back to the same tag and digest on `public.ecr.aws/supabase/cli` | no | +| `SUPABASE_HOME` | overrides the `~/.supabase` root used for the shadow baseline cache (and other CLI state) | no | +| `SUPABASE_SHADOW_CACHE` | shadow baseline cache; on by default, opt-out (`0`/`false`); the shadow's post-baseline state is saved under a managed snapshot key and restored into the next run's fresh stack database (see Notes) | no | +| `SUPABASE_EXPERIMENTAL_PG_DELTA` | force pg-delta diff engine | no | +| `SUPABASE_EXPERIMENTAL` | selects the deprecated in-process structured-dump export (same as `--declarative`) when `--declarative` is not set | no | +| `PG*` libpq | linked-project TLS, timeout, service; see `db push`. The `pg_dump` seed gets them (see `db dump`); the migra container gets none of these `PG*` TLS settings (migra gets the CLI's own bundled CA outside its OOM fallback) | no | ## Exit Codes diff --git a/apps/cli/src/commands/db/reset/SIDE_EFFECTS.md b/apps/cli/src/commands/db/reset/SIDE_EFFECTS.md index 4b83d29bfe..946453b2db 100644 --- a/apps/cli/src/commands/db/reset/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/db/reset/SIDE_EFFECTS.md @@ -162,25 +162,25 @@ the whole reset** (not just "skip buckets"). ## Environment Variables -| Variable | Purpose | Required? | -| -------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | -| `DOCKER_HOST` / `DOCKER_CONTEXT` / `DOCKER_TLS_VERIFY` / `DOCKER_CERT_PATH` / `DOCKER_API_VERSION` / `DOCKER_CONFIG` | local path: ambient shell environment only (project dotenv files deliberately never override Docker client keys) — resolves the daemon endpoint for the running probe (in-process) and steers the spawned `docker`/`podman` CLI itself | no | -| `SUPABASE_ACCESS_TOKEN` | auth token for the `--linked` resolver path | no (falls back to keyring → `~/.supabase/access-token`) | -| `SUPABASE_DB_PASSWORD` | password for the linked/remote connection | no | -| `SUPABASE_YES` | auto-confirm the reset prompt and the local path's bucket-seed overwrite/prune prompts (shell or project dotenv, same as `seed buckets`) | no (also `--yes`) | -| `SUPABASE_EXPERIMENTAL` | selects the schema-files apply branch on either target | no (also `--experimental`) | -| `SUPABASE_EXPERIMENTAL_PGDELTA_ENABLED` | overrides `[experimental.pgdelta].enabled`; a truthy value flips the reset gate (`experimental && resolvedVersion === "" && !toml.pgDelta.enabled`) back to timestamped migrations even with `--experimental` set — switches between two different destructive code paths | no | -| `SUPABASE_DB_MIGRATIONS_SCHEMA_PATHS` | overrides `[db.migrations].schema_paths` (viper `AutomaticEnv`, beats the config-file value) for the schema-files apply branch — genuinely effective on both targets now | no (no dedicated flag — config-file-only otherwise) | -| `SUPABASE_PROJECT_ID` | overrides the local container id; ALSO the linked-ref resolution fallback `--project-ref` supersedes — see Notes for the narrower scope of the flag | no | -| `SUPABASE_INTERNAL_IMAGE_REGISTRY` | overrides the image registry used to resolve the local path's container images (project `.env` or shell) | no (project `.env` or shell) | -| `SUPABASE_USE_SLIM_IMAGES` | resolves the local-reset Postgres image and the realtime/storage/auth migrate-job images from slim `ghcr.io/supabase/cli` builds (`true`/`1` enable); majors 13/15 use `15.14.1.167` when the flag is on; historical pins, PG14, OrioleDB tags the catalog does not pin, and flag-off `15.8.1.085` stay on docker.io | no (ambient shell only) | -| `SUPABASE_DB_PORT` / `SUPABASE_DB_MAJOR_VERSION` / `SUPABASE_DB_HEALTH_TIMEOUT` / `SUPABASE_DB_SETTINGS_*` | local-path container-recreate config overrides, same as `db start` | no | -| `SUPABASE_API_PORT` / `SUPABASE_API_EXTERNAL_URL` / `SUPABASE_API_TLS_*` / `SUPABASE_API_ENABLED` | legacy-backend bucket-seed step only: override the matching `[api]` fields for the Storage gateway URL/TLS, same as `seed buckets` (shell or project dotenv; #6452). Not consulted on the stack backend (see above) | no | -| `SUPABASE_AUTH_JWT_SECRET` / `SUPABASE_AUTH_SERVICE_ROLE_KEY` | local path: override `auth.jwt_secret` / `auth.service_role_key` (shell or project dotenv, `encrypted:` decrypted) — feeds the recreated Postgres container (jwt secret), the fresh-database setup jobs (both) and the legacy-backend bucket-seed Storage service-role key, same as `seed buckets`. Not consulted on the stack backend, which uses the stack's own service-role JWT | no | -| `SUPABASE_NETWORK_ID` (`--network-id`) | forces the recreated container/network onto an existing Docker network — legacy backend only | no | -| `SUPABASE_EXPERIMENTAL_STACK` | `1`/`0` selects the stack backend for the local path, overriding `experimental.stack` | no (→ `experimental.stack` → legacy) | -| `SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION` | deprecated alias of `SUPABASE_DB_ORIOLEDB_VERSION`, used only while that is unset; when set, stderr gets `WARN: SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION is deprecated and will be removed in v3, use SUPABASE_DB_ORIOLEDB_VERSION instead.` | no | -| `PG*` libpq | linked-project TLS, timeout, service; see `db push` | no | +| Variable | Purpose | Required? | +| -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | +| `DOCKER_HOST` / `DOCKER_CONTEXT` / `DOCKER_TLS_VERIFY` / `DOCKER_CERT_PATH` / `DOCKER_API_VERSION` / `DOCKER_CONFIG` | local path: ambient shell environment only (project dotenv files deliberately never override Docker client keys) — resolves the daemon endpoint for the running probe (in-process) and steers the spawned `docker`/`podman` CLI itself | no | +| `SUPABASE_ACCESS_TOKEN` | auth token for the `--linked` resolver path | no (falls back to keyring → `~/.supabase/access-token`) | +| `SUPABASE_DB_PASSWORD` | password for the linked/remote connection | no | +| `SUPABASE_YES` | auto-confirm the reset prompt and the local path's bucket-seed overwrite/prune prompts (shell or project dotenv, same as `seed buckets`) | no (also `--yes`) | +| `SUPABASE_EXPERIMENTAL` | selects the schema-files apply branch on either target | no (also `--experimental`) | +| `SUPABASE_EXPERIMENTAL_PGDELTA_ENABLED` | overrides `[experimental.pgdelta].enabled`; a truthy value flips the reset gate (`experimental && resolvedVersion === "" && !toml.pgDelta.enabled`) back to timestamped migrations even with `--experimental` set — switches between two different destructive code paths | no | +| `SUPABASE_DB_MIGRATIONS_SCHEMA_PATHS` | overrides `[db.migrations].schema_paths` (viper `AutomaticEnv`, beats the config-file value) for the schema-files apply branch — genuinely effective on both targets now | no (no dedicated flag — config-file-only otherwise) | +| `SUPABASE_PROJECT_ID` | overrides the local container id; ALSO the linked-ref resolution fallback `--project-ref` supersedes — see Notes for the narrower scope of the flag | no | +| `SUPABASE_INTERNAL_IMAGE_REGISTRY` | overrides the image registry used to resolve the local path's container images (project `.env` or shell) | no (project `.env` or shell) | +| `SUPABASE_USE_SLIM_IMAGES` | resolves the local-reset Postgres image and the realtime/storage/auth migrate-job images from slim `ghcr.io/supabase/cli` builds (`true`/`1` enable); majors 13/15 use `15.14.1.167` when the flag is on; historical pins, PG14, OrioleDB tags the catalog does not pin, and flag-off `15.8.1.085` stay on docker.io; a failed GHCR pull falls back to the same tag and digest on `public.ecr.aws/supabase/cli` | no (ambient shell only) | +| `SUPABASE_DB_PORT` / `SUPABASE_DB_MAJOR_VERSION` / `SUPABASE_DB_HEALTH_TIMEOUT` / `SUPABASE_DB_SETTINGS_*` | local-path container-recreate config overrides, same as `db start` | no | +| `SUPABASE_API_PORT` / `SUPABASE_API_EXTERNAL_URL` / `SUPABASE_API_TLS_*` / `SUPABASE_API_ENABLED` | legacy-backend bucket-seed step only: override the matching `[api]` fields for the Storage gateway URL/TLS, same as `seed buckets` (shell or project dotenv; #6452). Not consulted on the stack backend (see above) | no | +| `SUPABASE_AUTH_JWT_SECRET` / `SUPABASE_AUTH_SERVICE_ROLE_KEY` | local path: override `auth.jwt_secret` / `auth.service_role_key` (shell or project dotenv, `encrypted:` decrypted) — feeds the recreated Postgres container (jwt secret), the fresh-database setup jobs (both) and the legacy-backend bucket-seed Storage service-role key, same as `seed buckets`. Not consulted on the stack backend, which uses the stack's own service-role JWT | no | +| `SUPABASE_NETWORK_ID` (`--network-id`) | forces the recreated container/network onto an existing Docker network — legacy backend only | no | +| `SUPABASE_EXPERIMENTAL_STACK` | `1`/`0` selects the stack backend for the local path, overriding `experimental.stack` | no (→ `experimental.stack` → legacy) | +| `SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION` | deprecated alias of `SUPABASE_DB_ORIOLEDB_VERSION`, used only while that is unset; when set, stderr gets `WARN: SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION is deprecated and will be removed in v3, use SUPABASE_DB_ORIOLEDB_VERSION instead.` | no | +| `PG*` libpq | linked-project TLS, timeout, service; see `db push` | no | ## Connection loss during migration apply diff --git a/apps/cli/src/commands/db/schema/declarative/generate/SIDE_EFFECTS.md b/apps/cli/src/commands/db/schema/declarative/generate/SIDE_EFFECTS.md index 454898f45b..5087e45520 100644 --- a/apps/cli/src/commands/db/schema/declarative/generate/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/db/schema/declarative/generate/SIDE_EFFECTS.md @@ -42,17 +42,17 @@ formatting without disabling safe compaction. ## Environment Variables -| Variable | Purpose | Required? | -| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -| `SUPABASE_ACCESS_TOKEN` | auth token for `--linked` | no | -| `DB_PASSWORD` | password for `--linked` / `--db-url` | no | -| `SUPABASE_HOME` | overrides the `~/.supabase` root (access token and other CLI state) | no | -| `PGDELTA_DEBUG` | bundled-engine debug artifacts | no | -| `SUPABASE_SERVICES_HOSTNAME` | local DB host for `--local` | no | -| `DOCKER_HOST` | tcp daemon host used as the local DB host fallback | no | -| `SUPABASE_USE_SLIM_IMAGES` | resolves the expected local `db` image for the stale-container guard from the slim `ghcr.io/supabase/cli` builds (`true`/`1` enable); majors 13/15 use `15.14.1.167` when the flag is on; historical pins, PG14, OrioleDB tags the catalog does not pin, and flag-off `15.8.1.085` stay on docker.io | no | -| `SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION` | deprecated alias of `SUPABASE_DB_ORIOLEDB_VERSION`, used only while that is unset; when set, stderr gets `WARN: SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION is deprecated and will be removed in v3, use SUPABASE_DB_ORIOLEDB_VERSION instead.` | no | -| `PG*` libpq | linked-project TLS, timeout, service; see `db push` | no | +| Variable | Purpose | Required? | +| ---------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | +| `SUPABASE_ACCESS_TOKEN` | auth token for `--linked` | no | +| `DB_PASSWORD` | password for `--linked` / `--db-url` | no | +| `SUPABASE_HOME` | overrides the `~/.supabase` root (access token and other CLI state) | no | +| `PGDELTA_DEBUG` | bundled-engine debug artifacts | no | +| `SUPABASE_SERVICES_HOSTNAME` | local DB host for `--local` | no | +| `DOCKER_HOST` | tcp daemon host used as the local DB host fallback | no | +| `SUPABASE_USE_SLIM_IMAGES` | resolves the expected local `db` image for the stale-container guard from the slim `ghcr.io/supabase/cli` builds (`true`/`1` enable); majors 13/15 use `15.14.1.167` when the flag is on; historical pins, PG14, OrioleDB tags the catalog does not pin, and flag-off `15.8.1.085` stay on docker.io; a container started from the `public.ecr.aws/supabase/cli` mirror counts as slim | no | +| `SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION` | deprecated alias of `SUPABASE_DB_ORIOLEDB_VERSION`, used only while that is unset; when set, stderr gets `WARN: SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION is deprecated and will be removed in v3, use SUPABASE_DB_ORIOLEDB_VERSION instead.` | no | +| `PG*` libpq | linked-project TLS, timeout, service; see `db push` | no | ## Exit Codes diff --git a/apps/cli/src/commands/db/schema/declarative/sync/SIDE_EFFECTS.md b/apps/cli/src/commands/db/schema/declarative/sync/SIDE_EFFECTS.md index 4b21f138f9..024dbccea6 100644 --- a/apps/cli/src/commands/db/schema/declarative/sync/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/db/schema/declarative/sync/SIDE_EFFECTS.md @@ -55,15 +55,15 @@ disabling safe compaction. ## Environment Variables -| Variable | Purpose | Required? | -| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -| `SUPABASE_HOME` | overrides the `~/.supabase` root used for the shadow baseline cache (and other CLI state) | no | -| `SUPABASE_SHADOW_CACHE` | shadow baseline cache; on by default, opt-out (`0`/`false`); the shadow's post-baseline state is saved under a managed snapshot key and restored into the next run's fresh stack database (see Notes) | no | -| `PGDELTA_DEBUG` | bundled-engine debug artifacts | no | -| `SUPABASE_SERVICES_HOSTNAME` | local DB host for the bootstrap generate | no | -| `DOCKER_HOST` | tcp daemon host used as the local DB host fallback | no | -| `SUPABASE_USE_SLIM_IMAGES` | resolves the current-pin shadow Postgres and PG15+ realtime/storage/auth migrate-job images from the slim `ghcr.io/supabase/cli` builds (`true`/`1` enable); majors 13/15 use `15.14.1.167` when the flag is on; historical pins, PG14, OrioleDB tags the catalog does not pin, and flag-off `15.8.1.085` stay on docker.io | no | -| `PG*` libpq | no-files bootstrap only, when the target prompt picks **Linked project**: linked-project TLS, timeout, service; see `db push`. Sync itself has no `--linked` flag | no | +| Variable | Purpose | Required? | +| ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | +| `SUPABASE_HOME` | overrides the `~/.supabase` root used for the shadow baseline cache (and other CLI state) | no | +| `SUPABASE_SHADOW_CACHE` | shadow baseline cache; on by default, opt-out (`0`/`false`); the shadow's post-baseline state is saved under a managed snapshot key and restored into the next run's fresh stack database (see Notes) | no | +| `PGDELTA_DEBUG` | bundled-engine debug artifacts | no | +| `SUPABASE_SERVICES_HOSTNAME` | local DB host for the bootstrap generate | no | +| `DOCKER_HOST` | tcp daemon host used as the local DB host fallback | no | +| `SUPABASE_USE_SLIM_IMAGES` | resolves the current-pin shadow Postgres and PG15+ realtime/storage/auth migrate-job images from the slim `ghcr.io/supabase/cli` builds (`true`/`1` enable); majors 13/15 use `15.14.1.167` when the flag is on; historical pins, PG14, OrioleDB tags the catalog does not pin, and flag-off `15.8.1.085` stay on docker.io; a failed GHCR pull falls back to the same tag and digest on `public.ecr.aws/supabase/cli`; a container started from that mirror counts as slim | no | +| `PG*` libpq | no-files bootstrap only, when the target prompt picks **Linked project**: linked-project TLS, timeout, service; see `db push`. Sync itself has no `--linked` flag | no | ## Exit Codes diff --git a/apps/cli/src/commands/db/shared/pgdelta.seam.integration.test.ts b/apps/cli/src/commands/db/shared/pgdelta.seam.integration.test.ts index c056b58738..b581e9df22 100644 --- a/apps/cli/src/commands/db/shared/pgdelta.seam.integration.test.ts +++ b/apps/cli/src/commands/db/shared/pgdelta.seam.integration.test.ts @@ -52,28 +52,31 @@ const pgTag = vi.hoisted(() => { return match[1]; }); -vi.mock("@supabase/stack/internal/artifacts", () => { - const digest = "d348483ad1141c54bfb4eaae801f5385fe1c2970fc106f95f531b5247092d52c"; - const nativePin = { archive: digest, manifest: digest }; - return { - catalogPins: () => [ - { - service: "database", - sourceService: "postgres", - pin: { - upstreamVersion: pgTag, - revision: 0, - image: `ghcr.io/supabase/cli/postgres:${pgTag}-r0@sha256:${digest}`, - natives: { - "darwin-arm64": nativePin, - "linux-amd64": nativePin, - "linux-arm64": nativePin, +vi.mock("@supabase/stack/internal/artifacts", (importOriginal) => + importOriginal().then((actual) => { + const digest = "d348483ad1141c54bfb4eaae801f5385fe1c2970fc106f95f531b5247092d52c"; + const nativePin = { archive: digest, manifest: digest }; + return { + ...actual, + catalogPins: () => [ + { + service: "database", + sourceService: "postgres", + pin: { + upstreamVersion: pgTag, + revision: 0, + image: `ghcr.io/supabase/cli/postgres:${pgTag}-r0@sha256:${digest}`, + natives: { + "darwin-arm64": nativePin, + "linux-amd64": nativePin, + "linux-arm64": nativePin, + }, }, }, - }, - ], - }; -}); + ], + }; + }), +); /** * Integration coverage for the fully-native `declarativeSeamLayer`: `generate`/`sync`'s own @@ -270,6 +273,19 @@ describe("declarativeSeamLayer.ensureLocalPostgresImageCurrent", () => { }).pipe(Effect.provide(layer)); }); + it.effect("passes when a slim container was pulled from the ECR Public mirror", () => { + vi.stubEnv("SUPABASE_USE_SLIM_IMAGES", "true"); + const dir = tmp.current; + const { layer } = setup(dir, { + dbInspectImage: `public.ecr.aws/supabase/cli/postgres:${pgTag}-r0@sha256:d348483ad1141c54bfb4eaae801f5385fe1c2970fc106f95f531b5247092d52c`, + }); + return Effect.gen(function* () { + const seam = yield* DeclarativeSeam; + const exit = yield* seam.ensureLocalPostgresImageCurrent.pipe(Effect.exit); + expect(Exit.isSuccess(exit)).toBe(true); + }).pipe(Effect.provide(layer)); + }); + it.effect("bails out when inspect succeeds but the image name is unparseable", () => { vi.stubEnv("SUPABASE_USE_SLIM_IMAGES", "true"); const dir = tmp.current; diff --git a/apps/cli/src/commands/db/shared/pgdelta.seam.layer.ts b/apps/cli/src/commands/db/shared/pgdelta.seam.layer.ts index 79ae4290ea..04b0f18d5a 100644 --- a/apps/cli/src/commands/db/shared/pgdelta.seam.layer.ts +++ b/apps/cli/src/commands/db/shared/pgdelta.seam.layer.ts @@ -247,7 +247,7 @@ export const declarativeSeamLayer = Layer.effect( if (actual.length === 0 || actualTag === undefined || expectedTag === undefined) { return; } - // Slim refs never go through a registry mirror, so a family mismatch + // A slim ref's ECR Public mirror is the same family; any other family mismatch // (e.g. a docker.io container satisfying a ghcr.io/supabase/cli // expectation) is stale even when the upstream versions happen to match. const familyMismatch = isSlimImageRef(expected) !== isSlimImageRef(actual); diff --git a/apps/cli/src/commands/db/start/SIDE_EFFECTS.md b/apps/cli/src/commands/db/start/SIDE_EFFECTS.md index 10b9290306..7e017bd662 100644 --- a/apps/cli/src/commands/db/start/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/db/start/SIDE_EFFECTS.md @@ -41,6 +41,7 @@ composition reuses too — see that command's `SIDE_EFFECTS.md`): 13/15's published slim PG15 pin, `15.14.1.167`) to `ghcr.io/supabase/cli/postgres`; a historical `.temp/postgres-version` pin, PG14, OrioleDB tags the catalog does not pin, and flag-off majors 13/15 (`15.8.1.085`) stay on docker.io. + A failed GHCR pull falls back to the same tag and digest on `public.ecr.aws/supabase/cli/postgres`. The restore entrypoint is the same on both families. 6. Wait for the container to become healthy (`db.health_timeout`, default `2m`). A timeout fails the command UNLESS `--from-backup` is set, in which case it is swallowed (a large @@ -123,26 +124,26 @@ API request over the active context's local unix socket / named pipe: ## Environment Variables -| Variable | Purpose | Required? | -| -------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -| `SUPABASE_PROJECT_ID` | overrides the local container id | no | -| `SUPABASE_DB_PORT` | overrides `db.port` (the published host port) | no | -| `SUPABASE_DB_MAJOR_VERSION` | overrides `db.major_version` (image selection, schema branch) | no | -| `SUPABASE_DB_HEALTH_TIMEOUT` | overrides `db.health_timeout` | no | -| `SUPABASE_DB_SETTINGS_*` | overrides individual `[db.settings]` fields | no | -| `SUPABASE_DB_ORIOLEDB_VERSION` | overrides `db.orioledb_version` (image + env) | no | -| `SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION` | deprecated alias of `SUPABASE_DB_ORIOLEDB_VERSION`, used only while that is unset; when set, stderr gets `WARN: SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION is deprecated and will be removed in v3, use SUPABASE_DB_ORIOLEDB_VERSION instead.` | no | -| `SUPABASE_EXPERIMENTAL_S3_{HOST,REGION,ACCESS_KEY,SECRET_KEY}` | OrioleDB S3 env overrides | no | -| `SUPABASE_REALTIME_ENABLED` | gates the fresh-volume realtime migrate job | no | -| `SUPABASE_REALTIME_IP_VERSION` / `_MAX_HEADER_LENGTH` | realtime migrate job env overrides | no | -| `SUPABASE_STORAGE_ENABLED` | gates the fresh-volume storage migrate job | no | -| `SUPABASE_STORAGE_FILE_SIZE_LIMIT` | storage migrate job env override | no | -| `SUPABASE_AUTH_ENABLED` | gates the fresh-volume auth migrate job | no | -| `SUPABASE_AUTH_EXTERNAL_URL` / `SUPABASE_AUTH_SITE_URL` | auth migrate job env overrides | no | -| `SUPABASE_AUTH_JWT_EXPIRY` | Postgres's `JWT_EXP` env / signing | no | -| `SUPABASE_EXPERIMENTAL` (or `--experimental`) | fresh volume + no pg-delta: applies `db.migrations.schema_paths` files instead of `migrations/*.sql` | no | -| `DOCKER_HOST` / `DOCKER_CONTEXT` / `DOCKER_TLS_VERIFY` / `DOCKER_CERT_PATH` / `DOCKER_API_VERSION` / `DOCKER_CONFIG` | Read from the ambient shell environment to pick the Docker daemon this whole command talks to (project dotenv files deliberately never override Docker client keys) | no | -| `SUPABASE_USE_SLIM_IMAGES` | resolves the current Dockerfile pin (and majors 13/15's published slim PG15 pin, `15.14.1.167`) and PG15+ realtime/storage/auth migrate-job images from the slim `ghcr.io/supabase/cli` builds (`true`/`1` enable); historical `.temp` pins, PG14, OrioleDB tags the catalog does not pin, and flag-off majors 13/15 (`15.8.1.085`) stay on docker.io | no | +| Variable | Purpose | Required? | +| -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------- | +| `SUPABASE_PROJECT_ID` | overrides the local container id | no | +| `SUPABASE_DB_PORT` | overrides `db.port` (the published host port) | no | +| `SUPABASE_DB_MAJOR_VERSION` | overrides `db.major_version` (image selection, schema branch) | no | +| `SUPABASE_DB_HEALTH_TIMEOUT` | overrides `db.health_timeout` | no | +| `SUPABASE_DB_SETTINGS_*` | overrides individual `[db.settings]` fields | no | +| `SUPABASE_DB_ORIOLEDB_VERSION` | overrides `db.orioledb_version` (image + env) | no | +| `SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION` | deprecated alias of `SUPABASE_DB_ORIOLEDB_VERSION`, used only while that is unset; when set, stderr gets `WARN: SUPABASE_EXPERIMENTAL_ORIOLEDB_VERSION is deprecated and will be removed in v3, use SUPABASE_DB_ORIOLEDB_VERSION instead.` | no | +| `SUPABASE_EXPERIMENTAL_S3_{HOST,REGION,ACCESS_KEY,SECRET_KEY}` | OrioleDB S3 env overrides | no | +| `SUPABASE_REALTIME_ENABLED` | gates the fresh-volume realtime migrate job | no | +| `SUPABASE_REALTIME_IP_VERSION` / `_MAX_HEADER_LENGTH` | realtime migrate job env overrides | no | +| `SUPABASE_STORAGE_ENABLED` | gates the fresh-volume storage migrate job | no | +| `SUPABASE_STORAGE_FILE_SIZE_LIMIT` | storage migrate job env override | no | +| `SUPABASE_AUTH_ENABLED` | gates the fresh-volume auth migrate job | no | +| `SUPABASE_AUTH_EXTERNAL_URL` / `SUPABASE_AUTH_SITE_URL` | auth migrate job env overrides | no | +| `SUPABASE_AUTH_JWT_EXPIRY` | Postgres's `JWT_EXP` env / signing | no | +| `SUPABASE_EXPERIMENTAL` (or `--experimental`) | fresh volume + no pg-delta: applies `db.migrations.schema_paths` files instead of `migrations/*.sql` | no | +| `DOCKER_HOST` / `DOCKER_CONTEXT` / `DOCKER_TLS_VERIFY` / `DOCKER_CERT_PATH` / `DOCKER_API_VERSION` / `DOCKER_CONFIG` | Read from the ambient shell environment to pick the Docker daemon this whole command talks to (project dotenv files deliberately never override Docker client keys) | no | +| `SUPABASE_USE_SLIM_IMAGES` | resolves the current Dockerfile pin (and majors 13/15's published slim PG15 pin, `15.14.1.167`) and PG15+ realtime/storage/auth migrate-job images from the slim `ghcr.io/supabase/cli` builds (`true`/`1` enable); historical `.temp` pins, PG14, OrioleDB tags the catalog does not pin, and flag-off majors 13/15 (`15.8.1.085`) stay on docker.io; a failed GHCR pull falls back to the same tag and digest on `public.ecr.aws/supabase/cli` | no | `--network-id` (a global CLI flag, not an environment variable — `command-internal/global-flags.ts`) forces every created container/network onto that Docker network instead of the generated diff --git a/apps/cli/src/commands/functions/deploy/SIDE_EFFECTS.md b/apps/cli/src/commands/functions/deploy/SIDE_EFFECTS.md index d26ceb6bb7..7ef3b49ae0 100644 --- a/apps/cli/src/commands/functions/deploy/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/functions/deploy/SIDE_EFFECTS.md @@ -50,18 +50,18 @@ Docker bundling may pull or run the configured edge-runtime image and uses the ## Environment Variables -| Variable | Purpose | Required? | -| ------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | -| `SUPABASE_ACCESS_TOKEN` | auth token (bypasses credential file/keyring lookup) | no (falls back to keyring → `~/.supabase/access-token`) | -| `SUPABASE_PROJECT_ID` | optional project ref fallback; also read from project dotenv now (previously ambient-shell-only) | no | -| `SUPABASE_ENV` | selects environment-specific dotenv files (`.env..local`, `.env.`) | no (defaults to `development`) | -| `SUPABASE_INTERNAL_IMAGE_REGISTRY` | selects the Functions bundler image registry; read from the ambient shell **or** project dotenv; unset resolves ECR->GHCR->Docker-Hub candidates in order instead of a single URL | no | -| `SUPABASE_USE_SLIM_IMAGES` | resolves the Functions bundler image from the slim `ghcr.io/supabase/cli/edge-runtime` build (`true`/`1` enable); `deno_version = 1` and historical `.temp/edge-runtime-version` pins stay on docker.io; ambient shell only, unlike the neighboring registry override | no | -| `SUPABASE_NETWORK_ID` | overrides the generated `supabase_network_` Docker network name when `--network-id` isn't passed; read from the ambient shell or project dotenv | no | -| `BITBUCKET_CLONE_DIR` | when defined (including an empty value), skips creating the named Deno-cache volume and omits its bind mount from the bundler `docker run` (Bitbucket's restricted Docker environment rejects both); project values are passed explicitly to Docker setup | no | -| `SUPABASE_EDGE_RUNTIME_DENO_VERSION` | overrides `edge_runtime.deno_version` (which bundler image tag to use) when set, from the ambient shell or project dotenv — takes effect even with no `config.toml` on disk | no | -| `NPM_CONFIG_REGISTRY` | forwarded into Docker bundling when set (the only npm variable forwarded; `NPM_AUTH_TOKEN` is not) | no | -| `DEBUG` | enables verbose Docker bundle output when `true` | no | +| Variable | Purpose | Required? | +| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | +| `SUPABASE_ACCESS_TOKEN` | auth token (bypasses credential file/keyring lookup) | no (falls back to keyring → `~/.supabase/access-token`) | +| `SUPABASE_PROJECT_ID` | optional project ref fallback; also read from project dotenv now (previously ambient-shell-only) | no | +| `SUPABASE_ENV` | selects environment-specific dotenv files (`.env..local`, `.env.`) | no (defaults to `development`) | +| `SUPABASE_INTERNAL_IMAGE_REGISTRY` | selects the Functions bundler image registry; read from the ambient shell **or** project dotenv; unset resolves ECR->GHCR->Docker-Hub candidates in order instead of a single URL | no | +| `SUPABASE_USE_SLIM_IMAGES` | resolves the Functions bundler image from the slim `ghcr.io/supabase/cli/edge-runtime` build (`true`/`1` enable); `deno_version = 1` and historical `.temp/edge-runtime-version` pins stay on docker.io; a failed GHCR pull falls back to the same tag and digest on `public.ecr.aws/supabase/cli`; ambient shell only, unlike the neighboring registry override | no | +| `SUPABASE_NETWORK_ID` | overrides the generated `supabase_network_` Docker network name when `--network-id` isn't passed; read from the ambient shell or project dotenv | no | +| `BITBUCKET_CLONE_DIR` | when defined (including an empty value), skips creating the named Deno-cache volume and omits its bind mount from the bundler `docker run` (Bitbucket's restricted Docker environment rejects both); project values are passed explicitly to Docker setup | no | +| `SUPABASE_EDGE_RUNTIME_DENO_VERSION` | overrides `edge_runtime.deno_version` (which bundler image tag to use) when set, from the ambient shell or project dotenv — takes effect even with no `config.toml` on disk | no | +| `NPM_CONFIG_REGISTRY` | forwarded into Docker bundling when set (the only npm variable forwarded; `NPM_AUTH_TOKEN` is not) | no | +| `DEBUG` | enables verbose Docker bundle output when `true` | no | ## Exit Codes diff --git a/apps/cli/src/commands/functions/download/SIDE_EFFECTS.md b/apps/cli/src/commands/functions/download/SIDE_EFFECTS.md index 028e79b7b5..33906f61d0 100644 --- a/apps/cli/src/commands/functions/download/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/functions/download/SIDE_EFFECTS.md @@ -67,7 +67,7 @@ to stderr in machine-output modes (CLI-1546). | `SUPABASE_ENV` | Docker-unbundle path: selects environment-specific dotenv files (`.env..local`, `.env.`) | no (defaults to `development`) | | `BITBUCKET_CLONE_DIR` | Docker-unbundle path: when defined (including an empty value), skips creating the named Deno-cache volume and omits its bind mount from the `docker run` command (Bitbucket's restricted Docker environment rejects both) | no | | `SUPABASE_INTERNAL_IMAGE_REGISTRY` | selects the registry the edge-runtime unbundle image is pulled from (`getRegistryImageUrl`); read from the ambient shell **or** project dotenv (Docker-unbundle path); unset resolves ECR->GHCR->Docker-Hub candidates in order instead of a single URL — also consumed on the `--use-api` invocation even though it never pulls an image | no (defaults to `public.ecr.aws`) | -| `SUPABASE_USE_SLIM_IMAGES` | Docker-unbundle path: resolves the edge-runtime unbundle image from the slim `ghcr.io/supabase/cli/edge-runtime` build (`true`/`1` enable); `deno_version = 1` and historical `.temp/edge-runtime-version` pins stay on docker.io | no | +| `SUPABASE_USE_SLIM_IMAGES` | Docker-unbundle path: resolves the edge-runtime unbundle image from the slim `ghcr.io/supabase/cli/edge-runtime` build (`true`/`1` enable); `deno_version = 1` and historical `.temp/edge-runtime-version` pins stay on docker.io; a failed GHCR pull falls back to the same tag and digest on `public.ecr.aws/supabase/cli` | no | | `SUPABASE_NETWORK_ID` | Docker-unbundle path: overrides the generated `supabase_network_` Docker network name when `--network-id` isn't passed; read from the ambient shell or project dotenv | no | | `SUPABASE_EDGE_RUNTIME_DENO_VERSION` | Docker-unbundle path: overrides `edge_runtime.deno_version` (which image tag to pull) when set, from the ambient shell or project dotenv — takes effect even with no `config.toml` on disk | no | diff --git a/apps/cli/src/commands/functions/serve/SIDE_EFFECTS.md b/apps/cli/src/commands/functions/serve/SIDE_EFFECTS.md index 02270298a4..3161b7d89d 100644 --- a/apps/cli/src/commands/functions/serve/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/functions/serve/SIDE_EFFECTS.md @@ -46,18 +46,18 @@ back to local keys. No scheme/host validation is performed on the discovered URL ## Environment Variables -| Variable | Purpose | Required? | -| --------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | -| `SUPABASE_PROFILE` | resolves the profile / API base URL | no (defaults to `supabase`) | -| `SUPABASE_WORKDIR` | overrides the project workdir | no (falls back to CLI cwd discovery) | -| `SUPABASE_PROJECT_ID` | config-service override for project identity | no | -| `SUPABASE_ENV` | selects environment-specific dotenv files (`.env..local`, `.env.`) | no (defaults to `development`) | -| env vars referenced by `supabase/config.toml` | config interpolation; the full ambient `process.env` is layered under the project `.env*` files and passed to config loading | no | -| `SUPABASE_INTERNAL_IMAGE_REGISTRY` | overrides the edge-runtime Docker registry mirror; read from the ambient shell **or** project dotenv; unset resolves ECR->GHCR->Docker-Hub candidates in order instead of a single URL | no (defaults to `public.ecr.aws`) | -| `SUPABASE_USE_SLIM_IMAGES` | resolves the edge-runtime image from the slim `ghcr.io/supabase/cli/edge-runtime` build (`true`/`1` enable); `deno_version = 1` and historical `.temp/edge-runtime-version` pins stay on docker.io | no | -| `SUPABASE_NETWORK_ID` | overrides the generated `supabase_network_` Docker network name when `--network-id` isn't passed; read from the ambient shell or project dotenv | no | -| `SUPABASE_EDGE_RUNTIME_DENO_VERSION` | overrides `edge_runtime.deno_version` (which image tag to pull) when set, from the ambient shell or project dotenv — takes effect even with no `config.toml` on disk | no | -| `BITBUCKET_CLONE_DIR` | when defined (including an empty value), skips creating the named Deno-cache volume and omits its bind mount from the edge-runtime `docker create` (Bitbucket's restricted Docker environment rejects both); project values are passed explicitly to Docker setup | no | +| Variable | Purpose | Required? | +| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | +| `SUPABASE_PROFILE` | resolves the profile / API base URL | no (defaults to `supabase`) | +| `SUPABASE_WORKDIR` | overrides the project workdir | no (falls back to CLI cwd discovery) | +| `SUPABASE_PROJECT_ID` | config-service override for project identity | no | +| `SUPABASE_ENV` | selects environment-specific dotenv files (`.env..local`, `.env.`) | no (defaults to `development`) | +| env vars referenced by `supabase/config.toml` | config interpolation; the full ambient `process.env` is layered under the project `.env*` files and passed to config loading | no | +| `SUPABASE_INTERNAL_IMAGE_REGISTRY` | overrides the edge-runtime Docker registry mirror; read from the ambient shell **or** project dotenv; unset resolves ECR->GHCR->Docker-Hub candidates in order instead of a single URL | no (defaults to `public.ecr.aws`) | +| `SUPABASE_USE_SLIM_IMAGES` | resolves the edge-runtime image from the slim `ghcr.io/supabase/cli/edge-runtime` build (`true`/`1` enable); `deno_version = 1` and historical `.temp/edge-runtime-version` pins stay on docker.io; a failed GHCR pull falls back to the same tag and digest on `public.ecr.aws/supabase/cli` | no | +| `SUPABASE_NETWORK_ID` | overrides the generated `supabase_network_` Docker network name when `--network-id` isn't passed; read from the ambient shell or project dotenv | no | +| `SUPABASE_EDGE_RUNTIME_DENO_VERSION` | overrides `edge_runtime.deno_version` (which image tag to pull) when set, from the ambient shell or project dotenv — takes effect even with no `config.toml` on disk | no | +| `BITBUCKET_CLONE_DIR` | when defined (including an empty value), skips creating the named Deno-cache volume and omits its bind mount from the edge-runtime `docker create` (Bitbucket's restricted Docker environment rejects both); project values are passed explicitly to Docker setup | no | ## Exit Codes diff --git a/apps/cli/src/commands/migration/squash/SIDE_EFFECTS.md b/apps/cli/src/commands/migration/squash/SIDE_EFFECTS.md index f5ac95e80b..a5bb10c8cc 100644 --- a/apps/cli/src/commands/migration/squash/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/migration/squash/SIDE_EFFECTS.md @@ -88,7 +88,7 @@ live shadow available. `SUPABASE_YES`, `DB_PASSWORD`, `SUPABASE_ACCESS_TOKEN`, `SUPABASE_SERVICES_HOSTNAME`, `DOCKER_HOST`/`DOCKER_CONTEXT`/`DOCKER_CONFIG`, `SUPABASE_NETWORK_ID`, -`SUPABASE_INTERNAL_IMAGE_REGISTRY`, `SUPABASE_USE_SLIM_IMAGES` (current-pin shadow Postgres and PG15+ realtime/storage/auth migrate-job images → slim `ghcr.io/supabase/cli`; historical pins, PG14, OrioleDB tags the catalog does not pin, flag-off `15.8.1.085` stay on docker.io), `SUPABASE_PROJECT_ID`, `SUPABASE_DEBUG`, +`SUPABASE_INTERNAL_IMAGE_REGISTRY`, `SUPABASE_USE_SLIM_IMAGES` (current-pin shadow Postgres and PG15+ realtime/storage/auth migrate-job images → slim `ghcr.io/supabase/cli`; a failed GHCR pull falls back to the same tag and digest on `public.ecr.aws/supabase/cli`; historical pins, PG14, OrioleDB tags the catalog does not pin, flag-off `15.8.1.085` stay on docker.io), `SUPABASE_PROJECT_ID`, `SUPABASE_DEBUG`, `SUPABASE_EXPERIMENTAL`, `SUPABASE_SHADOW_CACHE` (stack shadow baseline cache; on by default, falsy disables restore and publication), `PG*` libpq on `--linked` (TLS, timeout, service; see `db push`). ## Exit Codes diff --git a/apps/cli/src/commands/start/SIDE_EFFECTS.md b/apps/cli/src/commands/start/SIDE_EFFECTS.md index 643804de2b..76860f2153 100644 --- a/apps/cli/src/commands/start/SIDE_EFFECTS.md +++ b/apps/cli/src/commands/start/SIDE_EFFECTS.md @@ -127,7 +127,8 @@ Kong's `custom_nginx.template`, Vector's `vector.yaml`, and Postgres's own boots script (`postgresql.conf`-equivalent setup) are all rendered in memory and injected directly into each container's entrypoint (a `sh -c '... heredoc ...'` command) — never written to the host filesystem, since none of them carries secret content. -`SUPABASE_USE_SLIM_IMAGES` rewrites image names to `ghcr.io/supabase/cli/*`. +`SUPABASE_USE_SLIM_IMAGES` rewrites image names to `ghcr.io/supabase/cli/*`; a failed GHCR pull +falls back to the same tag and digest on `public.ecr.aws/supabase/cli/*`. Slim postgres, studio, pg-meta, and edge-runtime keep the docker.io health probes (`pg_isready` / `node`). Slim analytics keeps the docker.io start spec (`./logflare` migrate/start wrapper and BigQuery bind at @@ -194,19 +195,19 @@ not implemented. ## Environment Variables -| Variable | Purpose | Required? | -| -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | -| `SUPABASE_*` (any dotted config field) | Generic Viper-style `AutomaticEnv` override of any `config.toml` field (e.g. `SUPABASE_AUTH_ENABLED`, `SUPABASE_API_PORT`) | no | -| `SUPABASE_EXPERIMENTAL` (or `--experimental`) | Fresh volume + no pg-delta: applies `db.migrations.schema_paths` files instead of `migrations/*.sql` (see "Fresh-volume DB setup" above) | no | -| `SUPABASE_INTERNAL_IMAGE_REGISTRY` | Overrides the image registry used to resolve every service's image | no | -| `SUPABASE_PROJECT_ID` | Overrides the resolved local project id (env → config.toml → workdir basename) | no | -| `SUPABASE_WORKDIR` | Resolves `CommandSettings.workdir` | no | -| `SUPABASE_YES` (or `--yes`) | Auto-confirms the fresh-volume bucket-seed overwrite/prune prompts (shell or project dotenv, same as `seed buckets`) | no | -| `BITBUCKET_CLONE_DIR` | When non-empty, drops named volumes and `--security-opt` from every container create | no | -| `DOCKER_HOST` / `DOCKER_CONTEXT` / `DOCKER_TLS_VERIFY` / `DOCKER_CERT_PATH` / `DOCKER_API_VERSION` / `DOCKER_CONFIG` | Read (ambient shell OR a project `.env`/`.env.`/`.env.local` file) to discover the Docker daemon this whole command talks to; `DOCKER_HOST` is also re-derived and set on Vector's container env so it can reach the host's Docker socket for log collection | no | -| `KONG_NGINX_WORKER_PROCESSES` | Read (ambient shell or project dotenv) into Kong's own container env (defaults to `"1"` when unset) | no | -| `HTTP_PROXY` / `http_proxy` / `HTTPS_PROXY` / `https_proxy` / `NO_PROXY` / `no_proxy` | Bun proxy settings. After project dotenv and container creation, `start` appends `localhost,127.0.0.1,[::1]` to the effective no-proxy value before local Kong probes and seeding; it never changes project/container env and ends with this CLI process. | no | -| `SUPABASE_USE_SLIM_IMAGES` | Ambient `process.env` only (`true`/`1` enable) — not project dotenv. Rewrites current Dockerfile pins to `ghcr.io/supabase/cli/` (including PG15+ realtime/storage/auth migrate jobs). Kong, PG14, OrioleDB tags the catalog does not pin, historical Postgres pins, and `deno_version = 1` remain non-slim (still subject to `SUPABASE_INTERNAL_IMAGE_REGISTRY`). Majors 13/15 use the published slim PG15 pin (`15.14.1.167`) when the flag is on; flag-off keeps `15.8.1.085`. `SUPABASE_INTERNAL_IMAGE_REGISTRY` does not apply to slim refs | no | +| Variable | Purpose | Required? | +| -------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | +| `SUPABASE_*` (any dotted config field) | Generic Viper-style `AutomaticEnv` override of any `config.toml` field (e.g. `SUPABASE_AUTH_ENABLED`, `SUPABASE_API_PORT`) | no | +| `SUPABASE_EXPERIMENTAL` (or `--experimental`) | Fresh volume + no pg-delta: applies `db.migrations.schema_paths` files instead of `migrations/*.sql` (see "Fresh-volume DB setup" above) | no | +| `SUPABASE_INTERNAL_IMAGE_REGISTRY` | Overrides the image registry used to resolve every service's image | no | +| `SUPABASE_PROJECT_ID` | Overrides the resolved local project id (env → config.toml → workdir basename) | no | +| `SUPABASE_WORKDIR` | Resolves `CommandSettings.workdir` | no | +| `SUPABASE_YES` (or `--yes`) | Auto-confirms the fresh-volume bucket-seed overwrite/prune prompts (shell or project dotenv, same as `seed buckets`) | no | +| `BITBUCKET_CLONE_DIR` | When non-empty, drops named volumes and `--security-opt` from every container create | no | +| `DOCKER_HOST` / `DOCKER_CONTEXT` / `DOCKER_TLS_VERIFY` / `DOCKER_CERT_PATH` / `DOCKER_API_VERSION` / `DOCKER_CONFIG` | Read (ambient shell OR a project `.env`/`.env.`/`.env.local` file) to discover the Docker daemon this whole command talks to; `DOCKER_HOST` is also re-derived and set on Vector's container env so it can reach the host's Docker socket for log collection | no | +| `KONG_NGINX_WORKER_PROCESSES` | Read (ambient shell or project dotenv) into Kong's own container env (defaults to `"1"` when unset) | no | +| `HTTP_PROXY` / `http_proxy` / `HTTPS_PROXY` / `https_proxy` / `NO_PROXY` / `no_proxy` | Bun proxy settings. After project dotenv and container creation, `start` appends `localhost,127.0.0.1,[::1]` to the effective no-proxy value before local Kong probes and seeding; it never changes project/container env and ends with this CLI process. | no | +| `SUPABASE_USE_SLIM_IMAGES` | Ambient `process.env` only (`true`/`1` enable) — not project dotenv. Rewrites current Dockerfile pins to `ghcr.io/supabase/cli/` (including PG15+ realtime/storage/auth migrate jobs). Kong, PG14, OrioleDB tags the catalog does not pin, historical Postgres pins, and `deno_version = 1` remain non-slim (still subject to `SUPABASE_INTERNAL_IMAGE_REGISTRY`). Majors 13/15 use the published slim PG15 pin (`15.14.1.167`) when the flag is on; flag-off keeps `15.8.1.085`. `SUPABASE_INTERNAL_IMAGE_REGISTRY` does not apply to slim refs. A failed GHCR pull falls back to the same tag and digest on `public.ecr.aws/supabase/cli/`. | no | `docker`/`podman` must be resolvable on `PATH` — same fallback behavior as `stop`/`status`. diff --git a/apps/cli/src/commands/start/start.slim-images.e2e.test.ts b/apps/cli/src/commands/start/start.slim-images.e2e.test.ts index 878efdbea1..d468ff1c84 100644 --- a/apps/cli/src/commands/start/start.slim-images.e2e.test.ts +++ b/apps/cli/src/commands/start/start.slim-images.e2e.test.ts @@ -2,7 +2,11 @@ import { FetchHttpClient, HttpClient, HttpClientRequest } from "effect/http"; import { BunServices } from "@effect/platform-bun"; import { Clock, Crypto, Data, Effect, FileSystem, Layer, Path, Schema } from "effect"; import { beforeAll, describe, expect, it } from "@effect/vitest"; -import { catalogPins, type ArtifactKind } from "@supabase/stack/internal/artifacts"; +import { + catalogPins, + slimImageMirrors, + type ArtifactKind, +} from "@supabase/stack/internal/artifacts"; import { dockerfileServiceImageRaw } from "../../shared/services/dockerfile-images.ts"; import { isSlimImageRef, toSlimImage } from "../../shared/services/slim-images.ts"; @@ -115,6 +119,11 @@ const containerImage = Effect.fnUntraced(function* (name: string) { return stdout.trim(); }); +/* A slim image GHCR cannot serve comes from the ECR Public mirror at the same tag and digest. */ +const expectSlimContainerImage = Effect.fnUntraced(function* (name: string, expected: string) { + expect([expected, ...slimImageMirrors(expected)]).toContain(yield* containerImage(name)); +}); + /** `kong` has no slim build at all, so this stays fallback-tolerant for the pull-ahead list. */ function expectedSlimImage(alias: string): string { const raw = dockerfileServiceImageRaw(alias); @@ -307,7 +316,11 @@ describe("supabase start slim images (e2e)", () => { Effect.gen(function* () { for (const entry of catalogPins()) { if (!WGET_PROBE_SERVICES.includes(entry.service)) continue; - const image = entry.pin.image; + const image = yield* Effect.tryPromise({ + try: () => ensureImage(entry.pin.image), + catch: (cause) => + new StartE2eSetupError({ message: "failed to ensure Docker image", cause }), + }); const probe = entry.service === "realtime" ? slimWgetHealthcheck("http://127.0.0.1:9/", { @@ -380,15 +393,17 @@ describe("supabase start slim images (e2e)", () => { expect(image).toMatch(GHCR_SLIM_IMAGE_PATTERN); } - expect(yield* containerImage(dbContainer)).toBe( + yield* expectSlimContainerImage( + dbContainer, expectedPinnedImage("pg", dockerfileServiceImageRaw("pg")), ); - expect(yield* containerImage(storageContainer)).toBe(storageImage); - expect(yield* containerImage(edgeRuntimeContainer)).toBe( + yield* expectSlimContainerImage(storageContainer, storageImage); + yield* expectSlimContainerImage( + edgeRuntimeContainer, expectedPinnedImage("edgeruntime", dockerfileServiceImageRaw("edgeruntime")), ); - expect(yield* containerImage(authContainer)).toBe(authImage); - expect(yield* containerImage(realtimeContainer)).toBe(realtimeImage); + yield* expectSlimContainerImage(authContainer, authImage); + yield* expectSlimContainerImage(realtimeContainer, realtimeImage); expect(yield* containerHealthcheckTest(authContainer)).toEqual([ "CMD-SHELL", diff --git a/apps/cli/src/shared/services/slim-images.ts b/apps/cli/src/shared/services/slim-images.ts index e75706dcd5..f10dece518 100644 --- a/apps/cli/src/shared/services/slim-images.ts +++ b/apps/cli/src/shared/services/slim-images.ts @@ -1,8 +1,11 @@ -import { catalogPins } from "@supabase/stack/internal/artifacts"; +import { + catalogPins, + SLIM_IMAGE_GHCR_REGISTRY, + SLIM_IMAGE_SUPABASE_ECR_MIRROR, +} from "@supabase/stack/internal/artifacts"; import { Config, ConfigProvider, Effect, Option } from "effect"; const SLIM_IMAGES_ENV = "SUPABASE_USE_SLIM_IMAGES"; -const SLIM_IMAGE_PREFIX = "ghcr.io/supabase/cli/"; /** Dockerfile alias to slim service; kong, `pg14`, and the job images have no slim build. */ const SLIM_SERVICE_BY_ALIAS = { @@ -175,14 +178,16 @@ export function slimImageForCurrentPin( return toSlimImage(alias, tagged) ?? tagged; } -/** Slim images are published only under this prefix; single home for the check. */ +/** Slim images live on GHCR or its ECR Public mirror; single home for the check. */ export function isSlimImageRef(image: string): boolean { - return image.startsWith(SLIM_IMAGE_PREFIX); + return ( + image.startsWith(SLIM_IMAGE_GHCR_REGISTRY) || image.startsWith(SLIM_IMAGE_SUPABASE_ECR_MIRROR) + ); } /** - * True when the flag is on AND `image` is a slim ghcr ref. Spec builders and - * one-shot jobs use this so a ghcr-shaped override with the flag off stays on + * True when the flag is on AND `image` is a slim ref. Spec builders and + * one-shot jobs use this so a slim-shaped override with the flag off stays on * the docker.io contract. */ export function usesSlimImageRuntime(image: string, enabled: boolean): boolean { diff --git a/apps/cli/src/shared/services/slim-images.unit.test.ts b/apps/cli/src/shared/services/slim-images.unit.test.ts index 01bb586ad1..1f525976ac 100644 --- a/apps/cli/src/shared/services/slim-images.unit.test.ts +++ b/apps/cli/src/shared/services/slim-images.unit.test.ts @@ -17,42 +17,45 @@ import { // Only `auth` and one OrioleDB postgres build are pinned, so every other alias falls through to // its upstream image. `vi.mock` factories are hoisted above top-level statements, so the fixture // is inlined. -vi.mock("@supabase/stack/internal/artifacts", () => { - const digest = "d348483ad1141c54bfb4eaae801f5385fe1c2970fc106f95f531b5247092d52c"; - const nativePin = { archive: digest, manifest: digest }; - return { - catalogPins: () => [ - { - service: "auth", - sourceService: "auth", - pin: { - upstreamVersion: "v2.197.0", - revision: 0, - image: `ghcr.io/supabase/cli/auth:v2.197.0-r0@sha256:${digest}`, - natives: { - "darwin-arm64": nativePin, - "linux-amd64": nativePin, - "linux-arm64": nativePin, +vi.mock("@supabase/stack/internal/artifacts", (importOriginal) => + importOriginal().then((actual) => { + const digest = "d348483ad1141c54bfb4eaae801f5385fe1c2970fc106f95f531b5247092d52c"; + const nativePin = { archive: digest, manifest: digest }; + return { + ...actual, + catalogPins: () => [ + { + service: "auth", + sourceService: "auth", + pin: { + upstreamVersion: "v2.197.0", + revision: 0, + image: `ghcr.io/supabase/cli/auth:v2.197.0-r0@sha256:${digest}`, + natives: { + "darwin-arm64": nativePin, + "linux-amd64": nativePin, + "linux-arm64": nativePin, + }, }, }, - }, - { - service: "database", - sourceService: "postgres", - pin: { - upstreamVersion: "17.11.0.002-orioledb", - revision: 0, - image: `ghcr.io/supabase/cli/postgres:17.11.0.002-orioledb-r0@sha256:${digest}`, - natives: { - "darwin-arm64": nativePin, - "linux-amd64": nativePin, - "linux-arm64": nativePin, + { + service: "database", + sourceService: "postgres", + pin: { + upstreamVersion: "17.11.0.002-orioledb", + revision: 0, + image: `ghcr.io/supabase/cli/postgres:17.11.0.002-orioledb-r0@sha256:${digest}`, + natives: { + "darwin-arm64": nativePin, + "linux-amd64": nativePin, + "linux-arm64": nativePin, + }, }, }, - }, - ], - }; -}); + ], + }; + }), +); const AUTH_FIXTURE_PIN_IMAGE = "ghcr.io/supabase/cli/auth:v2.197.0-r0@sha256:d348483ad1141c54bfb4eaae801f5385fe1c2970fc106f95f531b5247092d52c"; @@ -262,7 +265,9 @@ describe("usesSlimImageRuntime", () => { it("is true only when the flag is on and the ref is slim", () => { expect(usesSlimImageRuntime("ghcr.io/supabase/cli/auth:v2.196.0", true)).toBe(true); + expect(usesSlimImageRuntime("public.ecr.aws/supabase/cli/auth:v2.196.0", true)).toBe(true); expect(usesSlimImageRuntime("supabase/gotrue:v2.196.0", true)).toBe(false); + expect(usesSlimImageRuntime("public.ecr.aws/supabase/gotrue:v2.196.0", true)).toBe(false); }); }); diff --git a/packages/stack/src/Artifacts.ts b/packages/stack/src/Artifacts.ts index 6f3c1633d3..1c304459fe 100644 --- a/packages/stack/src/Artifacts.ts +++ b/packages/stack/src/Artifacts.ts @@ -602,7 +602,8 @@ const errorMessage = (cause: unknown): string => */ const SLIM_NATIVE_SUPABASE_S3_MIRROR = "https://supabase-cli-artifacts.s3.us-east-1.amazonaws.com"; -const SLIM_IMAGE_SUPABASE_ECR_MIRROR = "public.ecr.aws/supabase/cli/"; +/** ECR Public mirror of every slim image, under the same tags and digests. */ +export const SLIM_IMAGE_SUPABASE_ECR_MIRROR = "public.ecr.aws/supabase/cli/"; /** Mirrors carrying a catalog slim image under the same tag and digest, in fallback order. */ export const slimImageMirrors = (image: string): ReadonlyArray => diff --git a/packages/stack/src/internal/artifacts.ts b/packages/stack/src/internal/artifacts.ts index 5907e3bae5..16bca86003 100644 --- a/packages/stack/src/internal/artifacts.ts +++ b/packages/stack/src/internal/artifacts.ts @@ -12,6 +12,8 @@ export { postgresMajor, prepareNativeArtifact, resolveArtifact, + SLIM_IMAGE_SUPABASE_ECR_MIRROR, + slimImageMirrors, useNativeArtifact, type ArtifactKind, type ArtifactPin, @@ -19,6 +21,7 @@ export { type NativePin, type ServiceKind, } from "../Artifacts.ts"; +export { SLIM_IMAGE_GHCR_REGISTRY } from "../preparation/SlimEndpoints.ts"; export { ReleaseIntegrity, ReleaseTransient,