github.com/Redstoneguy129/cli@v0.0.0-20230211220159-15dca4e91917/internal/db/diff/diff.go (about)

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