fix(cli): support compiled next cli e2e runtime (#5240) · pascalarth-max/cli@53908ef · GitHub
Skip to content

Commit 53908ef

Browse files
authored
fix(cli): support compiled next cli e2e runtime (supabase#5240)
## Summary - Run the next CLI e2e suite against the compiled `dist/supabase-next` binary so tests exercise the same artifact users run. - Fix the two compiled-Bun blockers tracked by [CLI-1452](https://linear.app/supabase/issue/CLI-1452/compiled-bun-compile-next-binary-cant-run-supabase-functions-dev) and [CLI-1453](https://linear.app/supabase/issue/CLI-1453/compiled-bun-compile-next-binary-start-detach-daemon-fork-ignores). ## CLI-1452: `functions dev` and native/runtime assets The compiled next binary could not boot commands that pulled in `@parcel/watcher`, because the native optional package is not embedded by `bun --compile` and the fallback `.node` path does not exist inside Bun's embedded filesystem. This PR keeps the compiled binary path viable by making the next CLI e2e harness run through `dist/supabase-next`, by statically referencing the platform-specific watcher packages, and by embedding the Edge Runtime bootstrap with Bun's native text import attribute instead of resolving the bootstrap through a runtime source-file path. The Edge Runtime service writes that embedded bootstrap source into the runtime temp directory before launching the function runtime. ## CLI-1453: compiled binary daemon/supervisor dispatch Compiled Bun ignores the script-path argv shape that works in JIT mode, so `child_process.fork(daemonEntryPoint)` re-entered the compiled CLI entrypoint instead of running the daemon script. This PR makes compiled-binary dispatch explicit: `forkDaemon` marks daemon children through the environment, and the compiled next CLI entrypoint routes that child process directly to the daemon runner. The same pattern is used for process supervision: `@supabase/process-compose` forks through `process.execPath`, uses a tiny env protocol only for compiled-Bun self-dispatch, and otherwise remains a generic Node/Bun process supervision library. The supervisor runtime/protocol are native `.ts` files, so Bun forks Bun and Node forks Node. ## Reviewer Notes The process-compose changes are intentionally runtime-neutral: there are no Supabase-specific paths or command names in the library. Supabase-specific compiled-binary dispatch lives in the CLI/stack entrypoints that own that behavior. The compiled Bun runtime-dispatch decision is now documented in [ADR 0012](https://github.com/supabase/cli/blob/julien/cli-1452-compiled-bun-compile-next-binary-cant-run-supabase-functions/docs/adr/0012-compiled-bun-runtime-dispatch.md), and the detached-mode docs now describe the compiled-binary daemon dispatch path. The watcher loader was also reshaped into nested platform/arch branches per review feedback.
1 parent 0832142 commit 53908ef

36 files changed

Lines changed: 802 additions & 391 deletions

‎.gitmodules‎

Lines changed: 3 additions & 0 deletions

‎.repos/opencode‎

Submodule opencode added at 907281a

‎AGENTS.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ Use `nx show project <name> --json` to discover available targets before running
114114
## Pull Requests
115115

116116
PR titles must follow conventional-commits format because the `Lint Pull Request` workflow runs `amannn/action-semantic-pull-request` against the title. Use `<type>(<scope>): <subject>` (e.g. `fix(cli): …`, `test(cli): …`, `feat(api): …`). A bare descriptive title like "Build TypeScript CLI as compiled Bun binaries" will fail the lint. When a PR is created (including by the Claude Code UI or someone else), check the title against this rule and update it if needed.
117+
Do not include a validation, test plan, or list of checks in PR descriptions. CI enforces validation for PRs, so PR descriptions should focus on what changed, why it changed, and any reviewer-relevant context that CI cannot infer.
117118

118119
## Refactoring Policy
119120

‎apps/cli-go/internal/testing/fstest/stdin.go‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package fstest
22

33
import (
44
"os"
5+
"strings"
56
"testing"
67

78
"github.com/stretchr/testify/require"
@@ -11,6 +12,9 @@ func MockStdin(t *testing.T, input string) func() {
1112
// Setup stdin
1213
r, w, err := os.Pipe()
1314
require.NoError(t, err)
15+
if len(input) > 0 && !strings.HasSuffix(input, "\n") {
16+
input += "\n"
17+
}
1418
_, err = w.WriteString(input)
1519
require.NoError(t, err)
1620
require.NoError(t, w.Close())

‎apps/cli-go/internal/utils/console.go‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ func NewConsole() *Console {
3333

3434
// Prevent interactive terminals from hanging more than 10 minutes
3535
const ttyTimeout = time.Minute * 10
36+
const nonTTYTimeout = time.Millisecond * 100
3637

3738
func (c *Console) ReadLine(ctx context.Context) string {
38-
// Wait a few ms for input
39-
timeout := time.Millisecond
39+
timeout := nonTTYTimeout
4040
if c.IsTTY {
4141
timeout = ttyTimeout
4242
}

‎apps/cli/package.json‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"@effect/vitest": "catalog:",
5454
"@supabase/api": "workspace:*",
5555
"@supabase/config": "workspace:*",
56+
"@supabase/process-compose": "workspace:*",
5657
"@supabase/stack": "workspace:*",
5758
"@tsconfig/bun": "catalog:",
5859
"@types/bun": "catalog:",
@@ -67,6 +68,14 @@
6768
"vitest": "catalog:"
6869
},
6970
"optionalDependencies": {
71+
"@parcel/watcher-darwin-arm64": "2.5.6",
72+
"@parcel/watcher-darwin-x64": "2.5.6",
73+
"@parcel/watcher-linux-arm64-glibc": "2.5.6",
74+
"@parcel/watcher-linux-arm64-musl": "2.5.6",
75+
"@parcel/watcher-linux-x64-glibc": "2.5.6",
76+
"@parcel/watcher-linux-x64-musl": "2.5.6",
77+
"@parcel/watcher-win32-arm64": "2.5.6",
78+
"@parcel/watcher-win32-x64": "2.5.6",
7079
"@supabase/cli-darwin-arm64": "workspace:*",
7180
"@supabase/cli-darwin-x64": "workspace:*",
7281
"@supabase/cli-linux-arm64": "workspace:*",

‎apps/cli/scripts/build.ts‎

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,22 @@ const GO_TARGETS: Record<BunTarget, { goos: string; goarch: string }> = {
9898
"bun-windows-arm64": { goos: "windows", goarch: "arm64" },
9999
};
100100

101+
function libcForBunTarget(target: string): "glibc" | "musl" | "" {
102+
if (!target.startsWith("bun-linux-")) {
103+
return "";
104+
}
105+
return target.endsWith("-musl") ? "musl" : "glibc";
106+
}
107+
101108
async function buildTarget(target: (typeof TARGETS)[number]) {
102109
const binDir = path.join(root, "packages", target.pkg, "bin");
103110
await mkdir(binDir, { recursive: true });
104111

105112
const outfile = path.join(binDir, `supabase${target.ext}`);
113+
const libc = libcForBunTarget(target.bunTarget);
106114

107115
console.log(`[${target.pkg}] Compiling Bun CLI...`);
108-
await $`bun build ${entrypoint} --compile --minify --target=${target.bunTarget} --define=process.env.SUPABASE_CLI_VERSION=${JSON.stringify(version)} --outfile=${outfile}`;
116+
await $`bun build ${entrypoint} --compile --minify --target=${target.bunTarget} --define=process.env.SUPABASE_CLI_VERSION=${JSON.stringify(version)} --define=SUPABASE_LIBC=${JSON.stringify(libc)} --outfile=${outfile}`;
109117
console.log(`[${target.pkg}] Done.`);
110118
}
111119

@@ -150,8 +158,9 @@ async function buildMuslBinaries() {
150158
await mkdir(binDir, { recursive: true });
151159

152160
const outfile = path.join(binDir, "supabase");
161+
const libc = libcForBunTarget(target.bunTarget);
153162
console.log(`[${target.pkg}] Compiling Bun CLI (musl)...`);
154-
await $`bun build ${entrypoint} --compile --minify --target=${target.bunTarget} --define=process.env.SUPABASE_CLI_VERSION=${JSON.stringify(version)} --outfile=${outfile}`;
163+
await $`bun build ${entrypoint} --compile --minify --target=${target.bunTarget} --define=process.env.SUPABASE_CLI_VERSION=${JSON.stringify(version)} --define=SUPABASE_LIBC=${JSON.stringify(libc)} --outfile=${outfile}`;
155164

156165
if (shell === "legacy") {
157166
// Go binary is CGO_ENABLED=0 (fully static), so the glibc Linux build works on

‎apps/cli/src/next/commands/functions/dev/dev.e2e.test.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010

1111
const FUNCTIONS_DEV_STARTUP_TIMEOUT_MS = 60_000;
1212
const FUNCTIONS_DEV_STEP_TIMEOUT_MS = 30_000;
13-
const FUNCTIONS_DEV_TEST_TIMEOUT_MS = 75_000;
13+
const FUNCTIONS_DEV_TEST_TIMEOUT_MS = 90_000;
1414

1515
async function waitForFunctionResponse(
1616
url: string,
@@ -65,6 +65,7 @@ describe("supabase functions dev", () => {
6565
/Edge Functions dev server is running\./,
6666
FUNCTIONS_DEV_STARTUP_TIMEOUT_MS,
6767
);
68+
await new Promise((resolve) => setTimeout(resolve, 500));
6869

6970
const newResult = await runSupabase(["functions", "new", "hello-world"], {
7071
cwd: project.dir,

‎apps/cli/src/next/commands/logs/logs.e2e.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
spawnSupabase,
77
} from "../../../../tests/helpers/cli.ts";
88

9-
const LOGS_TIMEOUT_MS = 15_000;
9+
const LOGS_TIMEOUT_MS = 30_000;
1010
const LOGS_IDLE_WINDOW_MS = 500;
1111
const LIGHTWEIGHT_START_ARGS = [
1212
"start",

‎apps/cli/src/next/commands/start/start.e2e.test.ts‎

Lines changed: 1 addition & 1 deletion

0 commit comments

Comments
 (0)