Skip to content

Commit

Permalink
feat: supabase db changes
Browse files Browse the repository at this point in the history
  • Loading branch information
soedirgo committed Nov 25, 2021
1 parent 48b12d2 commit a71844a
Show file tree
Hide file tree
Showing 2 changed files with 248 additions and 0 deletions.
10 changes: 10 additions & 0 deletions cmd/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"github.com/spf13/cobra"
"github.com/supabase/cli/internal/db/changes"
"github.com/supabase/cli/internal/db/commit"
"github.com/supabase/cli/internal/db/reset"
)
Expand All @@ -14,6 +15,14 @@ var (
Short: "Commit or reset the local database.",
}

dbChangesCmd = &cobra.Command{
Use: "changes",
Short: "Diffs the local database with current migrations, then print the diff to standard output.",
RunE: func(cmd *cobra.Command, args []string) error {
return changes.Run()
},
}

dbCommitCmd = &cobra.Command{
Use: "commit",
Short: "Diffs the local database with current migrations, writing it as a new migration.",
Expand All @@ -35,6 +44,7 @@ func init() {
dbCommitCmd.Flags().StringVar(&migrationName, "name", "", "Name of the migration.")
cobra.CheckErr(dbCommitCmd.MarkFlagRequired("name"))

dbCmd.AddCommand(dbChangesCmd)
dbCmd.AddCommand(dbCommitCmd)
dbCmd.AddCommand(dbResetCmd)
rootCmd.AddCommand(dbCmd)
Expand Down
238 changes: 238 additions & 0 deletions internal/db/changes/changes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
package changes

import (
"bytes"
"context"
"errors"
"fmt"
"io"
"os"
"strings"

"github.com/charmbracelet/bubbles/progress"
"github.com/charmbracelet/bubbles/spinner"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"github.com/docker/docker/pkg/stdcopy"
"github.com/supabase/cli/internal/utils"
)

// TODO: Handle cleanup on SIGINT/SIGTERM.
func Run() error {
// Sanity checks.
{
utils.LoadConfig()
utils.AssertSupabaseStartIsRunning()

if branch, err := utils.GetCurrentBranch(); err != nil {
return err
} else {
currBranch = branch
}
}

s := spinner.NewModel()
s.Spinner = spinner.Dot
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205"))
p := tea.NewProgram(model{spinner: s})

errCh := make(chan error, 1)
go func() {
errCh <- run(p)
p.Send(tea.Quit())
}()

if err := p.Start(); err != nil {
return err
}
if errors.Is(ctx.Err(), context.Canceled) {
return errors.New("Aborted `supabase db changes`.")
}
if err := <-errCh; err != nil {
return err
}

return nil
}

type model struct {
spinner spinner.Model
status string
progress *progress.Model
psqlOutputs []string
}

func (m model) Init() tea.Cmd {
return spinner.Tick
}

func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.Type {
case tea.KeyCtrlC:
// Stop future runs
cancelCtx()
return m, tea.Quit
default:
return m, nil
}
case spinner.TickMsg:
spinnerModel, cmd := m.spinner.Update(msg)
m.spinner = spinnerModel
return m, cmd
case progress.FrameMsg:
if m.progress == nil {
return m, nil
}

tmp, cmd := m.progress.Update(msg)
progressModel := tmp.(progress.Model)
m.progress = &progressModel
return m, cmd
case utils.StatusMsg:
m.status = string(msg)
return m, nil
case utils.ProgressMsg:
if msg == nil {
m.progress = nil
return m, nil
}

if m.progress == nil {
progressModel := progress.NewModel(progress.WithDefaultGradient())
m.progress = &progressModel
}

return m, m.progress.SetPercent(*msg)
case utils.PsqlMsg:
if msg == nil {
m.psqlOutputs = []string{}
return m, nil
}

m.psqlOutputs = append(m.psqlOutputs, *msg)
if len(m.psqlOutputs) > 5 {
m.psqlOutputs = m.psqlOutputs[1:]
}
return m, nil
default:
return m, nil
}
}

func (m model) View() string {
var progress string
if m.progress != nil {
progress = "\n\n" + m.progress.View()
}

var psqlOutputs string
if len(m.psqlOutputs) > 0 {
psqlOutputs = "\n\n" + strings.Join(m.psqlOutputs, "\n")
}

return m.spinner.View() + m.status + progress + psqlOutputs
}

var (
ctx, cancelCtx = context.WithCancel(context.Background())

currBranch string
)

func run(p *tea.Program) error {
p.Send(utils.StatusMsg("Creating shadow database..."))

// 1. Create shadow db and run migrations
{
out, err := utils.DockerExec(
ctx,
utils.DbId,
[]string{"createdb", "--username", "postgres", utils.ShadowDbName},
)
if err != nil {
return err
}
var errBuf bytes.Buffer
if _, err := stdcopy.StdCopy(io.Discard, &errBuf, out); err != nil {
return err
}
if errBuf.Len() > 0 {
return errors.New("Error creating shadow database: " + errBuf.String())
}

migrations, err := os.ReadDir("supabase/migrations")
if err != nil {
return err
}

for _, migration := range migrations {
p.Send(utils.StatusMsg("Applying migration " + migration.Name() + "..."))

content, err := os.ReadFile("supabase/migrations/" + migration.Name())
if err != nil {
return err
}

out, err := utils.DockerExec(ctx, utils.DbId, []string{
"sh", "-c", "psql --username postgres --dbname '" + utils.ShadowDbName + `' <<'EOSQL'
BEGIN;
` + string(content) + `
COMMIT;
EOSQL
`,
})
if err != nil {
return err
}
if err := utils.ProcessPsqlOutput(out, p); err != nil {
return err
}
}
}

p.Send(utils.StatusMsg("Diffing local database with current migrations..."))

// 2. Diff it (target) with local db (source), write it as a new migration.
{
out, err := utils.DockerExec(ctx, utils.DifferId, []string{
"sh", "-c", "/venv/bin/python3 -u cli.py " +
"'postgres://postgres:postgres@" + utils.DbId + ":5432/" + currBranch + "' " +
"'postgres://postgres:postgres@" + utils.DbId + ":5432/" + utils.ShadowDbName + "'",
})
if err != nil {
return err
}

diffBytes, err := utils.ProcessDiffOutput(p, out)
if err != nil {
return err
}

fmt.Println(string(diffBytes))
}

p.Send(utils.StatusMsg("Dropping shadow database..."))

// 3. Drop shadow db.
{
out, err := utils.DockerExec(
ctx,
utils.DbId,
[]string{"dropdb", "--username", "postgres", utils.ShadowDbName},
)
if err != nil {
return err
}
var errBuf bytes.Buffer
if _, err := stdcopy.StdCopy(io.Discard, &errBuf, out); err != nil {
return err
}
if errBuf.Len() > 0 {
return errors.New("Error dropping shadow database: " + errBuf.String())
}
}

return nil
}

0 comments on commit a71844a

Please sign in to comment.