mirror of https://github.com/status-im/migrate.git
Support creating migrations using sequences in addition to timestamps
This commit is contained in:
parent
24dd870559
commit
f5ae4b42db
|
@ -1,24 +1,81 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/golang-migrate/migrate"
|
||||
_ "github.com/golang-migrate/migrate/database/stub" // TODO remove again
|
||||
_ "github.com/golang-migrate/migrate/source/file"
|
||||
"os"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func createCmd(dir string, timestamp int64, name string, ext string) {
|
||||
base := fmt.Sprintf("%v%v_%v.", dir, timestamp, name)
|
||||
func nextSeq(matches []string, dir string, seqDigits int) (string, error) {
|
||||
if seqDigits <= 0 {
|
||||
return "", errors.New("Digits must be positive")
|
||||
}
|
||||
|
||||
nextSeq := 1
|
||||
if len(matches) > 0 {
|
||||
filename := matches[len(matches)-1]
|
||||
matchSeqStr := strings.TrimPrefix(filename, dir)
|
||||
idx := strings.Index(matchSeqStr, "_")
|
||||
if idx < 1 { // Using 1 instead of 0 since there should be at least 1 digit
|
||||
return "", errors.New("Malformed migration filename: " + filename)
|
||||
}
|
||||
matchSeqStr = matchSeqStr[0:idx]
|
||||
var err error
|
||||
nextSeq, err = strconv.Atoi(matchSeqStr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
nextSeq++
|
||||
}
|
||||
if nextSeq <= 0 {
|
||||
return "", errors.New("Next sequence number must be positive")
|
||||
}
|
||||
|
||||
nextSeqStr := strconv.Itoa(nextSeq)
|
||||
if len(nextSeqStr) > seqDigits {
|
||||
return "", fmt.Errorf("Next sequence number %s too large. At most %d digits are allowed", nextSeqStr, seqDigits)
|
||||
}
|
||||
padding := seqDigits - len(nextSeqStr)
|
||||
if padding > 0 {
|
||||
nextSeqStr = strings.Repeat("0", padding) + nextSeqStr
|
||||
}
|
||||
return nextSeqStr, nil
|
||||
}
|
||||
|
||||
func createCmd(dir string, timestamp int64, name string, ext string, seq bool, seqDigits int) {
|
||||
var base string
|
||||
if seq {
|
||||
if seqDigits <= 0 {
|
||||
log.fatalErr(errors.New("Digits must be positive"))
|
||||
}
|
||||
matches, err := filepath.Glob(dir + "*" + ext)
|
||||
if err != nil {
|
||||
log.fatalErr(err)
|
||||
}
|
||||
nextSeqStr, err := nextSeq(matches, dir, seqDigits)
|
||||
if err != nil {
|
||||
log.fatalErr(err)
|
||||
}
|
||||
base = fmt.Sprintf("%v%v_%v.", dir, nextSeqStr, name)
|
||||
} else {
|
||||
base = fmt.Sprintf("%v%v_%v.", dir, timestamp, name)
|
||||
}
|
||||
|
||||
os.MkdirAll(dir, os.ModePerm)
|
||||
createFile(base + "up" + ext)
|
||||
createFile(base + "down" + ext)
|
||||
}
|
||||
|
||||
func createFile(fname string) {
|
||||
if _, err := os.Create(fname); err != nil {
|
||||
log.fatalErr(err)
|
||||
}
|
||||
if _, err := os.Create(fname); err != nil {
|
||||
log.fatalErr(err)
|
||||
}
|
||||
}
|
||||
|
||||
func gotoCmd(m *migrate.Migrate, v uint) {
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNextSeq(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
matches []string
|
||||
dir string
|
||||
seqDigits int
|
||||
expected string
|
||||
expectedErrStr string
|
||||
}{
|
||||
{"Bad digits", []string{}, "migrationDir", 0, "", "Digits must be positive"},
|
||||
{"Single digit initialize", []string{}, "migrationDir", 1, "1", ""},
|
||||
{"Single digit malformed", []string{"bad"}, "migrationDir", 1, "", "Malformed migration filename: bad"},
|
||||
{"Single digit no int", []string{"bad_bad"}, "migrationDir", 1, "", "strconv.Atoi: parsing \"bad\": invalid syntax"},
|
||||
{"Single digit negative seq", []string{"-5_test"}, "migrationDir", 1, "", "Next sequence number must be positive"},
|
||||
{"Single digit increment", []string{"3_test", "4_test"}, "migrationDir", 1, "5", ""},
|
||||
{"Single digit overflow", []string{"9_test"}, "migrationDir", 1, "", "Next sequence number 10 too large. At most 1 digits are allowed"},
|
||||
{"Zero-pad initialize", []string{}, "migrationDir", 6, "000001", ""},
|
||||
{"Zero-pad malformed", []string{"bad"}, "migrationDir", 6, "", "Malformed migration filename: bad"},
|
||||
{"Zero-pad no int", []string{"bad_bad"}, "migrationDir", 6, "", "strconv.Atoi: parsing \"bad\": invalid syntax"},
|
||||
{"Zero-pad negative seq", []string{"-000005_test"}, "migrationDir", 6, "", "Next sequence number must be positive"},
|
||||
{"Zero-pad increment", []string{"000003_test", "000004_test"}, "migrationDir", 6, "000005", ""},
|
||||
{"Zero-pad overflow", []string{"999999_test"}, "migrationDir", 6, "", "Next sequence number 1000000 too large. At most 6 digits are allowed"},
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
nextSeq, err := nextSeq(c.matches, c.dir, c.seqDigits)
|
||||
if nextSeq != c.expected {
|
||||
t.Error("Incorrect nextSeq: " + nextSeq + " != " + c.expected)
|
||||
}
|
||||
if err != nil {
|
||||
if err.Error() != c.expectedErrStr {
|
||||
t.Error("Incorrect error: " + err.Error() + " != " + c.expectedErrStr)
|
||||
}
|
||||
} else if c.expectedErrStr != "" {
|
||||
t.Error("Expected error: " + c.expectedErrStr + " but got nil instead")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
11
cli/main.go
11
cli/main.go
|
@ -42,8 +42,9 @@ Options:
|
|||
-help Print usage
|
||||
|
||||
Commands:
|
||||
create [-ext E] [-dir D] NAME
|
||||
Create a set of timestamped up/down migrations titled NAME, in directory D with extension E
|
||||
create [-ext E] [-dir D] [-seq] [-digits N] NAME
|
||||
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.
|
||||
goto V Migrate to version V
|
||||
up [N] Apply all or N up migrations
|
||||
down [N] Apply all or N down migrations
|
||||
|
@ -106,10 +107,14 @@ Commands:
|
|||
switch flag.Arg(0) {
|
||||
case "create":
|
||||
args := flag.Args()[1:]
|
||||
seq := false
|
||||
seqDigits := 6
|
||||
|
||||
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)")
|
||||
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)")
|
||||
createFlagSet.Parse(args)
|
||||
|
||||
if createFlagSet.NArg() == 0 {
|
||||
|
@ -126,7 +131,7 @@ Commands:
|
|||
|
||||
timestamp := startTime.Unix()
|
||||
|
||||
createCmd(*dirPtr, timestamp, name, *extPtr)
|
||||
createCmd(*dirPtr, timestamp, name, *extPtr, seq, seqDigits)
|
||||
|
||||
case "goto":
|
||||
if migraterErr != nil {
|
||||
|
|
Loading…
Reference in New Issue