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
10 changes: 6 additions & 4 deletions apps/cli/docs/release-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ It is **not** a valid test for Homebrew or Scoop — those paths are covered in

This is how you validate the Homebrew formula, Scoop manifest, and GitHub-Release-host resolution on real infrastructure without touching `supabase/`\* repos or risking a clash with an already-installed `supabase` CLI on the reviewer's machine.

Both updater scripts support a `--name <custom>` flag that pushes the formula / manifest under a different name (e.g., `supabase-shim-poc`) and, when the name differs from `supabase`, renames the installed binary (`bin.install "supabase" => "supabase-shim-poc"` on Homebrew, `["supabase.exe", "supabase-shim-poc"]` alias-tuple on Scoop). This keeps the reviewer's real `supabase` CLI intact and makes it obvious which binary is being exercised.
Both updater scripts support a `--name <custom>` flag that pushes the formula / manifest under a different name (e.g., `supabase-shim-poc`) — that is, a different filename and Ruby class / scoop manifest. The installed binary is always `supabase` (matching the Go CLI), so PoC reviewers should `brew uninstall supabase` / `scoop uninstall supabase` first if they already have the official CLI installed.

### One-time setup (per reviewer)

Expand Down Expand Up @@ -158,9 +158,10 @@ These are what a fresh reviewer would run — no repo clone required.
**macOS / Linux (Homebrew):**

```sh
brew uninstall supabase || true # PoC formula installs a `supabase` binary too
brew tap avallete/supabase-shim-poc # note: "avallete/<tap-suffix>", not the full repo name
brew install supabase-shim-poc
supabase-shim-poc --version # expect: supabase v0.0.1
supabase --version # expect: supabase v0.0.1
brew test supabase-shim-poc # expect: pass
```

Expand All @@ -169,9 +170,10 @@ The `brew tap <owner>/<suffix>` command looks up `https://github.com/<owner>/hom
**Windows (Scoop):**

```powershell
scoop uninstall supabase # PoC manifest also shims `supabase.exe`
scoop bucket add avallete-poc https://github.com/avallete/scoop-bucket
scoop install supabase-shim-poc
supabase-shim-poc --version # expect: supabase v0.0.1
supabase --version # expect: supabase v0.0.1
```

Validated on Windows x64 (`v0.0.1`, 2026-04-21): installed with no SmartScreen block on the unsigned Bun SFE, `--version` output matched. Windows arm64 (Surface / Copilot+ / ARM VM) still pending — needs hardware or a Windows-on-ARM VM to exercise the `windows_arm64.zip` archive added by this branch.
Expand All @@ -181,7 +183,7 @@ Validated on Windows x64 (`v0.0.1`, 2026-04-21): installed with no SmartScreen b
Beyond `--version` and `brew test`, exercise a Phase-0 proxied subcommand that requires the `supabase-go` sidecar (`--shell legacy` only):

```sh
supabase-shim-poc projects list --help
supabase projects list --help
```

This must spawn the colocated `supabase-go` and print help text — not return `NotFound: ChildProcess.spawn (supabase ...)`. If it fails, the Homebrew install step is wrong: check that `[apps/cli/scripts/update-homebrew.ts](../scripts/update-homebrew.ts)`'s install-lines block ran `bin.install "supabase-go" if File.exist?("supabase-go")`, and that the built archive actually contains `supabase-go` (it should, for any `--shell legacy` build).
Expand Down
25 changes: 12 additions & 13 deletions apps/cli/scripts/update-homebrew.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,30 +32,29 @@ const dryRun = values["dry-run"]!;
const root = path.resolve(import.meta.dir, "../../..");
const distDir = path.join(root, "dist");

// Convert name (e.g. "supabase-shim-poc") to the Ruby class name
// Homebrew expects (e.g. "SupabaseShimPoc").
// Convert name (e.g. "supabase-beta") to the Ruby class name Homebrew
// expects (e.g. "SupabaseBeta"). The class + filename differ by channel so
// `supabase` and `supabase-beta` can coexist as separate formulas in the
// same tap, but the installed binary is always `supabase` (matching the
// Go CLI's historical behaviour).
const className = name
.split(/[-_]/)
.filter(Boolean)
.map((part) => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase())
.join("");

// When name != "supabase", rename the main binary on install so it doesn't
// clash with the official `supabase` CLI a user may already have installed.
//
// `supabase-go` is the Go sidecar the legacy shell spawns via
// apps/cli/src/shared/legacy/go-proxy.layer.ts. It is looked up by exact
// filename colocated with process.execPath, so we MUST install it with its
// original name (not renamed) right next to the SFE. The `if File.exist?`
// guard makes the formula work for both the `legacy` shell (ships both
// binaries) and the future `next` shell (SFE only).
const installLines = [
name === "supabase" ? ` bin.install "supabase"` : ` bin.install "supabase" => "${name}"`,
// original name right next to the SFE. The `if File.exist?` guard makes the
// formula work for both the `legacy` shell (ships both binaries) and the
// future `next` shell (SFE only).
const installBlock = [
` bin.install "supabase"`,
` bin.install "supabase-go" if File.exist?("supabase-go")`,
];
const installBlock = installLines.join("\n");
].join("\n");

const testInvocation = `#{bin}/${name}`;
const testInvocation = `#{bin}/supabase`;

