108 lines
2.9 KiB
Go
108 lines
2.9 KiB
Go
package diff
|
|
|
|
import (
|
|
"context"
|
|
_ "embed"
|
|
"fmt"
|
|
"os"
|
|
|
|
"github.com/jackc/pgconn"
|
|
"github.com/spf13/afero"
|
|
"github.com/supabase/cli/internal/db/start"
|
|
"github.com/supabase/cli/internal/migration/new"
|
|
"github.com/supabase/cli/internal/utils"
|
|
"github.com/supabase/cli/pkg/config"
|
|
)
|
|
|
|
var warnDiff = `WARNING: The diff tool is not foolproof, so you may need to manually rearrange and modify the generated migration.
|
|
Run ` + utils.Aqua("supabase db reset") + ` to verify that the new migration does not generate errors.`
|
|
|
|
func SaveDiff(out, file string, fsys afero.Fs) error {
|
|
if len(out) < 2 {
|
|
fmt.Fprintln(os.Stderr, "No schema changes found")
|
|
} else if len(file) > 0 {
|
|
path := new.GetMigrationPath(utils.GetCurrentTimestamp(), file)
|
|
if err := utils.WriteFile(path, []byte(out), fsys); err != nil {
|
|
return err
|
|
}
|
|
fmt.Fprintln(os.Stderr, warnDiff)
|
|
} else {
|
|
fmt.Println(out)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func RunPgAdmin(ctx context.Context, schema []string, file string, config pgconn.Config, fsys afero.Fs) error {
|
|
// Sanity checks.
|
|
if err := utils.AssertSupabaseDbIsRunning(); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := utils.RunProgram(ctx, func(p utils.Program, ctx context.Context) error {
|
|
return run(p, ctx, schema, config, fsys)
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
return SaveDiff(output, file, fsys)
|
|
}
|
|
|
|
var output string
|
|
|
|
func run(p utils.Program, ctx context.Context, schema []string, config pgconn.Config, fsys afero.Fs) error {
|
|
p.Send(utils.StatusMsg("Creating shadow database..."))
|
|
|
|
// 1. Create shadow db and run migrations
|
|
shadow, err := CreateShadowDatabase(ctx, utils.Config.Db.ShadowPort)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer utils.DockerRemove(shadow)
|
|
if err := start.WaitForHealthyService(ctx, start.HealthTimeout, shadow); err != nil {
|
|
return err
|
|
}
|
|
if err := MigrateShadowDatabase(ctx, shadow, fsys); err != nil {
|
|
return err
|
|
}
|
|
|
|
p.Send(utils.StatusMsg("Diffing local database with current migrations..."))
|
|
|
|
// 2. Diff local db (source) with shadow db (target), print it.
|
|
source := utils.ToPostgresURL(config)
|
|
target := fmt.Sprintf("postgresql://postgres:postgres@127.0.0.1:%d/postgres", utils.Config.Db.ShadowPort)
|
|
output, err = DiffSchemaPgAdmin(ctx, source, target, schema, p)
|
|
return err
|
|
}
|
|
|
|
func DiffSchemaPgAdmin(ctx context.Context, source, target string, schema []string, p utils.Program) (string, error) {
|
|
stream := utils.NewDiffStream(p)
|
|
args := []string{"--json-diff", source, target}
|
|
if len(schema) == 0 {
|
|
if err := utils.DockerRunOnceWithStream(
|
|
ctx,
|
|
config.Images.Differ,
|
|
nil,
|
|
args,
|
|
stream.Stdout(),
|
|
stream.Stderr(),
|
|
); err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
for _, s := range schema {
|
|
p.Send(utils.StatusMsg("Diffing schema: " + s))
|
|
if err := utils.DockerRunOnceWithStream(
|
|
ctx,
|
|
config.Images.Differ,
|
|
nil,
|
|
append([]string{"--schema", s}, args...),
|
|
stream.Stdout(),
|
|
stream.Stderr(),
|
|
); err != nil {
|
|
return "", err
|
|
}
|
|
}
|
|
diffBytes, err := stream.Collect()
|
|
return string(diffBytes), err
|
|
}
|