github.com/bishtawi/migrate/v4@v4.8.11/internal/cli/commands.go (about) 1 package cli 2 3 import ( 4 "errors" 5 "fmt" 6 "github.com/bishtawi/migrate/v4" 7 _ "github.com/bishtawi/migrate/v4/database/stub" // TODO remove again 8 _ "github.com/bishtawi/migrate/v4/source/file" 9 "os" 10 "path/filepath" 11 "strconv" 12 "strings" 13 "time" 14 ) 15 16 func nextSeq(matches []string, dir string, seqDigits int) (string, error) { 17 if seqDigits <= 0 { 18 return "", errors.New("Digits must be positive") 19 } 20 21 nextSeq := 1 22 if len(matches) > 0 { 23 filename := matches[len(matches)-1] 24 matchSeqStr := strings.TrimPrefix(filename, dir) 25 idx := strings.Index(matchSeqStr, "_") 26 if idx < 1 { // Using 1 instead of 0 since there should be at least 1 digit 27 return "", errors.New("Malformed migration filename: " + filename) 28 } 29 matchSeqStr = matchSeqStr[0:idx] 30 var err error 31 nextSeq, err = strconv.Atoi(matchSeqStr) 32 if err != nil { 33 return "", err 34 } 35 nextSeq++ 36 } 37 if nextSeq <= 0 { 38 return "", errors.New("Next sequence number must be positive") 39 } 40 41 nextSeqStr := strconv.Itoa(nextSeq) 42 if len(nextSeqStr) > seqDigits { 43 return "", fmt.Errorf("Next sequence number %s too large. At most %d digits are allowed", nextSeqStr, seqDigits) 44 } 45 padding := seqDigits - len(nextSeqStr) 46 if padding > 0 { 47 nextSeqStr = strings.Repeat("0", padding) + nextSeqStr 48 } 49 return nextSeqStr, nil 50 } 51 52 // cleanDir normalizes the provided directory 53 func cleanDir(dir string) string { 54 dir = filepath.Clean(dir) 55 switch dir { 56 case ".": 57 return "" 58 case "/": 59 return dir 60 default: 61 return dir + "/" 62 } 63 } 64 65 // createCmd (meant to be called via a CLI command) creates a new migration 66 func createCmd(dir string, startTime time.Time, format string, name string, ext string, seq bool, seqDigits int) { 67 dir = cleanDir(dir) 68 var base string 69 if seq && format != defaultTimeFormat { 70 log.fatalErr(errors.New("The seq and format options are mutually exclusive")) 71 } 72 if seq { 73 if seqDigits <= 0 { 74 log.fatalErr(errors.New("Digits must be positive")) 75 } 76 matches, err := filepath.Glob(dir + "*" + ext) 77 if err != nil { 78 log.fatalErr(err) 79 } 80 nextSeqStr, err := nextSeq(matches, dir, seqDigits) 81 if err != nil { 82 log.fatalErr(err) 83 } 84 base = fmt.Sprintf("%v%v_%v.", dir, nextSeqStr, name) 85 } else { 86 switch format { 87 case "": 88 log.fatal("Time format may not be empty") 89 case "unix": 90 base = fmt.Sprintf("%v%v_%v.", dir, startTime.Unix(), name) 91 case "unixNano": 92 base = fmt.Sprintf("%v%v_%v.", dir, startTime.UnixNano(), name) 93 default: 94 base = fmt.Sprintf("%v%v_%v.", dir, startTime.Format(format), name) 95 } 96 } 97 98 if err := os.MkdirAll(dir, os.ModePerm); err != nil { 99 log.fatalErr(err) 100 } 101 102 createFile(base + "up" + ext) 103 createFile(base + "down" + ext) 104 } 105 106 func createFile(fname string) { 107 if _, err := os.Create(fname); err != nil { 108 log.fatalErr(err) 109 } 110 } 111 112 func gotoCmd(m *migrate.Migrate, v uint) { 113 if err := m.Migrate(v); err != nil { 114 if err != migrate.ErrNoChange { 115 log.fatalErr(err) 116 } else { 117 log.Println(err) 118 } 119 } 120 } 121 122 func upCmd(m *migrate.Migrate, limit int) { 123 if limit >= 0 { 124 if err := m.Steps(limit); err != nil { 125 if err != migrate.ErrNoChange { 126 log.fatalErr(err) 127 } else { 128 log.Println(err) 129 } 130 } 131 } else { 132 if err := m.Up(); err != nil { 133 if err != migrate.ErrNoChange { 134 log.fatalErr(err) 135 } else { 136 log.Println(err) 137 } 138 } 139 } 140 } 141 142 func downCmd(m *migrate.Migrate, limit int) { 143 if limit >= 0 { 144 if err := m.Steps(-limit); err != nil { 145 if err != migrate.ErrNoChange { 146 log.fatalErr(err) 147 } else { 148 log.Println(err) 149 } 150 } 151 } else { 152 if err := m.Down(); err != nil { 153 if err != migrate.ErrNoChange { 154 log.fatalErr(err) 155 } else { 156 log.Println(err) 157 } 158 } 159 } 160 } 161 162 func dropCmd(m *migrate.Migrate) { 163 if err := m.Drop(); err != nil { 164 log.fatalErr(err) 165 } 166 } 167 168 func forceCmd(m *migrate.Migrate, v int) { 169 if err := m.Force(v); err != nil { 170 log.fatalErr(err) 171 } 172 } 173 174 func versionCmd(m *migrate.Migrate) { 175 v, dirty, err := m.Version() 176 if err != nil { 177 log.fatalErr(err) 178 } 179 if dirty { 180 log.Printf("%v (dirty)\n", v) 181 } else { 182 log.Println(v) 183 } 184 } 185 186 // numDownMigrationsFromArgs returns an int for number of migrations to apply 187 // and a bool indicating if we need a confirm before applying 188 func numDownMigrationsFromArgs(applyAll bool, args []string) (int, bool, error) { 189 if applyAll { 190 if len(args) > 0 { 191 return 0, false, errors.New("-all cannot be used with other arguments") 192 } 193 return -1, false, nil 194 } 195 196 switch len(args) { 197 case 0: 198 return -1, true, nil 199 case 1: 200 downValue := args[0] 201 n, err := strconv.ParseUint(downValue, 10, 64) 202 if err != nil { 203 return 0, false, errors.New("can't read limit argument N") 204 } 205 return int(n), false, nil 206 default: 207 return 0, false, errors.New("too many arguments") 208 } 209 }