github.com/Redstoneguy129/cli@v0.0.0-20230211220159-15dca4e91917/internal/db/branch/switch_/switch_.go (about) 1 package switch_ 2 3 import ( 4 "context" 5 "errors" 6 "fmt" 7 "os" 8 "path/filepath" 9 10 "github.com/jackc/pgx/v4" 11 "github.com/spf13/afero" 12 "github.com/Redstoneguy129/cli/internal/db/reset" 13 "github.com/Redstoneguy129/cli/internal/utils" 14 ) 15 16 func Run(ctx context.Context, target string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { 17 // 1. Sanity checks 18 { 19 if err := utils.LoadConfigFS(fsys); err != nil { 20 return err 21 } 22 if err := utils.AssertSupabaseDbIsRunning(); err != nil { 23 return err 24 } 25 if target != "main" && utils.IsBranchNameReserved(target) { 26 return errors.New("Cannot switch branch " + utils.Aqua(target) + ": branch name is reserved.") 27 } 28 branchPath := filepath.Join(filepath.Dir(utils.CurrBranchPath), target) 29 if _, err := fsys.Stat(branchPath); errors.Is(err, os.ErrNotExist) { 30 return errors.New("Branch " + utils.Aqua(target) + " does not exist.") 31 } else if err != nil { 32 return err 33 } 34 } 35 36 // 2. Check current branch 37 currBranch, err := utils.GetCurrentBranchFS(fsys) 38 if err != nil { 39 // Assume we are on main branch 40 currBranch = "main" 41 } 42 43 // 3. Switch Postgres database 44 if currBranch == target { 45 fmt.Println("Already on branch " + utils.Aqua(target) + ".") 46 } else if err := switchDatabase(ctx, currBranch, target, options...); err != nil { 47 return errors.New("Error switching to branch " + utils.Aqua(target) + ": " + err.Error()) 48 } else { 49 fmt.Println("Switched to branch " + utils.Aqua(target) + ".") 50 } 51 52 // 4. Update current branch 53 if err := afero.WriteFile(fsys, utils.CurrBranchPath, []byte(target), 0644); err != nil { 54 return errors.New("Unable to update local branch file. Fix by running: echo '" + target + "' > " + utils.CurrBranchPath) 55 } 56 return nil 57 } 58 59 func switchDatabase(ctx context.Context, source, target string, options ...func(*pgx.ConnConfig)) error { 60 conn, err := utils.ConnectLocalPostgres(ctx, utils.Config.Hostname, utils.Config.Db.Port, "template1", options...) 61 if err != nil { 62 return err 63 } 64 defer conn.Close(context.Background()) 65 if err := reset.DisconnectClients(ctx, conn); err != nil { 66 return err 67 } 68 defer reset.RestartDatabase(context.Background()) 69 backup := "ALTER DATABASE postgres RENAME TO " + source + ";" 70 if _, err := conn.Exec(ctx, backup); err != nil { 71 return err 72 } 73 rename := "ALTER DATABASE " + target + " RENAME TO postgres;" 74 if _, err := conn.Exec(ctx, rename); err != nil { 75 rollback := "ALTER DATABASE " + source + " RENAME TO postgres;" 76 if _, err := conn.Exec(ctx, rollback); err != nil { 77 fmt.Fprintln(os.Stderr, "Failed to rollback database:", err) 78 } 79 return err 80 } 81 return nil 82 }