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