diff --git a/flags/flags.go b/flags/flags.go index 771d487..8f0f9fb 100644 --- a/flags/flags.go +++ b/flags/flags.go @@ -21,6 +21,7 @@ type VersionIncrementMethod int const IncrementMajor VersionIncrementMethod = 0 const IncrementMinor VersionIncrementMethod = 1 const IncrementAuto VersionIncrementMethod = 2 +const IncrementAsk VersionIncrementMethod = 3 func Parse(args []string) *Flags { f := Flags{} @@ -29,7 +30,7 @@ func Parse(args []string) *Flags { var major, minor, auto bool fs.BoolVar(&major, FlagMajor, false, "make a major version increment") fs.BoolVar(&minor, FlagMinor, false, "make a minor version increment") - fs.BoolVar(&auto, FlagAuto, false, "automatically decide how to increment version (default)") + fs.BoolVar(&auto, FlagAuto, false, "automatically decide how to increment version") fs.Parse(args) if auto && (major || minor) { fmt.Println("Explicit --auto disallows usage of --major and --minor.") @@ -62,5 +63,8 @@ func readVersionIncrementMethod(major, minor, auto bool) VersionIncrementMethod if minor { return IncrementMinor } - return IncrementAuto + if auto { + return IncrementAuto + } + return IncrementAsk } diff --git a/go.mod b/go.mod index b03cae6..c038e24 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,12 @@ module github.com/setlog/fly go 1.15 -require github.com/setlog/panik v0.5.0 +require ( + github.com/gdamore/tcell/v2 v2.5.3 + github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/rivo/uniseg v0.4.2 // indirect + github.com/setlog/panik v0.5.0 + golang.org/x/sys v0.1.0 // indirect + golang.org/x/term v0.1.0 // indirect + golang.org/x/text v0.4.0 // indirect +) diff --git a/go.sum b/go.sum index 9423702..a1ec107 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,45 @@ +github.com/gdamore/encoding v1.0.0 h1:+7OoQ1Bc6eTm5niUzBa0Ctsh6JbMW6Ra+YNuAtDBdko= +github.com/gdamore/encoding v1.0.0/go.mod h1:alR0ol34c49FCSBLjhosxzcPHQbf2trDkoo5dl+VrEg= +github.com/gdamore/tcell/v2 v2.5.3 h1:b9XQrT6QGbgI7JvZOJXFNczOQeIYbo8BfeSMzt2sAV0= +github.com/gdamore/tcell/v2 v2.5.3/go.mod h1:wSkrPaXoiIWZqW/g7Px4xc79di6FTcpB8tvaKJ6uGBo= +github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= +github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= +github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.2 h1:YwD0ulJSJytLpiaWua0sBDusfsCZohxjxzVTYjwxfV8= +github.com/rivo/uniseg v0.4.2/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/setlog/panik v0.5.0 h1:C31jqKIE/MoMnW4lZkscwKXnFJQtrMQ0qCSrFJJzzL4= github.com/setlog/panik v0.5.0/go.mod h1:3Z9sPLVrY6TBPmAZSpC1Iu2QwrCncRlIS59lJ3vQ97U= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220318055525-2edf467146b5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/main.go b/main.go index 373f81d..8960248 100644 --- a/main.go +++ b/main.go @@ -19,14 +19,33 @@ func main() { defer panik.ExitTraceTo(os.Stderr) f := flags.Parse(os.Args[1:]) panik.OnError(os.MkdirAll(filepath.FromSlash("src/main/migration"), 0700)) - scriptFilePath := filepath.FromSlash(path.Join("src/main/migration", nextFlywayScriptPrefix("src/main/migration", f.VersionIncrementMethod)+f.ScriptPrefix+".sql")) + scriptFilePath := filepath.FromSlash(path.Join("src/main/migration", nextFlywayScriptName("src/main/migration", f.VersionIncrementMethod, f.ScriptPrefix+".sql"))) panik.OnError(ioutil.WriteFile(scriptFilePath, []byte("USE ${"+extractSchemaName(f.ScriptPrefix)+"};\n\n\n"), 0600)) openInVsCode(scriptFilePath) } -func nextFlywayScriptPrefix(folderPath string, incrementMethod flags.VersionIncrementMethod) string { - major, minor := nextFlywayScriptVersion(folderPath, incrementMethod) - return fmt.Sprintf("V%s.%s__", major, minor) +func nextFlywayScriptName(folderPath string, incrementMethod flags.VersionIncrementMethod, append string) string { + var major, minor string + previousScriptFileName, wasMinorIncrement := latestFlywayScriptFileName(folderPath) + if incrementMethod == flags.IncrementAsk { + major, minor = nextFlywayScriptVersion(previousScriptFileName, wasMinorIncrement, flags.IncrementMajor) + option1 := fmt.Sprintf("V%s.%s__%s", major, minor, append) + major, minor = nextFlywayScriptVersion(previousScriptFileName, wasMinorIncrement, flags.IncrementMinor) + option2 := fmt.Sprintf("V%s.%s__%s", major, minor, append) + options := []string{ + option1, option2, + } + var title string + if previousScriptFileName != "" { + title = fmt.Sprintf("Choose new file name to follow %s:", previousScriptFileName) + } else { + title = "Choose name for first script file:" + } + return options[selectOption(title, options)] + } else { + major, minor = nextFlywayScriptVersion(previousScriptFileName, wasMinorIncrement, incrementMethod) + return fmt.Sprintf("V%s.%s__%s", major, minor, append) + } } func extractSchemaName(scriptPrefix string) string { @@ -40,19 +59,18 @@ func extractSchemaName(scriptPrefix string) string { return "schema_client" } -func nextFlywayScriptVersion(folderPath string, incrementMethod flags.VersionIncrementMethod) (major, minor string) { - latestScriptFileName, wasMinorIncrement := latestFlywayScriptFileName(folderPath) +func nextFlywayScriptVersion(previousScriptFileName string, wasMinorIncrement bool, incrementMethod flags.VersionIncrementMethod) (major, minor string) { incrementMinor := wasMinorIncrement if incrementMethod == flags.IncrementMajor { incrementMinor = false } else if incrementMethod == flags.IncrementMinor { incrementMinor = true } - if latestScriptFileName == "" { + if previousScriptFileName == "" { return incrementFlywayScriptVersion("000", "000", incrementMinor) } - major, minor, _ = getFlywayScriptVersion(latestScriptFileName) - return incrementFlywayScriptVersion(major, minor, incrementMinor) + prevMajor, prevMinor, _ := getFlywayScriptVersion(previousScriptFileName) + return incrementFlywayScriptVersion(prevMajor, prevMinor, incrementMinor) } func latestFlywayScriptFileName(folderPath string) (scriptFileName string, wasMinorIncrement bool) { diff --git a/terminal.go b/terminal.go new file mode 100644 index 0000000..27d45b2 --- /dev/null +++ b/terminal.go @@ -0,0 +1,47 @@ +package main + +import ( + "fmt" + "os" + + "github.com/gdamore/tcell/v2" + "github.com/setlog/panik" +) + +func selectOption(title string, options []string) int { + screen, err := tcell.NewScreen() + panik.OnError(err) + panik.OnError(screen.Init()) + defStyle := tcell.StyleDefault.Background(tcell.ColorReset).Foreground(tcell.ColorReset) + screen.SetStyle(defStyle) + screen.Clear() + drawText(screen, 0, 0, defStyle, title) + for i, option := range options { + drawText(screen, 0, i+1, defStyle, fmt.Sprintf("%d. %s", i+1, option)) + } + for { + screen.Show() + ev := screen.PollEvent() + switch ev := ev.(type) { + case *tcell.EventResize: + screen.Sync() + case *tcell.EventKey: + if ev.Key() == tcell.KeyEscape || ev.Key() == tcell.KeyCtrlC { + screen.Fini() + os.Exit(0) + } else if ev.Key() == tcell.KeyRune { + if ev.Rune() >= '1' && ev.Rune() <= rune('1'+len(options)-1) { + screen.Fini() + return int(ev.Rune() - '1') + } + } + } + } +} + +func drawText(s tcell.Screen, x, y int, style tcell.Style, text string) { + for _, r := range []rune(text) { + s.SetContent(x, y, r, nil, style) + x++ + } +}