131 lines
4.1 KiB
Go
131 lines
4.1 KiB
Go
package push
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/go-errors/errors"
|
|
"github.com/jackc/pgconn"
|
|
"github.com/jackc/pgx/v4"
|
|
"github.com/spf13/afero"
|
|
"github.com/supabase/cli/internal/migration/up"
|
|
"github.com/supabase/cli/internal/utils"
|
|
"github.com/supabase/cli/internal/utils/flags"
|
|
"github.com/supabase/cli/pkg/migration"
|
|
"github.com/supabase/cli/pkg/vault"
|
|
)
|
|
|
|
func Run(ctx context.Context, dryRun, ignoreVersionMismatch bool, includeRoles, includeSeed bool, config pgconn.Config, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
|
|
if dryRun {
|
|
fmt.Fprintln(os.Stderr, "DRY RUN: migrations will *not* be pushed to the database.")
|
|
}
|
|
conn, err := utils.ConnectByConfig(ctx, config, options...)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer conn.Close(context.Background())
|
|
pending, err := up.GetPendingMigrations(ctx, ignoreVersionMismatch, conn, fsys)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var seeds []migration.SeedFile
|
|
if includeSeed {
|
|
// 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
|
|
}
|
|
}
|
|
var globals []string
|
|
if includeRoles {
|
|
if exists, err := afero.Exists(fsys, utils.CustomRolesPath); err != nil {
|
|
return errors.Errorf("failed to find custom roles: %w", err)
|
|
} else if exists {
|
|
globals = append(globals, utils.CustomRolesPath)
|
|
}
|
|
}
|
|
if len(pending) == 0 && len(seeds) == 0 && len(globals) == 0 {
|
|
fmt.Println("Remote database is up to date.")
|
|
return nil
|
|
}
|
|
// Push pending migrations
|
|
if dryRun {
|
|
if len(globals) > 0 {
|
|
fmt.Fprintln(os.Stderr, "Would create custom roles "+utils.Bold(globals[0])+"...")
|
|
}
|
|
if len(pending) > 0 {
|
|
fmt.Fprintln(os.Stderr, "Would push these migrations:")
|
|
fmt.Fprint(os.Stderr, confirmPushAll(pending))
|
|
}
|
|
if len(seeds) > 0 {
|
|
fmt.Fprintln(os.Stderr, "Would seed these files:")
|
|
fmt.Fprint(os.Stderr, confirmSeedAll(seeds))
|
|
}
|
|
} else {
|
|
if len(globals) > 0 {
|
|
msg := "Do you want to create custom roles in the database cluster?"
|
|
if shouldPush, err := utils.NewConsole().PromptYesNo(ctx, msg, true); err != nil {
|
|
return err
|
|
} else if !shouldPush {
|
|
return errors.New(context.Canceled)
|
|
}
|
|
if err := migration.SeedGlobals(ctx, globals, conn, afero.NewIOFS(fsys)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if len(pending) > 0 {
|
|
msg := fmt.Sprintf("Do you want to push these migrations to the remote database?\n%s\n", confirmPushAll(pending))
|
|
if shouldPush, err := utils.NewConsole().PromptYesNo(ctx, msg, true); err != nil {
|
|
return err
|
|
} else if !shouldPush {
|
|
return errors.New(context.Canceled)
|
|
}
|
|
if err := vault.UpsertVaultSecrets(ctx, utils.Config.Db.Vault, conn); err != nil {
|
|
return err
|
|
}
|
|
if err := migration.ApplyMigrations(ctx, pending, conn, afero.NewIOFS(fsys)); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
fmt.Fprintln(os.Stderr, "Schema migrations are up to date.")
|
|
}
|
|
if len(seeds) > 0 {
|
|
msg := fmt.Sprintf("Do you want to seed the remote database with these files?\n%s\n", confirmSeedAll(seeds))
|
|
if shouldPush, err := utils.NewConsole().PromptYesNo(ctx, msg, true); err != nil {
|
|
return err
|
|
} else if !shouldPush {
|
|
return errors.New(context.Canceled)
|
|
}
|
|
if err := migration.SeedData(ctx, seeds, conn, afero.NewIOFS(fsys)); err != nil {
|
|
return err
|
|
}
|
|
} else if includeSeed {
|
|
fmt.Fprintln(os.Stderr, "Seed files are up to date.")
|
|
}
|
|
}
|
|
fmt.Println("Finished " + utils.Aqua("supabase db push") + ".")
|
|
return nil
|
|
}
|
|
|
|
func confirmPushAll(pending []string) (msg string) {
|
|
for _, path := range pending {
|
|
filename := filepath.Base(path)
|
|
msg += fmt.Sprintf(" • %s\n", utils.Bold(filename))
|
|
}
|
|
return msg
|
|
}
|
|
|
|
func confirmSeedAll(pending []migration.SeedFile) (msg string) {
|
|
for _, seed := range pending {
|
|
notice := seed.Path
|
|
if seed.Dirty {
|
|
notice += " (hash update)"
|
|
}
|
|
msg += fmt.Sprintf(" • %s\n", utils.Bold(notice))
|
|
}
|
|
return msg
|
|
}
|