github.com/supabase/cli@v1.168.1/internal/migration/up/up.go (about) 1 package up 2 3 import ( 4 "context" 5 "fmt" 6 "path/filepath" 7 "strings" 8 9 "github.com/go-errors/errors" 10 "github.com/jackc/pgconn" 11 "github.com/jackc/pgx/v4" 12 "github.com/spf13/afero" 13 "github.com/supabase/cli/internal/migration/apply" 14 "github.com/supabase/cli/internal/migration/list" 15 "github.com/supabase/cli/internal/utils" 16 ) 17 18 var ( 19 errMissingRemote = errors.New("Found local migration files to be inserted before the last migration on remote database.") 20 errMissingLocal = errors.New("Remote migration versions not found in " + utils.MigrationsDir + " directory.") 21 ) 22 23 func Run(ctx context.Context, includeAll bool, config pgconn.Config, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { 24 conn, err := utils.ConnectByConfig(ctx, config, options...) 25 if err != nil { 26 return err 27 } 28 defer conn.Close(context.Background()) 29 pending, err := GetPendingMigrations(ctx, includeAll, conn, fsys) 30 if err != nil { 31 return err 32 } 33 return apply.MigrateUp(ctx, conn, pending, fsys) 34 } 35 36 func GetPendingMigrations(ctx context.Context, includeAll bool, conn *pgx.Conn, fsys afero.Fs) ([]string, error) { 37 remoteMigrations, err := list.LoadRemoteMigrations(ctx, conn) 38 if err != nil { 39 return nil, err 40 } 41 localMigrations, err := list.LoadLocalMigrations(fsys) 42 if err != nil { 43 return nil, err 44 } 45 // Find unapplied local migrations older than the latest migration on 46 // remote, and remote migrations that are missing from local. 47 var unapplied, missing []string 48 i, j := 0, 0 49 for i < len(remoteMigrations) && j < len(localMigrations) { 50 remote := remoteMigrations[i] 51 filename := localMigrations[j] 52 // Check if migration has been applied before, LoadLocalMigrations guarantees a match 53 local := utils.MigrateFilePattern.FindStringSubmatch(filename)[1] 54 if remote == local { 55 j++ 56 i++ 57 } else if remote < local { 58 missing = append(missing, remote) 59 i++ 60 } else { 61 // Include out-of-order local migrations 62 unapplied = append(unapplied, filename) 63 j++ 64 } 65 } 66 // Ensure all remote versions exist on local 67 if j == len(localMigrations) { 68 missing = append(missing, remoteMigrations[i:]...) 69 } 70 if len(missing) > 0 { 71 utils.CmdSuggestion = suggestRevertHistory(missing) 72 return nil, errMissingLocal 73 } 74 // Enforce migrations are applied in chronological order by default 75 if !includeAll && len(unapplied) > 0 { 76 utils.CmdSuggestion = suggestIgnoreFlag(unapplied) 77 return nil, errMissingRemote 78 } 79 pending := localMigrations[len(remoteMigrations)+len(unapplied):] 80 return append(unapplied, pending...), nil 81 } 82 83 func suggestRevertHistory(versions []string) string { 84 result := fmt.Sprintln("\nMake sure your local git repo is up-to-date. If the error persists, try repairing the migration history table:") 85 result += fmt.Sprintln(utils.Bold("supabase migration repair --status reverted " + strings.Join(versions, " "))) 86 result += fmt.Sprintln("\nAnd update local migrations to match remote database:") 87 result += fmt.Sprintln(utils.Bold("supabase db pull")) 88 return result 89 } 90 91 func suggestIgnoreFlag(filenames []string) string { 92 result := "\nRerun the command with --include-all flag to apply these migrations:\n" 93 for _, name := range filenames { 94 result += fmt.Sprintln(utils.Bold(filepath.Join(utils.MigrationsDir, name))) 95 } 96 return result 97 }