// Parse checksums
const checksums = new Map<string, string>();
Expand Down
12 changes: 6 additions & 6 deletions apps/cli/scripts/update-scoop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ const name = values.name!;
const local = values.local!;
const dryRun = values["dry-run"]!;

// When name != "supabase", rename the binary on install so it doesn't clash
// with the official `supabase` CLI a user may already have installed.
// Scoop's `bin` field accepts either a string (no rename) or a
// [source, alias] tuple. See https://github.com/ScoopInstaller/Scoop/wiki/App-Manifests#bin
const binEntry: string | [string, string] =
name === "supabase" ? "supabase.exe" : ["supabase.exe", name];
// The shipped binary is always `supabase.exe`, regardless of channel — only
// the manifest filename differs (e.g. `supabase-beta.json`) so stable and
// beta can coexist in the same bucket. Matches the Go CLI's historical
// scoop-bucket layout (`supabase.json` and `supabase-beta.json` both shim
// `supabase.exe`).
const binEntry = "supabase.exe";
const root = path.resolve(import.meta.dir, "../../..");
const distDir = path.join(root, "dist");

Expand Down
2 changes: 1 addition & 1 deletion docs/adr/0011-cli-release-and-distribution-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ This section tracks the work that has landed against the pre-cutover gates. Deta
: `bun-windows-arm64` added to [`apps/cli/scripts/build.ts`](../../apps/cli/scripts/build.ts) `TARGETS` + `GO_TARGETS`; new [`packages/cli-windows-arm64/`](../../packages/cli-windows-arm64/) workspace package; `win32: { arm64, x64 }` in [`apps/cli/src/shared/cli/bin.ts`](../../apps/cli/src/shared/cli/bin.ts) and [`apps/cli/src/shared/legacy/go-proxy.layer.ts`](../../apps/cli/src/shared/legacy/go-proxy.layer.ts); 8th entry in [`apps/cli/package.json`](../../apps/cli/package.json) `optionalDependencies`; platform arrays extended in [`apps/cli/scripts/publish.ts`](../../apps/cli/scripts/publish.ts), [`sync-versions.ts`](../../apps/cli/scripts/sync-versions.ts), [`tools/release/local-release.ts`](../../tools/release/local-release.ts), [`apps/cli/tests/helpers/npm-registry.ts`](../../apps/cli/tests/helpers/npm-registry.ts); `windows_arm64.zip` uploaded by [`release-shared.yml`](../../.github/workflows/release-shared.yml).

