Skip to content

Commit 8a56273

Browse files
authored
Add help statement for cli (#460)
* Add help statement for cli Refers to #372 * Make constant for argument * Create a flagset for each command * Factor error handling * Factor flagset creation * fix: Typo and bad behaviour * Fix lint error * Fix according to the comments * Fix according to the comments
1 parent 110c0f7 commit 8a56273

File tree

1 file changed

+95
-32
lines changed

1 file changed

+95
-32
lines changed

internal/cli/main.go

Lines changed: 95 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,47 @@ import (
1515
"github.com/golang-migrate/migrate/v4/source"
1616
)
1717

18-
const defaultTimeFormat = "20060102150405"
18+
const (
19+
defaultTimeFormat = "20060102150405"
20+
createUsage = `create [-ext E] [-dir D] [-seq] [-digits N] [-format] NAME
21+
Create a set of timestamped up/down migrations titled NAME, in directory D with extension E.
22+
Use -seq option to generate sequential up/down migrations with N digits.
23+
Use -format option to specify a Go time format string. Note: migrations with the same time cause "duplicate migration version" error.
24+
`
25+
gotoUsage = `goto V Migrate to version V`
26+
upUsage = `up [N] Apply all or N up migrations`
27+
downUsage = `down [N] Apply all or N down migrations`
28+
dropUsage = `drop [-f] [-all] Drop everything inside database
29+
Use -f to bypass confirmation
30+
Use -all to apply all down migrations`
31+
forceUsage = `force V Set version V but don't run migration (ignores dirty state)`
32+
)
33+
34+
func handleSubCmdHelp(help bool, usage string, flagSet *flag.FlagSet) {
35+
if help {
36+
fmt.Fprintln(os.Stderr, usage)
37+
flagSet.PrintDefaults()
38+
os.Exit(0)
39+
}
40+
}
41+
42+
func newFlagSetWithHelp(name string) (*flag.FlagSet, *bool) {
43+
flagSet := flag.NewFlagSet(name, flag.ExitOnError)
44+
helpPtr := flagSet.Bool("help", false, "Print help information")
45+
return flagSet, helpPtr
46+
}
1947

2048
// set main log
2149
var log = &Log{}
2250

51+
func printUsageAndExit() {
52+
flag.Usage()
53+
54+
// If a command is not found we exit with a status 2 to match the behavior
55+
// of flag.Parse() with flag.ExitOnError when parsing an invalid flag.
56+
os.Exit(2)
57+
}
58+
2359
// Main function of a cli application. It is public for backwards compatibility with `cli` package
2460
func Main(version string) {
2561
helpPtr := flag.Bool("help", false, "")
@@ -32,7 +68,7 @@ func Main(version string) {
3268
sourcePtr := flag.String("source", "", "")
3369

3470
flag.Usage = func() {
35-
fmt.Fprint(os.Stderr,
71+
fmt.Fprintf(os.Stderr,
3672
`Usage: migrate OPTIONS COMMAND [arg...]
3773
migrate [ -version | -help ]
3874
@@ -47,20 +83,16 @@ Options:
4783
-help Print usage
4884
4985
Commands:
50-
create [-ext E] [-dir D] [-seq] [-digits N] [-format] NAME
51-
Create a set of timestamped up/down migrations titled NAME, in directory D with extension E.
52-
Use -seq option to generate sequential up/down migrations with N digits.
53-
Use -format option to specify a Go time format string. Note: migrations with the same time cause "duplicate migration version" error.
54-
goto V Migrate to version V
55-
up [N] Apply all or N up migrations
56-
down [N] Apply all or N down migrations
57-
drop [-f] Drop everything inside database
58-
Use -f to bypass confirmation
59-
force V Set version V but don't run migration (ignores dirty state)
86+
%s
87+
%s
88+
%s
89+
%s
90+
%s
91+
%s
6092
version Print current migration version
6193
6294
Source drivers: `+strings.Join(source.List(), ", ")+`
63-
Database drivers: `+strings.Join(database.List(), ", ")+"\n")
95+
Database drivers: `+strings.Join(database.List(), ", ")+"\n", createUsage, gotoUsage, upUsage, downUsage, dropUsage, forceUsage)
6496
}
6597

6698
flag.Parse()
@@ -115,22 +147,30 @@ Database drivers: `+strings.Join(database.List(), ", ")+"\n")
115147

116148
startTime := time.Now()
117149

150+
if len(flag.Args()) < 1 {
151+
printUsageAndExit()
152+
}
153+
args := flag.Args()[1:]
154+
118155
switch flag.Arg(0) {
119156
case "create":
120-
args := flag.Args()[1:]
157+
121158
seq := false
122159
seqDigits := 6
123160

124-
createFlagSet := flag.NewFlagSet("create", flag.ExitOnError)
161+
createFlagSet, help := newFlagSetWithHelp("create")
125162
extPtr := createFlagSet.String("ext", "", "File extension")
126163
dirPtr := createFlagSet.String("dir", "", "Directory to place file in (default: current working directory)")
127164
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`)
128165
createFlagSet.BoolVar(&seq, "seq", seq, "Use sequential numbers instead of timestamps (default: false)")
129166
createFlagSet.IntVar(&seqDigits, "digits", seqDigits, "The number of digits to use in sequences (default: 6)")
167+
130168
if err := createFlagSet.Parse(args); err != nil {
131-
log.Println(err)
169+
log.fatalErr(err)
132170
}
133171

172+
handleSubCmdHelp(*help, createUsage, createFlagSet)
173+
134174
if createFlagSet.NArg() == 0 {
135175
log.fatal("error: please specify name")
136176
}
@@ -145,11 +185,20 @@ Database drivers: `+strings.Join(database.List(), ", ")+"\n")
145185
}
146186

147187
case "goto":
188+
189+
gotoSet, helpPtr := newFlagSetWithHelp("goto")
190+
191+
if err := gotoSet.Parse(args); err != nil {
192+
log.fatalErr(err)
193+
}
194+
195+
handleSubCmdHelp(*helpPtr, gotoUsage, gotoSet)
196+
148197
if migraterErr != nil {
149198
log.fatalErr(migraterErr)
150199
}
151200

152-
if flag.Arg(1) == "" {
201+
if flag.NArg() == 0 {
153202
log.fatal("error: please specify version argument V")
154203
}
155204

@@ -167,12 +216,20 @@ Database drivers: `+strings.Join(database.List(), ", ")+"\n")
167216
}
168217

169218
case "up":
219+
upSet, helpPtr := newFlagSetWithHelp("up")
220+
221+
if err := upSet.Parse(args); err != nil {
222+
log.fatalErr(err)
223+
}
224+
225+
handleSubCmdHelp(*helpPtr, upUsage, upSet)
226+
170227
if migraterErr != nil {
171228
log.fatalErr(migraterErr)
172229
}
173230

174231
limit := -1
175-
if flag.Arg(1) != "" {
232+
if flag.NArg() > 0 {
176233
n, err := strconv.ParseUint(flag.Arg(1), 10, 64)
177234
if err != nil {
178235
log.fatal("error: can't read limit argument N")
@@ -189,18 +246,19 @@ Database drivers: `+strings.Join(database.List(), ", ")+"\n")
189246
}
190247

191248
case "down":
192-
if migraterErr != nil {
193-
log.fatalErr(migraterErr)
194-
}
195-
196-
downFlagSet := flag.NewFlagSet("down", flag.ExitOnError)
249+
downFlagSet, helpPtr := newFlagSetWithHelp("down")
197250
applyAll := downFlagSet.Bool("all", false, "Apply all down migrations")
198251

199-
args := flag.Args()[1:]
200252
if err := downFlagSet.Parse(args); err != nil {
201253
log.fatalErr(err)
202254
}
203255

256+
handleSubCmdHelp(*helpPtr, downUsage, downFlagSet)
257+
258+
if migraterErr != nil {
259+
log.fatalErr(migraterErr)
260+
}
261+
204262
downArgs := downFlagSet.Args()
205263
num, needsConfirm, err := numDownMigrationsFromArgs(*applyAll, downArgs)
206264
if err != nil {
@@ -228,14 +286,15 @@ Database drivers: `+strings.Join(database.List(), ", ")+"\n")
228286
}
229287

230288
case "drop":
231-
dropFlagSet := flag.NewFlagSet("drop", flag.ExitOnError)
289+
dropFlagSet, help := newFlagSetWithHelp("drop")
232290
forceDrop := dropFlagSet.Bool("f", false, "Force the drop command by bypassing the confirmation prompt")
233291

234-
args := flag.Args()[1:]
235292
if err := dropFlagSet.Parse(args); err != nil {
236293
log.fatalErr(err)
237294
}
238295

296+
handleSubCmdHelp(*help, dropUsage, dropFlagSet)
297+
239298
if !*forceDrop {
240299
log.Println("Are you sure you want to drop the entire database schema? [y/N]")
241300
var response string
@@ -262,11 +321,19 @@ Database drivers: `+strings.Join(database.List(), ", ")+"\n")
262321
}
263322

264323
case "force":
324+
forceSet, helpPtr := newFlagSetWithHelp("force")
325+
326+
if err := forceSet.Parse(args); err != nil {
327+
log.fatalErr(err)
328+
}
329+
330+
handleSubCmdHelp(*helpPtr, forceUsage, forceSet)
331+
265332
if migraterErr != nil {
266333
log.fatalErr(migraterErr)
267334
}
268335

269-
if flag.Arg(1) == "" {
336+
if flag.NArg() == 0 {
270337
log.fatal("error: please specify version argument V")
271338
}
272339

@@ -297,10 +364,6 @@ Database drivers: `+strings.Join(database.List(), ", ")+"\n")
297364
}
298365

299366
default:
300-
flag.Usage()
301-
302-
// If a command is not found we exit with a status 2 to match the behavior
303-
// of flag.Parse() with flag.ExitOnError when parsing an invalid flag.
304-
os.Exit(2)
367+
printUsageAndExit()
305368
}
306369
}

0 commit comments

Comments
 (0)