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
2 changes: 1 addition & 1 deletion .github/workflows/cli-go-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ jobs:
with:
persist-credentials: false

- uses: jdx/mise-action@9e7f7633ff6f6d6048a9418a68d48f288f50eb14 # v4
- uses: jdx/mise-action@7e36c90d9ab29c415a2384db3006f3ec8a8cc654 # v4
with:
version: 2026.7.0
install: true
Expand Down
89 changes: 49 additions & 40 deletions .github/workflows/verify-install-channels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,19 @@ name: Verify Install Channels
# instead of trusting the manifest the publish step wrote.
#
# Each leg goes beyond `supabase --version` (handled by the Bun wrapper without
# touching the sidecar) and runs `supabase completion bash`, a Go-proxied
# command, so a package that omits or misplaces the colocated `supabase-go`
# sidecar fails here instead of silently shipping broken proxied commands.
# touching the sidecar) and directly checks that the `supabase-go` sidecar
# binary is present and executable in the channel's install directory, so a
# package that omits or misplaces it fails here instead of silently shipping
# a CLI whose still-Go-proxied commands (see docs/go-cli-porting-status.md)
# would fail for every user.
#
# This used to run `supabase completion bash`, since that command was
# Go-proxied. It no longer is (CLI-1965 ported shell completion to native
# TypeScript, and the Go CLI's own completion command was subsequently
# removed too), so that probe silently stopped testing the sidecar at all.
# Checking for the sidecar file directly instead of routing through some
# still-proxied command avoids repeating that mistake as more commands get
# natively ported.

on:
workflow_call:
Expand Down Expand Up @@ -156,20 +166,20 @@ jobs:
- name: Verify Go sidecar
run: |
set -euo pipefail
# `completion bash` is proxied to the colocated `supabase-go` sidecar,
# so this fails (NotFound: ChildProcess.spawn) if the package omitted
# or misplaced supabase-go, even though `--version` above passed.
out="$(supabase completion bash 2>&1)" || {
echo "${out}"
echo "Go sidecar probe failed: 'supabase completion bash' did not exit 0" >&2
# Homebrew's `bin.install` symlinks both `supabase` and `supabase-go`
# into the same prefix bin/ directory, so the sidecar must sit right
# next to whichever `supabase` resolved from PATH.
bin_dir="$(dirname "$(command -v supabase)")"
sidecar="${bin_dir}/supabase-go"
if [ ! -e "${sidecar}" ]; then
echo "Go sidecar probe failed: ${sidecar} does not exist" >&2
exit 1
}
printf '%s' "${out}" | grep -q "supabase" || {
echo "${out}"
echo "Go sidecar probe failed: unexpected completion output" >&2
fi
if [ ! -x "${sidecar}" ]; then
echo "Go sidecar probe failed: ${sidecar} exists but is not executable" >&2
exit 1
}
echo "Go sidecar probe OK"
fi
echo "Go sidecar probe OK: ${sidecar}"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exercise the sidecar instead of only stat'ing it

With this change the Homebrew leg, and the matching Scoop/install-script checks, only verifies that supabase-go exists (and is executable on Unix). If a release package contains an executable but unrunnable sidecar, such as the wrong architecture or a corrupt binary, this workflow still passes because supabase --version exercises only the Bun wrapper and the sidecar is never invoked. Please run the sidecar itself with a harmless command after locating it so the channel verification still catches broken Go-proxied commands before shipping.

Useful? React with 👍 / 👎.


