Skip to content

Commit f54aba0

Browse files
fix(docker): bump the docker-minor group in /apps/cli-go/pkg/config/templates with 3 updates (#5517)
Bumps the docker-minor group in /apps/cli-go/pkg/config/templates with 3 updates: supabase/studio, supabase/realtime and supabase/storage-api. Updates `supabase/studio` from 2026.06.03-sha-0bca601 to 2026.06.08-sha-8af2bb0 Updates `supabase/realtime` from v2.103.4 to v2.105.0 Updates `supabase/storage-api` from v1.60.8 to v1.60.11 Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`. [//]: # (dependabot-automerge-start) [//]: # (dependabot-automerge-end) --- <details> <summary>Dependabot commands and options</summary> <br /> You can trigger Dependabot actions by commenting on this PR: - `@dependabot rebase` will rebase this PR - `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it - `@dependabot show <dependency name> ignore conditions` will show all of the ignore conditions of the specified dependency - `@dependabot ignore <dependency name> major version` will close this group update PR and stop Dependabot creating any more for the specific dependency's major version (unless you unignore this specific dependency's major version or upgrade to it yourself) - `@dependabot ignore <dependency name> minor version` will close this group update PR and stop Dependabot creating any more for the specific dependency's minor version (unless you unignore this specific dependency's minor version or upgrade to it yourself) - `@dependabot ignore <dependency name>` will close this group update PR and stop Dependabot creating any more for the specific dependency (unless you unignore this specific dependency or upgrade to it yourself) - `@dependabot unignore <dependency name>` will remove all of the ignore conditions of the specified dependency - `@dependabot unignore <dependency name> <ignore condition>` will remove the ignore condition of the specified dependency and ignore conditions </details> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Julien Goux <hi@jgoux.dev>
1 parent 0e06255 commit f54aba0

10 files changed

Lines changed: 189 additions & 43 deletions

File tree

apps/cli-e2e/src/tests/stack.e2e.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ describe("services", () => {
7373
expect(result.stdout).toContain("storage");
7474
});
7575

76-
testParity(["services"]);
76+
testParity(["services"], { normalizeVersions: false });
7777
});
7878

7979
// ---------------------------------------------------------------------------

apps/cli-e2e/src/tests/test-context.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ export function testParity(
145145
failureType?: FailureType;
146146
workspaceSetup?: (dir: string) => void;
147147
sortStdoutRows?: boolean;
148+
normalizeVersions?: boolean;
148149
},
149150
): void {
150151
const label = opts?.failureType
@@ -169,6 +170,7 @@ export function testParity(
169170
accessToken: ACCESS_TOKEN,
170171
workspaceSetup: opts?.workspaceSetup,
171172
sortStdoutRows: opts?.sortStdoutRows,
173+
normalize: { versions: opts?.normalizeVersions },
172174
},
173175
cmd,
174176
);

apps/cli-go/pkg/config/templates/Dockerfile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ FROM library/kong:2.8.1 AS kong
55
FROM axllent/mailpit:v1.22.3 AS mailpit
66
FROM postgrest/postgrest:v14.13 AS postgrest
77
FROM supabase/postgres-meta:v0.96.6 AS pgmeta
8-
FROM supabase/studio:2026.06.03-sha-0bca601 AS studio
8+
FROM supabase/studio:2026.06.08-sha-8af2bb0 AS studio
99
FROM darthsim/imgproxy:v3.8.0 AS imgproxy
1010
FROM supabase/edge-runtime:v1.74.0 AS edgeruntime
1111
FROM timberio/vector:0.53.0-alpine AS vector
1212
FROM supabase/supavisor:2.9.7 AS supavisor
1313
FROM supabase/gotrue:v2.189.0 AS gotrue
14-
FROM supabase/realtime:v2.103.4 AS realtime
15-
FROM supabase/storage-api:v1.60.8 AS storage
14+
FROM supabase/realtime:v2.105.0 AS realtime
15+
FROM supabase/storage-api:v1.60.11 AS storage
1616
FROM supabase/logflare:1.43.4 AS logflare
1717
# Append to JobImages when adding new dependencies below
1818
FROM supabase/pgadmin-schema-diff:cli-0.0.5 AS differ

