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  }