github.com/fr-nvriep/migrate/v4@v4.3.2/internal/cli/main.go (about)

     1  package cli
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"os"
     7  	"os/signal"
     8  	"strconv"
     9  	"strings"
    10  	"syscall"
    11  	"time"
    12  
    13  	"github.com/fr-nvriep/migrate/v4"
    14  	"github.com/fr-nvriep/migrate/v4/database"
    15  	"github.com/fr-nvriep/migrate/v4/source"
    16  )
    17  
    18  const defaultTimeFormat = "20060102150405"
    19  
    20  // set main log
    21  var log = &Log{}
    22  
    23  func Main(version string) {
    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 everything 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  			if _, err := migrater.Close(); err != nil {
    93  				log.Println(err)
    94  			}
    95  		}
    96  	}()
    97  	if migraterErr == nil {
    98  		migrater.Log = log
    99  		migrater.PrefetchMigrations = *prefetchPtr
   100  		migrater.LockTimeout = time.Duration(int64(*lockTimeoutPtr)) * time.Second
   101  
   102  		// handle Ctrl+c
   103  		signals := make(chan os.Signal, 1)
   104  		signal.Notify(signals, syscall.SIGINT)
   105  		go func() {
   106  			for range signals {
   107  				log.Println("Stopping after this running migration ...")
   108  				migrater.GracefulStop <- true
   109  				return
   110  			}
   111  		}()
   112  	}
   113  
   114  	startTime := time.Now()
   115  
   116  	switch flag.Arg(0) {
   117  	case "create":
   118  		args := flag.Args()[1:]
   119  		seq := false
   120  		seqDigits := 6
   121  
   122  		createFlagSet := flag.NewFlagSet("create", flag.ExitOnError)
   123  		extPtr := createFlagSet.String("ext", "", "File extension")
   124  		dirPtr := createFlagSet.String("dir", "", "Directory to place file in (default: current working directory)")
   125  		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`)
   126  		createFlagSet.BoolVar(&seq, "seq", seq, "Use sequential numbers instead of timestamps (default: false)")
   127  		createFlagSet.IntVar(&seqDigits, "digits", seqDigits, "The number of digits to use in sequences (default: 6)")
   128  		if err := createFlagSet.Parse(args); err != nil {
   129  			log.Println(err)
   130  		}
   131  
   132  		if createFlagSet.NArg() == 0 {
   133  			log.fatal("error: please specify name")
   134  		}
   135  		name := createFlagSet.Arg(0)
   136  
   137  		if *extPtr == "" {
   138  			log.fatal("error: -ext flag must be specified")
   139  		}
   140  		*extPtr = "." + strings.TrimPrefix(*extPtr, ".")
   141  
   142  		if *dirPtr != "" {
   143  			*dirPtr = strings.Trim(*dirPtr, "/") + "/"
   144  		}
   145  
   146  		createCmd(*dirPtr, startTime, *formatPtr, name, *extPtr, seq, seqDigits)
   147  
   148  	case "goto":
   149  		if migraterErr != nil {
   150  			log.fatalErr(migraterErr)
   151  		}
   152  
   153  		if flag.Arg(1) == "" {
   154  			log.fatal("error: please specify version argument V")
   155  		}
   156  
   157  		v, err := strconv.ParseUint(flag.Arg(1), 10, 64)
   158  		if err != nil {
   159  			log.fatal("error: can't read version argument V")
   160  		}
   161  
   162  		gotoCmd(migrater, uint(v))
   163  
   164  		if log.verbose {
   165  			log.Println("Finished after", time.Since(startTime))
   166  		}
   167  
   168  	case "up":
   169  		if migraterErr != nil {
   170  			log.fatalErr(migraterErr)
   171  		}
   172  
   173  		limit := -1
   174  		if flag.Arg(1) != "" {
   175  			n, err := strconv.ParseUint(flag.Arg(1), 10, 64)
   176  			if err != nil {
   177  				log.fatal("error: can't read limit argument N")
   178  			}
   179  			limit = int(n)
   180  		}
   181  
   182  		upCmd(migrater, limit)
   183  
   184  		if log.verbose {
   185  			log.Println("Finished after", time.Since(startTime))
   186  		}
   187  
   188  	case "down":
   189  		if migraterErr != nil {
   190  			log.fatalErr(migraterErr)
   191  		}
   192  
   193  		limit := -1
   194  		if flag.Arg(1) != "" {
   195  			n, err := strconv.ParseUint(flag.Arg(1), 10, 64)
   196  			if err != nil {
   197  				log.fatal("error: can't read limit argument N")
   198  			}
   199  			limit = int(n)
   200  		}
   201  
   202  		downCmd(migrater, limit)
   203  
   204  		if log.verbose {
   205  			log.Println("Finished after", time.Since(startTime))
   206  		}
   207  
   208  	case "drop":
   209  		if migraterErr != nil {
   210  			log.fatalErr(migraterErr)
   211  		}
   212  
   213  		dropCmd(migrater)
   214  
   215  		if log.verbose {
   216  			log.Println("Finished after", time.Since(startTime))
   217  		}
   218  
   219  	case "force":
   220  		if migraterErr != nil {
   221  			log.fatalErr(migraterErr)
   222  		}
   223  
   224  		if flag.Arg(1) == "" {
   225  			log.fatal("error: please specify version argument V")
   226  		}
   227  
   228  		v, err := strconv.ParseInt(flag.Arg(1), 10, 64)
   229  		if err != nil {
   230  			log.fatal("error: can't read version argument V")
   231  		}
   232  
   233  		if v < -1 {
   234  			log.fatal("error: argument V must be >= -1")
   235  		}
   236  
   237  		forceCmd(migrater, int(v))
   238  
   239  		if log.verbose {
   240  			log.Println("Finished after", time.Since(startTime))
   241  		}
   242  
   243  	case "version":
   244  		if migraterErr != nil {
   245  			log.fatalErr(migraterErr)
   246  		}
   247  
   248  		versionCmd(migrater)
   249  
   250  	default:
   251  		flag.Usage()
   252  		os.Exit(0)
   253  	}
   254  }