From 58ccb0bf28b4ed4f5cf0226a85a337504f615717 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Mon, 20 Jun 2022 17:08:10 -0400 Subject: [PATCH] Clean up the v2 manual a bit includes: - running each example through `goimports` - wrapping text at ~80 cols - consistently using `&cli.App{...}` - consistently using code example gates - removing trailing blankspace --- docs/v2/manual.md | 1751 +++++++++++++++++++++++---------------------- 1 file changed, 879 insertions(+), 872 deletions(-) diff --git a/docs/v2/manual.md b/docs/v2/manual.md index 7208ae6..1527e82 100644 --- a/docs/v2/manual.md +++ b/docs/v2/manual.md @@ -9,17 +9,17 @@ discovery. So a cli app can be as little as one line of code in `main()`. "args": ["--help"], "output": "A new cli application" } --> -``` go +```go package main import ( - "os" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - (&cli.App{}).Run(os.Args) + (&cli.App{}).Run(os.Args) } ``` @@ -29,31 +29,30 @@ action to execute and some help documentation: -``` go +```go package main import ( - "fmt" - "log" - "os" + "fmt" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - app := &cli.App{ - Name: "boom", - Usage: "make an explosive entrance", - Action: func(c *cli.Context) error { - fmt.Println("boom! I say!") - return nil - }, - } + app := &cli.App{ + Name: "boom", + Usage: "make an explosive entrance", + Action: func(*cli.Context) error { + fmt.Println("boom! I say!") + return nil + }, + } - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` @@ -72,50 +71,49 @@ Start by creating a directory named `greet`, and within it, add a file, -``` go +```go package main import ( - "fmt" - "log" - "os" + "fmt" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - app := &cli.App{ - Name: "greet", - Usage: "fight the loneliness!", - Action: func(c *cli.Context) error { - fmt.Println("Hello friend!") - return nil - }, - } + app := &cli.App{ + Name: "greet", + Usage: "fight the loneliness!", + Action: func(*cli.Context) error { + fmt.Println("Hello friend!") + return nil + }, + } - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` Install our command to the `$GOPATH/bin` directory: -``` +```sh-session $ go install ``` Finally run our new command: -``` +```sh-session $ greet Hello friend! ``` cli also generates neat help text: -``` +```sh-session $ greet help NAME: greet - fight the loneliness! @@ -137,29 +135,28 @@ You can lookup arguments by calling the `Args` function on `cli.Context`, e.g.: -``` go +```go package main import ( - "fmt" - "log" - "os" + "fmt" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - app := &cli.App{ - Action: func(c *cli.Context) error { - fmt.Printf("Hello %q", c.Args().Get(0)) - return nil - }, - } + app := &cli.App{ + Action: func(cCtx *cli.Context) error { + fmt.Printf("Hello %q", cCtx.Args().Get(0)) + return nil + }, + } - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` @@ -170,44 +167,43 @@ Setting and querying flags is simple. -``` go +```go package main import ( - "fmt" - "log" - "os" + "fmt" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - app := &cli.App{ - Flags: []cli.Flag { - &cli.StringFlag{ - Name: "lang", - Value: "english", - Usage: "language for the greeting", - }, - }, - Action: func(c *cli.Context) error { - name := "Nefertiti" - if c.NArg() > 0 { - name = c.Args().Get(0) - } - if c.String("lang") == "spanish" { - fmt.Println("Hola", name) - } else { - fmt.Println("Hello", name) - } - return nil - }, - } + app := &cli.App{ + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "lang", + Value: "english", + Usage: "language for the greeting", + }, + }, + Action: func(cCtx *cli.Context) error { + name := "Nefertiti" + if cCtx.NArg() > 0 { + name = cCtx.Args().Get(0) + } + if cCtx.String("lang") == "spanish" { + fmt.Println("Hola", name) + } else { + fmt.Println("Hello", name) + } + return nil + }, + } - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` @@ -217,47 +213,46 @@ scanned. -``` go +```go package main import ( - "log" - "os" - "fmt" + "fmt" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - var language string + var language string - app := &cli.App{ - Flags: []cli.Flag { - &cli.StringFlag{ - Name: "lang", - Value: "english", - Usage: "language for the greeting", - Destination: &language, - }, - }, - Action: func(c *cli.Context) error { - name := "someone" - if c.NArg() > 0 { - name = c.Args().Get(0) - } - if language == "spanish" { - fmt.Println("Hola", name) - } else { - fmt.Println("Hello", name) - } - return nil - }, - } + app := &cli.App{ + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "lang", + Value: "english", + Usage: "language for the greeting", + Destination: &language, + }, + }, + Action: func(cCtx *cli.Context) error { + name := "someone" + if cCtx.NArg() > 0 { + name = cCtx.Args().Get(0) + } + if language == "spanish" { + fmt.Println("Hola", name) + } else { + fmt.Println("Hello", name) + } + return nil + }, + } - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` @@ -278,27 +273,26 @@ For example this: package main import ( - "log" - "os" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - app := &cli.App{ - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "config", - Aliases: []string{"c"}, - Usage: "Load configuration from `FILE`", - }, - }, - } + app := &cli.App{ + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "config", + Aliases: []string{"c"}, + Usage: "Load configuration from `FILE`", + }, + }, + } - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` @@ -320,32 +314,31 @@ list for the `Name`. e.g. "args": ["--help"], "output": "--lang value, -l value.*language for the greeting.*default: \"english\"" } --> -``` go +```go package main import ( - "log" - "os" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - app := &cli.App{ - Flags: []cli.Flag { - &cli.StringFlag{ - Name: "lang", - Aliases: []string{"l"}, - Value: "english", - Usage: "language for the greeting", - }, - }, - } + app := &cli.App{ + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "lang", + Aliases: []string{"l"}, + Value: "english", + Usage: "language for the greeting", + }, + }, + } - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` @@ -365,59 +358,58 @@ For example this: "args": ["--help"], "output": ".*Load configuration from FILE\n.*\n.*Language for the greeting.*" } --> -``` go +```go package main import ( - "log" - "os" - "sort" + "log" + "os" + "sort" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - app := &cli.App{ - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "lang", - Aliases: []string{"l"}, - Value: "english", - Usage: "Language for the greeting", - }, - &cli.StringFlag{ - Name: "config", - Aliases: []string{"c"}, - Usage: "Load configuration from `FILE`", - }, - }, - Commands: []*cli.Command{ - { - Name: "complete", - Aliases: []string{"c"}, - Usage: "complete a task on the list", - Action: func(c *cli.Context) error { - return nil - }, - }, - { - Name: "add", - Aliases: []string{"a"}, - Usage: "add a task to the list", - Action: func(c *cli.Context) error { - return nil - }, - }, - }, - } + app := &cli.App{ + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "lang", + Aliases: []string{"l"}, + Value: "english", + Usage: "Language for the greeting", + }, + &cli.StringFlag{ + Name: "config", + Aliases: []string{"c"}, + Usage: "Load configuration from `FILE`", + }, + }, + Commands: []*cli.Command{ + { + Name: "complete", + Aliases: []string{"c"}, + Usage: "complete a task on the list", + Action: func(*cli.Context) error { + return nil + }, + }, + { + Name: "add", + Aliases: []string{"a"}, + Usage: "add a task to the list", + Action: func(*cli.Context) error { + return nil + }, + }, + }, + } - sort.Sort(cli.FlagsByName(app.Flags)) - sort.Sort(cli.CommandsByName(app.Commands)) + sort.Sort(cli.FlagsByName(app.Flags)) + sort.Sort(cli.CommandsByName(app.Commands)) - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` @@ -436,33 +428,32 @@ You can also have the default value set from the environment via `EnvVars`. e.g "args": ["--help"], "output": "language for the greeting.*APP_LANG" } --> -``` go +```go package main import ( - "log" - "os" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - app := &cli.App{ - Flags: []cli.Flag { - &cli.StringFlag{ - Name: "lang", - Aliases: []string{"l"}, - Value: "english", - Usage: "language for the greeting", - EnvVars: []string{"APP_LANG"}, - }, - }, - } + app := &cli.App{ + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "lang", + Aliases: []string{"l"}, + Value: "english", + Usage: "language for the greeting", + EnvVars: []string{"APP_LANG"}, + }, + }, + } - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` @@ -473,33 +464,32 @@ resolves is used. "args": ["--help"], "output": "language for the greeting.*LEGACY_COMPAT_LANG.*APP_LANG.*LANG" } --> -``` go +```go package main import ( - "log" - "os" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - app := &cli.App{ - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "lang", - Aliases: []string{"l"}, - Value: "english", - Usage: "language for the greeting", - EnvVars: []string{"LEGACY_COMPAT_LANG", "APP_LANG", "LANG"}, - }, - }, - } + app := &cli.App{ + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "lang", + Aliases: []string{"l"}, + Value: "english", + Usage: "language for the greeting", + EnvVars: []string{"LEGACY_COMPAT_LANG", "APP_LANG", "LANG"}, + }, + }, + } - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` @@ -511,32 +501,31 @@ You can also have the default value set from file via `FilePath`. e.g. "args": ["--help"], "output": "password for the mysql database" } --> -``` go +```go package main import ( - "log" - "os" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - app := cli.NewApp() + app := &cli.App{ + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "password", + Aliases: []string{"p"}, + Usage: "password for the mysql database", + FilePath: "/etc/mysql/password", + }, + }, + } - app.Flags = []cli.Flag { - &cli.StringFlag{ - Name: "password", - Aliases: []string{"p"}, - Usage: "password for the mysql database", - FilePath: "/etc/mysql/password", - }, - } - - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` @@ -549,21 +538,24 @@ There is a separate package altsrc that adds support for getting flag values from other file input sources. Currently supported input source formats: -* YAML -* JSON -* TOML + + YAML + JSON + TOML In order to get values for a flag from an alternate input source the following code would be added to wrap an existing cli.Flag like below: -``` go +```go + // --- >8 --- altsrc.NewIntFlag(&cli.IntFlag{Name: "test"}) ``` Initialization must also occur for these flags. Below is an example initializing getting data from a yaml file below. -``` go +```go + // --- >8 --- command.Before = altsrc.InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) ``` @@ -573,9 +565,9 @@ the yaml input source for any flags that are defined on that command. As a note the "load" flag used would also have to be defined on the command flags in order for this code snippet to work. -Currently only YAML, JSON, and TOML files are supported but developers can add support -for other input sources by implementing the altsrc.InputSourceContext for their -given sources. +Currently only YAML, JSON, and TOML files are supported but developers can add +support for other input sources by implementing the altsrc.InputSourceContext +for their given sources. Here is a more complete sample of a command using YAML support: @@ -583,33 +575,33 @@ Here is a more complete sample of a command using YAML support: "args": ["--help"], "output": "--test value.*default: 0" } --> -``` go +```go package main import ( - "fmt" - "os" + "fmt" + "os" - "github.com/urfave/cli/v2" - "github.com/urfave/cli/v2/altsrc" + "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2/altsrc" ) func main() { - flags := []cli.Flag{ - altsrc.NewIntFlag(&cli.IntFlag{Name: "test"}), - &cli.StringFlag{Name: "load"}, - } + flags := []cli.Flag{ + altsrc.NewIntFlag(&cli.IntFlag{Name: "test"}), + &cli.StringFlag{Name: "load"}, + } - app := &cli.App{ - Action: func(c *cli.Context) error { - fmt.Println("--test value.*default: 0") - return nil - }, - Before: altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("load")), - Flags: flags, - } + app := &cli.App{ + Action: func(*cli.Context) error { + fmt.Println("--test value.*default: 0") + return nil + }, + Before: altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("load")), + Flags: flags, + } - app.Run(os.Args) + app.Run(os.Args) } ``` @@ -627,40 +619,36 @@ Take for example this app that requires the `lang` flag: package main import ( - "fmt" - "log" - "os" + "fmt" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - app := cli.NewApp() + app := &cli.App{ + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "lang", + Value: "english", + Usage: "language for the greeting", + Required: true, + }, + }, + Action: func(cCtx *cli.Context) error { + output := "Hello" + if cCtx.String("lang") == "spanish" { + output = "Hola" + } + fmt.Println(output) + return nil + }, + } - app.Flags = []cli.Flag { - &cli.StringFlag{ - Name: "lang", - Value: "english", - Usage: "language for the greeting", - Required: true, - }, - } - - app.Action = func(c *cli.Context) error { - var output string - if c.String("lang") == "spanish" { - output = "Hola" - } else { - output = "Hello" - } - fmt.Println(output) - return nil - } - - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` @@ -672,7 +660,9 @@ Required flag "lang" not set #### Default Values for help output -Sometimes it's useful to specify a flag's default help-text value within the flag declaration. This can be useful if the default value for a flag is a computed value. The default value can be set via the `DefaultText` struct field. +Sometimes it's useful to specify a flag's default help-text value within the +flag declaration. This can be useful if the default value for a flag is a +computed value. The default value can be set via the `DefaultText` struct field. For example this: @@ -684,28 +674,27 @@ For example this: package main import ( - "log" - "os" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - app := &cli.App{ - Flags: []cli.Flag{ - &cli.IntFlag{ - Name: "port", - Usage: "Use a randomized port", - Value: 0, - DefaultText: "random", - }, - }, - } + app := &cli.App{ + Flags: []cli.Flag{ + &cli.IntFlag{ + Name: "port", + Usage: "Use a randomized port", + Value: 0, + DefaultText: "random", + }, + }, + } - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` @@ -736,64 +725,63 @@ Subcommands can be defined for a more git-like command line app. package main import ( - "fmt" - "log" - "os" + "fmt" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - app := &cli.App{ - Commands: []*cli.Command{ - { - Name: "add", - Aliases: []string{"a"}, - Usage: "add a task to the list", - Action: func(c *cli.Context) error { - fmt.Println("added task: ", c.Args().First()) - return nil - }, - }, - { - Name: "complete", - Aliases: []string{"c"}, - Usage: "complete a task on the list", - Action: func(c *cli.Context) error { - fmt.Println("completed task: ", c.Args().First()) - return nil - }, - }, - { - Name: "template", - Aliases: []string{"t"}, - Usage: "options for task templates", - Subcommands: []*cli.Command{ - { - Name: "add", - Usage: "add a new template", - Action: func(c *cli.Context) error { - fmt.Println("new task template: ", c.Args().First()) - return nil - }, - }, - { - Name: "remove", - Usage: "remove an existing template", - Action: func(c *cli.Context) error { - fmt.Println("removed task template: ", c.Args().First()) - return nil - }, - }, - }, - }, - }, - } + app := &cli.App{ + Commands: []*cli.Command{ + { + Name: "add", + Aliases: []string{"a"}, + Usage: "add a task to the list", + Action: func(cCtx *cli.Context) error { + fmt.Println("added task: ", cCtx.Args().First()) + return nil + }, + }, + { + Name: "complete", + Aliases: []string{"c"}, + Usage: "complete a task on the list", + Action: func(cCtx *cli.Context) error { + fmt.Println("completed task: ", cCtx.Args().First()) + return nil + }, + }, + { + Name: "template", + Aliases: []string{"t"}, + Usage: "options for task templates", + Subcommands: []*cli.Command{ + { + Name: "add", + Usage: "add a new template", + Action: func(cCtx *cli.Context) error { + fmt.Println("new task template: ", cCtx.Args().First()) + return nil + }, + }, + { + Name: "remove", + Usage: "remove an existing template", + Action: func(cCtx *cli.Context) error { + fmt.Println("removed task template: ", cCtx.Args().First()) + return nil + }, + }, + }, + }, + }, + } - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` @@ -801,41 +789,38 @@ func main() { For additional organization in apps that have many subcommands, you can associate a category for each command to group them together in the help -output. - -E.g. +output, e.g.: ```go package main import ( - "log" - "os" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - app := &cli.App{ - Commands: []*cli.Command{ - { - Name: "noop", - }, - { - Name: "add", - Category: "template", - }, - { - Name: "remove", - Category: "template", - }, - }, - } + app := &cli.App{ + Commands: []*cli.Command{ + { + Name: "noop", + }, + { + Name: "add", + Category: "template", + }, + { + Name: "remove", + Category: "template", + }, + }, + } - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` @@ -859,36 +844,35 @@ may be set by returning a non-nil error that fulfills `cli.ExitCoder`, *or* a -``` go +```go package main import ( - "log" - "os" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - app := &cli.App{ - Flags: []cli.Flag{ - &cli.BoolFlag{ - Name: "ginger-crouton", - Usage: "is it in the soup?", - }, - }, - Action: func(ctx *cli.Context) error { - if !ctx.Bool("ginger-crouton") { - return cli.Exit("Ginger croutons are not in the soup", 86) - } - return nil - }, - } + app := &cli.App{ + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "ginger-crouton", + Usage: "is it in the soup?", + }, + }, + Action: func(ctx *cli.Context) error { + if !ctx.Bool("ginger-crouton") { + return cli.Exit("Ginger croutons are not in the soup", 86) + } + return nil + }, + } - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` @@ -896,7 +880,7 @@ func main() { Traditional use of options using their shortnames look like this: -``` +```sh-session $ cmd -s -o -m "Some message" ``` @@ -909,42 +893,42 @@ example: "args": ["short", "-som", "Some message"], "output": "serve: true\noption: true\nmessage: Some message\n" } --> -``` go +```go package main import ( - "fmt" - "log" - "os" + "fmt" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - app := &cli.App{} - app.UseShortOptionHandling = true - app.Commands = []*cli.Command{ - { - Name: "short", - Usage: "complete a task on the list", - Flags: []cli.Flag{ - &cli.BoolFlag{Name: "serve", Aliases: []string{"s"}}, - &cli.BoolFlag{Name: "option", Aliases: []string{"o"}}, - &cli.StringFlag{Name: "message", Aliases: []string{"m"}}, - }, - Action: func(c *cli.Context) error { - fmt.Println("serve:", c.Bool("serve")) - fmt.Println("option:", c.Bool("option")) - fmt.Println("message:", c.String("message")) - return nil - }, - }, - } + app := &cli.App{ + UseShortOptionHandling: true, + Commands: []*cli.Command{ + { + Name: "short", + Usage: "complete a task on the list", + Flags: []cli.Flag{ + &cli.BoolFlag{Name: "serve", Aliases: []string{"s"}}, + &cli.BoolFlag{Name: "option", Aliases: []string{"o"}}, + &cli.StringFlag{Name: "message", Aliases: []string{"m"}}, + }, + Action: func(cCtx *cli.Context) error { + fmt.Println("serve:", cCtx.Bool("serve")) + fmt.Println("option:", cCtx.Bool("option")) + fmt.Println("message:", cCtx.String("message")) + return nil + }, + }, + }, + } - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` @@ -953,7 +937,7 @@ optionally one non-bool flag `message`, with the short options of `-s`, `-o`, and `-m` respectively, setting `UseShortOptionHandling` will also support the following syntax: -``` +```sh-session $ cmd -som "Some message" ``` @@ -964,69 +948,73 @@ have a single leading `-` or this will result in failures. For example, ### Bash Completion -You can enable completion commands by setting the `EnableBashCompletion` -flag on the `App` object to `true`. By default, this setting will allow auto-completion -for an app's subcommands, but you can write your own completion methods for -the App or its subcommands as well. +You can enable completion commands by setting the `EnableBashCompletion` flag on +the `App` object to `true`. By default, this setting will allow auto-completion +for an app's subcommands, but you can write your own completion methods for the +App or its subcommands as well. #### Default auto-completion ```go package main + import ( "fmt" "log" "os" + "github.com/urfave/cli/v2" ) + func main() { - app := cli.NewApp() - app.EnableBashCompletion = true - app.Commands = []*cli.Command{ - { - Name: "add", - Aliases: []string{"a"}, - Usage: "add a task to the list", - Action: func(c *cli.Context) error { - fmt.Println("added task: ", c.Args().First()) - return nil - }, - }, - { - Name: "complete", - Aliases: []string{"c"}, - Usage: "complete a task on the list", - Action: func(c *cli.Context) error { - fmt.Println("completed task: ", c.Args().First()) - return nil - }, - }, - { - Name: "template", - Aliases: []string{"t"}, - Usage: "options for task templates", - Subcommands: []*cli.Command{ - { - Name: "add", - Usage: "add a new template", - Action: func(c *cli.Context) error { - fmt.Println("new task template: ", c.Args().First()) - return nil - }, + app := &cli.App{ + EnableBashCompletion: true, + Commands: []*cli.Command{ + { + Name: "add", + Aliases: []string{"a"}, + Usage: "add a task to the list", + Action: func(cCtx *cli.Context) error { + fmt.Println("added task: ", cCtx.Args().First()) + return nil }, - { - Name: "remove", - Usage: "remove an existing template", - Action: func(c *cli.Context) error { - fmt.Println("removed task template: ", c.Args().First()) - return nil + }, + { + Name: "complete", + Aliases: []string{"c"}, + Usage: "complete a task on the list", + Action: func(cCtx *cli.Context) error { + fmt.Println("completed task: ", cCtx.Args().First()) + return nil + }, + }, + { + Name: "template", + Aliases: []string{"t"}, + Usage: "options for task templates", + Subcommands: []*cli.Command{ + { + Name: "add", + Usage: "add a new template", + Action: func(cCtx *cli.Context) error { + fmt.Println("new task template: ", cCtx.Args().First()) + return nil + }, + }, + { + Name: "remove", + Usage: "remove an existing template", + Action: func(cCtx *cli.Context) error { + fmt.Println("removed task template: ", cCtx.Args().First()) + return nil + }, }, }, }, }, } - err := app.Run(os.Args) - if err != nil { + + if err := app.Run(os.Args); err != nil { log.Fatal(err) } } @@ -1038,48 +1026,47 @@ func main() { "args": ["complete", "--generate-bash-completion"], "output": "laundry" } --> -``` go +```go package main import ( - "fmt" - "log" - "os" + "fmt" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - tasks := []string{"cook", "clean", "laundry", "eat", "sleep", "code"} + tasks := []string{"cook", "clean", "laundry", "eat", "sleep", "code"} - app := &cli.App{ - EnableBashCompletion: true, - Commands: []*cli.Command{ - { - Name: "complete", - Aliases: []string{"c"}, - Usage: "complete a task on the list", - Action: func(c *cli.Context) error { - fmt.Println("completed task: ", c.Args().First()) - return nil - }, - BashComplete: func(c *cli.Context) { - // This will complete if no args are passed - if c.NArg() > 0 { - return - } - for _, t := range tasks { - fmt.Println(t) - } - }, - }, - }, - } + app := &cli.App{ + EnableBashCompletion: true, + Commands: []*cli.Command{ + { + Name: "complete", + Aliases: []string{"c"}, + Usage: "complete a task on the list", + Action: func(cCtx *cli.Context) error { + fmt.Println("completed task: ", cCtx.Args().First()) + return nil + }, + BashComplete: func(cCtx *cli.Context) { + // This will complete if no args are passed + if cCtx.NArg() > 0 { + return + } + for _, t := range tasks { + fmt.Println(t) + } + }, + }, + }, + } - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` ![](/docs/v2/images/custom-bash-autocomplete.gif) @@ -1089,14 +1076,18 @@ func main() { To enable auto-completion for the current shell session, a bash script, `autocomplete/bash_autocomplete` is included in this repo. -To use `autocomplete/bash_autocomplete` set an environment variable named `PROG` to -the name of your program and then `source` the `autocomplete/bash_autocomplete` file. +To use `autocomplete/bash_autocomplete` set an environment variable named `PROG` +to the name of your program and then `source` the +`autocomplete/bash_autocomplete` file. For example, if your cli program is called `myprogram`: -`PROG=myprogram source path/to/cli/autocomplete/bash_autocomplete` +```sh-session +$ PROG=myprogram source path/to/cli/autocomplete/bash_autocomplete +``` -Auto-completion is now enabled for the current shell, but will not persist into a new shell. +Auto-completion is now enabled for the current shell, but will not persist into +a new shell. #### Distribution and Persistent Autocompletion @@ -1105,28 +1096,30 @@ it to the name of the program you wish to add autocomplete support for (or automatically install it there if you are distributing a package). Don't forget to source the file or restart your shell to activate the auto-completion. -``` -sudo cp path/to/autocomplete/bash_autocomplete /etc/bash_completion.d/ -source /etc/bash_completion.d/ +```sh-session +$ sudo cp path/to/autocomplete/bash_autocomplete /etc/bash_completion.d/ +$ source /etc/bash_completion.d/ ``` Alternatively, you can just document that users should `source` the generic -`autocomplete/bash_autocomplete` and set `$PROG` within their bash configuration +`autocomplete/bash_autocomplete` and set `$PROG` within their bash configuration file, adding these lines: +```sh-session +$ PROG= +$ source path/to/cli/autocomplete/bash_autocomplete ``` -PROG= -source path/to/cli/autocomplete/bash_autocomplete -``` -Keep in mind that if they are enabling auto-completion for more than one program, -they will need to set `PROG` and source `autocomplete/bash_autocomplete` for each -program, like so: -``` -PROG= -source path/to/cli/autocomplete/bash_autocomplete -PROG= -source path/to/cli/autocomplete/bash_autocomplete +Keep in mind that if they are enabling auto-completion for more than one +program, they will need to set `PROG` and source +`autocomplete/bash_autocomplete` for each program, like so: + +```sh-session +$ PROG= +$ source path/to/cli/autocomplete/bash_autocomplete + +$ PROG= +$ source path/to/cli/autocomplete/bash_autocomplete ``` #### Customization @@ -1138,64 +1131,73 @@ The default shell completion flag (`--generate-bash-completion`) is defined as "args": ["--generate-bash-completion"], "output": "wat\nhelp\nh" } --> -``` go +```go package main import ( - "log" - "os" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - app := &cli.App{ - EnableBashCompletion: true, - Commands: []*cli.Command{ - { - Name: "wat", - }, - }, - } - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + app := &cli.App{ + EnableBashCompletion: true, + Commands: []*cli.Command{ + { + Name: "wat", + }, + }, + } + + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` #### ZSH Support -Auto-completion for ZSH is also supported using the `autocomplete/zsh_autocomplete` -file included in this repo. One environment variable is used, `PROG`. Set -`PROG` to the program name as before, and then `source path/to/autocomplete/zsh_autocomplete`. -Adding the following lines to your ZSH configuration file (usually `.zshrc`) -will allow the auto-completion to persist across new shells: +Auto-completion for ZSH is also supported using the +`autocomplete/zsh_autocomplete` file included in this repo. One environment +variable is used, `PROG`. Set `PROG` to the program name as before, and then +`source path/to/autocomplete/zsh_autocomplete`. Adding the following lines to +your ZSH configuration file (usually `.zshrc`) will allow the auto-completion to +persist across new shells: + +```sh-session +$ PROG= +$ source path/to/autocomplete/zsh_autocomplete ``` -PROG= -source path/to/autocomplete/zsh_autocomplete -``` + #### ZSH default auto-complete example ![](/docs/v2/images/default-zsh-autocomplete.gif) + #### ZSH custom auto-complete example ![](/docs/v2/images/custom-zsh-autocomplete.gif) #### PowerShell Support -Auto-completion for PowerShell is also supported using the `autocomplete/powershell_autocomplete.ps1` -file included in this repo. -Rename the script to `.ps1` and move it anywhere in your file system. -The location of script does not matter, only the file name of the script has to match -the your program's binary name. +Auto-completion for PowerShell is also supported using the +`autocomplete/powershell_autocomplete.ps1` file included in this repo. -To activate it, enter `& path/to/autocomplete/.ps1` +Rename the script to `.ps1` and move it anywhere in your file +system. The location of script does not matter, only the file name of the +script has to match the your program's binary name. -To persist across new shells, open the PowerShell profile (with `code $profile` or `notepad $profile`) -and add the line: -``` +To activate it, enter: + +```powershell & path/to/autocomplete/.ps1 ``` +To persist across new shells, open the PowerShell profile (with `code $profile` +or `notepad $profile`) and add the line: + +```powershell +& path/to/autocomplete/.ps1 +``` ### Generated Help Text @@ -1214,20 +1216,20 @@ e.g.: -``` go +```go package main import ( - "fmt" - "io" - "os" + "fmt" + "io" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - // EXAMPLE: Append to an existing template - cli.AppHelpTemplate = fmt.Sprintf(`%s + // EXAMPLE: Append to an existing template + cli.AppHelpTemplate = fmt.Sprintf(`%s WEBSITE: http://awesometown.example.com @@ -1235,8 +1237,8 @@ SUPPORT: support@awesometown.example.com `, cli.AppHelpTemplate) - // EXAMPLE: Override a template - cli.AppHelpTemplate = `NAME: + // EXAMPLE: Override a template + cli.AppHelpTemplate = `NAME: {{.Name}} - {{.Usage}} USAGE: {{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}} @@ -1257,12 +1259,12 @@ VERSION: {{end}} ` - // EXAMPLE: Replace the `HelpPrinter` func - cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) { - fmt.Println("Ha HA. I pwnd the help!!1") - } + // EXAMPLE: Replace the `HelpPrinter` func + cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) { + fmt.Println("Ha HA. I pwnd the help!!1") + } - (&cli.App{}).Run(os.Args) + (&cli.App{}).Run(os.Args) } ``` @@ -1273,24 +1275,24 @@ setting `cli.HelpFlag`, e.g.: "args": ["--halp"], "output": "haaaaalp.*HALP" } --> -``` go +```go package main import ( - "os" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - cli.HelpFlag = &cli.BoolFlag{ - Name: "haaaaalp", - Aliases: []string{"halp"}, - Usage: "HALP", - EnvVars: []string{"SHOW_HALP", "HALPPLZ"}, - } + cli.HelpFlag = &cli.BoolFlag{ + Name: "haaaaalp", + Aliases: []string{"halp"}, + Usage: "HALP", + EnvVars: []string{"SHOW_HALP", "HALPPLZ"}, + } - (&cli.App{}).Run(os.Args) + (&cli.App{}).Run(os.Args) } ``` @@ -1309,105 +1311,110 @@ setting `cli.VersionFlag`, e.g.: "args": ["--print-version"], "output": "partay version v19\\.99\\.0" } --> -``` go +```go package main import ( - "os" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - cli.VersionFlag = &cli.BoolFlag{ - Name: "print-version", - Aliases: []string{"V"}, - Usage: "print only the version", - } + cli.VersionFlag = &cli.BoolFlag{ + Name: "print-version", + Aliases: []string{"V"}, + Usage: "print only the version", + } - app := &cli.App{ - Name: "partay", - Version: "v19.99.0", - } - app.Run(os.Args) + app := &cli.App{ + Name: "partay", + Version: "v19.99.0", + } + app.Run(os.Args) } ``` -Alternatively, the version printer at `cli.VersionPrinter` may be overridden, e.g.: +Alternatively, the version printer at `cli.VersionPrinter` may be overridden, +e.g.: -``` go +```go package main import ( - "fmt" - "os" + "fmt" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) var ( - Revision = "fafafaf" + Revision = "fafafaf" ) func main() { - cli.VersionPrinter = func(c *cli.Context) { - fmt.Printf("version=%s revision=%s\n", c.App.Version, Revision) - } + cli.VersionPrinter = func(cCtx *cli.Context) { + fmt.Printf("version=%s revision=%s\n", cCtx.App.Version, Revision) + } - app := &cli.App{ - Name: "partay", - Version: "v19.99.0", - } - app.Run(os.Args) + app := &cli.App{ + Name: "partay", + Version: "v19.99.0", + } + app.Run(os.Args) } ``` ### Timestamp Flag -Using the timestamp flag is simple. Please refer to [`time.Parse`](https://golang.org/pkg/time/#example_Parse) to get possible formats. +Using the timestamp flag is simple. Please refer to +[`time.Parse`](https://golang.org/pkg/time/#example_Parse) to get possible +formats. -``` go +```go package main import ( - "fmt" - "log" - "os" + "fmt" + "log" + "os" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func main() { - app := &cli.App{ - Flags: []cli.Flag { - &cli.TimestampFlag{Name: "meeting", Layout: "2006-01-02T15:04:05"}, - }, - Action: func(c *cli.Context) error { - fmt.Printf("%s", c.Timestamp("meeting").String()) - return nil - }, - } + app := &cli.App{ + Flags: []cli.Flag{ + &cli.TimestampFlag{Name: "meeting", Layout: "2006-01-02T15:04:05"}, + }, + Action: func(cCtx *cli.Context) error { + fmt.Printf("%s", cCtx.Timestamp("meeting").String()) + return nil + }, + } - err := app.Run(os.Args) - if err != nil { - log.Fatal(err) - } + if err := app.Run(os.Args); err != nil { + log.Fatal(err) + } } ``` -In this example the flag could be used like this : +In this example the flag could be used like this: -`myapp --meeting 2019-08-12T15:04:05` +```sh-session +$ myapp --meeting 2019-08-12T15:04:05 +``` -Side note: quotes may be necessary around the date depending on your layout (if you have spaces for instance) +Side note: quotes may be necessary around the date depending on your layout (if +you have spaces for instance) ### Suggestions @@ -1419,265 +1426,265 @@ available. ### Full API Example **Notice**: This is a contrived (functioning) example meant strictly for API -demonstration purposes. Use of one's imagination is encouraged. +demonstration purposes. Use of one's imagination is encouraged. -``` go +```go package main import ( - "errors" - "flag" - "fmt" - "io" - "io/ioutil" - "os" - "time" + "errors" + "flag" + "fmt" + "io" + "io/ioutil" + "os" + "time" - "github.com/urfave/cli/v2" + "github.com/urfave/cli/v2" ) func init() { - cli.AppHelpTemplate += "\nCUSTOMIZED: you bet ur muffins\n" - cli.CommandHelpTemplate += "\nYMMV\n" - cli.SubcommandHelpTemplate += "\nor something\n" + cli.AppHelpTemplate += "\nCUSTOMIZED: you bet ur muffins\n" + cli.CommandHelpTemplate += "\nYMMV\n" + cli.SubcommandHelpTemplate += "\nor something\n" - cli.HelpFlag = &cli.BoolFlag{Name: "halp"} - cli.VersionFlag = &cli.BoolFlag{Name: "print-version", Aliases: []string{"V"}} + cli.HelpFlag = &cli.BoolFlag{Name: "halp"} + cli.VersionFlag = &cli.BoolFlag{Name: "print-version", Aliases: []string{"V"}} - cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) { - fmt.Fprintf(w, "best of luck to you\n") - } - cli.VersionPrinter = func(c *cli.Context) { - fmt.Fprintf(c.App.Writer, "version=%s\n", c.App.Version) - } - cli.OsExiter = func(c int) { - fmt.Fprintf(cli.ErrWriter, "refusing to exit %d\n", c) - } - cli.ErrWriter = ioutil.Discard - cli.FlagStringer = func(fl cli.Flag) string { - return fmt.Sprintf("\t\t%s", fl.Names()[0]) - } + cli.HelpPrinter = func(w io.Writer, templ string, data interface{}) { + fmt.Fprintf(w, "best of luck to you\n") + } + cli.VersionPrinter = func(cCtx *cli.Context) { + fmt.Fprintf(cCtx.App.Writer, "version=%s\n", cCtx.App.Version) + } + cli.OsExiter = func(cCtx int) { + fmt.Fprintf(cli.ErrWriter, "refusing to exit %d\n", cCtx) + } + cli.ErrWriter = ioutil.Discard + cli.FlagStringer = func(fl cli.Flag) string { + return fmt.Sprintf("\t\t%s", fl.Names()[0]) + } } type hexWriter struct{} func (w *hexWriter) Write(p []byte) (int, error) { - for _, b := range p { - fmt.Printf("%x", b) - } - fmt.Printf("\n") + for _, b := range p { + fmt.Printf("%x", b) + } + fmt.Printf("\n") - return len(p), nil + return len(p), nil } type genericType struct { - s string + s string } func (g *genericType) Set(value string) error { - g.s = value - return nil + g.s = value + return nil } func (g *genericType) String() string { - return g.s + return g.s } func main() { - app := &cli.App{ - Name: "kənˈtrīv", - Version: "v19.99.0", - Compiled: time.Now(), - Authors: []*cli.Author{ - &cli.Author{ - Name: "Example Human", - Email: "human@example.com", - }, - }, - Copyright: "(c) 1999 Serious Enterprise", - HelpName: "contrive", - Usage: "demonstrate available API", - UsageText: "contrive - demonstrating the available API", - ArgsUsage: "[args and such]", - Commands: []*cli.Command{ - &cli.Command{ - Name: "doo", - Aliases: []string{"do"}, - Category: "motion", - Usage: "do the doo", - UsageText: "doo - does the dooing", - Description: "no really, there is a lot of dooing to be done", - ArgsUsage: "[arrgh]", - Flags: []cli.Flag{ - &cli.BoolFlag{Name: "forever", Aliases: []string{"forevvarr"}}, - }, - Subcommands: []*cli.Command{ - &cli.Command{ - Name: "wop", - Action: wopAction, - }, - }, - SkipFlagParsing: false, - HideHelp: false, - Hidden: false, - HelpName: "doo!", - BashComplete: func(c *cli.Context) { - fmt.Fprintf(c.App.Writer, "--better\n") - }, - Before: func(c *cli.Context) error { - fmt.Fprintf(c.App.Writer, "brace for impact\n") - return nil - }, - After: func(c *cli.Context) error { - fmt.Fprintf(c.App.Writer, "did we lose anyone?\n") - return nil - }, - Action: func(c *cli.Context) error { - c.Command.FullName() - c.Command.HasName("wop") - c.Command.Names() - c.Command.VisibleFlags() - fmt.Fprintf(c.App.Writer, "dodododododoodododddooooododododooo\n") - if c.Bool("forever") { - c.Command.Run(c) - } - return nil - }, - OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error { - fmt.Fprintf(c.App.Writer, "for shame\n") - return err - }, - }, - }, - Flags: []cli.Flag{ - &cli.BoolFlag{Name: "fancy"}, - &cli.BoolFlag{Value: true, Name: "fancier"}, - &cli.DurationFlag{Name: "howlong", Aliases: []string{"H"}, Value: time.Second * 3}, - &cli.Float64Flag{Name: "howmuch"}, - &cli.GenericFlag{Name: "wat", Value: &genericType{}}, - &cli.Int64Flag{Name: "longdistance"}, - &cli.Int64SliceFlag{Name: "intervals"}, - &cli.IntFlag{Name: "distance"}, - &cli.IntSliceFlag{Name: "times"}, - &cli.StringFlag{Name: "dance-move", Aliases: []string{"d"}}, - &cli.StringSliceFlag{Name: "names", Aliases: []string{"N"}}, - &cli.UintFlag{Name: "age"}, - &cli.Uint64Flag{Name: "bigage"}, - }, - EnableBashCompletion: true, - HideHelp: false, - HideVersion: false, - BashComplete: func(c *cli.Context) { - fmt.Fprintf(c.App.Writer, "lipstick\nkiss\nme\nlipstick\nringo\n") - }, - Before: func(c *cli.Context) error { - fmt.Fprintf(c.App.Writer, "HEEEERE GOES\n") - return nil - }, - After: func(c *cli.Context) error { - fmt.Fprintf(c.App.Writer, "Phew!\n") - return nil - }, - CommandNotFound: func(c *cli.Context, command string) { - fmt.Fprintf(c.App.Writer, "Thar be no %q here.\n", command) - }, - OnUsageError: func(c *cli.Context, err error, isSubcommand bool) error { - if isSubcommand { - return err - } + app := &cli.App{ + Name: "kənˈtrīv", + Version: "v19.99.0", + Compiled: time.Now(), + Authors: []*cli.Author{ + &cli.Author{ + Name: "Example Human", + Email: "human@example.com", + }, + }, + Copyright: "(c) 1999 Serious Enterprise", + HelpName: "contrive", + Usage: "demonstrate available API", + UsageText: "contrive - demonstrating the available API", + ArgsUsage: "[args and such]", + Commands: []*cli.Command{ + &cli.Command{ + Name: "doo", + Aliases: []string{"do"}, + Category: "motion", + Usage: "do the doo", + UsageText: "doo - does the dooing", + Description: "no really, there is a lot of dooing to be done", + ArgsUsage: "[arrgh]", + Flags: []cli.Flag{ + &cli.BoolFlag{Name: "forever", Aliases: []string{"forevvarr"}}, + }, + Subcommands: []*cli.Command{ + &cli.Command{ + Name: "wop", + Action: wopAction, + }, + }, + SkipFlagParsing: false, + HideHelp: false, + Hidden: false, + HelpName: "doo!", + BashComplete: func(cCtx *cli.Context) { + fmt.Fprintf(cCtx.App.Writer, "--better\n") + }, + Before: func(cCtx *cli.Context) error { + fmt.Fprintf(cCtx.App.Writer, "brace for impact\n") + return nil + }, + After: func(cCtx *cli.Context) error { + fmt.Fprintf(cCtx.App.Writer, "did we lose anyone?\n") + return nil + }, + Action: func(cCtx *cli.Context) error { + cCtx.Command.FullName() + cCtx.Command.HasName("wop") + cCtx.Command.Names() + cCtx.Command.VisibleFlags() + fmt.Fprintf(cCtx.App.Writer, "dodododododoodododddooooododododooo\n") + if cCtx.Bool("forever") { + cCtx.Command.Run(cCtx) + } + return nil + }, + OnUsageError: func(cCtx *cli.Context, err error, isSubcommand bool) error { + fmt.Fprintf(cCtx.App.Writer, "for shame\n") + return err + }, + }, + }, + Flags: []cli.Flag{ + &cli.BoolFlag{Name: "fancy"}, + &cli.BoolFlag{Value: true, Name: "fancier"}, + &cli.DurationFlag{Name: "howlong", Aliases: []string{"H"}, Value: time.Second * 3}, + &cli.Float64Flag{Name: "howmuch"}, + &cli.GenericFlag{Name: "wat", Value: &genericType{}}, + &cli.Int64Flag{Name: "longdistance"}, + &cli.Int64SliceFlag{Name: "intervals"}, + &cli.IntFlag{Name: "distance"}, + &cli.IntSliceFlag{Name: "times"}, + &cli.StringFlag{Name: "dance-move", Aliases: []string{"d"}}, + &cli.StringSliceFlag{Name: "names", Aliases: []string{"N"}}, + &cli.UintFlag{Name: "age"}, + &cli.Uint64Flag{Name: "bigage"}, + }, + EnableBashCompletion: true, + HideHelp: false, + HideVersion: false, + BashComplete: func(cCtx *cli.Context) { + fmt.Fprintf(cCtx.App.Writer, "lipstick\nkiss\nme\nlipstick\nringo\n") + }, + Before: func(cCtx *cli.Context) error { + fmt.Fprintf(cCtx.App.Writer, "HEEEERE GOES\n") + return nil + }, + After: func(cCtx *cli.Context) error { + fmt.Fprintf(cCtx.App.Writer, "Phew!\n") + return nil + }, + CommandNotFound: func(cCtx *cli.Context, command string) { + fmt.Fprintf(cCtx.App.Writer, "Thar be no %q here.\n", command) + }, + OnUsageError: func(cCtx *cli.Context, err error, isSubcommand bool) error { + if isSubcommand { + return err + } - fmt.Fprintf(c.App.Writer, "WRONG: %#v\n", err) - return nil - }, - Action: func(c *cli.Context) error { - cli.DefaultAppComplete(c) - cli.HandleExitCoder(errors.New("not an exit coder, though")) - cli.ShowAppHelp(c) - cli.ShowCommandCompletions(c, "nope") - cli.ShowCommandHelp(c, "also-nope") - cli.ShowCompletions(c) - cli.ShowSubcommandHelp(c) - cli.ShowVersion(c) + fmt.Fprintf(cCtx.App.Writer, "WRONG: %#v\n", err) + return nil + }, + Action: func(cCtx *cli.Context) error { + cli.DefaultAppComplete(cCtx) + cli.HandleExitCoder(errors.New("not an exit coder, though")) + cli.ShowAppHelp(cCtx) + cli.ShowCommandCompletions(cCtx, "nope") + cli.ShowCommandHelp(cCtx, "also-nope") + cli.ShowCompletions(cCtx) + cli.ShowSubcommandHelp(cCtx) + cli.ShowVersion(cCtx) - fmt.Printf("%#v\n", c.App.Command("doo")) - if c.Bool("infinite") { - c.App.Run([]string{"app", "doo", "wop"}) - } + fmt.Printf("%#v\n", cCtx.App.Command("doo")) + if cCtx.Bool("infinite") { + cCtx.App.Run([]string{"app", "doo", "wop"}) + } - if c.Bool("forevar") { - c.App.RunAsSubcommand(c) - } - c.App.Setup() - fmt.Printf("%#v\n", c.App.VisibleCategories()) - fmt.Printf("%#v\n", c.App.VisibleCommands()) - fmt.Printf("%#v\n", c.App.VisibleFlags()) + if cCtx.Bool("forevar") { + cCtx.App.RunAsSubcommand(cCtx) + } + cCtx.App.Setup() + fmt.Printf("%#v\n", cCtx.App.VisibleCategories()) + fmt.Printf("%#v\n", cCtx.App.VisibleCommands()) + fmt.Printf("%#v\n", cCtx.App.VisibleFlags()) - fmt.Printf("%#v\n", c.Args().First()) - if c.Args().Len() > 0 { - fmt.Printf("%#v\n", c.Args().Get(1)) - } - fmt.Printf("%#v\n", c.Args().Present()) - fmt.Printf("%#v\n", c.Args().Tail()) + fmt.Printf("%#v\n", cCtx.Args().First()) + if cCtx.Args().Len() > 0 { + fmt.Printf("%#v\n", cCtx.Args().Get(1)) + } + fmt.Printf("%#v\n", cCtx.Args().Present()) + fmt.Printf("%#v\n", cCtx.Args().Tail()) - set := flag.NewFlagSet("contrive", 0) - nc := cli.NewContext(c.App, set, c) + set := flag.NewFlagSet("contrive", 0) + nc := cli.NewContext(cCtx.App, set, cCtx) - fmt.Printf("%#v\n", nc.Args()) - fmt.Printf("%#v\n", nc.Bool("nope")) - fmt.Printf("%#v\n", !nc.Bool("nerp")) - fmt.Printf("%#v\n", nc.Duration("howlong")) - fmt.Printf("%#v\n", nc.Float64("hay")) - fmt.Printf("%#v\n", nc.Generic("bloop")) - fmt.Printf("%#v\n", nc.Int64("bonk")) - fmt.Printf("%#v\n", nc.Int64Slice("burnks")) - fmt.Printf("%#v\n", nc.Int("bips")) - fmt.Printf("%#v\n", nc.IntSlice("blups")) - fmt.Printf("%#v\n", nc.String("snurt")) - fmt.Printf("%#v\n", nc.StringSlice("snurkles")) - fmt.Printf("%#v\n", nc.Uint("flub")) - fmt.Printf("%#v\n", nc.Uint64("florb")) + fmt.Printf("%#v\n", nc.Args()) + fmt.Printf("%#v\n", nc.Bool("nope")) + fmt.Printf("%#v\n", !nc.Bool("nerp")) + fmt.Printf("%#v\n", nc.Duration("howlong")) + fmt.Printf("%#v\n", nc.Float64("hay")) + fmt.Printf("%#v\n", nc.Generic("bloop")) + fmt.Printf("%#v\n", nc.Int64("bonk")) + fmt.Printf("%#v\n", nc.Int64Slice("burnks")) + fmt.Printf("%#v\n", nc.Int("bips")) + fmt.Printf("%#v\n", nc.IntSlice("blups")) + fmt.Printf("%#v\n", nc.String("snurt")) + fmt.Printf("%#v\n", nc.StringSlice("snurkles")) + fmt.Printf("%#v\n", nc.Uint("flub")) + fmt.Printf("%#v\n", nc.Uint64("florb")) - fmt.Printf("%#v\n", nc.FlagNames()) - fmt.Printf("%#v\n", nc.IsSet("wat")) - fmt.Printf("%#v\n", nc.Set("wat", "nope")) - fmt.Printf("%#v\n", nc.NArg()) - fmt.Printf("%#v\n", nc.NumFlags()) - fmt.Printf("%#v\n", nc.Lineage()[1]) - nc.Set("wat", "also-nope") + fmt.Printf("%#v\n", nc.FlagNames()) + fmt.Printf("%#v\n", nc.IsSet("wat")) + fmt.Printf("%#v\n", nc.Set("wat", "nope")) + fmt.Printf("%#v\n", nc.NArg()) + fmt.Printf("%#v\n", nc.NumFlags()) + fmt.Printf("%#v\n", nc.Lineage()[1]) + nc.Set("wat", "also-nope") - ec := cli.Exit("ohwell", 86) - fmt.Fprintf(c.App.Writer, "%d", ec.ExitCode()) - fmt.Printf("made it!\n") - return ec - }, - Metadata: map[string]interface{}{ - "layers": "many", - "explicable": false, - "whatever-values": 19.99, - }, - } + ec := cli.Exit("ohwell", 86) + fmt.Fprintf(cCtx.App.Writer, "%d", ec.ExitCode()) + fmt.Printf("made it!\n") + return ec + }, + Metadata: map[string]interface{}{ + "layers": "many", + "explicable": false, + "whatever-values": 19.99, + }, + } - if os.Getenv("HEXY") != "" { - app.Writer = &hexWriter{} - app.ErrWriter = &hexWriter{} - } + if os.Getenv("HEXY") != "" { + app.Writer = &hexWriter{} + app.ErrWriter = &hexWriter{} + } - app.Run(os.Args) + app.Run(os.Args) } -func wopAction(c *cli.Context) error { - fmt.Fprintf(c.App.Writer, ":wave: over here, eh\n") - return nil +func wopAction(cCtx *cli.Context) error { + fmt.Fprintf(cCtx.App.Writer, ":wave: over here, eh\n") + return nil } ``` ## Migrating From Older Releases -There are a small set of breaking changes between v1 and v2. -Converting is relatively straightforward and typically takes less than -an hour. Specific steps are included in -[Migration Guide: v1 to v2](../migrate-v1-to-v2.md). Also see the [pkg.go.dev docs](https://pkg.go.dev/github.com/urfave/cli/v2) for v2 API documentation. - +There are a small set of breaking changes between v1 and v2. Converting is +relatively straightforward and typically takes less than an hour. Specific steps +are included in [Migration Guide: v1 to v2](../migrate-v1-to-v2.md). Also see +the [pkg.go.dev docs](https://pkg.go.dev/github.com/urfave/cli/v2) for v2 API +documentation.