From 01256a1721f04e3bd8e032340a530870c7d67539 Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Tue, 24 Sep 2024 17:19:42 +0800 Subject: [PATCH 01/15] feat: support remote config overrides (#2704) * feat: setup basics for branch config override * fix: use pointers for falsy values to determine emptyness * chore: refactor turn Auth.EnableSignup into pointer * wip: attemps non pointer approach * fix: use direct toml parsing to distinguish undefined values * chore: restore gomod * chore: refactor to a single function leverage mergo * chore: fix lint * fix: add branch override logic to LoadConfigFs * fix: inverted logic * chore: add env branch override test * chore: add test for slices merging * chore: remote config overrides * chore: validate project id * Apply suggestions from code review Co-authored-by: Andrew Valleteau --------- Co-authored-by: avallete Co-authored-by: Andrew Valleteau --- pkg/config/config.go | 57 +++++++++++++++++++++++++++++---- pkg/config/config_test.go | 35 ++++++++++++++++++++ pkg/config/testdata/config.toml | 14 ++++++++ 3 files changed, 99 insertions(+), 7 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index db441e9bc..18414546a 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "io/fs" + "maps" "net" "net/http" "net/url" @@ -119,7 +120,8 @@ func (c CustomClaims) NewToken() *jwt.Token { // // Default values for internal configs should be added to `var Config` initializer. type ( - config struct { + // Common config fields between our "base" config and any "remote" branch specific + baseConfig struct { ProjectId string `toml:"project_id"` Hostname string `toml:"-"` Api api `toml:"api"` @@ -135,6 +137,12 @@ type ( Experimental experimental `toml:"experimental" mapstructure:"-"` } + config struct { + baseConfig + Overrides map[string]interface{} `toml:"remotes"` + Remotes map[string]baseConfig `toml:"-"` + } + api struct { Enabled bool `toml:"enabled"` Image string `toml:"-"` @@ -438,6 +446,16 @@ type ( } ) +func (c *baseConfig) Clone() baseConfig { + copy := *c + copy.Storage.Buckets = maps.Clone(c.Storage.Buckets) + copy.Functions = maps.Clone(c.Functions) + copy.Auth.External = maps.Clone(c.Auth.External) + copy.Auth.Email.Template = maps.Clone(c.Auth.Email.Template) + copy.Auth.Sms.TestOTP = maps.Clone(c.Auth.Sms.TestOTP) + return copy +} + type ConfigEditor func(*config) func WithHostname(hostname string) ConfigEditor { @@ -447,7 +465,7 @@ func WithHostname(hostname string) ConfigEditor { } func NewConfig(editors ...ConfigEditor) config { - initial := config{ + initial := config{baseConfig: baseConfig{ Hostname: "127.0.0.1", Api: api{ Image: postgrestImage, @@ -543,7 +561,7 @@ func NewConfig(editors ...ConfigEditor) config { EdgeRuntime: edgeRuntime{ Image: edgeRuntimeImage, }, - } + }} for _, apply := range editors { apply(&initial) } @@ -587,7 +605,6 @@ func (c *config) Load(path string, fsys fs.FS) error { if _, err := dec.Decode(c); err != nil { return errors.Errorf("failed to decode config template: %w", err) } - // Load user defined config if metadata, err := toml.DecodeFS(fsys, builder.ConfigPath, c); err != nil { cwd, osErr := os.Getwd() if osErr != nil { @@ -595,7 +612,11 @@ func (c *config) Load(path string, fsys fs.FS) error { } return errors.Errorf("cannot read config in %s: %w", cwd, err) } else if undecoded := metadata.Undecoded(); len(undecoded) > 0 { - fmt.Fprintf(os.Stderr, "Unknown config fields: %+v\n", undecoded) + for _, key := range undecoded { + if key[0] != "remotes" { + fmt.Fprintf(os.Stderr, "Unknown config field: [%s]\n", key) + } + } } // Load secrets from .env file if err := loadDefaultEnv(); err != nil { @@ -685,10 +706,32 @@ func (c *config) Load(path string, fsys fs.FS) error { } c.Functions[slug] = function } - return c.Validate() + if err := c.baseConfig.Validate(); err != nil { + return err + } + c.Remotes = make(map[string]baseConfig, len(c.Overrides)) + for name, remote := range c.Overrides { + base := c.baseConfig.Clone() + // 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, "Unknown config fields: %+v\n", undecoded) + } + if err := base.Validate(); err != nil { + return err + } + c.Remotes[name] = base + } + return nil } -func (c *config) Validate() error { +func (c *baseConfig) Validate() error { if c.ProjectId == "" { return errors.New("Missing required field in config: project_id") } else if sanitized := sanitizeProjectId(c.ProjectId); sanitized != c.ProjectId { diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 29a605059..733b3c7b7 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -55,6 +55,41 @@ func TestConfigParsing(t *testing.T) { // Run test assert.Error(t, config.Load("", fsys)) }) + + t.Run("config file with remotes", func(t *testing.T) { + config := NewConfig() + // Setup in-memory fs + fsys := fs.MapFS{ + "supabase/config.toml": &fs.MapFile{Data: testInitConfigEmbed}, + "supabase/templates/invite.html": &fs.MapFile{}, + } + // Run test + t.Setenv("TWILIO_AUTH_TOKEN", "token") + t.Setenv("AZURE_CLIENT_ID", "hello") + t.Setenv("AZURE_SECRET", "this is cool") + t.Setenv("AUTH_SEND_SMS_SECRETS", "v1,whsec_aWxpa2VzdXBhYmFzZXZlcnltdWNoYW5kaWhvcGV5b3Vkb3Rvbw==") + t.Setenv("SENDGRID_API_KEY", "sendgrid") + assert.NoError(t, config.Load("", fsys)) + // Check the default value in the config + assert.Equal(t, "http://127.0.0.1:3000", config.Auth.SiteUrl) + assert.Equal(t, true, config.Auth.EnableSignup) + assert.Equal(t, true, config.Auth.External["azure"].Enabled) + assert.Equal(t, []string{"image/png", "image/jpeg"}, config.Storage.Buckets["images"].AllowedMimeTypes) + // Check the values for remotes override + production, ok := config.Remotes["production"] + assert.True(t, ok) + 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, "http://feature-auth-branch.com/", production.Auth.SiteUrl) + assert.Equal(t, false, production.Auth.EnableSignup) + assert.Equal(t, false, production.Auth.External["azure"].Enabled) + assert.Equal(t, "nope", production.Auth.External["azure"].ClientId) + // Check the values for the staging override + assert.Equal(t, "staging-project", staging.ProjectId) + assert.Equal(t, []string{"image/png"}, staging.Storage.Buckets["images"].AllowedMimeTypes) + }) } func TestFileSizeLimitConfigParsing(t *testing.T) { diff --git a/pkg/config/testdata/config.toml b/pkg/config/testdata/config.toml index f7061c1e7..a7aa36544 100644 --- a/pkg/config/testdata/config.toml +++ b/pkg/config/testdata/config.toml @@ -217,3 +217,17 @@ s3_region = "ap-southeast-1" s3_access_key = "" # Configures AWS_SECRET_ACCESS_KEY for S3 bucket s3_secret_key = "" + +[remotes.production.auth] +site_url = "http://feature-auth-branch.com/" +enable_signup = false + +[remotes.production.auth.external.azure] +enabled = false +client_id = "nope" + +[remotes.staging] +project_id = "staging-project" + +[remotes.staging.storage.buckets.images] +allowed_mime_types = ["image/png"] From 11c5004fcbe963aae9e7a48e3884018d179a1a95 Mon Sep 17 00:00:00 2001 From: Andrew Valleteau Date: Tue, 24 Sep 2024 12:05:22 +0200 Subject: [PATCH 02/15] fix: disable security opts for db test on bitbucket runner (#2705) hotfix(cli): disable security opts for db test on bitbucket runner --- internal/utils/docker.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/utils/docker.go b/internal/utils/docker.go index 80106583b..6264c4056 100644 --- a/internal/utils/docker.go +++ b/internal/utils/docker.go @@ -287,6 +287,9 @@ func DockerStart(ctx context.Context, config container.Config, hostConfig contai // Skip named volume for BitBucket pipeline if os.Getenv("BITBUCKET_CLONE_DIR") != "" { hostConfig.Binds = binds + // Bitbucket doesn't allow for --security-opt option to be set + // https://support.atlassian.com/bitbucket-cloud/docs/run-docker-commands-in-bitbucket-pipelines/#Full-list-of-restricted-commands + hostConfig.SecurityOpt = nil } else { // Create named volumes with labels for _, name := range sources { From 2ad2d330be6a0b8e74041f7bf725fdeff39a0358 Mon Sep 17 00:00:00 2001 From: Ivan Vasilov Date: Tue, 24 Sep 2024 14:49:29 +0200 Subject: [PATCH 03/15] fix: Bump studio to the latest image version 20240923 (#2706) Update studio version to 20240923 --- pkg/config/constants.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/constants.go b/pkg/config/constants.go index 93331225a..d75ce61bc 100644 --- a/pkg/config/constants.go +++ b/pkg/config/constants.go @@ -10,7 +10,7 @@ const ( inbucketImage = "inbucket/inbucket:3.0.3" postgrestImage = "postgrest/postgrest:v12.2.0" pgmetaImage = "supabase/postgres-meta:v0.83.2" - studioImage = "supabase/studio:20240729-ce42139" + studioImage = "supabase/studio:20240923-2e3e90c" imageProxyImage = "darthsim/imgproxy:v3.8.0" edgeRuntimeImage = "supabase/edge-runtime:v1.58.3" vectorImage = "timberio/vector:0.28.1-alpine" From bb28463e6b75264dc97b2874a4b15d3561c23152 Mon Sep 17 00:00:00 2001 From: avallete Date: Tue, 24 Sep 2024 10:58:37 +0200 Subject: [PATCH 04/15] chore: use _supabase database for internals This is to avoid overloading our user postgres database with every new addition to _analytics or _realtime --- internal/db/dump/dump.go | 3 --- internal/db/reset/reset.go | 2 ++ internal/db/start/start.go | 2 +- internal/db/start/templates/schema.sql | 24 ++++++++++++++++-------- internal/start/start.go | 8 ++++---- 5 files changed, 23 insertions(+), 16 deletions(-) diff --git a/internal/db/dump/dump.go b/internal/db/dump/dump.go index a52b1dc41..e75cbb8b9 100644 --- a/internal/db/dump/dump.go +++ b/internal/db/dump/dump.go @@ -100,9 +100,6 @@ func dumpData(ctx context.Context, config pgconn.Config, schema, excludeTable [] // "storage", // "supabase_functions", "supabase_migrations", - "_analytics", - "_realtime", - "_supavisor", } var env []string if len(schema) > 0 { diff --git a/internal/db/reset/reset.go b/internal/db/reset/reset.go index 62c3d3350..89a33fda5 100644 --- a/internal/db/reset/reset.go +++ b/internal/db/reset/reset.go @@ -170,7 +170,9 @@ func recreateDatabase(ctx context.Context, options ...func(*pgx.ConnConfig)) err sql := migration.MigrationFile{ Statements: []string{ "DROP DATABASE IF EXISTS postgres WITH (FORCE)", + "DROP DATABASE IF EXISTS _supabase WITH (FORCE)", "CREATE DATABASE postgres WITH OWNER postgres", + "CREATE DATABASE _supabase WITH OWNER postgres", }, } return sql.ExecBatch(ctx, conn) diff --git a/internal/db/start/start.go b/internal/db/start/start.go index 3039b04d9..c25aa24e1 100644 --- a/internal/db/start/start.go +++ b/internal/db/start/start.go @@ -228,7 +228,7 @@ func initRealtimeJob(host string) utils.DockerJob { "DB_PORT=5432", "DB_USER=supabase_admin", "DB_PASSWORD=" + utils.Config.Db.Password, - "DB_NAME=postgres", + "DB_NAME=_supabase", "DB_AFTER_CONNECT_QUERY=SET search_path TO _realtime", "DB_ENC_KEY=" + utils.Config.Realtime.EncryptionKey, "API_JWT_SECRET=" + utils.Config.Auth.JwtSecret, diff --git a/internal/db/start/templates/schema.sql b/internal/db/start/templates/schema.sql index 0d0010231..418f0ee05 100644 --- a/internal/db/start/templates/schema.sql +++ b/internal/db/start/templates/schema.sql @@ -12,14 +12,22 @@ ALTER USER supabase_storage_admin WITH PASSWORD :'pgpass'; ALTER USER supabase_replication_admin WITH PASSWORD :'pgpass'; ALTER USER supabase_read_only_user WITH PASSWORD :'pgpass'; -create schema if not exists _realtime; -alter schema _realtime owner to postgres; - -create schema if not exists _analytics; -alter schema _analytics owner to postgres; - -create schema if not exists _supavisor; -alter schema _supavisor owner to postgres; +CREATE DATABASE _supabase WITH OWNER postgres; +-- Connect to the _supabase database +\c _supabase +-- Create schemas in _supabase database for +-- internals tools and reports to not overload user database +-- with non-user activity +CREATE SCHEMA IF NOT EXISTS _realtime; +ALTER SCHEMA _realtime OWNER TO postgres; + +CREATE SCHEMA IF NOT EXISTS _analytics; +ALTER SCHEMA _analytics OWNER TO postgres; + +CREATE SCHEMA IF NOT EXISTS _supavisor; +ALTER SCHEMA _supavisor OWNER TO postgres; +-- Switch back to the main database +\c postgres BEGIN; diff --git a/internal/start/start.go b/internal/start/start.go index 2c3017ce5..413b8e2ed 100644 --- a/internal/start/start.go +++ b/internal/start/start.go @@ -195,7 +195,7 @@ func run(p utils.Program, ctx context.Context, fsys afero.Fs, excludedContainers // Start Logflare if utils.Config.Analytics.Enabled && !isContainerExcluded(utils.Config.Analytics.Image, excluded) { env := []string{ - "DB_DATABASE=" + dbConfig.Database, + "DB_DATABASE=_supabase", "DB_HOSTNAME=" + dbConfig.Host, fmt.Sprintf("DB_PORT=%d", dbConfig.Port), "DB_SCHEMA=_analytics", @@ -228,7 +228,7 @@ func run(p utils.Program, ctx context.Context, fsys afero.Fs, excludedContainers ) case config.LogflarePostgres: env = append(env, - fmt.Sprintf("POSTGRES_BACKEND_URL=postgresql://%s:%s@%s:%d/%s", dbConfig.User, dbConfig.Password, dbConfig.Host, dbConfig.Port, dbConfig.Database), + fmt.Sprintf("POSTGRES_BACKEND_URL=postgresql://%s:%s@%s:%d/%s", dbConfig.User, dbConfig.Password, dbConfig.Host, dbConfig.Port, "_supabase"), "POSTGRES_BACKEND_SCHEMA=_analytics", ) } @@ -750,7 +750,7 @@ EOF fmt.Sprintf("DB_PORT=%d", dbConfig.Port), "DB_USER=supabase_admin", "DB_PASSWORD=" + dbConfig.Password, - "DB_NAME=" + dbConfig.Database, + "DB_NAME=_supabase", "DB_AFTER_CONNECT_QUERY=SET search_path TO _realtime", "DB_ENC_KEY=" + utils.Config.Realtime.EncryptionKey, "API_JWT_SECRET=" + utils.Config.Auth.JwtSecret, @@ -1045,7 +1045,7 @@ EOF "PORT=4000", fmt.Sprintf("PROXY_PORT_SESSION=%d", portSession), fmt.Sprintf("PROXY_PORT_TRANSACTION=%d", portTransaction), - fmt.Sprintf("DATABASE_URL=ecto://%s:%s@%s:%d/%s", dbConfig.User, dbConfig.Password, dbConfig.Host, dbConfig.Port, dbConfig.Database), + fmt.Sprintf("DATABASE_URL=ecto://%s:%s@%s:%d/%s", dbConfig.User, dbConfig.Password, dbConfig.Host, dbConfig.Port, "_supabase"), "CLUSTER_POSTGRES=true", "SECRET_KEY_BASE=" + utils.Config.Db.Pooler.SecretKeyBase, "VAULT_ENC_KEY=" + utils.Config.Db.Pooler.EncryptionKey, From 5e946870f2127d6e4bbf55edc0b4252f08f12148 Mon Sep 17 00:00:00 2001 From: avallete Date: Tue, 24 Sep 2024 15:15:26 +0200 Subject: [PATCH 05/15] chore: fix tests mocks --- internal/db/reset/reset.go | 2 +- internal/db/reset/reset_test.go | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/internal/db/reset/reset.go b/internal/db/reset/reset.go index 89a33fda5..d53392ae9 100644 --- a/internal/db/reset/reset.go +++ b/internal/db/reset/reset.go @@ -170,8 +170,8 @@ func recreateDatabase(ctx context.Context, options ...func(*pgx.ConnConfig)) err sql := migration.MigrationFile{ Statements: []string{ "DROP DATABASE IF EXISTS postgres WITH (FORCE)", - "DROP DATABASE IF EXISTS _supabase WITH (FORCE)", "CREATE DATABASE postgres WITH OWNER postgres", + "DROP DATABASE IF EXISTS _supabase WITH (FORCE)", "CREATE DATABASE _supabase WITH OWNER postgres", }, } diff --git a/internal/db/reset/reset_test.go b/internal/db/reset/reset_test.go index 03da968c5..ffb9898cf 100644 --- a/internal/db/reset/reset_test.go +++ b/internal/db/reset/reset_test.go @@ -145,6 +145,10 @@ func TestRecreateDatabase(t *testing.T) { Query("DROP DATABASE IF EXISTS postgres WITH (FORCE)"). Reply("DROP DATABASE"). Query("CREATE DATABASE postgres WITH OWNER postgres"). + Reply("CREATE DATABASE"). + Query("DROP DATABASE IF EXISTS _supabase WITH (FORCE)"). + Reply("DROP DATABASE"). + Query("CREATE DATABASE _supabase WITH OWNER postgres"). Reply("CREATE DATABASE") // Run test assert.NoError(t, recreateDatabase(context.Background(), conn.Intercept)) @@ -194,8 +198,11 @@ func TestRecreateDatabase(t *testing.T) { Reply("DO"). Query("DROP DATABASE IF EXISTS postgres WITH (FORCE)"). ReplyError(pgerrcode.ObjectInUse, `database "postgres" is used by an active logical replication slot`). - Query("CREATE DATABASE postgres WITH OWNER postgres") - // Run test + Query("CREATE DATABASE postgres WITH OWNER postgres"). + Query("DROP DATABASE IF EXISTS _supabase WITH (FORCE)"). + Reply("DROP DATABASE"). + Query("CREATE DATABASE _supabase WITH OWNER postgres"). + Reply("CREATE DATABASE") err := recreateDatabase(context.Background(), conn.Intercept) // Check error assert.ErrorContains(t, err, `ERROR: database "postgres" is used by an active logical replication slot (SQLSTATE 55006)`) From e6788f3bc03e91af979c000435cf6ba4610c3dcf Mon Sep 17 00:00:00 2001 From: avallete Date: Tue, 24 Sep 2024 18:03:41 +0200 Subject: [PATCH 06/15] chore: add realtime schema as well --- internal/db/start/templates/schema.sql | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/db/start/templates/schema.sql b/internal/db/start/templates/schema.sql index 418f0ee05..45a936a2e 100644 --- a/internal/db/start/templates/schema.sql +++ b/internal/db/start/templates/schema.sql @@ -20,6 +20,8 @@ CREATE DATABASE _supabase WITH OWNER postgres; -- with non-user activity CREATE SCHEMA IF NOT EXISTS _realtime; ALTER SCHEMA _realtime OWNER TO postgres; +CREATE SCHEMA IF NOT EXISTS realtime; +ALTER SCHEMA realtime OWNER TO postgres; CREATE SCHEMA IF NOT EXISTS _analytics; ALTER SCHEMA _analytics OWNER TO postgres; From 76ef3d2d3ed394302a05bb67194aa30a0136ce87 Mon Sep 17 00:00:00 2001 From: avallete Date: Tue, 24 Sep 2024 19:25:36 +0200 Subject: [PATCH 07/15] fix: make the start work for postgres 14 and 13 --- internal/db/dump/dump.go | 4 ++++ internal/db/start/start.go | 11 +++++++++-- internal/db/start/templates/_supabase.sql | 18 ++++++++++++++++++ internal/db/start/templates/schema.sql | 19 ------------------- 4 files changed, 31 insertions(+), 21 deletions(-) create mode 100644 internal/db/start/templates/_supabase.sql diff --git a/internal/db/dump/dump.go b/internal/db/dump/dump.go index e75cbb8b9..94e2ba7af 100644 --- a/internal/db/dump/dump.go +++ b/internal/db/dump/dump.go @@ -100,6 +100,10 @@ func dumpData(ctx context.Context, config pgconn.Config, schema, excludeTable [] // "storage", // "supabase_functions", "supabase_migrations", + // TODO: Remove in a few version in favor of _supabase internal db + "_analytics", + "_realtime", + "_supavisor", } var env []string if len(schema) > 0 { diff --git a/internal/db/start/start.go b/internal/db/start/start.go index c25aa24e1..8ed608ad7 100644 --- a/internal/db/start/start.go +++ b/internal/db/start/start.go @@ -29,6 +29,8 @@ var ( HealthTimeout = 120 * time.Second //go:embed templates/schema.sql initialSchema string + //go:embed templates/_supabase.sql + _supabaseSchema string ) func Run(ctx context.Context, fsys afero.Fs) error { @@ -82,7 +84,7 @@ func NewContainerConfig() container.Config { Retries: 3, }, Entrypoint: []string{"sh", "-c", `cat <<'EOF' > /etc/postgresql.schema.sql && cat <<'EOF' > /etc/postgresql-custom/pgsodium_root.key && docker-entrypoint.sh postgres -D /etc/postgresql -` + initialSchema + ` +` + initialSchema + "\n" + _supabaseSchema + "\n" + ` EOF ` + utils.Config.Db.RootKey + ` EOF @@ -122,7 +124,12 @@ func StartDatabase(ctx context.Context, fsys afero.Fs, w io.Writer, options ...f }, } if utils.Config.Db.MajorVersion <= 14 { - config.Entrypoint = nil + config.Entrypoint = []string{"sh", "-c", ` + cat <<'EOF' > /docker-entrypoint-initdb.d/supabase_schema.sql +` + _supabaseSchema + ` +EOF + docker-entrypoint.sh postgres -D /etc/postgresql + `} hostConfig.Tmpfs = map[string]string{"/docker-entrypoint-initdb.d": ""} } // Creating volume will not override existing volume, so we must inspect explicitly diff --git a/internal/db/start/templates/_supabase.sql b/internal/db/start/templates/_supabase.sql new file mode 100644 index 000000000..d391166a2 --- /dev/null +++ b/internal/db/start/templates/_supabase.sql @@ -0,0 +1,18 @@ +CREATE DATABASE _supabase WITH OWNER postgres; + +-- Switch to the newly created _supabase database +\c _supabase +-- Create schemas in _supabase database for +-- internals tools and reports to not overload user database +-- with non-user activity +CREATE SCHEMA IF NOT EXISTS _realtime; +ALTER SCHEMA _realtime OWNER TO postgres; +CREATE SCHEMA IF NOT EXISTS realtime; +ALTER SCHEMA realtime OWNER TO postgres; + +CREATE SCHEMA IF NOT EXISTS _analytics; +ALTER SCHEMA _analytics OWNER TO postgres; + +CREATE SCHEMA IF NOT EXISTS _supavisor; +ALTER SCHEMA _supavisor OWNER TO postgres; +\c postgres \ No newline at end of file diff --git a/internal/db/start/templates/schema.sql b/internal/db/start/templates/schema.sql index 45a936a2e..2ff6cf48f 100644 --- a/internal/db/start/templates/schema.sql +++ b/internal/db/start/templates/schema.sql @@ -12,25 +12,6 @@ ALTER USER supabase_storage_admin WITH PASSWORD :'pgpass'; ALTER USER supabase_replication_admin WITH PASSWORD :'pgpass'; ALTER USER supabase_read_only_user WITH PASSWORD :'pgpass'; -CREATE DATABASE _supabase WITH OWNER postgres; --- Connect to the _supabase database -\c _supabase --- Create schemas in _supabase database for --- internals tools and reports to not overload user database --- with non-user activity -CREATE SCHEMA IF NOT EXISTS _realtime; -ALTER SCHEMA _realtime OWNER TO postgres; -CREATE SCHEMA IF NOT EXISTS realtime; -ALTER SCHEMA realtime OWNER TO postgres; - -CREATE SCHEMA IF NOT EXISTS _analytics; -ALTER SCHEMA _analytics OWNER TO postgres; - -CREATE SCHEMA IF NOT EXISTS _supavisor; -ALTER SCHEMA _supavisor OWNER TO postgres; --- Switch back to the main database -\c postgres - BEGIN; -- Create pg_net extension From fd04d07c60c8634326975b27b962bd729eaff182 Mon Sep 17 00:00:00 2001 From: Andrew Valleteau Date: Wed, 25 Sep 2024 08:28:05 +0200 Subject: [PATCH 08/15] feat: add custom seed path to config (#2702) * chore: replace SeedDataPath by DefaultSeedDataPath * wip: add path seed matching logic * chore: add test for utils.GetSeedFiles * chore: wip tests mock * chore: fix lint * chore: show seed path * chore: change comment * chore: apply pr suggestions * chore: fix lint * chore: keep default value assignation * chore: remove DefaultSeedPath * chore: keep consistent WARNING message * chore: inline get seed file path * chore: address review comments --------- Co-authored-by: Qiao Han --- cmd/db.go | 2 +- cmd/start.go | 2 +- internal/db/push/push.go | 6 ++- internal/db/push/push_test.go | 4 +- internal/db/reset/reset_test.go | 2 +- internal/db/start/start_test.go | 3 +- internal/init/init.go | 18 +------ internal/init/init_test.go | 13 ----- internal/migration/apply/apply.go | 8 +-- internal/migration/apply/apply_test.go | 10 ++-- internal/utils/misc.go | 22 +++++++- internal/utils/misc_test.go | 73 ++++++++++++++++++++++++++ pkg/config/config.go | 6 ++- pkg/config/templates/config.toml | 8 +++ pkg/config/testdata/config.toml | 8 +++ pkg/config/utils.go | 2 - pkg/migration/seed.go | 3 +- 17 files changed, 140 insertions(+), 50 deletions(-) diff --git a/cmd/db.go b/cmd/db.go index 2ddb4072f..2d64edd74 100644 --- a/cmd/db.go +++ b/cmd/db.go @@ -276,7 +276,7 @@ func init() { pushFlags := dbPushCmd.Flags() pushFlags.BoolVar(&includeAll, "include-all", false, "Include all migrations not found on remote history table.") pushFlags.BoolVar(&includeRoles, "include-roles", false, "Include custom roles from "+utils.CustomRolesPath+".") - pushFlags.BoolVar(&includeSeed, "include-seed", false, "Include seed data from "+utils.SeedDataPath+".") + pushFlags.BoolVar(&includeSeed, "include-seed", false, "Include seed data from your config.") pushFlags.BoolVar(&dryRun, "dry-run", false, "Print the migrations that would be applied, but don't actually apply them.") pushFlags.String("db-url", "", "Pushes to the database specified by the connection string (must be percent-encoded).") pushFlags.Bool("linked", true, "Pushes to the linked project.") diff --git a/cmd/start.go b/cmd/start.go index ae3faf01e..a7af80e0c 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -27,7 +27,7 @@ func validateExcludedContainers(excludedContainers []string) { // Sort the names list so it's easier to visually spot the one you looking for sort.Strings(validContainers) warning := fmt.Sprintf("%s The following container names are not valid to exclude: %s\nValid containers to exclude are: %s\n", - utils.Yellow("Warning:"), + utils.Yellow("WARNING:"), utils.Aqua(strings.Join(invalidContainers, ", ")), utils.Aqua(strings.Join(validContainers, ", "))) fmt.Fprint(os.Stderr, warning) diff --git a/internal/db/push/push.go b/internal/db/push/push.go index 68e3be9ac..0bff15163 100644 --- a/internal/db/push/push.go +++ b/internal/db/push/push.go @@ -41,7 +41,11 @@ func Run(ctx context.Context, dryRun, ignoreVersionMismatch bool, includeRoles, fmt.Fprintln(os.Stderr, "Would push these migrations:") fmt.Fprint(os.Stderr, utils.Bold(confirmPushAll(pending))) if includeSeed { - fmt.Fprintln(os.Stderr, "Would seed data "+utils.Bold(utils.SeedDataPath)+"...") + seedPaths, err := utils.GetSeedFiles(fsys) + if err != nil { + return err + } + fmt.Fprintf(os.Stderr, "Would seed data %v...\n", seedPaths) } } else { msg := fmt.Sprintf("Do you want to push these migrations to the remote database?\n%s\n", confirmPushAll(pending)) diff --git a/internal/db/push/push_test.go b/internal/db/push/push_test.go index e4f6353ec..72df15ef9 100644 --- a/internal/db/push/push_test.go +++ b/internal/db/push/push_test.go @@ -162,7 +162,9 @@ func TestPushAll(t *testing.T) { t.Run("throws error on seed failure", func(t *testing.T) { // Setup in-memory fs - fsys := &fstest.OpenErrorFs{DenyPath: utils.SeedDataPath} + seedPath := filepath.Join(utils.SupabaseDirPath, "seed.sql") + fsys := &fstest.OpenErrorFs{DenyPath: seedPath} + _, _ = fsys.Create(seedPath) path := filepath.Join(utils.MigrationsDir, "0_test.sql") require.NoError(t, afero.WriteFile(fsys, path, []byte{}, 0644)) // Setup mock postgres diff --git a/internal/db/reset/reset_test.go b/internal/db/reset/reset_test.go index 03da968c5..0be21f3b2 100644 --- a/internal/db/reset/reset_test.go +++ b/internal/db/reset/reset_test.go @@ -362,7 +362,7 @@ func TestResetRemote(t *testing.T) { fsys := afero.NewMemMapFs() path := filepath.Join(utils.MigrationsDir, "0_schema.sql") require.NoError(t, afero.WriteFile(fsys, path, nil, 0644)) - seedPath := filepath.Join(utils.SeedDataPath) + seedPath := filepath.Join(utils.SupabaseDirPath, "seed.sql") // Will raise an error when seeding require.NoError(t, afero.WriteFile(fsys, seedPath, []byte("INSERT INTO test_table;"), 0644)) // Setup mock postgres diff --git a/internal/db/start/start_test.go b/internal/db/start/start_test.go index dcc93b4c8..e4072df98 100644 --- a/internal/db/start/start_test.go +++ b/internal/db/start/start_test.go @@ -6,6 +6,7 @@ import ( "io" "net/http" "os" + "path/filepath" "testing" "github.com/docker/docker/api/types" @@ -60,7 +61,7 @@ func TestStartDatabase(t *testing.T) { roles := "create role test" require.NoError(t, afero.WriteFile(fsys, utils.CustomRolesPath, []byte(roles), 0644)) seed := "INSERT INTO employees(name) VALUES ('Alice')" - require.NoError(t, afero.WriteFile(fsys, utils.SeedDataPath, []byte(seed), 0644)) + require.NoError(t, afero.WriteFile(fsys, filepath.Join(utils.SupabaseDirPath, "seed.sql"), []byte(seed), 0644)) // Setup mock docker require.NoError(t, apitest.MockDocker(utils.Docker)) defer gock.OffAll() diff --git a/internal/init/init.go b/internal/init/init.go index 033638dd9..f4e470b02 100644 --- a/internal/init/init.go +++ b/internal/init/init.go @@ -40,19 +40,14 @@ func Run(ctx context.Context, fsys afero.Fs, createVscodeSettings, createIntelli return err } - // 2. Create `seed.sql`. - if err := initSeed(fsys); err != nil { - return err - } - - // 3. Append to `.gitignore`. + // 2. Append to `.gitignore`. if utils.IsGitRepo() { if err := updateGitIgnore(utils.GitIgnorePath, fsys); err != nil { return err } } - // 4. Generate VS Code settings. + // 3. Generate VS Code settings. if createVscodeSettings != nil { if *createVscodeSettings { return writeVscodeConfig(fsys) @@ -77,15 +72,6 @@ func Run(ctx context.Context, fsys afero.Fs, createVscodeSettings, createIntelli return nil } -func initSeed(fsys afero.Fs) error { - f, err := fsys.OpenFile(utils.SeedDataPath, os.O_WRONLY|os.O_CREATE, 0644) - if err != nil { - return errors.Errorf("failed to create seed file: %w", err) - } - defer f.Close() - return nil -} - func updateGitIgnore(ignorePath string, fsys afero.Fs) error { var contents []byte diff --git a/internal/init/init_test.go b/internal/init/init_test.go index 09bf6ab0f..47e35b89e 100644 --- a/internal/init/init_test.go +++ b/internal/init/init_test.go @@ -28,10 +28,6 @@ func TestInitCommand(t *testing.T) { exists, err = afero.Exists(fsys, utils.GitIgnorePath) assert.NoError(t, err) assert.True(t, exists) - // Validate generated seed.sql - exists, err = afero.Exists(fsys, utils.SeedDataPath) - assert.NoError(t, err) - assert.True(t, exists) // Validate vscode settings file isn't generated exists, err = afero.Exists(fsys, settingsPath) assert.NoError(t, err) @@ -70,15 +66,6 @@ func TestInitCommand(t *testing.T) { assert.Error(t, Run(context.Background(), fsys, nil, nil, utils.InitParams{})) }) - t.Run("throws error on seed failure", func(t *testing.T) { - // Setup in-memory fs - fsys := &fstest.OpenErrorFs{DenyPath: utils.SeedDataPath} - // Run test - err := Run(context.Background(), fsys, nil, nil, utils.InitParams{}) - // Check error - assert.ErrorIs(t, err, os.ErrPermission) - }) - t.Run("creates vscode settings file", func(t *testing.T) { // Setup in-memory fs fsys := &afero.MemMapFs{} diff --git a/internal/migration/apply/apply.go b/internal/migration/apply/apply.go index 44195297f..6796930d2 100644 --- a/internal/migration/apply/apply.go +++ b/internal/migration/apply/apply.go @@ -27,11 +27,11 @@ func MigrateAndSeed(ctx context.Context, version string, conn *pgx.Conn, fsys af } func SeedDatabase(ctx context.Context, conn *pgx.Conn, fsys afero.Fs) error { - err := migration.SeedData(ctx, []string{utils.SeedDataPath}, conn, afero.NewIOFS(fsys)) - if errors.Is(err, os.ErrNotExist) { - return nil + seedPaths, err := utils.GetSeedFiles(fsys) + if err != nil { + return err } - return err + return migration.SeedData(ctx, seedPaths, conn, afero.NewIOFS(fsys)) } func CreateCustomRoles(ctx context.Context, conn *pgx.Conn, fsys afero.Fs) error { diff --git a/internal/migration/apply/apply_test.go b/internal/migration/apply/apply_test.go index b8c041749..6c5b915fd 100644 --- a/internal/migration/apply/apply_test.go +++ b/internal/migration/apply/apply_test.go @@ -44,7 +44,7 @@ func TestMigrateDatabase(t *testing.T) { path := filepath.Join(utils.MigrationsDir, "0_test.sql") sql := "create schema public" require.NoError(t, afero.WriteFile(fsys, path, []byte(sql), 0644)) - seedPath := filepath.Join(utils.SeedDataPath) + seedPath := filepath.Join(utils.SupabaseDirPath, "seed.sql") // This will raise an error when seeding require.NoError(t, afero.WriteFile(fsys, seedPath, []byte("INSERT INTO test_table;"), 0644)) // Setup mock postgres @@ -82,7 +82,7 @@ func TestSeedDatabase(t *testing.T) { fsys := afero.NewMemMapFs() // Setup seed file sql := "INSERT INTO employees(name) VALUES ('Alice')" - require.NoError(t, afero.WriteFile(fsys, utils.SeedDataPath, []byte(sql), 0644)) + require.NoError(t, afero.WriteFile(fsys, filepath.Join(utils.SupabaseDirPath, "seed.sql"), []byte(sql), 0644)) // Setup mock postgres conn := pgtest.NewConn() defer conn.Close(t) @@ -100,7 +100,9 @@ func TestSeedDatabase(t *testing.T) { t.Run("throws error on read failure", func(t *testing.T) { // Setup in-memory fs - fsys := &fstest.OpenErrorFs{DenyPath: utils.SeedDataPath} + seedPath := filepath.Join(utils.SupabaseDirPath, "seed.sql") + fsys := &fstest.OpenErrorFs{DenyPath: seedPath} + _, _ = fsys.Create(seedPath) // Run test err := SeedDatabase(context.Background(), nil, fsys) // Check error @@ -112,7 +114,7 @@ func TestSeedDatabase(t *testing.T) { fsys := afero.NewMemMapFs() // Setup seed file sql := "INSERT INTO employees(name) VALUES ('Alice')" - require.NoError(t, afero.WriteFile(fsys, utils.SeedDataPath, []byte(sql), 0644)) + require.NoError(t, afero.WriteFile(fsys, filepath.Join(utils.SupabaseDirPath, "seed.sql"), []byte(sql), 0644)) // Setup mock postgres conn := pgtest.NewConn() defer conn.Close(t) diff --git a/internal/utils/misc.go b/internal/utils/misc.go index 8118b8213..d4e49f046 100644 --- a/internal/utils/misc.go +++ b/internal/utils/misc.go @@ -8,6 +8,7 @@ import ( "os" "path/filepath" "regexp" + "sort" "time" "github.com/docker/docker/client" @@ -148,7 +149,6 @@ var ( FallbackImportMapPath = filepath.Join(FunctionsDir, "import_map.json") FallbackEnvFilePath = filepath.Join(FunctionsDir, ".env") DbTestsDir = filepath.Join(SupabaseDirPath, "tests") - SeedDataPath = filepath.Join(SupabaseDirPath, "seed.sql") CustomRolesPath = filepath.Join(SupabaseDirPath, "roles.sql") ErrNotLinked = errors.Errorf("Cannot find project ref. Have you run %s?", Aqua("supabase link")) @@ -157,6 +157,26 @@ var ( ErrNotRunning = errors.Errorf("%s is not running.", Aqua("supabase start")) ) +// Match the glob patterns from the config to get a deduplicated +// array of all migrations files to apply in the declared order. +func GetSeedFiles(fsys afero.Fs) ([]string, error) { + seedPaths := Config.Db.Seed.SqlPaths + var files []string + for _, pattern := range seedPaths { + fullPattern := filepath.Join(SupabaseDirPath, pattern) + matches, err := afero.Glob(fsys, fullPattern) + if err != nil { + return nil, errors.Errorf("failed to apply glob pattern for %w", err) + } + if len(matches) == 0 { + fmt.Fprintf(os.Stderr, "%s Your pattern %s matched 0 seed files.\n", Yellow("WARNING:"), pattern) + } + sort.Strings(matches) + files = append(files, matches...) + } + return RemoveDuplicates(files), nil +} + func GetCurrentTimestamp() string { // Magic number: https://stackoverflow.com/q/45160822. return time.Now().UTC().Format("20060102150405") diff --git a/internal/utils/misc_test.go b/internal/utils/misc_test.go index 6472c2145..c16abdf00 100644 --- a/internal/utils/misc_test.go +++ b/internal/utils/misc_test.go @@ -75,3 +75,76 @@ func TestProjectRoot(t *testing.T) { assert.Equal(t, cwd, path) }) } + +func TestGetSeedFiles(t *testing.T) { + t.Run("returns seed files matching patterns", func(t *testing.T) { + // Setup in-memory fs + fsys := afero.NewMemMapFs() + // Create seed files + require.NoError(t, afero.WriteFile(fsys, "supabase/seeds/seed1.sql", []byte("INSERT INTO table1 VALUES (1);"), 0644)) + require.NoError(t, afero.WriteFile(fsys, "supabase/seeds/seed2.sql", []byte("INSERT INTO table2 VALUES (2);"), 0644)) + require.NoError(t, afero.WriteFile(fsys, "supabase/seeds/seed3.sql", []byte("INSERT INTO table2 VALUES (2);"), 0644)) + require.NoError(t, afero.WriteFile(fsys, "supabase/seeds/another.sql", []byte("INSERT INTO table2 VALUES (2);"), 0644)) + require.NoError(t, afero.WriteFile(fsys, "supabase/seeds/ignore.sql", []byte("INSERT INTO table3 VALUES (3);"), 0644)) + // Mock config patterns + Config.Db.Seed.SqlPaths = []string{"seeds/seed[12].sql", "seeds/ano*.sql"} + + // Run test + files, err := GetSeedFiles(fsys) + + // Check error + assert.NoError(t, err) + // Validate files + assert.ElementsMatch(t, []string{"supabase/seeds/seed1.sql", "supabase/seeds/seed2.sql", "supabase/seeds/another.sql"}, files) + }) + t.Run("returns seed files matching patterns skip duplicates", func(t *testing.T) { + // Setup in-memory fs + fsys := afero.NewMemMapFs() + // Create seed files + require.NoError(t, afero.WriteFile(fsys, "supabase/seeds/seed1.sql", []byte("INSERT INTO table1 VALUES (1);"), 0644)) + require.NoError(t, afero.WriteFile(fsys, "supabase/seeds/seed2.sql", []byte("INSERT INTO table2 VALUES (2);"), 0644)) + require.NoError(t, afero.WriteFile(fsys, "supabase/seeds/seed3.sql", []byte("INSERT INTO table2 VALUES (2);"), 0644)) + require.NoError(t, afero.WriteFile(fsys, "supabase/seeds/another.sql", []byte("INSERT INTO table2 VALUES (2);"), 0644)) + require.NoError(t, afero.WriteFile(fsys, "supabase/seeds/ignore.sql", []byte("INSERT INTO table3 VALUES (3);"), 0644)) + // Mock config patterns + Config.Db.Seed.SqlPaths = []string{"seeds/seed[12].sql", "seeds/ano*.sql", "seeds/seed*.sql"} + + // Run test + files, err := GetSeedFiles(fsys) + + // Check error + assert.NoError(t, err) + // Validate files + assert.ElementsMatch(t, []string{"supabase/seeds/seed1.sql", "supabase/seeds/seed2.sql", "supabase/seeds/another.sql", "supabase/seeds/seed3.sql"}, files) + }) + + t.Run("returns error on invalid pattern", func(t *testing.T) { + // Setup in-memory fs + fsys := afero.NewMemMapFs() + // Mock config patterns + Config.Db.Seed.SqlPaths = []string{"[*!#@D#"} + + // Run test + files, err := GetSeedFiles(fsys) + + // Check error + assert.Nil(t, err) + // The resuling seed list should be empty + assert.ElementsMatch(t, []string{}, files) + }) + + t.Run("returns empty list if no files match", func(t *testing.T) { + // Setup in-memory fs + fsys := afero.NewMemMapFs() + // Mock config patterns + Config.Db.Seed.SqlPaths = []string{"seeds/*.sql"} + + // Run test + files, err := GetSeedFiles(fsys) + + // Check error + assert.NoError(t, err) + // Validate files + assert.Empty(t, files) + }) +} diff --git a/pkg/config/config.go b/pkg/config/config.go index 18414546a..3636e7e9c 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -172,7 +172,8 @@ type ( } seed struct { - Enabled bool `toml:"enabled"` + Enabled bool `toml:"enabled"` + SqlPaths []string `toml:"sql_paths"` } pooler struct { @@ -482,7 +483,8 @@ func NewConfig(editors ...ConfigEditor) config { SecretKeyBase: "EAx3IQ/wRG1v47ZD4NE4/9RzBI8Jmil3x0yhcW4V2NHBP6c2iPIzwjofi2Ep4HIG", }, Seed: seed{ - Enabled: true, + Enabled: true, + SqlPaths: []string{"./seed.sql"}, }, }, Realtime: realtime{ diff --git a/pkg/config/templates/config.toml b/pkg/config/templates/config.toml index 37646aa63..112237748 100644 --- a/pkg/config/templates/config.toml +++ b/pkg/config/templates/config.toml @@ -39,6 +39,14 @@ default_pool_size = 20 # Maximum number of client connections allowed. max_client_conn = 100 +[db.seed] +# If enabled, seeds the database after migrations during a db reset. +enabled = true +# Specifies an ordered list of seed files to load during db reset. +# Supports glob patterns relative to supabase directory. For example: +# sql_paths = ['./seeds/*.sql', '../project-src/seeds/*-load-testing.sql'] +sql_paths = ['./seed.sql'] + [realtime] enabled = true # Bind realtime via either IPv4 or IPv6. (default: IPv4) diff --git a/pkg/config/testdata/config.toml b/pkg/config/testdata/config.toml index a7aa36544..76e070c60 100644 --- a/pkg/config/testdata/config.toml +++ b/pkg/config/testdata/config.toml @@ -39,6 +39,14 @@ default_pool_size = 20 # Maximum number of client connections allowed. max_client_conn = 100 +[db.seed] +# If enabled, seeds the database after migrations during a db reset. +enabled = true +# Specifies an ordered list of seed files to load during db reset. +# Supports glob patterns relative to supabase directory. For example: +# sql_paths = ['./seeds/*.sql', '../project-src/seeds/*-load-testing.sql'] +sql_paths = ['./seed.sql'] + [realtime] enabled = true # Bind realtime via either IPv4 or IPv6. (default: IPv6) diff --git a/pkg/config/utils.go b/pkg/config/utils.go index b2318b5a9..ac26a38d2 100644 --- a/pkg/config/utils.go +++ b/pkg/config/utils.go @@ -29,7 +29,6 @@ type pathBuilder struct { FallbackImportMapPath string FallbackEnvFilePath string DbTestsDir string - SeedDataPath string CustomRolesPath string } @@ -63,7 +62,6 @@ func NewPathBuilder(configPath string) pathBuilder { FallbackImportMapPath: filepath.Join(base, "functions", "import_map.json"), FallbackEnvFilePath: filepath.Join(base, "functions", ".env"), DbTestsDir: filepath.Join(base, "tests"), - SeedDataPath: filepath.Join(base, "seed.sql"), CustomRolesPath: filepath.Join(base, "roles.sql"), } } diff --git a/pkg/migration/seed.go b/pkg/migration/seed.go index f299e9f05..62a2c875b 100644 --- a/pkg/migration/seed.go +++ b/pkg/migration/seed.go @@ -12,8 +12,7 @@ import ( func SeedData(ctx context.Context, pending []string, conn *pgx.Conn, fsys fs.FS) error { for _, path := range pending { - filename := filepath.Base(path) - fmt.Fprintf(os.Stderr, "Seeding data from %s...\n", filename) + fmt.Fprintf(os.Stderr, "Seeding data from %s...\n", path) // Batch seed commands, safe to use statement cache if seed, err := NewMigrationFromFile(path, fsys); err != nil { return err From 371a163cc6bafc72fa5a3eca3718a6c3a1d063a8 Mon Sep 17 00:00:00 2001 From: avallete Date: Wed, 25 Sep 2024 09:57:59 +0200 Subject: [PATCH 09/15] chore: add bitbucket build canary badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 63ca688be..3229bc369 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Supabase CLI (v1) -[![Coverage Status](https://coveralls.io/repos/github/supabase/cli/badge.svg?branch=main)](https://coveralls.io/github/supabase/cli?branch=main) +[![Coverage Status](https://coveralls.io/repos/github/supabase/cli/badge.svg?branch=main)](https://coveralls.io/github/supabase/cli?branch=main) [![Bitbucket Pipelines](https://img.shields.io/bitbucket/pipelines/supabase-cli/setup-cli/master?style=flat-square&label=Bitbucket%20Canary)](https://bitbucket.org/supabase-cli/setup-cli/pipelines) [Supabase](https://supabase.io) is an open source Firebase alternative. We're building the features of Firebase using enterprise-grade open source tools. From d5a34fc78536fa0d1cb8f21f2d35476440dfd6d3 Mon Sep 17 00:00:00 2001 From: avallete Date: Wed, 25 Sep 2024 10:12:45 +0200 Subject: [PATCH 10/15] chore: add gitlab canary badge --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3229bc369..750bd8895 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Supabase CLI (v1) -[![Coverage Status](https://coveralls.io/repos/github/supabase/cli/badge.svg?branch=main)](https://coveralls.io/github/supabase/cli?branch=main) [![Bitbucket Pipelines](https://img.shields.io/bitbucket/pipelines/supabase-cli/setup-cli/master?style=flat-square&label=Bitbucket%20Canary)](https://bitbucket.org/supabase-cli/setup-cli/pipelines) +[![Coverage Status](https://coveralls.io/repos/github/supabase/cli/badge.svg?branch=main)](https://coveralls.io/github/supabase/cli?branch=main) [![Bitbucket Pipelines](https://img.shields.io/bitbucket/pipelines/supabase-cli/setup-cli/master?style=flat-square&label=Bitbucket%20Canary)](https://bitbucket.org/supabase-cli/setup-cli/pipelines) [![Gitlab Pipeline Status](https://img.shields.io/gitlab/pipeline-status/sweatybridge%2Fsetup-cli?label=Gitlab%20Canary) +](https://gitlab.com/sweatybridge/setup-cli/-/pipelines) [Supabase](https://supabase.io) is an open source Firebase alternative. We're building the features of Firebase using enterprise-grade open source tools. From 1623aa9b95ec90e21c5bae5a0d50dcf272abe92f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20Caba=C3=A7o?= Date: Thu, 26 Sep 2024 02:02:07 +0100 Subject: [PATCH 11/15] fix: Bump up studio image (#2711) From ccf214d252a76ea87e5b3d72ed61c66c59ef7a48 Mon Sep 17 00:00:00 2001 From: avallete Date: Fri, 27 Sep 2024 13:53:47 +0200 Subject: [PATCH 12/15] chore: restore realtime to postgres database --- internal/db/start/templates/_supabase.sql | 5 ----- internal/db/start/templates/schema.sql | 3 +++ internal/start/start.go | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/internal/db/start/templates/_supabase.sql b/internal/db/start/templates/_supabase.sql index d391166a2..c339d2b61 100644 --- a/internal/db/start/templates/_supabase.sql +++ b/internal/db/start/templates/_supabase.sql @@ -5,11 +5,6 @@ CREATE DATABASE _supabase WITH OWNER postgres; -- Create schemas in _supabase database for -- internals tools and reports to not overload user database -- with non-user activity -CREATE SCHEMA IF NOT EXISTS _realtime; -ALTER SCHEMA _realtime OWNER TO postgres; -CREATE SCHEMA IF NOT EXISTS realtime; -ALTER SCHEMA realtime OWNER TO postgres; - CREATE SCHEMA IF NOT EXISTS _analytics; ALTER SCHEMA _analytics OWNER TO postgres; diff --git a/internal/db/start/templates/schema.sql b/internal/db/start/templates/schema.sql index 2ff6cf48f..534dd1207 100644 --- a/internal/db/start/templates/schema.sql +++ b/internal/db/start/templates/schema.sql @@ -12,6 +12,9 @@ ALTER USER supabase_storage_admin WITH PASSWORD :'pgpass'; ALTER USER supabase_replication_admin WITH PASSWORD :'pgpass'; ALTER USER supabase_read_only_user WITH PASSWORD :'pgpass'; +create schema if not exists _realtime; +alter schema _realtime owner to postgres; + BEGIN; -- Create pg_net extension diff --git a/internal/start/start.go b/internal/start/start.go index 413b8e2ed..72f34b8e7 100644 --- a/internal/start/start.go +++ b/internal/start/start.go @@ -750,7 +750,7 @@ EOF fmt.Sprintf("DB_PORT=%d", dbConfig.Port), "DB_USER=supabase_admin", "DB_PASSWORD=" + dbConfig.Password, - "DB_NAME=_supabase", + "DB_NAME=" + dbConfig.Database, "DB_AFTER_CONNECT_QUERY=SET search_path TO _realtime", "DB_ENC_KEY=" + utils.Config.Realtime.EncryptionKey, "API_JWT_SECRET=" + utils.Config.Auth.JwtSecret, From e911718c22eecae9b039119c289b766de58fe8fa Mon Sep 17 00:00:00 2001 From: avallete Date: Fri, 27 Sep 2024 16:06:33 +0200 Subject: [PATCH 13/15] chore: remove relatime migration --- internal/db/start/start.go | 2 +- internal/db/start/templates/_supabase.sql | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/db/start/start.go b/internal/db/start/start.go index 8ed608ad7..9614528f5 100644 --- a/internal/db/start/start.go +++ b/internal/db/start/start.go @@ -235,7 +235,7 @@ func initRealtimeJob(host string) utils.DockerJob { "DB_PORT=5432", "DB_USER=supabase_admin", "DB_PASSWORD=" + utils.Config.Db.Password, - "DB_NAME=_supabase", + "DB_NAME=postgres", "DB_AFTER_CONNECT_QUERY=SET search_path TO _realtime", "DB_ENC_KEY=" + utils.Config.Realtime.EncryptionKey, "API_JWT_SECRET=" + utils.Config.Auth.JwtSecret, diff --git a/internal/db/start/templates/_supabase.sql b/internal/db/start/templates/_supabase.sql index c339d2b61..6e5d8487b 100644 --- a/internal/db/start/templates/_supabase.sql +++ b/internal/db/start/templates/_supabase.sql @@ -10,4 +10,4 @@ ALTER SCHEMA _analytics OWNER TO postgres; CREATE SCHEMA IF NOT EXISTS _supavisor; ALTER SCHEMA _supavisor OWNER TO postgres; -\c postgres \ No newline at end of file +\c postgres From 3115143937d6ed665bc547e66def7f96032b8c81 Mon Sep 17 00:00:00 2001 From: Andrew Valleteau Date: Fri, 27 Sep 2024 18:10:42 +0200 Subject: [PATCH 14/15] Update internal/db/start/start.go Co-authored-by: Han Qiao --- internal/db/start/start.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/db/start/start.go b/internal/db/start/start.go index 9614528f5..74e40554a 100644 --- a/internal/db/start/start.go +++ b/internal/db/start/start.go @@ -84,7 +84,8 @@ func NewContainerConfig() container.Config { Retries: 3, }, Entrypoint: []string{"sh", "-c", `cat <<'EOF' > /etc/postgresql.schema.sql && cat <<'EOF' > /etc/postgresql-custom/pgsodium_root.key && docker-entrypoint.sh postgres -D /etc/postgresql -` + initialSchema + "\n" + _supabaseSchema + "\n" + ` +` + initialSchema + ` +` + _supabaseSchema + ` EOF ` + utils.Config.Db.RootKey + ` EOF From c8e553faacdfc90d4d6f04a4c05890d60a1dab21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=83=A5=EB=83=90=EC=B1=A0?= Date: Tue, 1 Oct 2024 13:22:30 +0900 Subject: [PATCH 15/15] fix: bump edge-runtime to 1.58.11 (#2718) --- pkg/config/constants.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/constants.go b/pkg/config/constants.go index d75ce61bc..9d91a8489 100644 --- a/pkg/config/constants.go +++ b/pkg/config/constants.go @@ -12,7 +12,7 @@ const ( pgmetaImage = "supabase/postgres-meta:v0.83.2" studioImage = "supabase/studio:20240923-2e3e90c" imageProxyImage = "darthsim/imgproxy:v3.8.0" - edgeRuntimeImage = "supabase/edge-runtime:v1.58.3" + edgeRuntimeImage = "supabase/edge-runtime:v1.58.11" vectorImage = "timberio/vector:0.28.1-alpine" supavisorImage = "supabase/supavisor:1.1.56" gotrueImage = "supabase/gotrue:v2.158.1"