scoop:
name: Scoop (${{ inputs.scoop_name }})
Expand Down Expand Up @@ -212,20 +222,19 @@ jobs:
shell: bash
run: |
set -euo pipefail
# `completion bash` is proxied to the colocated `supabase-go` sidecar,
# so this fails if the package omitted or misplaced supabase-go.exe,
# even though `--version` above passed.
out="$(supabase completion bash 2>&1)" || {
echo "${out}"
echo "Go sidecar probe failed: 'supabase completion bash' did not exit 0" >&2
exit 1
}
printf '%s' "${out}" | grep -q "supabase" || {
echo "${out}"
echo "Go sidecar probe failed: unexpected completion output" >&2
# Scoop's manifest only declares `supabase.exe` in `bin` (see
# apps/cli/scripts/update-scoop.ts), so only `supabase` gets a shim in
# ~/scoop/shims — `dirname "$(command -v supabase)"` would resolve to
# the shim directory, not the real install directory supabase-go.exe
# actually lives in. Go straight to the app's current version
# directory instead, which Scoop always maintains regardless of shims.
app_dir="${HOME}/scoop/apps/${SCOOP_NAME}/current"
sidecar="${app_dir}/supabase-go.exe"
if [ ! -e "${sidecar}" ]; then
echo "Go sidecar probe failed: ${sidecar} does not exist" >&2
exit 1
}
echo "Go sidecar probe OK"
fi
echo "Go sidecar probe OK: ${sidecar}"

