Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,6 @@
[submodule ".repos/t3code"]
path = .repos/t3code
url = https://github.com/pingdotgg/t3code.git
[submodule ".repos/opencode"]
path = .repos/opencode
url = https://github.com/anomalyco/opencode.git
1 change: 1 addition & 0 deletions .repos/opencode
Submodule opencode added at 907281
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@ Use `nx show project <name> --json` to discover available targets before running
## Pull Requests

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.
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.

## Refactoring Policy

Expand Down
4 changes: 4 additions & 0 deletions apps/cli-go/internal/testing/fstest/stdin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package fstest

import (
"os"
"strings"
"testing"

"github.com/stretchr/testify/require"
Expand All @@ -11,6 +12,9 @@ func MockStdin(t *testing.T, input string) func() {
// Setup stdin
r, w, err := os.Pipe()
require.NoError(t, err)
if len(input) > 0 && !strings.HasSuffix(input, "\n") {
input += "\n"
}
_, err = w.WriteString(input)
require.NoError(t, err)
require.NoError(t, w.Close())
Expand Down
4 changes: 2 additions & 2 deletions apps/cli-go/internal/utils/console.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ func NewConsole() *Console {

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

func (c *Console) ReadLine(ctx context.Context) string {
// Wait a few ms for input
timeout := time.Millisecond
timeout := nonTTYTimeout
if c.IsTTY {
timeout = ttyTimeout
}
Expand Down
9 changes: 9 additions & 0 deletions apps/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"@effect/vitest": "catalog:",
"@supabase/api": "workspace:*",
"@supabase/config": "workspace:*",
"@supabase/process-compose": "workspace:*",
"@supabase/stack": "workspace:*",
"@tsconfig/bun": "catalog:",
"@types/bun": "catalog:",
Expand All @@ -67,6 +68,14 @@
"vitest": "catalog:"
},
"optionalDependencies": {
"@parcel/watcher-darwin-arm64": "2.5.6",
"@parcel/watcher-darwin-x64": "2.5.6",
"@parcel/watcher-linux-arm64-glibc": "2.5.6",
"@parcel/watcher-linux-arm64-musl": "2.5.6",
"@parcel/watcher-linux-x64-glibc": "2.5.6",
"@parcel/watcher-linux-x64-musl": "2.5.6",
"@parcel/watcher-win32-arm64": "2.5.6",
"@parcel/watcher-win32-x64": "2.5.6",
"@supabase/cli-darwin-arm64": "workspace:*",
"@supabase/cli-darwin-x64": "workspace:*",
"@supabase/cli-linux-arm64": "workspace:*",
Expand Down
13 changes: 11 additions & 2 deletions apps/cli/scripts/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,14 +98,22 @@ const GO_TARGETS: Record<BunTarget, { goos: string; goarch: string }> = {
"bun-windows-arm64": { goos: "windows", goarch: "arm64" },
};

function libcForBunTarget(target: string): "glibc" | "musl" | "" {
if (!target.startsWith("bun-linux-")) {
return "";
}
return target.endsWith("-musl") ? "musl" : "glibc";
}

async function buildTarget(target: (typeof TARGETS)[number]) {
const binDir = path.join(root, "packages", target.pkg, "bin");
await mkdir(binDir, { recursive: true });

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

console.log(`[${target.pkg}] Compiling Bun CLI...`);
await $`bun build ${entrypoint} --compile --minify --target=${target.bunTarget} --define=process.env.SUPABASE_CLI_VERSION=${JSON.stringify(version)} --outfile=${outfile}`;
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}`;
console.log(`[${target.pkg}] Done.`);
}

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

const outfile = path.join(binDir, "supabase");
const libc = libcForBunTarget(target.bunTarget);
console.log(`[${target.pkg}] Compiling Bun CLI (musl)...`);
await $`bun build ${entrypoint} --compile --minify --target=${target.bunTarget} --define=process.env.SUPABASE_CLI_VERSION=${JSON.stringify(version)} --outfile=${outfile}`;
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}`;

if (shell === "legacy") {
// Go binary is CGO_ENABLED=0 (fully static), so the glibc Linux build works on
Expand Down
3 changes: 2 additions & 1 deletion apps/cli/src/next/commands/functions/dev/dev.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {

const FUNCTIONS_DEV_STARTUP_TIMEOUT_MS = 60_000;
const FUNCTIONS_DEV_STEP_TIMEOUT_MS = 30_000;
const FUNCTIONS_DEV_TEST_TIMEOUT_MS = 75_000;
const FUNCTIONS_DEV_TEST_TIMEOUT_MS = 90_000;

async function waitForFunctionResponse(
url: string,
Expand Down Expand Up @@ -65,6 +65,7 @@ describe("supabase functions dev", () => {
/Edge Functions dev server is running\./,
FUNCTIONS_DEV_STARTUP_TIMEOUT_MS,
);
await new Promise((resolve) => setTimeout(resolve, 500));

const newResult = await runSupabase(["functions", "new", "hello-world"], {
cwd: project.dir,
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/src/next/commands/logs/logs.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
spawnSupabase,
} from "../../../../tests/helpers/cli.ts";

const LOGS_TIMEOUT_MS = 15_000;
const LOGS_TIMEOUT_MS = 30_000;
const LOGS_IDLE_WINDOW_MS = 500;
const LIGHTWEIGHT_START_ARGS = [
"start",
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/src/next/commands/start/start.e2e.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, expect, test } from "vitest";
import { makeTempHome, makeTempStackProject, runSupabase } from "../../../../tests/helpers/cli.ts";

const DETACHED_START_TIMEOUT_MS = 15_000;
const DETACHED_START_TIMEOUT_MS = 30_000;
const LIGHTWEIGHT_START_ARGS = [
"start",
"--detach",
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/src/next/commands/status/status.e2e.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, expect, test } from "vitest";
import { makeTempHome, makeTempStackProject, runSupabase } from "../../../../tests/helpers/cli.ts";

const STATUS_TIMEOUT_MS = 15_000;
const STATUS_TIMEOUT_MS = 30_000;
const LIGHTWEIGHT_START_ARGS = [
"start",
"--detach",
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/src/next/commands/stop/stop.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const LIGHTWEIGHT_START_ARGS = [
"--exclude",
"pooler",
] as const;
const STOP_STACK_TIMEOUT_MS = 15_000;
const STOP_STACK_TIMEOUT_MS = 30_000;

describe("supabase stop", () => {
test(
Expand Down
17 changes: 16 additions & 1 deletion apps/cli/src/next/main.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,17 @@
#!/usr/bin/env bun
import "./cli/main.ts";
import {
enableSupervisorSelfDispatchForCompiledBun,
isSupervisorRuntimeRequested,
runSupervisorRuntimeFromEnv,
} from "@supabase/process-compose";

enableSupervisorSelfDispatchForCompiledBun(import.meta.url);

if (isSupervisorRuntimeRequested()) {
runSupervisorRuntimeFromEnv();
} else if (process.env.SUPABASE_STACK_RUN_DAEMON === "1") {
const { runBunDaemon } = await import("@supabase/stack/daemon-bun");
runBunDaemon();
} else {
await import("./cli/main.ts");
}
57 changes: 51 additions & 6 deletions apps/cli/src/shared/runtime/parcel-file-watcher.layer.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as ParcelWatcher from "@parcel/watcher";
import { Cause, Effect, Layer, Queue, Stream } from "effect";
import { createWrapper } from "@parcel/watcher/wrapper";
import type ParcelWatcher from "@parcel/watcher";

import {
FileWatcher,
Expand All @@ -8,6 +9,49 @@ import {
type FileWatchOptions,
} from "./file-watcher.service.ts";

declare const SUPABASE_LIBC: string | undefined;

function wrapBinding(binding: unknown): typeof import("@parcel/watcher") {
return createWrapper(binding);
}

function loadParcelWatcher(): typeof import("@parcel/watcher") {
if (process.platform === "darwin") {
if (process.arch === "arm64") {
return wrapBinding(require("@parcel/watcher-darwin-arm64"));
}
if (process.arch === "x64") {
return wrapBinding(require("@parcel/watcher-darwin-x64"));
}
}

if (process.platform === "linux") {
if (process.arch === "arm64") {
if (typeof SUPABASE_LIBC !== "undefined" && SUPABASE_LIBC === "musl") {
return wrapBinding(require("@parcel/watcher-linux-arm64-musl"));
}
return wrapBinding(require("@parcel/watcher-linux-arm64-glibc"));
}
if (process.arch === "x64") {
if (typeof SUPABASE_LIBC !== "undefined" && SUPABASE_LIBC === "musl") {
return wrapBinding(require("@parcel/watcher-linux-x64-musl"));
}
return wrapBinding(require("@parcel/watcher-linux-x64-glibc"));
}
}

if (process.platform === "win32") {
if (process.arch === "arm64") {
return wrapBinding(require("@parcel/watcher-win32-arm64"));
}
if (process.arch === "x64") {
return wrapBinding(require("@parcel/watcher-win32-x64"));
}
}

throw new Error(`Unsupported @parcel/watcher platform: ${process.platform}-${process.arch}`);
}

function toParcelOptions(options?: FileWatchOptions): ParcelWatcher.Options | undefined {
if (options?.ignore === undefined) {
return undefined;
Expand All @@ -20,11 +64,12 @@ function toParcelOptions(options?: FileWatchOptions): ParcelWatcher.Options | un
export const parcelFileWatcherLayer = Layer.sync(FileWatcher, () =>
FileWatcher.of({
watch: (path, options) =>
Stream.callback<ReadonlyArray<FileWatchEvent>, FileWatcherError>((queue) =>
Effect.acquireRelease(
Stream.callback<ReadonlyArray<FileWatchEvent>, FileWatcherError>((queue) => {
const watcher = loadParcelWatcher();
return Effect.acquireRelease(
Effect.tryPromise({
try: () =>
ParcelWatcher.subscribe(
watcher.subscribe(
path,
(error, events) => {
if (error !== null) {
Expand All @@ -44,7 +89,7 @@ export const parcelFileWatcherLayer = Layer.sync(FileWatcher, () =>
Effect.promise(() => subscription.unsubscribe()).pipe(
Effect.ignore({ log: true, message: "Failed to unsubscribe file watcher" }),
),
),
),
);
}),
}),
);
3 changes: 3 additions & 0 deletions apps/cli/src/shared/runtime/parcel-watcher-wrapper.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare module "@parcel/watcher/wrapper" {
export function createWrapper(binding: unknown): typeof import("@parcel/watcher");
}
Loading
Loading