github.com/golang-Migrate/Migrate@v3.5.4+incompatible/cli/main.go (about) 1 package main 2 3 import ( 4 "flag" 5 "fmt" 6 "os" 7 "os/signal" 8 "strconv" 9 "strings" 10 "syscall" 11 "time" 12 13 "github.com/golang-migrate/migrate" 14 "github.com/golang-migrate/migrate/database" 15 "github.com/golang-migrate/migrate/source" 16 ) 17 18 const defaultTimeFormat = "20060102150405" 19 20 // set main log 21 var log = &Log{} 22 23 func main() { 24 helpPtr := flag.Bool("help", false, "") 25 versionPtr := flag.Bool("version", false, "") 26 verbosePtr := flag.Bool("verbose", false, "") 27 prefetchPtr := flag.Uint("prefetch", 10, "") 28 lockTimeoutPtr := flag.Uint("lock-timeout", 15, "") 29 pathPtr := flag.String("path", "", "") 30 databasePtr := flag.String("database", "", "") 31 sourcePtr := flag.String("source", "", "") 32 33 flag.Usage = func() { 34 fmt.Fprint(os.Stderr, 35 `Usage: migrate OPTIONS COMMAND [arg...] 36 migrate [ -version | -help ] 37 38 Options: 39 -source Location of the migrations (driver://url) 40 -path Shorthand for -source=file://path 41 -database Run migrations against this database (driver://url) 42 -prefetch N Number of migrations to load in advance before executing (default 10) 43 -lock-timeout N Allow N seconds to acquire database lock (default 15) 44 -verbose Print verbose logging 45 -version Print version 46 -help Print usage 47 48 Commands: 49 create [-ext E] [-dir D] [-seq] [-digits N] [-format] NAME 50 Create a set of timestamped up/down migrations titled NAME, in directory D with extension E. 51 Use -seq option to generate sequential up/down migrations with N digits. 52 Use -format option to specify a Go time format string. 53 goto V Migrate to version V 54 up [N] Apply all or N up migrations 55 down [N] Apply all or N down migrations 56 drop Drop everyting inside database 57 force V Set version V but don't run migration (ignores dirty state) 58 version Print current migration version 59 60 Source drivers: `+strings.Join(source.List(), ", ")+` 61 Database drivers: `+strings.Join(database.List(), ", ")+"\n") 62 } 63 64 flag.Parse() 65 66 // initialize logger 67 log.verbose = *verbosePtr 68 69 // show cli version 70 if *versionPtr { 71 fmt.Fprintln(os.Stderr, Version) 72 os.Exit(0) 73 } 74 75 // show help 76 if *helpPtr { 77 flag.Usage() 78 os.Exit(0) 79 } 80 81 // translate -path into -source if given 82 if *sourcePtr == "" && *pathPtr != "" { 83 *sourcePtr = fmt.Sprintf("file://%v", *pathPtr) 84 } 85 86 // initialize migrate 87 // don't catch migraterErr here and let each command decide 88 // how it wants to handle the error 89 migrater, migraterErr := migrate.New(*sourcePtr, *databasePtr) 90 defer func() { 91 if migraterErr == nil { 92 migrater.Close() 93 } 94 }() 95 if migraterErr == nil { 96 migrater.Log = log 97 migrater.PrefetchMigrations = *prefetchPtr 98 migrater.LockTimeout = time.Duration(int64(*lockTimeoutPtr)) * time.Second 99 100 // handle Ctrl+c 101 signals := make(chan os.Signal, 1) 102 signal.Notify(signals, syscall.SIGINT) 103 go func() { 104 for range signals { 105 log.Println("Stopping after this running migration ...") 106 migrater.GracefulStop <- true 107 return 108 } 109 }() 110 } 111 112 startTime := time.Now() 113 114 switch flag.Arg(0) { 115 case "create": 116 args := flag.Args()[1:] 117 seq := false 118 seqDigits := 6 119 120 createFlagSet := flag.NewFlagSet("create", flag.ExitOnError) 121 extPtr := createFlagSet.String("ext", "", "File extension") 122 dirPtr := createFlagSet.String("dir", "", "Directory to place file in (default: current working directory)") 123 formatPtr := createFlagSet.String("format", defaultTimeFormat, `The Go time format string to use. If the string "unix" or "unixNano" is specified, then the seconds or nanoseconds since January 1, 1970 UTC respectively will be used. Caution, due to the behavior of time.Time.Format(), invalid format strings will not error`) 124 createFlagSet.BoolVar(&seq, "seq", seq, "Use sequential numbers instead of timestamps (default: false)") 125 createFlagSet.IntVar(&seqDigits, "digits", seqDigits, "The number of digits to use in sequences (default: 6)") 126 createFlagSet.Parse(args) 127 128 if createFlagSet.NArg() == 0 { 129 log.fatal("error: please specify name") 130 } 131 name := createFlagSet.Arg(0) 132 133 if *extPtr == "" { 134 log.fatal("error: -ext flag must be specified") 135 } 136 *extPtr = "." + strings.TrimPrefix(*extPtr, ".") 137 138 if *dirPtr != "" { 139 *dirPtr = strings.Trim(*dirPtr, "/") + "/" 140 } 141 142 createCmd(*dirPtr, startTime, *formatPtr, name, *extPtr, seq, seqDigits) 143 144 case "goto": 145 if migraterErr != nil { 146 log.fatalErr(migraterErr) 147 } 148 149 if flag.Arg(1) == "" { 150 log.fatal("error: please specify version argument V") 151 } 152 153 v, err := strconv.ParseUint(flag.Arg(1), 10, 64) 154 if err != nil { 155 log.fatal("error: can't read version argument V") 156 } 157 158 gotoCmd(migrater, uint(v)) 159 160 if log.verbose { 161 log.Println("Finished after", time.Now().Sub(startTime)) 162 } 163 164 case "up": 165 if migraterErr != nil { 166 log.fatalErr(migraterErr) 167 } 168 169 limit := -1 170 if flag.Arg(1) != "" { 171 n, err := strconv.ParseUint(flag.Arg(1), 10, 64) 172 if err != nil { 173 log.fatal("error: can't read limit argument N") 174 } 175 limit = int(n) 176 } 177 178 upCmd(migrater, limit) 179 180 if log.verbose { 181 log.Println("Finished after", time.Now().Sub(startTime)) 182 } 183 184 case "down": 185 if migraterErr != nil { 186 log.fatalErr(migraterErr) 187 } 188 189 limit := -1 190 if flag.Arg(1) != "" { 191 n, err := strconv.ParseUint(flag.Arg(1), 10, 64) 192 if err != nil { 193 log.fatal("error: can't read limit argument N") 194 } 195 limit = int(n) 196 } 197 198 downCmd(migrater, limit) 199 200 if log.verbose { 201 log.Println("Finished after", time.Now().Sub(startTime)) 202 } 203 204 case "drop": 205 if migraterErr != nil { 206 log.fatalErr(migraterErr) 207 } 208 209 dropCmd(migrater) 210 211 if log.verbose { 212 log.Println("Finished after", time.Now().Sub(startTime)) 213 } 214 215 case "force": 216 if migraterErr != nil { 217 log.fatalErr(migraterErr) 218 } 219 220 if flag.Arg(1) == "" { 221 log.fatal("error: please specify version argument V") 222 } 223 224 v, err := strconv.ParseInt(flag.Arg(1), 10, 64) 225 if err != nil { 226 log.fatal("error: can't read version argument V") 227 } 228 229 if v < -1 { 230 log.fatal("error: argument V must be >= -1") 231 } 232 233 forceCmd(migrater, int(v)) 234 235 if log.verbose { 236 log.Println("Finished after", time.Now().Sub(startTime)) 237 } 238 239 case "version": 240 if migraterErr != nil { 241 log.fatalErr(migraterErr) 242 } 243 244 versionCmd(migrater) 245 246 default: 247 flag.Usage() 248 os.Exit(0) 249 } 250 }