diff --git a/cmd/link.go b/cmd/link.go index 59c93545c..65b8b5f08 100644 --- a/cmd/link.go +++ b/cmd/link.go @@ -8,7 +8,6 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/supabase/cli/internal/link" - "github.com/supabase/cli/internal/utils" "github.com/supabase/cli/internal/utils/flags" "golang.org/x/term" ) @@ -31,8 +30,7 @@ var ( return err } fsys := afero.NewOsFs() - utils.Config.ProjectId = flags.ProjectRef - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { return err } return link.Run(ctx, flags.ProjectRef, fsys) diff --git a/internal/bootstrap/bootstrap.go b/internal/bootstrap/bootstrap.go index 677ef1530..8310a82fb 100644 --- a/internal/bootstrap/bootstrap.go +++ b/internal/bootstrap/bootstrap.go @@ -95,7 +95,7 @@ func Run(ctx context.Context, starter StarterTemplate, fsys afero.Fs, options .. return err } // 4. Link project - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { return err } link.LinkServices(ctx, flags.ProjectRef, tenant.NewApiKey(keys).Anon, fsys) diff --git a/internal/config/push/push.go b/internal/config/push/push.go index b1b887765..d2dadddc3 100644 --- a/internal/config/push/push.go +++ b/internal/config/push/push.go @@ -7,16 +7,20 @@ import ( "github.com/spf13/afero" "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/internal/utils/flags" "github.com/supabase/cli/pkg/config" ) func Run(ctx context.Context, ref string, fsys afero.Fs) error { - utils.Config.ProjectId = ref - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { return err } client := config.NewConfigUpdater(*utils.GetSupabase()) - remote, _ := utils.Config.GetRemoteByProjectRef(ref) + remote, err := utils.Config.GetRemoteByProjectRef(ref) + if err != nil { + // Use base config when no remote is declared + remote.ProjectId = ref + } fmt.Fprintln(os.Stderr, "Pushing config to project:", remote.ProjectId) console := utils.NewConsole() keep := func(name string) bool { diff --git a/internal/db/branch/create/create.go b/internal/db/branch/create/create.go index 085e10f9d..550128376 100644 --- a/internal/db/branch/create/create.go +++ b/internal/db/branch/create/create.go @@ -14,6 +14,7 @@ import ( "github.com/go-errors/errors" "github.com/spf13/afero" "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/internal/utils/flags" ) var ( @@ -22,7 +23,7 @@ var ( ) func Run(branch string, fsys afero.Fs) error { - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { return err } if err := utils.AssertSupabaseDbIsRunning(); err != nil { diff --git a/internal/db/branch/delete/delete.go b/internal/db/branch/delete/delete.go index f59f5e51c..ea14cd34d 100644 --- a/internal/db/branch/delete/delete.go +++ b/internal/db/branch/delete/delete.go @@ -12,10 +12,11 @@ import ( "github.com/go-errors/errors" "github.com/spf13/afero" "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/internal/utils/flags" ) func Run(branch string, fsys afero.Fs) error { - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { return err } if err := utils.AssertSupabaseDbIsRunning(); err != nil { diff --git a/internal/db/branch/switch_/switch_.go b/internal/db/branch/switch_/switch_.go index 462b26c69..d11803e88 100644 --- a/internal/db/branch/switch_/switch_.go +++ b/internal/db/branch/switch_/switch_.go @@ -12,12 +12,13 @@ import ( "github.com/spf13/afero" "github.com/supabase/cli/internal/db/reset" "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/internal/utils/flags" ) func Run(ctx context.Context, target string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { // 1. Sanity checks { - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { return err } if err := utils.AssertSupabaseDbIsRunning(); err != nil { diff --git a/internal/db/diff/diff.go b/internal/db/diff/diff.go index 6c5faa892..d54f5e534 100644 --- a/internal/db/diff/diff.go +++ b/internal/db/diff/diff.go @@ -24,6 +24,7 @@ import ( "github.com/supabase/cli/internal/db/start" "github.com/supabase/cli/internal/gen/keys" "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/internal/utils/flags" "github.com/supabase/cli/pkg/migration" "github.com/supabase/cli/pkg/parser" ) @@ -32,7 +33,7 @@ type DiffFunc func(context.Context, string, string, []string) (string, error) func Run(ctx context.Context, schema []string, file string, config pgconn.Config, differ DiffFunc, fsys afero.Fs, options ...func(*pgx.ConnConfig)) (err error) { // Sanity checks. - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { return err } if utils.IsLocalDatabase(config) { diff --git a/internal/db/diff/pgadmin.go b/internal/db/diff/pgadmin.go index 298023580..55046bf24 100644 --- a/internal/db/diff/pgadmin.go +++ b/internal/db/diff/pgadmin.go @@ -11,6 +11,7 @@ import ( "github.com/supabase/cli/internal/db/start" "github.com/supabase/cli/internal/migration/new" "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/internal/utils/flags" "github.com/supabase/cli/pkg/config" ) @@ -35,7 +36,7 @@ func SaveDiff(out, file string, fsys afero.Fs) error { func RunPgAdmin(ctx context.Context, schema []string, file string, config pgconn.Config, fsys afero.Fs) error { // Sanity checks. { - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { return err } if err := utils.AssertSupabaseDbIsRunning(); err != nil { diff --git a/internal/db/pull/pull.go b/internal/db/pull/pull.go index f6a235e11..6cc8dccab 100644 --- a/internal/db/pull/pull.go +++ b/internal/db/pull/pull.go @@ -18,6 +18,7 @@ import ( "github.com/supabase/cli/internal/migration/new" "github.com/supabase/cli/internal/migration/repair" "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/internal/utils/flags" "github.com/supabase/cli/pkg/migration" ) @@ -35,7 +36,7 @@ var ( func Run(ctx context.Context, schema []string, config pgconn.Config, name string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { // 1. Sanity checks. - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { return err } // 2. Check postgres connection diff --git a/internal/db/push/push.go b/internal/db/push/push.go index 2141255ae..7e5241558 100644 --- a/internal/db/push/push.go +++ b/internal/db/push/push.go @@ -31,9 +31,10 @@ func Run(ctx context.Context, dryRun, ignoreVersionMismatch bool, includeRoles, } var seeds []migration.SeedFile if includeSeed { - if remote, _ := utils.Config.GetRemoteByProjectRef(flags.ProjectRef); !remote.Db.Seed.Enabled { - fmt.Fprintln(os.Stderr, "Skipping seed because it is disabled in config.toml for project:", remote.ProjectId) - } else if seeds, err = migration.GetPendingSeeds(ctx, remote.Db.Seed.SqlPaths, conn, afero.NewIOFS(fsys)); err != nil { + // TODO: flag should override config but we don't resolve glob paths when seed is disabled. + if !utils.Config.Db.Seed.Enabled { + fmt.Fprintln(os.Stderr, "Skipping seed because it is disabled in config.toml for project:", flags.ProjectRef) + } else if seeds, err = migration.GetPendingSeeds(ctx, utils.Config.Db.Seed.SqlPaths, conn, afero.NewIOFS(fsys)); err != nil { return err } } diff --git a/internal/db/remote/changes/changes.go b/internal/db/remote/changes/changes.go index c735b5984..9aa4a47b0 100644 --- a/internal/db/remote/changes/changes.go +++ b/internal/db/remote/changes/changes.go @@ -8,6 +8,7 @@ import ( "github.com/spf13/afero" "github.com/supabase/cli/internal/db/diff" "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/internal/utils/flags" "github.com/supabase/cli/pkg/migration" ) @@ -15,7 +16,7 @@ var output string func Run(ctx context.Context, schema []string, config pgconn.Config, fsys afero.Fs) error { // Sanity checks. - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { return err } diff --git a/internal/db/remote/commit/commit.go b/internal/db/remote/commit/commit.go index f9062d492..6e00a0295 100644 --- a/internal/db/remote/commit/commit.go +++ b/internal/db/remote/commit/commit.go @@ -15,12 +15,13 @@ import ( "github.com/supabase/cli/internal/migration/list" "github.com/supabase/cli/internal/migration/repair" "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/internal/utils/flags" "github.com/supabase/cli/pkg/migration" ) func Run(ctx context.Context, schema []string, config pgconn.Config, fsys afero.Fs) error { // Sanity checks. - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { return err } diff --git a/internal/db/reset/reset.go b/internal/db/reset/reset.go index 3830a019d..307f14f55 100644 --- a/internal/db/reset/reset.go +++ b/internal/db/reset/reset.go @@ -27,7 +27,6 @@ import ( "github.com/supabase/cli/internal/migration/repair" "github.com/supabase/cli/internal/seed/buckets" "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/internal/utils/flags" "github.com/supabase/cli/pkg/migration" ) @@ -250,15 +249,11 @@ func resetRemote(ctx context.Context, version string, config pgconn.Config, fsys if err := migration.ApplyMigrations(ctx, migrations, conn, afero.NewIOFS(fsys)); err != nil { return err } - remote, _ := utils.Config.GetRemoteByProjectRef(flags.ProjectRef) - if !remote.Db.Seed.Enabled { - fmt.Fprintln(os.Stderr, "Skipping seed because it is disabled in config.toml for project:", remote.ProjectId) - return nil - } else if !utils.Config.Db.Seed.Enabled { + if !utils.Config.Db.Seed.Enabled { // Skip because --no-seed flag is set return nil } - seeds, err := migration.GetPendingSeeds(ctx, remote.Db.Seed.SqlPaths, conn, afero.NewIOFS(fsys)) + seeds, err := migration.GetPendingSeeds(ctx, utils.Config.Db.Seed.SqlPaths, conn, afero.NewIOFS(fsys)) if err != nil { return err } diff --git a/internal/db/start/start.go b/internal/db/start/start.go index aa8b9cee5..4180fe05c 100644 --- a/internal/db/start/start.go +++ b/internal/db/start/start.go @@ -23,6 +23,7 @@ import ( "github.com/supabase/cli/internal/migration/apply" "github.com/supabase/cli/internal/status" "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/internal/utils/flags" "github.com/supabase/cli/pkg/migration" ) @@ -39,7 +40,7 @@ var ( ) func Run(ctx context.Context, fromBackup string, fsys afero.Fs) error { - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { return err } if err := utils.AssertSupabaseDbIsRunning(); err == nil { diff --git a/internal/functions/deploy/deploy.go b/internal/functions/deploy/deploy.go index 5ee2ae845..5b6bb8e47 100644 --- a/internal/functions/deploy/deploy.go +++ b/internal/functions/deploy/deploy.go @@ -18,7 +18,7 @@ import ( func Run(ctx context.Context, slugs []string, projectRef string, noVerifyJWT *bool, importMapPath string, fsys afero.Fs) error { // Load function config and project id - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { return err } else if len(slugs) > 0 { for _, s := range slugs { @@ -60,8 +60,7 @@ func GetFunctionSlugs(fsys afero.Fs) (slugs []string, err error) { } } // Add all function slugs declared in config file - remote, _ := utils.Config.GetRemoteByProjectRef(flags.ProjectRef) - for slug := range remote.Functions { + for slug := range utils.Config.Functions { slugs = append(slugs, slug) } return slugs, nil @@ -82,10 +81,9 @@ func GetFunctionConfig(slugs []string, importMapPath string, noVerifyJWT *bool, if len(importMapPath) > 0 && !filepath.IsAbs(importMapPath) { importMapPath = filepath.Join(utils.CurrentDirAbs, importMapPath) } - remote, _ := utils.Config.GetRemoteByProjectRef(flags.ProjectRef) functionConfig := make(config.FunctionConfig, len(slugs)) for _, name := range slugs { - function := remote.Functions[name] + function := utils.Config.Functions[name] // Precedence order: flag > config > fallback functionDir := filepath.Join(utils.FunctionsDir, name) if len(function.Entrypoint) == 0 { diff --git a/internal/functions/download/download.go b/internal/functions/download/download.go index e3b68cae9..ca99c1bcf 100644 --- a/internal/functions/download/download.go +++ b/internal/functions/download/download.go @@ -16,6 +16,7 @@ import ( "github.com/go-errors/errors" "github.com/spf13/afero" "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/internal/utils/flags" "github.com/supabase/cli/pkg/api" ) @@ -112,7 +113,7 @@ func Run(ctx context.Context, slug string, projectRef string, useLegacyBundle bo return RunLegacy(ctx, slug, projectRef, fsys) } // 1. Sanity check - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { return err } // 2. Download eszip to temp file diff --git a/internal/functions/new/new.go b/internal/functions/new/new.go index f787ce90c..5eca5b67a 100644 --- a/internal/functions/new/new.go +++ b/internal/functions/new/new.go @@ -11,6 +11,7 @@ import ( "github.com/go-errors/errors" "github.com/spf13/afero" "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/internal/utils/flags" ) var ( @@ -45,7 +46,7 @@ func Run(ctx context.Context, slug string, fsys afero.Fs) error { } // Load config if available - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { utils.CmdSuggestion = "" } diff --git a/internal/functions/serve/serve.go b/internal/functions/serve/serve.go index 62811d769..485d8c7cf 100644 --- a/internal/functions/serve/serve.go +++ b/internal/functions/serve/serve.go @@ -19,6 +19,7 @@ import ( "github.com/supabase/cli/internal/functions/deploy" "github.com/supabase/cli/internal/secrets/set" "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/internal/utils/flags" ) type InspectMode string @@ -70,7 +71,7 @@ var ( func Run(ctx context.Context, envFilePath string, noVerifyJWT *bool, importMapPath string, runtimeOption RuntimeOption, fsys afero.Fs) error { // 1. Sanity checks. - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { return err } if err := utils.AssertSupabaseDbIsRunning(); err != nil { diff --git a/internal/gen/types/types.go b/internal/gen/types/types.go index b399cfb5c..9ffc7dc72 100644 --- a/internal/gen/types/types.go +++ b/internal/gen/types/types.go @@ -31,8 +31,7 @@ func Run(ctx context.Context, projectId string, dbConfig pgconn.Config, lang str originalURL := utils.ToPostgresURL(dbConfig) // Add default schemas if --schema flag is not specified if len(schemas) == 0 { - remote, _ := utils.Config.GetRemoteByProjectRef(projectId) - schemas = utils.RemoveDuplicates(append([]string{"public"}, remote.Api.Schemas...)) + schemas = utils.RemoveDuplicates(append([]string{"public"}, utils.Config.Api.Schemas...)) } included := strings.Join(schemas, ",") diff --git a/internal/migration/squash/squash.go b/internal/migration/squash/squash.go index 2ed9e6252..4aad8b832 100644 --- a/internal/migration/squash/squash.go +++ b/internal/migration/squash/squash.go @@ -20,6 +20,7 @@ import ( "github.com/supabase/cli/internal/migration/list" "github.com/supabase/cli/internal/migration/repair" "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/internal/utils/flags" "github.com/supabase/cli/pkg/migration" ) @@ -34,7 +35,7 @@ func Run(ctx context.Context, version string, config pgconn.Config, fsys afero.F return err } } - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { return err } // 1. Squash local migrations diff --git a/internal/seed/buckets/buckets.go b/internal/seed/buckets/buckets.go index b460fdc79..365c3a395 100644 --- a/internal/seed/buckets/buckets.go +++ b/internal/seed/buckets/buckets.go @@ -26,9 +26,8 @@ func Run(ctx context.Context, projectRef string, interactive bool, fsys afero.Fs } return shouldOverwrite } - remote, _ := utils.Config.GetRemoteByProjectRef(projectRef) - if err := api.UpsertBuckets(ctx, remote.Storage.Buckets, filter); err != nil { + if err := api.UpsertBuckets(ctx, utils.Config.Storage.Buckets, filter); err != nil { return err } - return api.UpsertObjects(ctx, remote.Storage.Buckets, utils.NewRootFS(fsys)) + return api.UpsertObjects(ctx, utils.Config.Storage.Buckets, utils.NewRootFS(fsys)) } diff --git a/internal/start/start.go b/internal/start/start.go index 6f75d1377..2501d5b8d 100644 --- a/internal/start/start.go +++ b/internal/start/start.go @@ -37,7 +37,7 @@ import ( func Run(ctx context.Context, fsys afero.Fs, excludedContainers []string, ignoreHealthCheck bool) error { // Sanity checks. { - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { return err } if err := utils.AssertSupabaseDbIsRunning(); err == nil { diff --git a/internal/status/status.go b/internal/status/status.go index 1b9d02f90..68eae2fe3 100644 --- a/internal/status/status.go +++ b/internal/status/status.go @@ -19,6 +19,7 @@ import ( "github.com/go-errors/errors" "github.com/spf13/afero" "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/internal/utils/flags" "github.com/supabase/cli/pkg/fetcher" ) @@ -67,7 +68,7 @@ func (c *CustomName) toValues(exclude ...string) map[string]string { func Run(ctx context.Context, names CustomName, format string, fsys afero.Fs) error { // Sanity checks. - if err := utils.LoadConfigFS(fsys); err != nil { + if err := flags.LoadConfig(fsys); err != nil { return err } if err := assertContainerHealthy(ctx, utils.DbId); err != nil { diff --git a/internal/stop/stop.go b/internal/stop/stop.go index 39c5e00e5..da98f1600 100644 --- a/internal/stop/stop.go +++ b/internal/stop/stop.go @@ -9,6 +9,7 @@ import ( "github.com/docker/docker/api/types/volume" "github.com/spf13/afero" "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/internal/utils/flags" ) func Run(ctx context.Context, backup bool, projectId string, all bool, fsys afero.Fs) error { @@ -17,7 +18,7 @@ func Run(ctx context.Context, backup bool, projectId string, all bool, fsys afer // Sanity checks. if len(projectId) > 0 { utils.Config.ProjectId = projectId - } else if err := utils.LoadConfigFS(fsys); err != nil { + } else if err := flags.LoadConfig(fsys); err != nil { return err } searchProjectIdFilter = utils.Config.ProjectId diff --git a/internal/storage/cp/cp.go b/internal/storage/cp/cp.go index e2e304ced..4ead44639 100644 --- a/internal/storage/cp/cp.go +++ b/internal/storage/cp/cp.go @@ -115,7 +115,6 @@ func UploadStorageObjectAll(ctx context.Context, api storage.StorageAPI, remoteP return err } } - remote, _ := utils.Config.GetRemoteByProjectRef(flags.ProjectRef) // Overwrites existing object when using --recursive flag opts = append(opts, func(fo *storage.FileOptions) { fo.Overwrite = true @@ -154,7 +153,7 @@ func UploadStorageObjectAll(ctx context.Context, api storage.StorageAPI, remoteP // Retry after creating bucket if bucket, prefix := client.SplitBucketPrefix(dstPath); len(prefix) > 0 { body := storage.CreateBucketRequest{Name: bucket} - if config, ok := remote.Storage.Buckets[bucket]; ok { + if config, ok := utils.Config.Storage.Buckets[bucket]; ok { body.Public = config.Public body.FileSizeLimit = int64(config.FileSizeLimit) body.AllowedMimeTypes = config.AllowedMimeTypes diff --git a/internal/utils/config.go b/internal/utils/config.go index 3f89dc75c..06d42d443 100644 --- a/internal/utils/config.go +++ b/internal/utils/config.go @@ -2,7 +2,6 @@ package utils import ( _ "embed" - "fmt" "io/fs" "net" "net/url" @@ -101,17 +100,6 @@ func GetDockerIds() []string { var Config = config.NewConfig(config.WithHostname(GetHostname())) -func LoadConfigFS(fsys afero.Fs) error { - if err := Config.Load("", NewRootFS(fsys)); err != nil { - if errors.Is(err, os.ErrNotExist) { - CmdSuggestion = fmt.Sprintf("Have you set up the project with %s?", Aqua("supabase init")) - } - return err - } - UpdateDockerIds() - return nil -} - // Adapts fs.FS to support absolute paths type rootFS struct { fsys afero.Fs diff --git a/internal/utils/flags/config_path.go b/internal/utils/flags/config_path.go new file mode 100644 index 000000000..d0ed105c7 --- /dev/null +++ b/internal/utils/flags/config_path.go @@ -0,0 +1,22 @@ +package flags + +import ( + "fmt" + "os" + + "github.com/go-errors/errors" + "github.com/spf13/afero" + "github.com/supabase/cli/internal/utils" +) + +func LoadConfig(fsys afero.Fs) error { + utils.Config.ProjectId = ProjectRef + if err := utils.Config.Load("", utils.NewRootFS(fsys)); err != nil { + if errors.Is(err, os.ErrNotExist) { + utils.CmdSuggestion = fmt.Sprintf("Have you set up the project with %s?", utils.Aqua("supabase init")) + } + return err + } + utils.UpdateDockerIds() + return nil +} diff --git a/internal/utils/flags/db_url.go b/internal/utils/flags/db_url.go index 013453b86..04565939d 100644 --- a/internal/utils/flags/db_url.go +++ b/internal/utils/flags/db_url.go @@ -61,7 +61,7 @@ func ParseDatabaseConfig(flagSet *pflag.FlagSet, fsys afero.Fs) error { DbConfig = *config } case local: - if err := utils.LoadConfigFS(fsys); err != nil { + if err := LoadConfig(fsys); err != nil { return err } // Ignore other PG settings @@ -71,10 +71,10 @@ func ParseDatabaseConfig(flagSet *pflag.FlagSet, fsys afero.Fs) error { DbConfig.Password = utils.Config.Db.Password DbConfig.Database = "postgres" case linked: - if err := utils.LoadConfigFS(fsys); err != nil { + if err := LoadProjectRef(fsys); err != nil { return err } - if err := LoadProjectRef(fsys); err != nil { + if err := LoadConfig(fsys); err != nil { return err } DbConfig = NewDbConfigWithPassword(ProjectRef) diff --git a/pkg/config/config.go b/pkg/config/config.go index 837f305a9..93cc252c9 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -136,8 +136,7 @@ type ( config struct { baseConfig `mapstructure:",squash"` - Overrides map[string]interface{} `toml:"remotes"` - Remotes map[string]baseConfig `toml:"-"` + Remotes map[string]baseConfig `toml:"remotes"` } realtime struct { @@ -360,6 +359,7 @@ var ( invalidProjectId = regexp.MustCompile("[^a-zA-Z0-9_.-]+") envPattern = regexp.MustCompile(`^env\((.*)\)$`) + refPattern = regexp.MustCompile(`^[a-z]{20}$`) ) func (c *config) Eject(w io.Writer) error { @@ -406,6 +406,24 @@ func (c *config) loadFromReader(v *viper.Viper, r io.Reader) error { if err := v.MergeConfig(r); err != nil { return errors.Errorf("failed to merge config: %w", err) } + // Find [remotes.*] block to override base config + baseId := v.GetString("project_id") + idToName := map[string]string{baseId: "base"} + for name, remote := range v.GetStringMap("remotes") { + projectId := v.GetString(fmt.Sprintf("remotes.%s.project_id", name)) + // Track remote project_id to check for duplication + if other, exists := idToName[projectId]; exists { + return errors.Errorf("duplicate project_id for [remotes.%s] and %s", name, other) + } + idToName[projectId] = fmt.Sprintf("[remotes.%s]", name) + if projectId == c.ProjectId { + fmt.Fprintln(os.Stderr, "Loading config override:", idToName[projectId]) + if err := v.MergeConfigMap(remote.(map[string]any)); err != nil { + return err + } + v.Set("project_id", baseId) + } + } // Manually parse [functions.*] to empty struct for backwards compatibility for key, value := range v.GetStringMap("functions") { if m, ok := value.(map[string]any); ok && len(m) == 0 { @@ -532,50 +550,11 @@ func (c *config) Load(path string, fsys fs.FS) error { if version, err := fs.ReadFile(fsys, builder.PgmetaVersionPath); err == nil && len(version) > 0 { c.Studio.PgmetaImage = replaceImageTag(pgmetaImage, string(version)) } - // Resolve remote config, then base config - idToName := map[string]string{} - c.Remotes = make(map[string]baseConfig, len(c.Overrides)) - for name, remote := range c.Overrides { - base := c.baseConfig.Clone() - // On remotes branches set seed as disabled by default - base.Db.Seed.Enabled = false - // Encode a toml file with only config overrides - var buf bytes.Buffer - if err := toml.NewEncoder(&buf).Encode(remote); err != nil { - return errors.Errorf("failed to encode map to TOML: %w", err) - } - // Decode overrides using base config as defaults - if metadata, err := toml.NewDecoder(&buf).Decode(&base); err != nil { - return errors.Errorf("failed to decode remote config: %w", err) - } else if undecoded := metadata.Undecoded(); len(undecoded) > 0 { - fmt.Fprintf(os.Stderr, "WARN: unknown config fields: %+v\n", undecoded) - } - // Cross validate remote project id - if base.ProjectId == c.baseConfig.ProjectId { - fmt.Fprintf(os.Stderr, "WARN: project_id is missing for [remotes.%s]\n", name) - } else if other, exists := idToName[base.ProjectId]; exists { - return errors.Errorf("duplicate project_id for [remotes.%s] and [remotes.%s]", other, name) - } else { - idToName[base.ProjectId] = name - } - if err := base.resolve(builder, fsys); err != nil { - return err - } - c.Remotes[name] = base - } + // TODO: replace derived config resolution with viper decode hooks if err := c.baseConfig.resolve(builder, fsys); err != nil { return err } - // Validate base config, then remote config - if err := c.baseConfig.Validate(fsys); err != nil { - return err - } - for name, base := range c.Remotes { - if err := base.Validate(fsys); err != nil { - return errors.Errorf("invalid config for [remotes.%s]: %w", name, err) - } - } - return nil + return c.Validate(fsys) } func (c *baseConfig) resolve(builder pathBuilder, fsys fs.FS) error { @@ -624,13 +603,19 @@ func (c *baseConfig) resolve(builder pathBuilder, fsys fs.FS) error { return c.Db.Seed.loadSeedPaths(builder.SupabaseDirPath, fsys) } -func (c *baseConfig) Validate(fsys fs.FS) error { +func (c *config) Validate(fsys fs.FS) error { if c.ProjectId == "" { return errors.New("Missing required field in config: project_id") } else if sanitized := sanitizeProjectId(c.ProjectId); sanitized != c.ProjectId { fmt.Fprintln(os.Stderr, "WARN: project_id field in config is invalid. Auto-fixing to", sanitized) c.ProjectId = sanitized } + // Since remote config is merged to base, we only need to validate the project_id field. + for name, remote := range c.Remotes { + if !refPattern.MatchString(remote.ProjectId) { + return errors.Errorf("Invalid config for remotes.%s.project_id. Must be like: abcdefghijklmnopqrst", name) + } + } // Validate api config if c.Api.Enabled { if c.Api.Port == 0 { @@ -641,7 +626,7 @@ func (c *baseConfig) Validate(fsys fs.FS) error { if c.Db.Settings.SessionReplicationRole != nil { allowedRoles := []SessionReplicationRole{SessionReplicationRoleOrigin, SessionReplicationRoleReplica, SessionReplicationRoleLocal} if !sliceContains(allowedRoles, *c.Db.Settings.SessionReplicationRole) { - return errors.Errorf("Invalid config for db.session_replication_role: %s. Must be one of: %v", *c.Db.Settings.SessionReplicationRole, allowedRoles) + return errors.Errorf("Invalid config for db.session_replication_role. Must be one of: %v", allowedRoles) } } if c.Db.Port == 0 { @@ -1328,25 +1313,16 @@ func (c *baseConfig) GetServiceImages() []string { } // Retrieve the final base config to use taking into account the remotes override +// Pre: config must be loaded after setting config.ProjectID = "ref" func (c *config) GetRemoteByProjectRef(projectRef string) (baseConfig, error) { - var result []string - // Iterate over all the config.Remotes - for name, remoteConfig := range c.Remotes { - // Check if there is one matching project_id - if remoteConfig.ProjectId == projectRef { - // Check for duplicate project IDs across remotes - result = append(result, name) + base := c.baseConfig.Clone() + for _, remote := range c.Remotes { + if remote.ProjectId == projectRef { + base.ProjectId = projectRef + return base, nil } } - // If no matching remote config is found, return the base config - if len(result) == 0 { - return c.baseConfig, errors.Errorf("no remote found for project_id: %s", projectRef) - } - remote := c.Remotes[result[0]] - if len(result) > 1 { - return remote, errors.Errorf("multiple remotes %v have the same project_id: %s", result, projectRef) - } - return remote, nil + return base, errors.Errorf("no remote found for project_id: %s", projectRef) } func ToTomlBytes(config any) ([]byte, error) { diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 45b2d04f6..2280ae40b 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -88,7 +88,7 @@ func TestConfigParsing(t *testing.T) { staging, ok := config.Remotes["staging"] assert.True(t, ok) // Check the values for production override - assert.Equal(t, config.ProjectId, production.ProjectId) + assert.Equal(t, "vpefcjyosynxeiebfscx", production.ProjectId) assert.Equal(t, "http://feature-auth-branch.com/", production.Auth.SiteUrl) assert.Equal(t, false, production.Auth.EnableSignup) assert.Equal(t, false, production.Auth.External["azure"].Enabled) @@ -96,7 +96,7 @@ func TestConfigParsing(t *testing.T) { // Check seed should be disabled by default for remote configs assert.Equal(t, false, production.Db.Seed.Enabled) // Check the values for the staging override - assert.Equal(t, "staging-project", staging.ProjectId) + assert.Equal(t, "bvikqvbczudanvggcord", staging.ProjectId) assert.Equal(t, []string{"image/png"}, staging.Storage.Buckets["images"].AllowedMimeTypes) assert.Equal(t, true, staging.Db.Seed.Enabled) }) @@ -386,7 +386,7 @@ func TestLoadFunctionImportMap(t *testing.T) { config := NewConfig() fsys := fs.MapFS{ "supabase/config.toml": &fs.MapFile{Data: []byte(` - project_id = "test" + project_id = "bvikqvbczudanvggcord" [functions.hello] `)}, "supabase/functions/hello/deno.json": &fs.MapFile{}, @@ -402,7 +402,7 @@ func TestLoadFunctionImportMap(t *testing.T) { config := NewConfig() fsys := fs.MapFS{ "supabase/config.toml": &fs.MapFile{Data: []byte(` - project_id = "test" + project_id = "bvikqvbczudanvggcord" [functions.hello] `)}, "supabase/functions/hello/deno.jsonc": &fs.MapFile{}, @@ -418,7 +418,7 @@ func TestLoadFunctionImportMap(t *testing.T) { config := NewConfig() fsys := fs.MapFS{ "supabase/config.toml": &fs.MapFile{Data: []byte(` - project_id = "test" + project_id = "bvikqvbczudanvggcord" [functions] hello.import_map = "custom_import_map.json" `)}, diff --git a/pkg/config/testdata/config.toml b/pkg/config/testdata/config.toml index e3093924a..65705646c 100644 --- a/pkg/config/testdata/config.toml +++ b/pkg/config/testdata/config.toml @@ -251,6 +251,9 @@ s3_access_key = "" # Configures AWS_SECRET_ACCESS_KEY for S3 bucket s3_secret_key = "" +[remotes.production] +project_id = "vpefcjyosynxeiebfscx" + [remotes.production.auth] site_url = "http://feature-auth-branch.com/" enable_signup = false @@ -260,7 +263,7 @@ enabled = false client_id = "nope" [remotes.staging] -project_id = "staging-project" +project_id = "bvikqvbczudanvggcord" [remotes.staging.db.seed] enabled = true