install-script:
name: install script (${{ matrix.runner }})
Expand Down Expand Up @@ -270,17 +279,17 @@ jobs:
shell: bash
run: |
set -euo pipefail
# `completion bash` is proxied to the colocated `supabase-go` sidecar,
# so this fails if the install script did not place supabase-go next
# to supabase, even though `--version` above passed.
out="$(supabase completion bash 2>&1)" || {
echo "${out}"
echo "Go sidecar probe failed: 'supabase completion bash' did not exit 0" >&2
# The install script places `supabase-go` right next to `supabase`,
# so the sidecar must sit in the same directory `supabase` resolved
# from on PATH.
bin_dir="$(dirname "$(command -v supabase)")"
sidecar="${bin_dir}/supabase-go"
if [ ! -e "${sidecar}" ]; then
echo "Go sidecar probe failed: ${sidecar} does not exist" >&2
exit 1
}
printf '%s' "${out}" | grep -q "supabase" || {
echo "${out}"
echo "Go sidecar probe failed: unexpected completion output" >&2
fi
if [ ! -x "${sidecar}" ]; then
echo "Go sidecar probe failed: ${sidecar} exists but is not executable" >&2
exit 1
}
echo "Go sidecar probe OK"
fi
echo "Go sidecar probe OK: ${sidecar}"
14 changes: 8 additions & 6 deletions apps/cli-e2e/src/tests/live/db-reset-start.live.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ import { testLive } from "./live-context.ts";
// Exercises `db start`'s native container-bootstrap sequence (network/volume/container
// bring-up, health wait, the fresh-volume SetupLocalDatabase-equivalent pipeline, and
// `_current_branch`) and `db reset --local`'s container-recreate flow end-to-end — the
// real-Docker boundary the in-process integration suites mock. `db reset --local` still
// delegates its container-recreate flow to the bundled Go binary's hidden
// `db __db-bootstrap --mode recreate` seam (CLI-1955, unclaimed as of CLI-1954); `db start`
// no longer does (see `commands/db/start/start.handler.ts`). The start → already-running →
// reset cycle runs in one test so it shares a single booted stack, and `finally` stops it
// (legacy proxies `stop` to Go) so the run never leaves containers behind.
// real-Docker boundary the in-process integration suites mock. Both are fully native TS
// now: `db reset --local`'s hidden Go `db __db-bootstrap` seam (`--mode recreate`/
// `--mode await-storage`) was removed in CLI-1955 (see
// `commands/db/reset/reset.handler.ts` / `shared/db-bootstrap/recreate-local-database.ts`),
// the same way `db start`'s own seam usage was removed in CLI-1954 (see
// `commands/db/start/start.handler.ts`). The start → already-running → reset cycle runs
// in one test so it shares a single booted stack, and `finally` stops it (legacy proxies
// `stop` to Go) so the run never leaves containers behind.
describe.skipIf(TARGET === "ts-next")("db start / db reset --local (live, local Docker)", () => {
testLive(
"db start boots, is idempotent, and db reset --local recreates",
Expand Down
67 changes: 0 additions & 67 deletions apps/cli-go/cmd/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -270,66 +270,6 @@ var (
},
}

bootstrapMode string
bootstrapSqlPaths []string
bootstrapVersion string
bootstrapNoSeed bool

// dbBootstrapCmd is a hidden seam used by the native-TypeScript `db reset --local`
// command to drive the container-bootstrap primitives that are not yet ported to
// TypeScript: recreating the local Postgres container, applying the initial
// schema, and the storage health gate. The TS caller orchestrates everything else
// (version/last resolution, bucket seeding, the git-branch "Finished…" line,
// telemetry, and --output-format shaping); the seam stays in Go only for the
// Docker lifecycle. It mirrors the existing db __shadow seam: it carries no
// db-url/local/linked target flags, so it loads supabase/config.toml explicitly
// (the root PersistentPreRunE only loads it when a target flag is set). Progress
// goes to stderr; the only stdout output is a single machine-parseable marker
// for --mode await-storage ("ready" or "absent"). `db start`'s own container
// bootstrap (--mode start) was removed from this seam by CLI-1954 — it is now a
// fully native TypeScript implementation
// (apps/cli/src/legacy/commands/db/start/start.handler.ts), reusing
// legacy/shared/db-bootstrap/'s already-ported container-bootstrap primitives
// instead of shelling out to this binary. `start.StartDatabase` itself (called
// below by the real, customer-facing `db start` Go command) is untouched — it
// remains the parity oracle this TS port was checked against.
dbBootstrapCmd = &cobra.Command{
Use: "__db-bootstrap",
Hidden: true,
Short: "Internal: container bootstrap for the native db start / db reset commands",
RunE: func(cmd *cobra.Command, args []string) error {
fsys := afero.NewOsFs()
if err := flags.LoadConfig(fsys); err != nil {
return err
}
switch bootstrapMode {
case "recreate":
// The PG14/PG15 container-recreate half of local db reset. The TS
// caller has already printed "Resetting local database…" and validated
// the flags. Apply the same seed handling as `db reset` (dbResetCmd):
// `--no-seed` disables the seed, `--sql-paths` overrides the seed paths,
// before MigrateAndSeed runs inside the recreate.
if err := applyDbResetSeedFlags(bootstrapNoSeed, bootstrapSqlPaths); err != nil {
return err
}
return reset.RecreateLocalDatabase(cmd.Context(), bootstrapVersion, fsys)
case "await-storage":
ready, err := reset.AwaitStorageReady(cmd.Context())
if err != nil {
return err
}
if ready {
fmt.Println("ready")
} else {
fmt.Println("absent")
}
return nil
default:
return fmt.Errorf("unknown bootstrap mode: %s", bootstrapMode)
}
},
}

dbRemoteCmd = &cobra.Command{
Hidden: true,
Use: "remote",
Expand Down Expand Up @@ -680,13 +620,6 @@ func init() {
shadowFlags.StringSliceVarP(&shadowSchema, "schema", "s", []string{}, "Comma separated list of schema to include.")
shadowFlags.StringVar(&shadowProjectRef, "project-ref", "", "Linked project ref, so the shadow merges the matching [remotes.<ref>] config override.")
dbCmd.AddCommand(dbShadowCmd)
// Build hidden container-bootstrap seam command (native db start / db reset)
bootstrapFlags := dbBootstrapCmd.Flags()
bootstrapFlags.StringVar(&bootstrapMode, "mode", "recreate", "Bootstrap mode: recreate or await-storage.")
bootstrapFlags.StringVar(&bootstrapVersion, "version", "", "Reset up to the specified version (recreate mode).")
bootstrapFlags.BoolVar(&bootstrapNoSeed, "no-seed", false, "Skip the seed script after recreate (recreate mode).")
bootstrapFlags.StringArrayVar(&bootstrapSqlPaths, "sql-paths", nil, "Override [db.seed].sql_paths for the recreate (recreate mode).")
dbCmd.AddCommand(dbBootstrapCmd)
// Build remote command
remoteFlags := dbRemoteCmd.PersistentFlags()
remoteFlags.StringSliceVarP(&schema, "schema", "s", []string{}, "Comma separated list of schema to include.")
Expand Down
11 changes: 11 additions & 0 deletions apps/cli-go/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,17 @@ func init() {
viper.AutomaticEnv()
})

// Shell tab-completion is fully native in the TS shim now (CLI-1965); the
// TS entrypoint intercepts completion/__complete/__completeNoDesc before
// ever delegating to this binary, so cobra's own completion command is
// unreachable dead weight here. This only removes the visible
// `completion <shell>` command — cobra's ExecuteC() unconditionally
// (re-)registers the hidden __complete/__completeNoDesc responder on
// every run with no opt-out (command.go's initCompleteCmd), so that
// protocol handler stays present but, same as above, unreachable through
// the shipped CLI.
rootCmd.CompletionOptions.DisableDefaultCmd = true

flags := rootCmd.PersistentFlags()
flags.Bool("yes", false, "answer yes to all prompts")
flags.Bool("debug", false, "output debug logs to stderr")
Expand Down
4 changes: 0 additions & 4 deletions apps/cli-go/cmd/sso.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,6 @@ func init() {
ssoAddFlags.Var(&ssoNameIDFormat, "name-id-format", "URI reference representing the classification of string-based identifier information.")
ssoAddCmd.MarkFlagsMutuallyExclusive("metadata-file", "metadata-url")
cobra.CheckErr(ssoAddCmd.MarkFlagRequired("type"))
cobra.CheckErr(ssoAddCmd.MarkFlagFilename("metadata-file", "xml"))
cobra.CheckErr(ssoAddCmd.MarkFlagFilename("attribute-mapping-file", "json"))

ssoUpdateFlags := ssoUpdateCmd.Flags()
ssoUpdateFlags.StringSliceVar(&ssoDomains, "domains", []string{}, "Replace domains with this comma separated list of email domains.")
Expand All @@ -178,8 +176,6 @@ func init() {
ssoUpdateCmd.MarkFlagsMutuallyExclusive("metadata-file", "metadata-url")
ssoUpdateCmd.MarkFlagsMutuallyExclusive("domains", "add-domains")
ssoUpdateCmd.MarkFlagsMutuallyExclusive("domains", "remove-domains")
cobra.CheckErr(ssoUpdateCmd.MarkFlagFilename("metadata-file", "xml"))
cobra.CheckErr(ssoUpdateCmd.MarkFlagFilename("attribute-mapping-file", "json"))

ssoShowFlags := ssoShowCmd.Flags()
ssoShowFlags.BoolVar(&ssoMetadata, "metadata", false, "Show SAML 2.0 XML Metadata only")
Expand Down
6 changes: 4 additions & 2 deletions apps/cli-go/cmd/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ package cmd
// talks to Docker directly for `start` and never delegates to this binary
// for it, and no other still-live TS->Go delegation seam (db test, db
// branch/remote, db diff --use-pgadmin/--use-pg-schema, db pull
// --experimental, the hidden db __db-bootstrap/__shadow/__catalog seams,
// etc.) ever called into internal/start either -- see
// --experimental, the hidden db __shadow/__catalog seams -- the sibling
// hidden db __db-bootstrap seam was removed outright by CLI-1955, once
// native `db reset --local` became its last remaining caller -- etc.) ever
// called into internal/start either -- see
// apps/cli/docs/binary-distribution.md § Removed commands for the full
// rationale. This command's registration and flags are kept only so this
// binary's cobra tree / `--help` / `__complete` output stays stable for
Expand Down
1 change: 0 additions & 1 deletion apps/cli-go/docs/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ func generate(version string) error {
return err
}
root := cli.GetRootCmd()
root.InitDefaultCompletionCmd()
root.InitDefaultHelpFlag()
spec := SpecDoc{
Clispec: "001",
Expand Down
32 changes: 0 additions & 32 deletions apps/cli-go/internal/db/reset/reset.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,38 +93,6 @@ func toLogMessage(version string) string {
return "..."
}

// RecreateLocalDatabase is the container-lifecycle half of a local `db reset`,
// exposed for the native-TypeScript `db reset --local` seam (cmd db __db-bootstrap).
// It performs the PG14/PG15 branch — recreate the db container/volume, init schema,
// migrate + seed, and restart the satellite containers — WITHOUT the leading
// "Resetting local database…" line, which the TS caller prints itself. Mirrors
// resetDatabase (above) minus that message.
func RecreateLocalDatabase(ctx context.Context, version string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
if utils.Config.Db.MajorVersion <= 14 {
return resetDatabase14(ctx, version, fsys, options...)
}
return resetDatabase15(ctx, version, fsys, options...)
}

// AwaitStorageReady mirrors the storage-health gate that local `db reset` runs
// before seeding buckets (Run, above): if the storage container exists but is not
// healthy, wait up to 30s for it. It reports whether the storage container exists
// so the native-TypeScript caller knows whether to run the (already-ported) bucket
// seeding. Any inspect error is treated as "storage not running" → false, matching
// Go's `err == nil` gate, which silently skips buckets on any inspect failure.
func AwaitStorageReady(ctx context.Context) (bool, error) {
resp, err := utils.Docker.ContainerInspect(ctx, utils.StorageId)
if err != nil {
return false, nil
}
if resp.State.Health == nil || resp.State.Health.Status != types.Healthy {
if err := start.WaitForHealthyService(ctx, 30*time.Second, utils.StorageId); err != nil {
return false, err
}
}
return true, nil
}

func resetDatabase14(ctx context.Context, version string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
if err := recreateDatabase(ctx, options...); err != nil {
return err
Expand Down
6 changes: 3 additions & 3 deletions apps/cli-go/pkg/config/templates/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ FROM supabase/edge-runtime:v1.74.3 AS edgeruntime
FROM timberio/vector:0.53.0-alpine AS vector
FROM supabase/supavisor:2.9.7 AS supavisor
FROM supabase/gotrue:v2.195.0 AS gotrue
FROM supabase/realtime:v2.123.5 AS realtime
FROM supabase/storage-api:v1.68.8 AS storage
FROM supabase/logflare:1.50.0 AS logflare
FROM supabase/realtime:v2.124.2 AS realtime
FROM supabase/storage-api:v1.68.10 AS storage
FROM supabase/logflare:1.50.1 AS logflare
# Append to JobImages when adding new dependencies below
FROM supabase/pgadmin-schema-diff:cli-0.0.5 AS differ
FROM supabase/migra:3.0.1663481299 AS migra
Expand Down
9 changes: 6 additions & 3 deletions apps/cli-go/pkg/migration/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,12 @@ func trimLeadingSQLComments(sql string) string {
}
}

// ExecBatch is also reached from the shipped supabase-go sidecar: local `db
// start` / `db reset` delegate migration apply to the `db __db-bootstrap`
// seam (apps/cli-go/cmd/db.go), which calls this via apply.MigrateAndSeed.
// ExecBatch is also reached from the shipped supabase-go sidecar via the
// remaining Go-delegated paths (e.g. remote `db push`/`db reset`'s
// apply.MigrateAndSeed) — local `db start`/`db reset` no longer delegate here
// at all: CLI-1954/CLI-1955 removed the hidden `db __db-bootstrap` seam
// (apps/cli-go/cmd/db.go) this comment used to describe, in favor of a fully
// native TypeScript container-bootstrap port.
func (m *MigrationFile) ExecBatch(ctx context.Context, conn *pgx.Conn) error {
batch := &pgconn.Batch{}
batchSize := 0
Expand Down
Loading
Loading