From e47e3b9f4dd3aaebdf4f1abd049b8b79b408c4d3 Mon Sep 17 00:00:00 2001 From: 7ttp <117663341+7ttp@users.noreply.github.com> Date: Fri, 26 Jun 2026 19:13:55 +0530 Subject: [PATCH] fix --- .../functions/serve/serve.integration.test.ts | 17 ++++- apps/cli/src/shared/functions/serve.ts | 64 +++++++++---------- .../src/shared/functions/serve.unit.test.ts | 32 ++++------ 3 files changed, 56 insertions(+), 57 deletions(-) diff --git a/apps/cli/src/legacy/commands/functions/serve/serve.integration.test.ts b/apps/cli/src/legacy/commands/functions/serve/serve.integration.test.ts index d234aa388f..be575f1409 100644 --- a/apps/cli/src/legacy/commands/functions/serve/serve.integration.test.ts +++ b/apps/cli/src/legacy/commands/functions/serve/serve.integration.test.ts @@ -477,6 +477,14 @@ describe("legacy functions serve integration", () => { expect(dockerRun.args).toContain("--add-host"); expect(dockerRun.args).toContain("host.docker.internal:host-gateway"); expect(dockerRun.args).toContain("public.ecr.aws/supabase/edge-runtime:v1.73.13"); + expect( + extractFlagValues(dockerRun.args, "-v").some((value) => + value.endsWith(":/root/index.ts:ro"), + ), + ).toBe(true); + expect(dockerRun.args[dockerRun.args.length - 1]).toBe( + "edge-runtime start --main-service=/root --port=8081 --policy=per_worker\n", + ); const envs = yield* Effect.promise(() => extractDockerEnvEntries(dockerRun)); expect(envs).toContain("HELLO=WORLD"); @@ -1147,7 +1155,14 @@ describe("legacy functions serve integration", () => { } const commandScript = dockerRun.args[dockerRun.args.length - 1] ?? ""; - expect(commandScript).toContain("cat <<'EOF' > /root/index.ts"); + expect(commandScript).toBe( + "edge-runtime start --main-service=/root --port=8081 --policy=per_worker\n", + ); + expect( + extractFlagValues(dockerRun.args, "-v").some((value) => + value.endsWith(":/root/index.ts:ro"), + ), + ).toBe(true); expect(commandScript).not.toContain("@ts-nocheck"); expect(commandScript).not.toContain("declare const Deno"); expect(commandScript).not.toContain("declare const EdgeRuntime"); diff --git a/apps/cli/src/shared/functions/serve.ts b/apps/cli/src/shared/functions/serve.ts index 057f878e9a..91874fb603 100644 --- a/apps/cli/src/shared/functions/serve.ts +++ b/apps/cli/src/shared/functions/serve.ts @@ -85,6 +85,7 @@ const dockerLogDiagnosticTailLength = 4_096; const remoteJwksTimeoutMs = 10_000; const legacyDefaultEdgeRuntimeVersion = "v1.74.1"; const defaultSupabaseEnv = "development"; +const serveMainContainerPath = "/root/index.ts"; const clerkDomainPattern = /^(clerk([.][a-z0-9-]+){2,}|([a-z0-9-]+[.])+clerk[.]accounts[.]dev)$/; const shellVariableNamePattern = /^[A-Za-z_][A-Za-z0-9_]*$/; let cachedLegacyFunctionsServeMainTemplate: string | undefined; @@ -1229,30 +1230,26 @@ const writeStoppedServingMessage = Effect.fnUntraced(function* () { yield* output.raw(`Stopped serving ${styleText("bold", functionsDirName)}\n`, "stdout"); }); -// The Go CLI writes the runtime template to /root/index.ts via a quoted `<<'EOF'` -// heredoc; we keep the same terminator for byte-parity with its entrypoint. A line -// equal to the terminator inside the template would close the heredoc early and -// silently corrupt the script, so fail loudly instead. `serve.main.ts` (the only -// template) is asserted to contain no such line by a unit test. -const serveEntrypointHeredocTerminator = "EOF"; - -export function buildServeEntrypointScript( - template: string, +export function buildServeEntrypointCommand( command: ReadonlyArray, multilineEnvScriptPath?: string, ) { - if (template.split("\n").includes(serveEntrypointHeredocTerminator)) { - throw new Error( - `functions serve runtime template contains a line equal to the heredoc terminator "${serveEntrypointHeredocTerminator}"`, - ); - } - return `cat <<'${serveEntrypointHeredocTerminator}' > /root/index.ts -${template} -${serveEntrypointHeredocTerminator} -${multilineEnvScriptPath === undefined ? "" : `. ${multilineEnvScriptPath}\n`}${command.join(" ")} + return `${multilineEnvScriptPath === undefined ? "" : `. ${multilineEnvScriptPath}\n`}${command.join(" ")} `; } +async function writeServeMainTemplateFile(template: string) { + // Mount the bundled runtime template instead of embedding it in `sh -c` so + // Windows does not hit `uv_spawn` ENAMETOOLONG on path-heavy projects. + const dir = await mkdtemp(join(tmpdir(), "supabase-functions-serve-main-")); + const pathname = join(dir, "index.ts"); + await writeFile(pathname, template); + return { + bind: `${pathname}:${serveMainContainerPath}:ro`, + cleanup: () => rm(dir, { recursive: true, force: true }), + } as const; +} + function edgeRuntimeImageTag(version: string) { return version.startsWith("v") ? version : `v${version}`; } @@ -1420,6 +1417,9 @@ const startEdgeRuntime = Effect.fnUntraced(function* (input: { ...(input.debug ? ["--verbose"] : []), ]; const serveMainTemplate = yield* Effect.promise(() => getLegacyFunctionsServeMainTemplate()); + const serveMainTemplateFile = yield* Effect.tryPromise(() => + writeServeMainTemplateFile(serveMainTemplate), + ).pipe(Effect.mapError((cause) => (cause instanceof Error ? cause : new Error(String(cause))))); const command = [ "run", "-d", @@ -1437,6 +1437,8 @@ const startEdgeRuntime = Effect.fnUntraced(function* (input: { `com.supabase.cli.project=${labels["com.supabase.cli.project"]}`, "--label", `com.docker.compose.project=${labels["com.docker.compose.project"]}`, + "-v", + serveMainTemplateFile.bind, ...([...binds] as ReadonlyArray).flatMap((bind) => ["-v", bind]), ...(dockerMultilineEnvScript === undefined ? [] : ["-v", dockerMultilineEnvScript.bind]), ...(dockerEnvFile === undefined ? [] : ["--env-file", dockerEnvFile.path]), @@ -1450,26 +1452,18 @@ const startEdgeRuntime = Effect.fnUntraced(function* (input: { "sh", legacyGetRegistryImageUrl(`supabase/edge-runtime:${edgeRuntimeImageTag(edgeRuntimeVersion)}`), "-c", - buildServeEntrypointScript( - serveMainTemplate, - runtimeCommand, - dockerMultilineEnvScript?.scriptPath, - ), + buildServeEntrypointCommand(runtimeCommand, dockerMultilineEnvScript?.scriptPath), ]; - const cleanupRuntimeArtifacts = + const cleanupRuntimeArtifacts = Effect.all([ + Effect.tryPromise(() => serveMainTemplateFile.cleanup()).pipe(Effect.orDie), dockerEnvFile === undefined - ? dockerMultilineEnvScript === undefined - ? Effect.void - : Effect.tryPromise(() => dockerMultilineEnvScript.cleanup()).pipe(Effect.orDie) - : Effect.tryPromise(() => dockerEnvFile.cleanup()).pipe( - Effect.andThen( - dockerMultilineEnvScript === undefined - ? Effect.void - : Effect.tryPromise(() => dockerMultilineEnvScript.cleanup()).pipe(Effect.orDie), - ), - Effect.orDie, - ); + ? Effect.void + : Effect.tryPromise(() => dockerEnvFile.cleanup()).pipe(Effect.orDie), + dockerMultilineEnvScript === undefined + ? Effect.void + : Effect.tryPromise(() => dockerMultilineEnvScript.cleanup()).pipe(Effect.orDie), + ]).pipe(Effect.asVoid); return yield* Effect.gen(function* () { yield* output.raw("Setting up Edge Functions runtime...\n", "stderr"); diff --git a/apps/cli/src/shared/functions/serve.unit.test.ts b/apps/cli/src/shared/functions/serve.unit.test.ts index f265c28412..a12ac1b9db 100644 --- a/apps/cli/src/shared/functions/serve.unit.test.ts +++ b/apps/cli/src/shared/functions/serve.unit.test.ts @@ -1,34 +1,24 @@ import { describe, expect, it } from "vitest"; import { bundleServeMainTemplate } from "./serve-main-bundler.ts"; -import { buildServeEntrypointScript } from "./serve.ts"; +import { buildServeEntrypointCommand } from "./serve.ts"; -describe("buildServeEntrypointScript", () => { - const template = ['import { x } from "y";', "Deno.serve(() => new Response());"].join("\n"); - - it("writes the template through the heredoc and appends the runtime command", () => { - const script = buildServeEntrypointScript(template, ["edge-runtime", "start"]); - expect(script).toContain("cat <<'EOF' > /root/index.ts"); - expect(script).toContain(template); - expect(script).toContain("edge-runtime start"); - expect(script).not.toContain(". /"); +describe("buildServeEntrypointCommand", () => { + it("returns the runtime command without embedding the template body", () => { + const script = buildServeEntrypointCommand(["edge-runtime", "start"]); + expect(script).toBe("edge-runtime start\n"); + expect(script).not.toContain("Deno.serve"); }); it("sources the multiline env script before the runtime command when provided", () => { - const script = buildServeEntrypointScript(template, ["edge-runtime", "start"], "/root/env.sh"); + const script = buildServeEntrypointCommand(["edge-runtime", "start"], "/root/env.sh"); expect(script).toContain(". /root/env.sh\nedge-runtime start"); }); - it("fails loudly when the template contains a bare heredoc terminator line", () => { - const poisoned = ["line-1", "EOF", "line-3"].join("\n"); - expect(() => buildServeEntrypointScript(poisoned, ["edge-runtime", "start"])).toThrow( - 'heredoc terminator "EOF"', - ); - }); - - it("does not let the real bundled serve.main.ts template close the heredoc early", async () => { + it("keeps the spawned command short even with the real bundled template", async () => { const bundled = await bundleServeMainTemplate(); - expect(bundled.split("\n")).not.toContain("EOF"); - expect(() => buildServeEntrypointScript(bundled, ["edge-runtime", "start"])).not.toThrow(); + const script = buildServeEntrypointCommand(["edge-runtime", "start"]); + expect(bundled.length).toBeGreaterThan(20_000); + expect(script.length).toBeLessThan(128); }); });