From 413d8bfb1fc51633fab23532815cdd104f98be2c Mon Sep 17 00:00:00 2001 From: Michael Stahnke Date: Fri, 10 Jul 2026 12:04:40 -0500 Subject: [PATCH] Remove ext supabase command and Supabase launch support Supabase is no longer supported. This removes the fly ext supabase command package and all Supabase references from the launch planner (the SupabasePostgres plan type, its describe/validate paths, and the now-defunct deprecation notice). The generated gql AddOnType enum still carries the server-side supabase value and is left untouched. --- internal/command/extensions/extensions.go | 2 - .../command/extensions/supabase/dashboard.go | 48 ----------- .../command/extensions/supabase/destroy.go | 79 ------------------- internal/command/extensions/supabase/list.go | 74 ----------------- .../command/extensions/supabase/status.go | 38 --------- .../command/extensions/supabase/supabase.go | 19 ----- internal/command/launch/cmd.go | 2 +- internal/command/launch/describe_plan.go | 7 -- internal/command/launch/launch_databases.go | 4 - internal/command/launch/plan/postgres.go | 29 +------ internal/command/launch/state.go | 70 +--------------- 11 files changed, 4 insertions(+), 368 deletions(-) delete mode 100644 internal/command/extensions/supabase/dashboard.go delete mode 100644 internal/command/extensions/supabase/destroy.go delete mode 100644 internal/command/extensions/supabase/list.go delete mode 100644 internal/command/extensions/supabase/status.go delete mode 100644 internal/command/extensions/supabase/supabase.go diff --git a/internal/command/extensions/extensions.go b/internal/command/extensions/extensions.go index 54a59da225..c9e384c52d 100644 --- a/internal/command/extensions/extensions.go +++ b/internal/command/extensions/extensions.go @@ -8,7 +8,6 @@ import ( "github.com/superfly/flyctl/internal/command/extensions/arcjet" "github.com/superfly/flyctl/internal/command/extensions/kubernetes" sentry_ext "github.com/superfly/flyctl/internal/command/extensions/sentry" - "github.com/superfly/flyctl/internal/command/extensions/supabase" "github.com/superfly/flyctl/internal/command/extensions/tigris" "github.com/superfly/flyctl/internal/command/extensions/vector" "github.com/superfly/flyctl/internal/command/extensions/wafris" @@ -26,7 +25,6 @@ func New() (cmd *cobra.Command) { cmd.AddCommand( sentry_ext.New(), - supabase.New(), tigris.New(), kubernetes.New(), vector.New(), diff --git a/internal/command/extensions/supabase/dashboard.go b/internal/command/extensions/supabase/dashboard.go deleted file mode 100644 index 2b330bb7e9..0000000000 --- a/internal/command/extensions/supabase/dashboard.go +++ /dev/null @@ -1,48 +0,0 @@ -package supabase - -import ( - "context" - - "github.com/spf13/cobra" - - "github.com/superfly/flyctl/gql" - "github.com/superfly/flyctl/internal/command" - extensions_core "github.com/superfly/flyctl/internal/command/extensions/core" - "github.com/superfly/flyctl/internal/flag" -) - -func dashboard() (cmd *cobra.Command) { - const ( - long = `Visit the Supabase database dashboard` - - short = long - usage = "dashboard [database_name]" - ) - - cmd = command.New(usage, short, long, runDashboard, command.RequireSession, command.LoadAppNameIfPresent) - - flag.Add(cmd, - flag.App(), - flag.AppConfig(), - flag.Org(), - extensions_core.SharedFlags, - ) - cmd.Args = cobra.MaximumNArgs(1) - - return cmd -} - -func runDashboard(ctx context.Context) error { - org := flag.GetOrg(ctx) - - if org != "" { - return extensions_core.OpenOrgDashboard(ctx, org, "supabase") - } - - extension, _, err := extensions_core.Discover(ctx, gql.AddOnTypeSupabase) - if err != nil { - return err - } - - return extensions_core.OpenDashboard(ctx, extension.Name, gql.AddOnTypeSupabase) -} diff --git a/internal/command/extensions/supabase/destroy.go b/internal/command/extensions/supabase/destroy.go deleted file mode 100644 index 98ca08714b..0000000000 --- a/internal/command/extensions/supabase/destroy.go +++ /dev/null @@ -1,79 +0,0 @@ -package supabase - -import ( - "context" - "fmt" - - "github.com/spf13/cobra" - - "github.com/superfly/flyctl/gql" - "github.com/superfly/flyctl/iostreams" - - "github.com/superfly/flyctl/internal/command" - extensions_core "github.com/superfly/flyctl/internal/command/extensions/core" - "github.com/superfly/flyctl/internal/flag" - "github.com/superfly/flyctl/internal/flyutil" - "github.com/superfly/flyctl/internal/prompt" -) - -func destroy() (cmd *cobra.Command) { - const ( - long = `Permanently destroy a Supabase database` - - short = long - usage = "destroy [name]" - ) - - cmd = command.New(usage, short, long, runDestroy, command.RequireSession, command.LoadAppNameIfPresent) - - cmd.Args = cobra.MaximumNArgs(1) - - flag.Add(cmd, - flag.App(), - flag.AppConfig(), - extensions_core.SharedFlags, - ) - - return cmd -} - -func runDestroy(ctx context.Context) (err error) { - io := iostreams.FromContext(ctx) - colorize := io.ColorScheme() - - extension, _, err := extensions_core.Discover(ctx, gql.AddOnTypeSupabase) - if err != nil { - return err - } - - if !flag.GetYes(ctx) { - const msg = "Destroying a Supabase database is not reversible." - fmt.Fprintln(io.ErrOut, colorize.Red(msg)) - - switch confirmed, err := prompt.Confirmf(ctx, "Destroy Supabase database %s?", extension.Name); { - case err == nil: - if !confirmed { - return nil - } - case prompt.IsNonInteractive(err): - return prompt.NonInteractiveError("yes flag must be specified when not running interactively") - default: - return err - } - } - - var ( - out = iostreams.FromContext(ctx).Out - client = flyutil.ClientFromContext(ctx).GenqClient() - ) - - _, err = gql.DeleteAddOn(ctx, client, extension.Name, string(gql.AddOnTypeSupabase)) - - if err != nil { - return - } - - fmt.Fprintf(out, "Your Supabase database %s was destroyed\n", extension.Name) - - return -} diff --git a/internal/command/extensions/supabase/list.go b/internal/command/extensions/supabase/list.go deleted file mode 100644 index 2f0b58a099..0000000000 --- a/internal/command/extensions/supabase/list.go +++ /dev/null @@ -1,74 +0,0 @@ -package supabase - -import ( - "context" - - "github.com/spf13/cobra" - - "github.com/superfly/flyctl/gql" - "github.com/superfly/flyctl/iostreams" - - "github.com/superfly/flyctl/internal/command" - extensions_core "github.com/superfly/flyctl/internal/command/extensions/core" - "github.com/superfly/flyctl/internal/flag" - "github.com/superfly/flyctl/internal/flyutil" - "github.com/superfly/flyctl/internal/render" -) - -func list() (cmd *cobra.Command) { - const ( - long = `List your Supabase databases` - short = long - usage = "list" - ) - - cmd = command.New(usage, short, long, runList, command.RequireSession) - - cmd.Aliases = []string{"ls"} - - flag.Add(cmd, - flag.Org(), - extensions_core.SharedFlags, - ) - - return cmd -} - -func runList(ctx context.Context) (err error) { - var ( - out = iostreams.FromContext(ctx).Out - apiClient = flyutil.ClientFromContext(ctx) - client = apiClient.GenqClient() - orgSlug = flag.GetOrg(ctx) - ) - - var rows [][]string - - if orgSlug != "" { - if _, err := apiClient.GetOrganizationBySlug(ctx, orgSlug); err != nil { - return err - } - - response, err := gql.ListOrganizationAddOns(ctx, client, orgSlug, "supabase") - if err != nil { - return err - } - - for _, addon := range response.Organization.AddOns.Nodes { - rows = append(rows, []string{addon.Name, orgSlug, addon.PrimaryRegion}) - } - } else { - response, err := gql.ListAddOns(ctx, client, "supabase") - if err != nil { - return err - } - - for _, addon := range response.AddOns.Nodes { - rows = append(rows, []string{addon.Name, addon.Organization.Slug, addon.PrimaryRegion}) - } - } - - _ = render.Table(out, "", rows, "Name", "Org", "Primary Region") - - return -} diff --git a/internal/command/extensions/supabase/status.go b/internal/command/extensions/supabase/status.go deleted file mode 100644 index f57890ae81..0000000000 --- a/internal/command/extensions/supabase/status.go +++ /dev/null @@ -1,38 +0,0 @@ -package supabase - -import ( - "context" - - "github.com/spf13/cobra" - "github.com/superfly/flyctl/gql" - "github.com/superfly/flyctl/internal/command" - extensions_core "github.com/superfly/flyctl/internal/command/extensions/core" - "github.com/superfly/flyctl/internal/flag" -) - -func status() *cobra.Command { - const ( - short = "Show details about a Supabase Postgres database" - long = short + "\n" - - usage = "status [name]" - ) - - cmd := command.New(usage, short, long, runStatus, - command.RequireSession, command.LoadAppNameIfPresent, - ) - - cmd.Args = cobra.MaximumNArgs(1) - - flag.Add(cmd, - flag.App(), - flag.AppConfig(), - extensions_core.SharedFlags, - ) - - return cmd -} - -func runStatus(ctx context.Context) (err error) { - return extensions_core.Status(ctx, gql.AddOnTypeSupabase) -} diff --git a/internal/command/extensions/supabase/supabase.go b/internal/command/extensions/supabase/supabase.go deleted file mode 100644 index 440fb7ea0a..0000000000 --- a/internal/command/extensions/supabase/supabase.go +++ /dev/null @@ -1,19 +0,0 @@ -package supabase - -import ( - "github.com/spf13/cobra" - "github.com/superfly/flyctl/internal/command" -) - -func New() (cmd *cobra.Command) { - - const ( - short = "Provision and manage Supabase Postgres databases" - long = short + "\n" - ) - - cmd = command.New("supabase", short, long, nil) - cmd.AddCommand(destroy(), dashboard(), list(), status()) - - return cmd -} diff --git a/internal/command/launch/cmd.go b/internal/command/launch/cmd.go index e9df05598b..a1f6b043ff 100644 --- a/internal/command/launch/cmd.go +++ b/internal/command/launch/cmd.go @@ -466,7 +466,7 @@ func run(ctx context.Context) (err error) { status.VM.ProcessN = len(vm.Processes) } - status.HasPostgres = launchManifest.Plan.Postgres.FlyPostgres != nil || launchManifest.Plan.Postgres.SupabasePostgres != nil || launchManifest.Plan.Postgres.ManagedPostgres != nil + status.HasPostgres = launchManifest.Plan.Postgres.FlyPostgres != nil || launchManifest.Plan.Postgres.ManagedPostgres != nil status.HasRedis = launchManifest.Plan.Redis.UpstashRedis != nil status.HasSentry = launchManifest.Plan.Sentry diff --git a/internal/command/launch/describe_plan.go b/internal/command/launch/describe_plan.go index fc9bdaaf5f..38478270b5 100644 --- a/internal/command/launch/describe_plan.go +++ b/internal/command/launch/describe_plan.go @@ -20,8 +20,6 @@ func describePostgresPlan(launchPlan *plan.LaunchPlan) (string, error) { switch provider := launchPlan.Postgres.Provider().(type) { case *plan.FlyPostgresPlan: return describeFlyPostgresPlan(provider) - case *plan.SupabasePostgresPlan: - return describeSupabasePostgresPlan(provider, launchPlan) case *plan.ManagedPostgresPlan: return describeManagedPostgresPlan(provider, launchPlan) } @@ -49,11 +47,6 @@ func describeFlyPostgresPlan(p *plan.FlyPostgresPlan) (string, error) { return strings.Join(info, ", "), nil } -func describeSupabasePostgresPlan(p *plan.SupabasePostgresPlan, launchPlan *plan.LaunchPlan) (string, error) { - - return fmt.Sprintf("(Supabase) %s in %s", p.GetDbName(launchPlan), p.GetRegion(launchPlan)), nil -} - func describeRedisPlan(ctx context.Context, p plan.RedisPlan) (string, error) { switch provider := p.Provider().(type) { diff --git a/internal/command/launch/launch_databases.go b/internal/command/launch/launch_databases.go index b489cc40bc..cc8bb31762 100644 --- a/internal/command/launch/launch_databases.go +++ b/internal/command/launch/launch_databases.go @@ -44,10 +44,6 @@ func (state *launchState) createDatabases(ctx context.Context) error { } } - if state.Plan.Postgres.SupabasePostgres != nil && (planStep == "" || planStep == "postgres") { - fmt.Fprintf(iostreams.FromContext(ctx).ErrOut, "Supabase Postgres is no longer supported.\n") - } - if state.Plan.Redis.UpstashRedis != nil && (planStep == "" || planStep == "redis") { err := state.createUpstashRedis(ctx) if err != nil { diff --git a/internal/command/launch/plan/postgres.go b/internal/command/launch/plan/postgres.go index 3fc6cc9a06..338873388c 100644 --- a/internal/command/launch/plan/postgres.go +++ b/internal/command/launch/plan/postgres.go @@ -13,9 +13,8 @@ import ( ) type PostgresPlan struct { - FlyPostgres *FlyPostgresPlan `json:"fly_postgres"` - SupabasePostgres *SupabasePostgresPlan `json:"supabase_postgres"` - ManagedPostgres *ManagedPostgresPlan `json:"managed_postgres"` + FlyPostgres *FlyPostgresPlan `json:"fly_postgres"` + ManagedPostgres *ManagedPostgresPlan `json:"managed_postgres"` } func (p *PostgresPlan) Provider() any { @@ -25,9 +24,6 @@ func (p *PostgresPlan) Provider() any { if p.FlyPostgres != nil { return p.FlyPostgres } - if p.SupabasePostgres != nil { - return p.SupabasePostgres - } if p.ManagedPostgres != nil { return p.ManagedPostgres } @@ -249,27 +245,6 @@ func (p *FlyPostgresPlan) Guest() *fly.MachineGuest { return &guest } -type SupabasePostgresPlan struct { - DbName string `json:"db_name"` - Region string `json:"region"` -} - -func (p *SupabasePostgresPlan) GetDbName(plan *LaunchPlan) string { - if p.DbName == "" { - return plan.AppName + "-db" - } - - return p.DbName -} - -func (p *SupabasePostgresPlan) GetRegion(plan *LaunchPlan) string { - if p.Region == "" { - return plan.RegionCode - } - - return p.Region -} - type ManagedPostgresPlan struct { DbName string `json:"db_name"` Region string `json:"region"` diff --git a/internal/command/launch/state.go b/internal/command/launch/state.go index fb43029abf..aefeaf152e 100644 --- a/internal/command/launch/state.go +++ b/internal/command/launch/state.go @@ -2,7 +2,6 @@ package launch import ( "context" - "errors" "fmt" "strconv" "strings" @@ -11,11 +10,8 @@ import ( fly "github.com/superfly/fly-go" "github.com/superfly/flyctl/gql" "github.com/superfly/flyctl/internal/appconfig" - extensions_core "github.com/superfly/flyctl/internal/command/extensions/core" "github.com/superfly/flyctl/internal/command/launch/plan" - "github.com/superfly/flyctl/internal/flag" "github.com/superfly/flyctl/internal/flyutil" - "github.com/superfly/flyctl/internal/prompt" "github.com/superfly/flyctl/iostreams" ) @@ -217,69 +213,5 @@ func (state *launchState) validateExtensions(ctx context.Context) error { // This is written a little awkwardly with the expectation // that we'll probably need more validation in the future. // When that happens we can just errors.Join(a(), b(), c()...) - - io := iostreams.FromContext(ctx) - noConfirm := !io.IsInteractive() || flag.GetBool(ctx, "now") - - org, err := state.orgCompact(ctx) - if err != nil { - return err - } - - validateSupabase := func() error { - supabase := state.Plan.Postgres.SupabasePostgres - if supabase == nil { - return nil - } - - // We're using Supabase. Ensure that we're within plan limits for the target org. - client := flyutil.ClientFromContext(ctx).GenqClient() - - response, err := gql.ListOrganizationAddOns(ctx, client, org.Slug, "supabase") - if err != nil { - return fmt.Errorf("failed to list Supabase databases for org %q: %w", org.Slug, err) - } - - // TODO: We'd like to be able to query the user's plan to see if they're on a paid plan. - // For now, we'll just nag when they create their second database, every time. - - if len(response.Organization.AddOns.Nodes) != 1 { - // If we're at zero databases, we're within the free plan. - // If we're at >=2 databases, we know we're on a paid plan. - // It's only 1 existing database where we need to validate the plan. - return nil - } - - if noConfirm { - // We can't validate this any further until we can query the plan info. - // Assume it's okay, and let the launch fail if it's not. - - // TODO: Once we can query whether or not the user is on a paid plan, - // we'll be able to early-exit in non-interactive mode and prevent a failed launch. - return nil - } - - fmt.Fprintf(io.Out, "You're about to create a second Supabase database. This requires a paid plan.\n") - fmt.Fprintf(io.Out, "Please check to ensure that your plan supports this, otherwise your launch may fail.\n") - openDashboard, err := prompt.Confirm(ctx, "Open the dashboard to check your plan?") - if err != nil { - return err - } - if openDashboard { - if err = extensions_core.OpenOrgDashboard(ctx, org.Slug, "supabase"); err != nil { - return err - } - } - confirm, err := prompt.Confirm(ctx, fmt.Sprintf("Continue launching %s?", state.Plan.AppName)) - if err != nil { - return err - } - if !confirm { - return errors.New("aborted by user") - } - - return nil - } - - return validateSupabase() + return nil }