2017-02-07 22:01:29 -08:00
package main
import (
"flag"
"fmt"
"os"
"os/signal"
"strconv"
2017-09-17 13:13:45 +02:00
"strings"
2017-02-07 22:01:29 -08:00
"syscall"
"time"
2018-01-19 10:56:55 -08:00
"github.com/golang-migrate/migrate"
2017-02-07 22:01:29 -08:00
)
// set main log
var log = & Log { }
func main ( ) {
helpPtr := flag . Bool ( "help" , false , "" )
versionPtr := flag . Bool ( "version" , false , "" )
verbosePtr := flag . Bool ( "verbose" , false , "" )
prefetchPtr := flag . Uint ( "prefetch" , 10 , "" )
2017-02-11 19:15:54 -08:00
lockTimeoutPtr := flag . Uint ( "lock-timeout" , 15 , "" )
2017-02-07 22:01:29 -08:00
pathPtr := flag . String ( "path" , "" , "" )
databasePtr := flag . String ( "database" , "" , "" )
sourcePtr := flag . String ( "source" , "" , "" )
flag . Usage = func ( ) {
fmt . Fprint ( os . Stderr ,
` Usage : migrate OPTIONS COMMAND [ arg ... ]
migrate [ - version | - help ]
Options :
2017-02-11 19:15:54 -08:00
- source Location of the migrations ( driver : //url)
- path Shorthand for - source = file : //path
- database Run migrations against this database ( driver : //url)
- prefetch N Number of migrations to load in advance before executing ( default 10 )
- lock - timeout N Allow N seconds to acquire database lock ( default 15 )
- verbose Print verbose logging
- version Print version
- help Print usage
2017-02-07 22:01:29 -08:00
Commands :
2018-04-17 11:09:08 -06:00
create [ - ext E ] [ - dir D ] [ - seq ] [ - digits N ] [ - format ] NAME
2017-12-24 23:08:43 -08:00
Create a set of timestamped up / down migrations titled NAME , in directory D with extension E .
Use - seq option to generate sequential up / down migrations with N digits .
2018-04-17 11:09:08 -06:00
Use - format option to specify a Go time format string .
2017-02-07 22:01:29 -08:00
goto V Migrate to version V
up [ N ] Apply all or N up migrations
down [ N ] Apply all or N down migrations
drop Drop everyting inside database
2017-02-19 15:15:00 -08:00
force V Set version V but don ' t run migration ( ignores dirty state )
2017-02-07 22:01:29 -08:00
version Print current migration version
` )
}
flag . Parse ( )
// initialize logger
log . verbose = * verbosePtr
// show cli version
if * versionPtr {
fmt . Fprintln ( os . Stderr , Version )
os . Exit ( 0 )
}
// show help
if * helpPtr {
flag . Usage ( )
os . Exit ( 0 )
}
// translate -path into -source if given
if * sourcePtr == "" && * pathPtr != "" {
2017-03-01 11:48:38 -08:00
* sourcePtr = fmt . Sprintf ( "file://%v" , * pathPtr )
2017-02-07 22:01:29 -08:00
}
// initialize migrate
// don't catch migraterErr here and let each command decide
// how it wants to handle the error
migrater , migraterErr := migrate . New ( * sourcePtr , * databasePtr )
defer func ( ) {
if migraterErr == nil {
migrater . Close ( )
}
} ( )
if migraterErr == nil {
migrater . Log = log
migrater . PrefetchMigrations = * prefetchPtr
2017-02-11 19:15:54 -08:00
migrater . LockTimeout = time . Duration ( int64 ( * lockTimeoutPtr ) ) * time . Second
2017-02-07 22:01:29 -08:00
// handle Ctrl+c
signals := make ( chan os . Signal , 1 )
signal . Notify ( signals , syscall . SIGINT )
go func ( ) {
for range signals {
log . Println ( "Stopping after this running migration ..." )
migrater . GracefulStop <- true
return
}
} ( )
}
startTime := time . Now ( )
switch flag . Arg ( 0 ) {
2017-06-10 20:03:17 +08:00
case "create" :
args := flag . Args ( ) [ 1 : ]
2017-12-24 23:08:43 -08:00
seq := false
seqDigits := 6
2017-06-10 20:03:17 +08:00
createFlagSet := flag . NewFlagSet ( "create" , flag . ExitOnError )
extPtr := createFlagSet . String ( "ext" , "" , "File extension" )
dirPtr := createFlagSet . String ( "dir" , "" , "Directory to place file in (default: current working directory)" )
2018-04-17 11:09:08 -06:00
formatPtr := createFlagSet . String ( "format" , "" , "Specify the format of the version using a Go time format string. For yyyymmddhhmmss use format of 20060102150405. If not specified the version will be the unix timestamp." )
2017-12-24 23:08:43 -08:00
createFlagSet . BoolVar ( & seq , "seq" , seq , "Use sequential numbers instead of timestamps (default: false)" )
createFlagSet . IntVar ( & seqDigits , "digits" , seqDigits , "The number of digits to use in sequences (default: 6)" )
2017-06-10 20:03:17 +08:00
createFlagSet . Parse ( args )
if createFlagSet . NArg ( ) == 0 {
log . fatal ( "error: please specify name" )
}
name := createFlagSet . Arg ( 0 )
if * extPtr != "" {
* extPtr = "." + strings . TrimPrefix ( * extPtr , "." )
}
if * dirPtr != "" {
* dirPtr = strings . Trim ( * dirPtr , "/" ) + "/"
}
timestamp := startTime . Unix ( )
2018-04-17 11:09:08 -06:00
createCmd ( * dirPtr , timestamp , * formatPtr , name , * extPtr , seq , seqDigits )
2017-06-10 20:03:17 +08:00
2017-02-07 22:01:29 -08:00
case "goto" :
if migraterErr != nil {
log . fatalErr ( migraterErr )
}
if flag . Arg ( 1 ) == "" {
log . fatal ( "error: please specify version argument V" )
}
v , err := strconv . ParseUint ( flag . Arg ( 1 ) , 10 , 64 )
if err != nil {
log . fatal ( "error: can't read version argument V" )
}
gotoCmd ( migrater , uint ( v ) )
if log . verbose {
log . Println ( "Finished after" , time . Now ( ) . Sub ( startTime ) )
}
case "up" :
if migraterErr != nil {
log . fatalErr ( migraterErr )
}
limit := - 1
if flag . Arg ( 1 ) != "" {
n , err := strconv . ParseUint ( flag . Arg ( 1 ) , 10 , 64 )
if err != nil {
log . fatal ( "error: can't read limit argument N" )
}
limit = int ( n )
}
upCmd ( migrater , limit )
if log . verbose {
log . Println ( "Finished after" , time . Now ( ) . Sub ( startTime ) )
}
case "down" :
if migraterErr != nil {
log . fatalErr ( migraterErr )
}
limit := - 1
if flag . Arg ( 1 ) != "" {
n , err := strconv . ParseUint ( flag . Arg ( 1 ) , 10 , 64 )
if err != nil {
log . fatal ( "error: can't read limit argument N" )
}
limit = int ( n )
}
downCmd ( migrater , limit )
if log . verbose {
log . Println ( "Finished after" , time . Now ( ) . Sub ( startTime ) )
}
case "drop" :
if migraterErr != nil {
log . fatalErr ( migraterErr )
}
dropCmd ( migrater )
if log . verbose {
log . Println ( "Finished after" , time . Now ( ) . Sub ( startTime ) )
}
2017-02-19 15:15:00 -08:00
case "force" :
if migraterErr != nil {
log . fatalErr ( migraterErr )
}
if flag . Arg ( 1 ) == "" {
log . fatal ( "error: please specify version argument V" )
}
2017-02-19 16:06:11 -08:00
v , err := strconv . ParseInt ( flag . Arg ( 1 ) , 10 , 64 )
2017-02-19 15:15:00 -08:00
if err != nil {
log . fatal ( "error: can't read version argument V" )
}
2017-02-19 16:06:11 -08:00
if v < - 1 {
log . fatal ( "error: argument V must be >= -1" )
}
forceCmd ( migrater , int ( v ) )
2017-02-19 15:15:00 -08:00
if log . verbose {
log . Println ( "Finished after" , time . Now ( ) . Sub ( startTime ) )
}
2017-02-07 22:01:29 -08:00
case "version" :
if migraterErr != nil {
log . fatalErr ( migraterErr )
}
versionCmd ( migrater )
default :
flag . Usage ( )
os . Exit ( 0 )
}
}