From a3510e760452e89cbac008b1c882ae10b1998e87 Mon Sep 17 00:00:00 2001 From: Muzzaiyyan Hussain Date: Sat, 10 Jan 2026 22:18:45 +0530 Subject: [PATCH 1/3] feat(cli): add --no-seed flag to supabase start --- cmd/start.go | 4 +++- internal/start/start.go | 11 ++++++++--- internal/start/start_test.go | 10 +++++----- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/cmd/start.go b/cmd/start.go index bd1cc1f8a..2a91dbee4 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -40,6 +40,7 @@ var ( excludedContainers []string ignoreHealthCheck bool preview bool + skipSeed bool startCmd = &cobra.Command{ GroupID: groupLocalDev, @@ -47,7 +48,7 @@ var ( Short: "Start containers for Supabase local development", RunE: func(cmd *cobra.Command, args []string) error { validateExcludedContainers(excludedContainers) - return start.Run(cmd.Context(), afero.NewOsFs(), excludedContainers, ignoreHealthCheck) + return start.Run(cmd.Context(), afero.NewOsFs(), excludedContainers, ignoreHealthCheck, skipSeed) }, } ) @@ -58,6 +59,7 @@ func init() { flags.StringSliceVarP(&excludedContainers, "exclude", "x", []string{}, "Names of containers to not start. ["+names+"]") flags.BoolVar(&ignoreHealthCheck, "ignore-health-check", false, "Ignore unhealthy services and exit 0") flags.BoolVar(&preview, "preview", false, "Connect to feature preview branch") + flags.BoolVar(&skipSeed, "no-seed", false, "Skip storage seeding on startup") cobra.CheckErr(flags.MarkHidden("preview")) rootCmd.AddCommand(startCmd) } diff --git a/internal/start/start.go b/internal/start/start.go index bcc2cde48..904510b3c 100644 --- a/internal/start/start.go +++ b/internal/start/start.go @@ -43,7 +43,7 @@ import ( "github.com/supabase/cli/pkg/config" ) -func Run(ctx context.Context, fsys afero.Fs, excludedContainers []string, ignoreHealthCheck bool) error { +func Run(ctx context.Context, fsys afero.Fs, excludedContainers []string, ignoreHealthCheck bool, skipSeed bool) error { // Sanity checks. { if err := flags.LoadConfig(fsys); err != nil { @@ -68,7 +68,7 @@ func Run(ctx context.Context, fsys afero.Fs, excludedContainers []string, ignore Password: utils.Config.Db.Password, Database: "postgres", } - if err := run(ctx, fsys, excludedContainers, dbConfig); err != nil { + if err := run(ctx, fsys, excludedContainers, dbConfig, skipSeed); err != nil { if ignoreHealthCheck && start.IsUnhealthyError(err) { fmt.Fprintln(os.Stderr, err) } else { @@ -212,7 +212,7 @@ func pullImagesUsingCompose(ctx context.Context, project types.Project) error { return service.Pull(ctx, &project, api.PullOptions{IgnoreFailures: true}) } -func run(ctx context.Context, fsys afero.Fs, excludedContainers []string, dbConfig pgconn.Config, options ...func(*pgx.ConnConfig)) error { +func run(ctx context.Context, fsys afero.Fs, excludedContainers []string, dbConfig pgconn.Config, skipSeed bool, options ...func(*pgx.ConnConfig)) error { excluded := make(map[string]bool) for _, name := range excludedContainers { excluded[name] = true @@ -1282,11 +1282,16 @@ EOF if err := start.WaitForHealthyService(ctx, serviceTimeout, utils.StorageId); err != nil { return err } + // Disable prompts when seeding + if skipSeed { + fmt.Fprintln(os.Stderr, "Skipping storage seeding (--no-seed enabled)") + } else { // Disable prompts when seeding if err := buckets.Run(ctx, "", false, fsys); err != nil { return err } } + } return start.WaitForHealthyService(ctx, serviceTimeout, started...) } diff --git a/internal/start/start_test.go b/internal/start/start_test.go index a90d7a719..8edc3569e 100644 --- a/internal/start/start_test.go +++ b/internal/start/start_test.go @@ -31,7 +31,7 @@ func TestStartCommand(t *testing.T) { fsys := afero.NewMemMapFs() require.NoError(t, afero.WriteFile(fsys, utils.ConfigPath, []byte("malformed"), 0644)) // Run test - err := Run(context.Background(), fsys, []string{}, false) + err := Run(context.Background(), fsys, []string{}, false, false) // Check error assert.ErrorContains(t, err, "toml: expected = after a key, but the document ends there") }) @@ -47,7 +47,7 @@ func TestStartCommand(t *testing.T) { Get("/v" + utils.Docker.ClientVersion() + "/containers"). ReplyError(errors.New("network error")) // Run test - err := Run(context.Background(), fsys, []string{}, false) + err := Run(context.Background(), fsys, []string{}, false, false) // Check error assert.ErrorContains(t, err, "network error") assert.Empty(t, apitest.ListUnmatchedRequests()) @@ -84,7 +84,7 @@ func TestStartCommand(t *testing.T) { Reply(http.StatusOK). JSON(running) // Run test - err := Run(context.Background(), fsys, []string{}, false) + err := Run(context.Background(), fsys, []string{}, false, false) // Check error assert.NoError(t, err) assert.Empty(t, apitest.ListUnmatchedRequests()) @@ -202,7 +202,7 @@ func TestDatabaseStart(t *testing.T) { Reply(http.StatusOK). JSON([]storage.BucketResponse{}) // Run test - err := run(context.Background(), fsys, []string{}, pgconn.Config{Host: utils.DbId}, conn.Intercept) + err := run(context.Background(), fsys, []string{}, pgconn.Config{Host: utils.DbId}, false, conn.Intercept) // Check error assert.NoError(t, err) assert.Empty(t, apitest.ListUnmatchedRequests()) @@ -248,7 +248,7 @@ func TestDatabaseStart(t *testing.T) { // Run test exclude := ExcludableContainers() exclude = append(exclude, "invalid", exclude[0]) - err := run(context.Background(), fsys, exclude, pgconn.Config{Host: utils.DbId}) + err := run(context.Background(), fsys, exclude, pgconn.Config{Host: utils.DbId}, false) // Check error assert.NoError(t, err) assert.Empty(t, apitest.ListUnmatchedRequests()) From 66e0f72a69834f6f6160d6e4a88f0450db45027a Mon Sep 17 00:00:00 2001 From: Muzzaiyyan Hussain Date: Sat, 10 Jan 2026 22:31:21 +0530 Subject: [PATCH 2/3] feat(start): add --no-seed flag to skip storage bucket seeding --- internal/start/start.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/start/start.go b/internal/start/start.go index 904510b3c..88ba06e3f 100644 --- a/internal/start/start.go +++ b/internal/start/start.go @@ -249,7 +249,9 @@ func run(ctx context.Context, fsys afero.Fs, excludedContainers []string, dbConf utils.Config.Storage.ImageTransformation.Enabled && !isContainerExcluded(utils.Config.Storage.ImgProxyImage, excluded) isS3ProtocolEnabled := utils.Config.Storage.S3Protocol != nil && utils.Config.Storage.S3Protocol.Enabled fmt.Fprintln(os.Stderr, "Starting containers...") - + if skipSeed { + fmt.Fprintln(os.Stderr, "Skipping storage seeding (--no-seed enabled)") +} workdir, err := os.Getwd() if err != nil { return errors.Errorf("failed to get working directory: %w", err) From 67c42e93f66762f2d358ee1e718b6d6d6c7fdb53 Mon Sep 17 00:00:00 2001 From: Muzzaiyyan Hussain Date: Sun, 11 Jan 2026 14:51:21 +0530 Subject: [PATCH 3/3] fix(start): make storage seeding respect db.seed.enabled with --no-seed override --- internal/start/start.go | 23 ++++----- internal/start/start_test.go | 91 ++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+), 11 deletions(-) diff --git a/internal/start/start.go b/internal/start/start.go index 88ba06e3f..bd37816c4 100644 --- a/internal/start/start.go +++ b/internal/start/start.go @@ -249,9 +249,6 @@ func run(ctx context.Context, fsys afero.Fs, excludedContainers []string, dbConf utils.Config.Storage.ImageTransformation.Enabled && !isContainerExcluded(utils.Config.Storage.ImgProxyImage, excluded) isS3ProtocolEnabled := utils.Config.Storage.S3Protocol != nil && utils.Config.Storage.S3Protocol.Enabled fmt.Fprintln(os.Stderr, "Starting containers...") - if skipSeed { - fmt.Fprintln(os.Stderr, "Skipping storage seeding (--no-seed enabled)") -} workdir, err := os.Getwd() if err != nil { return errors.Errorf("failed to get working directory: %w", err) @@ -1280,19 +1277,23 @@ EOF } fmt.Fprintln(os.Stderr, "Waiting for health checks...") + if utils.NoBackupVolume && slices.Contains(started, utils.StorageId) { if err := start.WaitForHealthyService(ctx, serviceTimeout, utils.StorageId); err != nil { return err } - // Disable prompts when seeding - if skipSeed { - fmt.Fprintln(os.Stderr, "Skipping storage seeding (--no-seed enabled)") - } else { - // Disable prompts when seeding - if err := buckets.Run(ctx, "", false, fsys); err != nil { - return err + + // Follow db.seed.enabled by default, allow --no-seed as override + if skipSeed { + fmt.Fprintln(os.Stderr, "Skipping storage seeding (--no-seed enabled)") + } else if !utils.Config.Db.Seed.Enabled { + fmt.Fprintln(os.Stderr, "Skipping storage seeding (db.seed.enabled = false)") + } else { + if err := buckets.Run(ctx, "", false, fsys); err != nil { + return err + } } - } + } return start.WaitForHealthyService(ctx, serviceTimeout, started...) } diff --git a/internal/start/start_test.go b/internal/start/start_test.go index 8edc3569e..b1412f02e 100644 --- a/internal/start/start_test.go +++ b/internal/start/start_test.go @@ -253,6 +253,97 @@ func TestDatabaseStart(t *testing.T) { assert.NoError(t, err) assert.Empty(t, apitest.ListUnmatchedRequests()) }) + + t.Run("skips storage seeding when db.seed.enabled is false", func(t *testing.T) { + utils.Config.Analytics.Enabled = false + utils.Config.Api.Enabled = false + utils.Config.Auth.Enabled = false + utils.Config.Realtime.Enabled = false + utils.Config.Studio.Enabled = false + utils.Config.EdgeRuntime.Enabled = false + utils.Config.Inbucket.Enabled = false + utils.Config.Db.Pooler.Enabled = false + + fsys := afero.NewMemMapFs() + + // Restore global state + origSeed := utils.Config.Db.Seed.Enabled + t.Cleanup(func() { + utils.Config.Db.Seed.Enabled = origSeed + }) + utils.Config.Db.Seed.Enabled = false + + require.NoError(t, apitest.MockDocker(utils.Docker)) + defer gock.OffAll() + + gock.New(utils.Docker.DaemonHost()). + Head("/_ping"). + Reply(http.StatusOK) + + gock.New(utils.Docker.DaemonHost()). + Post("/v" + utils.Docker.ClientVersion() + "/networks/create"). + Reply(http.StatusCreated). + JSON(network.CreateResponse{}) + + // 🔑 REQUIRED: cache all images + for _, img := range config.Images.Services() { + service := utils.GetRegistryImageUrl(img) + gock.New(utils.Docker.DaemonHost()). + Get("/v" + utils.Docker.ClientVersion() + "/images/" + service + "/json"). + Reply(http.StatusOK). + JSON(image.InspectResponse{}) + } + + // ALSO mock postgres image (DB is not part of Services()) + dbImage := utils.GetRegistryImageUrl(utils.Config.Db.Image) + gock.New(utils.Docker.DaemonHost()). + Get("/v" + utils.Docker.ClientVersion() + "/images/" + dbImage + "/json"). + Reply(http.StatusOK). + JSON(image.InspectResponse{}) + + utils.DbId = "test-postgres" + utils.Config.Db.Port = 54322 + utils.Config.Db.MajorVersion = 15 + + gock.New(utils.Docker.DaemonHost()). + Get("/v" + utils.Docker.ClientVersion() + "/volumes/" + utils.DbId). + Reply(http.StatusOK). + JSON(volume.Volume{}) + + apitest.MockDockerStart( + utils.Docker, + utils.GetRegistryImageUrl(utils.Config.Db.Image), + utils.DbId, + ) + + gock.New(utils.Docker.DaemonHost()). + Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json"). + Reply(http.StatusOK). + JSON(container.InspectResponse{ + ContainerJSONBase: &container.ContainerJSONBase{ + State: &container.State{ + Running: true, + Health: &container.Health{Status: types.Healthy}, + }, + }, + }) + exclude := []string{ + utils.ShortContainerImageName(utils.Config.Api.KongImage), + utils.ShortContainerImageName(utils.Config.Storage.Image), + } + + err := run( + context.Background(), + fsys, + exclude, + pgconn.Config{Host: utils.DbId}, + false, + ) + + assert.NoError(t, err) + assert.Empty(t, apitest.ListUnmatchedRequests()) + }) + } func TestFormatMapForEnvConfig(t *testing.T) {