**B. `--name` + user-owned-repo support on updater scripts** (unblocks gates 2 & 3 end-to-end validation)
: [`apps/cli/scripts/update-homebrew.ts`](../../apps/cli/scripts/update-homebrew.ts) and [`apps/cli/scripts/update-scoop.ts`](../../apps/cli/scripts/update-scoop.ts) now accept `--name <custom>` plus the pre-existing `--repo`, `--tap`/`--bucket`, so the exact production updater code paths can be exercised against a reviewer's own `homebrew-*` / `scoop-*` repos under a non-`supabase` name (e.g., `supabase-shim-poc`) that doesn't clash with an already-installed real CLI. Homebrew computes the Ruby class name from the kebab-case value and, when non-default, installs the SFE via `bin.install "supabase" => "<name>"`. Scoop uses the `[source, alias]` tuple form in the `bin` field. **Side-effect bug fix**: the Homebrew formula now installs the `supabase-go` sidecar alongside the SFE (`bin.install "supabase-go" if File.exist?("supabase-go")`) — see gate 2 above for root-cause analysis.
: [`apps/cli/scripts/update-homebrew.ts`](../../apps/cli/scripts/update-homebrew.ts) and [`apps/cli/scripts/update-scoop.ts`](../../apps/cli/scripts/update-scoop.ts) now accept `--name <custom>` plus the pre-existing `--repo`, `--tap`/`--bucket`, so the exact production updater code paths can be exercised against a reviewer's own `homebrew-*` / `scoop-*` repos under a non-`supabase` name (e.g., `supabase-shim-poc`). The `--name` flag controls the formula filename + Ruby class on Homebrew, and the manifest filename on Scoop, so `supabase` (stable) and `supabase-beta` can coexist as separate formulas / manifests in the same tap / bucket. The installed binary is always `supabase` (matching the Go CLI's historical behaviour — both `Formula/supabase.rb` and `Formula/supabase-beta.rb` ran `bin.install "supabase"`); PoC reviewers must `brew uninstall supabase` first if the official CLI is already installed. **Side-effect bug fix**: the Homebrew formula now installs the `supabase-go` sidecar alongside the SFE (`bin.install "supabase-go" if File.exist?("supabase-go")`) — see gate 2 above for root-cause analysis.

**C. Real CLI version plumbing** (hygiene; surfaced via gate 2 validation)
: The CLI used to hard-code `"0.1.0"` for both `--version` output and telemetry. New [`apps/cli/src/shared/cli/version.ts`](../../apps/cli/src/shared/cli/version.ts) exports `CLI_VERSION = process.env.SUPABASE_CLI_VERSION ?? "0.0.0-dev"`. [`apps/cli/scripts/build.ts`](../../apps/cli/scripts/build.ts) injects the real version at compile time via `bun build --define=process.env.SUPABASE_CLI_VERSION=...` (both glibc and musl builds). [`apps/cli/src/shared/cli/run.ts`](../../apps/cli/src/shared/cli/run.ts) feeds it into Effect CLI's `Command.runWith`; [`apps/cli/src/shared/telemetry/runtime.layer.ts`](../../apps/cli/src/shared/telemetry/runtime.layer.ts) uses the same constant.
Expand Down
3 changes: 2 additions & 1 deletion packages/cli-darwin-arm64/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"preferUnplugged": true,
"files": ["bin/"],
"publishConfig": {
"access": "public"
"access": "public",
"executableFiles": ["bin/supabase", "bin/supabase-go"]
}
}
3 changes: 2 additions & 1 deletion packages/cli-darwin-x64/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"preferUnplugged": true,
"files": ["bin/"],
"publishConfig": {
"access": "public"
"access": "public",
"executableFiles": ["bin/supabase", "bin/supabase-go"]
}
}
3 changes: 2 additions & 1 deletion packages/cli-linux-arm64-musl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"preferUnplugged": true,
"files": ["bin/"],
"publishConfig": {
"access": "public"
"access": "public",
"executableFiles": ["bin/supabase", "bin/supabase-go"]
}
}
3 changes: 2 additions & 1 deletion packages/cli-linux-arm64/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"preferUnplugged": true,
"files": ["bin/"],
"publishConfig": {
"access": "public"
"access": "public",
"executableFiles": ["bin/supabase", "bin/supabase-go"]
}
}
3 changes: 2 additions & 1 deletion packages/cli-linux-x64-musl/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"preferUnplugged": true,
"files": ["bin/"],
"publishConfig": {
"access": "public"
"access": "public",
"executableFiles": ["bin/supabase", "bin/supabase-go"]
}
}
3 changes: 2 additions & 1 deletion packages/cli-linux-x64/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"preferUnplugged": true,
"files": ["bin/"],
"publishConfig": {
"access": "public"
"access": "public",
"executableFiles": ["bin/supabase", "bin/supabase-go"]
}
}
3 changes: 2 additions & 1 deletion packages/cli-windows-arm64/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"preferUnplugged": true,
"files": ["bin/"],
"publishConfig": {
"access": "public"
"access": "public",
"executableFiles": ["bin/supabase.exe", "bin/supabase-go.exe"]
}
}
3 changes: 2 additions & 1 deletion packages/cli-windows-x64/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"preferUnplugged": true,
"files": ["bin/"],
"publishConfig": {
"access": "public"
"access": "public",
"executableFiles": ["bin/supabase.exe", "bin/supabase-go.exe"]
}
}
Loading