github.com/supabase/cli@v1.168.1/internal/db/diff/pgadmin.go (about)

     1  package diff
     2  
     3  import (
     4  	"context"
     5  	_ "embed"
     6  	"fmt"
     7  	"os"
     8  
     9  	"github.com/go-errors/errors"
    10  	"github.com/jackc/pgconn"
    11  	"github.com/spf13/afero"
    12  	"github.com/supabase/cli/internal/db/start"
    13  	"github.com/supabase/cli/internal/migration/new"
    14  	"github.com/supabase/cli/internal/utils"
    15  )
    16  
    17  var warnDiff = `WARNING: The diff tool is not foolproof, so you may need to manually rearrange and modify the generated migration.
    18  Run ` + utils.Aqua("supabase db reset") + ` to verify that the new migration does not generate errors.`
    19  
    20  func SaveDiff(out, file string, fsys afero.Fs) error {
    21  	if len(out) < 2 {
    22  		fmt.Fprintln(os.Stderr, "No schema changes found")
    23  	} else if len(file) > 0 {
    24  		path := new.GetMigrationPath(utils.GetCurrentTimestamp(), file)
    25  		if err := utils.WriteFile(path, []byte(out), fsys); err != nil {
    26  			return err
    27  		}
    28  		fmt.Fprintln(os.Stderr, warnDiff)
    29  	} else {
    30  		fmt.Println(out)
    31  	}
    32  	return nil
    33  }
    34  
    35  func RunPgAdmin(ctx context.Context, schema []string, file string, config pgconn.Config, fsys afero.Fs) error {
    36  	// Sanity checks.
    37  	{
    38  		if err := utils.LoadConfigFS(fsys); err != nil {
    39  			return err
    40  		}
    41  		if err := utils.AssertSupabaseDbIsRunning(); err != nil {
    42  			return err
    43  		}
    44  	}
    45  
    46  	if err := utils.RunProgram(ctx, func(p utils.Program, ctx context.Context) error {
    47  		return run(p, ctx, schema, config, fsys)
    48  	}); err != nil {
    49  		return err
    50  	}
    51  
    52  	return SaveDiff(output, file, fsys)
    53  }
    54  
    55  var output string
    56  
    57  func run(p utils.Program, ctx context.Context, schema []string, config pgconn.Config, fsys afero.Fs) error {
    58  	p.Send(utils.StatusMsg("Creating shadow database..."))
    59  
    60  	// 1. Create shadow db and run migrations
    61  	shadow, err := CreateShadowDatabase(ctx, utils.Config.Db.ShadowPort)
    62  	if err != nil {
    63  		return err
    64  	}
    65  	defer utils.DockerRemove(shadow)
    66  	if !start.WaitForHealthyService(ctx, shadow, start.HealthTimeout) {
    67  		return errors.New(start.ErrDatabase)
    68  	}
    69  	if err := MigrateShadowDatabase(ctx, shadow, fsys); err != nil {
    70  		return err
    71  	}
    72  
    73  	p.Send(utils.StatusMsg("Diffing local database with current migrations..."))
    74  
    75  	// 2. Diff local db (source) with shadow db (target), print it.
    76  	source := utils.ToPostgresURL(config)
    77  	target := fmt.Sprintf("postgresql://postgres:postgres@127.0.0.1:%d/postgres", utils.Config.Db.ShadowPort)
    78  	output, err = DiffSchemaPgAdmin(ctx, source, target, schema, p)
    79  	return err
    80  }
    81  
    82  func DiffSchemaPgAdmin(ctx context.Context, source, target string, schema []string, p utils.Program) (string, error) {
    83  	stream := utils.NewDiffStream(p)
    84  	args := []string{"--json-diff", source, target}
    85  	if len(schema) == 0 {
    86  		if err := utils.DockerRunOnceWithStream(
    87  			ctx,
    88  			utils.DifferImage,
    89  			nil,
    90  			args,
    91  			stream.Stdout(),
    92  			stream.Stderr(),
    93  		); err != nil {
    94  			return "", err
    95  		}
    96  	}
    97  	for _, s := range schema {
    98  		p.Send(utils.StatusMsg("Diffing schema: " + s))
    99  		if err := utils.DockerRunOnceWithStream(
   100  			ctx,
   101  			utils.DifferImage,
   102  			nil,
   103  			append([]string{"--schema", s}, args...),
   104  			stream.Stdout(),
   105  			stream.Stderr(),
   106  		); err != nil {
   107  			return "", err
   108  		}
   109  	}
   110  	diffBytes, err := stream.Collect()
   111  	return string(diffBytes), err
   112  }