apps/cli/src/globals.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,8 @@ declare module "*.md" {
22
const content: string;
33
export default content;
44
}
5+
6+
declare module "*Dockerfile" {
7+
const content: string;
8+
export default content;
9+
}

apps/cli/src/shared/services/services.shared.ts

Lines changed: 73 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { makeApiClient, type ApiClient } from "@supabase/api/effect";
33
import { Data, Duration, Effect, Exit, Redacted } from "effect";
44
import * as HttpClient from "effect/unstable/http/HttpClient";
55
import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest";
6+
import serviceImagesDockerfile from "../../../../cli-go/pkg/config/templates/Dockerfile" with { type: "text" };
67
import { renderGlamourTable } from "../../legacy/output/legacy-glamour-table.ts";
78

89
export type RemoteServiceName = "postgres" | "auth" | "postgrest" | "storage";
@@ -19,28 +20,73 @@ interface ServiceImageSpec {
1920
readonly remoteService: RemoteServiceName | undefined;
2021
}
2122

22-
// Mirrors the legacy `services` image matrix:
23-
// - source versions: `apps/cli-go/pkg/config/templates/Dockerfile`
24-
// - source order: `apps/cli-go/pkg/config/config.go` `GetServiceImages()`
25-
//
26-
// We keep this compiled into the TS CLI because the published package does not
27-
// ship the Go source tree at runtime, but the user-visible `services` output
28-
// still needs to match the bundled image manifest.
29-
const LOCAL_SERVICE_IMAGES = [
30-
{ image: "supabase/postgres:17.6.1.132", remoteService: "postgres" },
31-
{ image: "supabase/gotrue:v2.189.0", remoteService: "auth" },
32-
{ image: "postgrest/postgrest:v14.12", remoteService: "postgrest" },
33-
{ image: "supabase/realtime:v2.103.2", remoteService: undefined },
34-
{ image: "supabase/storage-api:v1.60.4", remoteService: "storage" },
35-
{ image: "supabase/edge-runtime:v1.74.0", remoteService: undefined },
36-
{
37-
image: "supabase/studio:2026.06.03-sha-0bca601",
38-
remoteService: undefined,
39-
},
40-
{ image: "supabase/postgres-meta:v0.96.6", remoteService: undefined },
41-
{ image: "supabase/logflare:1.43.3", remoteService: undefined },
42-
{ image: "supabase/supavisor:2.9.7", remoteService: undefined },
43-
] as const satisfies ReadonlyArray<ServiceImageSpec>;
23+
interface DockerfileImageSpec {
24+
readonly alias: string;
25+
readonly image: string;
26+
}
27+
28+
interface ServiceImageAliasSpec {
29+
readonly alias: string;
30+
readonly remoteService: RemoteServiceName | undefined;
31+
}
32+
33+
const SERVICE_IMAGE_ALIASES: ReadonlyArray<ServiceImageAliasSpec> = [
34+
{ alias: "pg", remoteService: "postgres" },
35+
{ alias: "gotrue", remoteService: "auth" },
36+
{ alias: "postgrest", remoteService: "postgrest" },
37+
{ alias: "realtime", remoteService: undefined },
38+
{ alias: "storage", remoteService: "storage" },
39+
{ alias: "edgeruntime", remoteService: undefined },
40+
{ alias: "studio", remoteService: undefined },
41+
{ alias: "pgmeta", remoteService: undefined },
42+
{ alias: "logflare", remoteService: undefined },
43+
{ alias: "supavisor", remoteService: undefined },
44+
];
45+
46+
const FROM_LINE_PATTERN = /^FROM\s+(.+):([^:\s]+)\s+AS\s+([^\s#]+)/i;
47+
48+
export function parseDockerfileServiceImages(
49+
dockerfile: string,
50+
): ReadonlyArray<DockerfileImageSpec> {
51+
return dockerfile
52+
.split("\n")
53+
.map((line) => line.trim())
54+
.flatMap((line) => {
55+
const match = FROM_LINE_PATTERN.exec(line);
56+
if (match === null) {
57+
return [];
58+
}
59+
60+
const [, repository, tag, alias] = match;
61+
if (repository === undefined || tag === undefined || alias === undefined) {
62+
return [];
63+
}
64+
65+
return [{ alias, image: `${repository}:${tag}` }];
66+
});
67+
}
68+
69+
export function localServiceImagesFromDockerfile(
70+
dockerfile: string,
71+
): ReadonlyArray<ServiceImageSpec> {
72+
const imagesByAlias = new Map(
73+
parseDockerfileServiceImages(dockerfile).map((service) => [service.alias, service.image]),
74+
);
75+
76+
return SERVICE_IMAGE_ALIASES.map((service) => {
77+
const image = imagesByAlias.get(service.alias);
78+
if (image === undefined) {
79+
throw new Error(`Missing service image alias '${service.alias}' in Dockerfile manifest.`);
80+
}
81+
82+
return {
83+
image,
84+
remoteService: service.remoteService,
85+
};
86+
});
87+
}
88+
89+
const LOCAL_SERVICE_IMAGES = localServiceImagesFromDockerfile(serviceImagesDockerfile);
4490

4591
const TABLE_HEADERS = ["SERVICE IMAGE", "LOCAL", "LINKED"] as const;
4692

@@ -61,14 +107,14 @@ function toServiceVersionRow(
61107
service: ServiceImageSpec,
62108
remote: Partial<Record<RemoteServiceName, string>> = {},
63109
): ServiceVersionRow {
64-
const parts = service.image.split(":");
65-
const name = parts[0];
66-
const local = parts[1];
67-
68-
if (name === undefined || local === undefined) {
110+
const tagSeparator = service.image.lastIndexOf(":");
111+
if (tagSeparator === -1) {
69112
throw new Error(`Invalid service image entry: ${service.image}`);
70113
}
71114

115+
const name = service.image.slice(0, tagSeparator);
116+
const local = service.image.slice(tagSeparator + 1);
117+
72118
return {
73119
name,
74120
local,

apps/cli/src/shared/services/services.shared.unit.test.ts

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import { describe, expect, test } from "vitest";
22
import { Effect, Redacted } from "effect";
33
import { FetchHttpClient } from "effect/unstable/http";
4+
import serviceImagesDockerfile from "../../../../cli-go/pkg/config/templates/Dockerfile" with { type: "text" };
45
import {
56
fetchLinkedServiceVersions,
67
listLocalServiceVersions,
8+
localServiceImagesFromDockerfile,
9+
parseDockerfileServiceImages,
710
renderServicesTable,
811
renderServicesWarning,
912
} from "./services.shared.ts";
@@ -17,6 +20,54 @@ const runLinkedFetch = (input: Parameters<typeof fetchLinkedServiceVersions>[0])
1720
Effect.runPromise(fetchLinkedServiceVersions(input).pipe(Effect.provide(FetchHttpClient.layer)));
1821

1922
describe("services shared", () => {
23+
test("parses service images from Dockerfile FROM aliases", () => {
24+
expect(
25+
parseDockerfileServiceImages(`
26+
# comment
27+
FROM supabase/postgres:17.6.1.132 AS pg
28+
29+
RUN echo ignored
30+
FROM localhost:5000/custom/image:1.2.3 AS custom
31+
`),
32+
).toEqual([
33+
{ alias: "pg", image: "supabase/postgres:17.6.1.132" },
34+
{ alias: "custom", image: "localhost:5000/custom/image:1.2.3" },
35+
]);
36+
});
37+
38+
test("fails clearly when the Dockerfile manifest misses a required service alias", () => {
39+
expect(() =>
40+
localServiceImagesFromDockerfile("FROM supabase/postgres:17.6.1.132 AS pg\n"),
41+
).toThrow("Missing service image alias 'gotrue' in Dockerfile manifest.");
42+
});
43+
44+
test("derives local service versions from the Go Dockerfile manifest", () => {
45+
const rows = listLocalServiceVersions();
46+
const dockerfileImages = localServiceImagesFromDockerfile(serviceImagesDockerfile);
47+
const expectedRows = dockerfileImages.map((service) => {
48+
const tagSeparator = service.image.lastIndexOf(":");
49+
return {
50+
name: service.image.slice(0, tagSeparator),
51+
local: service.image.slice(tagSeparator + 1),
52+
remote: "",
53+
};
54+
});
55+
56+
expect(rows).toEqual(expectedRows);
57+
expect(rows.map((row) => row.name)).toEqual([
58+
"supabase/postgres",
59+
"supabase/gotrue",
60+
"postgrest/postgrest",
61+
"supabase/realtime",
62+
"supabase/storage-api",
63+
"supabase/edge-runtime",
64+
"supabase/studio",
65+
"supabase/postgres-meta",
66+
"supabase/logflare",
67+
"supabase/supavisor",
68+
]);
69+
});
70+
2071
test("returns postgres only when no service-role key is available", async () => {
2172
const server = Bun.serve({
2273
port: 0,

apps/cli/vitest.config.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,22 @@
1+
import { readFileSync } from "node:fs";
12
import { defineConfig } from "vitest/config";
23

4+
function dockerfileTextPlugin() {
5+
return {
6+
name: "dockerfile-text-loader",
7+
load(id: string) {
8+
const [filePath] = id.split("?", 2);
9+
if (filePath?.endsWith("/Dockerfile") !== true) {
10+
return undefined;
11+
}
12+
13+
return `export default ${JSON.stringify(readFileSync(filePath, "utf8"))};`;
14+
},
15+
};
16+
}
17+
318
export default defineConfig({
19+
plugins: [dockerfileTextPlugin()],
420
test: {
521
passWithNoTests: true,
622
coverage: {
@@ -24,18 +40,21 @@ export default defineConfig({
2440
},
2541
projects: [
2642
{
43+
plugins: [dockerfileTextPlugin()],
2744
test: {
2845
name: "unit",
2946
include: ["**/*.unit.test.ts"],
3047
},
3148
},
3249
{
50+
plugins: [dockerfileTextPlugin()],
3351
test: {
3452
name: "integration",
3553
include: ["**/*.integration.test.ts"],
3654
},
3755
},
3856
{
57+
plugins: [dockerfileTextPlugin()],
3958
test: {
4059
name: "e2e",
4160
include: ["**/*.e2e.test.ts"],

packages/cli-test-helpers/src/normalize.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,21 +29,33 @@ export function sortTableRows(output: string): string {
2929
return result.join("\n");
3030
}
3131

32+
export interface NormalizeOptions {
33+
readonly versions?: boolean;
34+
}
35+
3236
/**
3337
* Normalize CLI output by stripping non-deterministic content before parity
3438
* comparisons. Applied to both Go and ts-legacy output so spurious differences
3539
* in timestamps, versions, paths, and stack traces don't produce false failures.
3640
*/
37-
export function normalize(output: string): string {
41+
export function normalize(output: string, options: NormalizeOptions = {}): string {
42+
const normalizeVersions = options.versions ?? true;
43+
const withoutAnsi = output
44+
// 1. Strip ANSI escape codes (color, bold, reset, etc.) — \u001b is ESC
45+
// eslint-disable-next-line no-control-regex
46+
.replace(/\u001b\[[0-9;]*[a-zA-Z]/g, "");
47+
const withoutVersions = normalizeVersions
48+
? withoutAnsi.replace(
49+
// 2. Semantic version strings (e.g. 1.187.0, v2.0.0-rc.1, v14.13).
50+
// Lookbehind prevents matching mid-IP-address (e.g. 0.0.1 inside 127.0.0.1).
51+
// Lookahead prevents matching where more dotted-number segments follow.
52+
/(?<![.\d])\bv?\d+\.\d+(?:\.\d+)?(?:-[\w.]+)?\b(?!\.)/g,
53+
"<VERSION>",
54+
)
55+
: withoutAnsi;
56+
3857
return (
39-
output
40-
// 1. Strip ANSI escape codes (color, bold, reset, etc.) — \u001b is ESC
41-
// eslint-disable-next-line no-control-regex
42-
.replace(/\u001b\[[0-9;]*[a-zA-Z]/g, "")
43-
// 2. Semantic version strings (e.g. 1.187.0, v2.0.0-rc.1, v14.13).
44-
// Lookbehind prevents matching mid-IP-address (e.g. 0.0.1 inside 127.0.0.1).
45-
// Lookahead prevents matching where more dotted-number segments follow.
46-
.replace(/(?<![.\d])\bv?\d+\.\d+(?:\.\d+)?(?:-[\w.]+)?\b(?!\.)/g, "<VERSION>")
58+
withoutVersions
4759
// 3. ISO-8601 timestamps (2026-04-15T10:46:15Z or with milliseconds)
4860
.replace(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z/g, "<TIMESTAMP>")
4961
// 4. Display timestamps (2026-04-15 10:46:15 — space-separated, no T)

packages/cli-test-helpers/src/normalize.unit.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,12 @@ describe("normalize", () => {
1515
expect(normalize("Version: 0.1.0-rc.1")).toBe("Version: <VERSION>");
1616
});
1717

18+
it("can preserve semantic version strings", () => {
19+
expect(normalize("postgrest/postgrest:v14.13", { versions: false })).toBe(
20+
"postgrest/postgrest:v14.13",
21+
);
22+
});
23+
1824
it("does not normalize IP addresses as version strings", () => {
1925
expect(normalize("host 127.0.0.1")).toBe("host 127.0.0.1");
2026
expect(normalize("192.168.1.1")).toBe("192.168.1.1");

packages/cli-test-helpers/src/parity.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
22
import { readFileSync } from "node:fs";
33
import { join } from "node:path";
44
import { createHarness, exec, makeTempDir } from "./harness.ts";
5-
import { normalize, sortTableRows } from "./normalize.ts";
5+
import { normalize, type NormalizeOptions, sortTableRows } from "./normalize.ts";
66

77
// ---------------------------------------------------------------------------
88
// Table parsing (Level 2)
@@ -215,13 +215,14 @@ async function collectRunResult(
215215
dir: string,
216216
apiUrl: string,
217217
extraEnv?: Record<string, string>,
218+
normalizeOptions?: NormalizeOptions,
218219
): Promise<RunResult> {
219220
const result = await exec(harness, cmd, extraEnv ? { env: extraEnv } : undefined);
220221
const requests = await fetchRequestLog(apiUrl);
221222
const files = snapshotChangedFiles(dir);
222223
return {
223-
stdout: normalize(result.stdout),
224-
stderr: normalize(result.stderr),
224+
stdout: normalize(result.stdout, normalizeOptions),
225+
stderr: normalize(result.stderr, normalizeOptions),
225226
exitCode: result.exitCode,
226227
requests,
227228
files,
@@ -311,6 +312,8 @@ export interface ParityOptions {
311312
sortStdoutRows?: boolean;
312313
/** Additional environment variables injected into both CLI subprocesses. */
313314
extraEnv?: Record<string, string>;
315+
/** Fine-grained normalization controls for stdout/stderr parity comparison. */
316+
normalize?: NormalizeOptions;
314317
}
315318

316319
/**
@@ -341,6 +344,7 @@ export async function runParity(opts: ParityOptions, cmd: string[]): Promise<voi
341344
goDir.path,
342345
opts.apiUrl,
343346
opts.extraEnv,
347+
opts.normalize,
344348
);
345349

346350
await fetch(`${opts.apiUrl}/_ctrl/requests`, { method: "DELETE" });
@@ -357,6 +361,7 @@ export async function runParity(opts: ParityOptions, cmd: string[]): Promise<voi
357361
tsDir.path,
358362
opts.apiUrl,
359363
opts.extraEnv,
364+
opts.normalize,
360365
);
361366

362367
// Self-cleaning: reset after ts-legacy so callers start with a clean log.

0 commit comments

Comments
 (0)