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  }