github.com/supabase/cli@v1.168.1/internal/db/branch/create/create.go (about) 1 package create 2 3 import ( 4 "bytes" 5 "context" 6 _ "embed" 7 "fmt" 8 "io" 9 "os" 10 "path/filepath" 11 12 "github.com/docker/docker/api/types" 13 "github.com/docker/docker/pkg/stdcopy" 14 "github.com/go-errors/errors" 15 "github.com/spf13/afero" 16 "github.com/supabase/cli/internal/utils" 17 ) 18 19 var ( 20 //go:embed templates/clone.sh 21 cloneScript string 22 ) 23 24 func Run(branch string, fsys afero.Fs) error { 25 if err := utils.LoadConfigFS(fsys); err != nil { 26 return err 27 } 28 if err := utils.AssertSupabaseDbIsRunning(); err != nil { 29 return err 30 } 31 32 branchPath := filepath.Join(filepath.Dir(utils.CurrBranchPath), branch) 33 if err := assertNewBranchIsValid(branchPath, fsys); err != nil { 34 return nil 35 } 36 37 var ctx = context.Background() 38 if err := createBranch(ctx, branch); err != nil { 39 return err 40 } 41 42 if err := fsys.MkdirAll(branchPath, 0755); err != nil { 43 return err 44 } 45 46 fmt.Println("Created branch " + utils.Aqua(branch) + ".") 47 return nil 48 } 49 50 func assertNewBranchIsValid(branchPath string, fsys afero.Fs) error { 51 branch := filepath.Base(branchPath) 52 53 if utils.IsBranchNameReserved(branch) { 54 return errors.New("Cannot create branch " + utils.Aqua(branch) + ": branch name is reserved.") 55 } 56 57 if !utils.BranchNamePattern.MatchString(branch) { 58 return errors.New("Branch name " + utils.Aqua(branch) + " is invalid. Must match [0-9A-Za-z_-]+.") 59 } 60 61 if _, err := afero.ReadDir(fsys, branchPath); errors.Is(err, os.ErrNotExist) { 62 // skip 63 } else if err != nil { 64 return err 65 } else { 66 return errors.New("Branch " + utils.Aqua(branch) + " already exists.") 67 } 68 69 return nil 70 } 71 72 func createBranch(ctx context.Context, branch string) error { 73 exec, err := utils.Docker.ContainerExecCreate(ctx, utils.DbId, types.ExecConfig{ 74 Cmd: []string{"/bin/bash", "-c", cloneScript}, 75 Env: []string{"DB_NAME=" + branch}, 76 AttachStderr: true, 77 AttachStdout: true, 78 }) 79 if err != nil { 80 return err 81 } 82 // Read exec output 83 resp, err := utils.Docker.ContainerExecAttach(ctx, exec.ID, types.ExecStartCheck{}) 84 if err != nil { 85 return err 86 } 87 defer resp.Close() 88 // Capture error details 89 var errBuf bytes.Buffer 90 if _, err := stdcopy.StdCopy(io.Discard, &errBuf, resp.Reader); err != nil { 91 return err 92 } 93 // Get the exit code 94 iresp, err := utils.Docker.ContainerExecInspect(ctx, exec.ID) 95 if err != nil { 96 return err 97 } 98 if iresp.ExitCode > 0 { 99 return errors.New("Error creating branch: " + errBuf.String()) 100 } 101 return nil 102 }