diff --git a/docs/v3/examples/arguments.md b/docs/v3/examples/arguments.md deleted file mode 100644 index acf902035d..0000000000 --- a/docs/v3/examples/arguments.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -tags: - - v3 -search: - boost: 2 ---- - -You can lookup arguments by calling the `Args` function on `cli.Command`, e.g.: - - -```go -package main - -import ( - "fmt" - "log" - "os" - "context" - - "github.com/urfave/cli/v3" -) - -func main() { - cmd := &cli.Command{ - Action: func(ctx context.Context, cmd *cli.Command) error { - fmt.Printf("Hello %q", cmd.Args().Get(0)) - return nil - }, - } - - if err := cmd.Run(context.Background(), os.Args); err != nil { - log.Fatal(err) - } -} -``` diff --git a/docs/v3/examples/arguments/advanced.md b/docs/v3/examples/arguments/advanced.md new file mode 100644 index 0000000000..2a0a681fe9 --- /dev/null +++ b/docs/v3/examples/arguments/advanced.md @@ -0,0 +1,69 @@ +--- +tags: + - v3 +search: + boost: 2 +--- + +Instead of the user having to look through all the arguments one by one we can also specify the argument types and destination +fields so that the value can be directly retrieved by the user. Lets go back to the greeter app and specifying the types for arguments + + +```go +package main + +import ( + "fmt" + "log" + "os" + "context" + + "github.com/urfave/cli/v3" +) + +func main() { + var name, soap string + var count int64 + var version float64 + cmd := &cli.Command{ + Arguments: []Argument{ + &StringArg{ + Name: "name", + Min: 1, + Max: 1, + Destination: &name, + }, + &IntArg{ + Name: "count", + Min: 1, + Max: 1, + Destination: &count, + }, + &StringArg{ + Name: "soap", + Min: 1, + Max: 1, + Destination: &soap, + }, + &FloatArg{ + Name: "version", + Min: 1, + Max: 1, + Destination: &version, + }, + }, + Action: func(ctx context.Context, cmd *cli.Command) error { + fmt.Printf("%s-%d-%s-%f", name, count, soap, version) + return nil + }, + } + + if err := cmd.Run(context.Background(), os.Args); err != nil { + log.Fatal(err) + } +} +``` + diff --git a/docs/v3/examples/arguments/basics.md b/docs/v3/examples/arguments/basics.md new file mode 100644 index 0000000000..ddde3a4779 --- /dev/null +++ b/docs/v3/examples/arguments/basics.md @@ -0,0 +1,93 @@ +--- +tags: + - v3 +search: + boost: 2 +--- + +Lets add some arguments to our greeter app. This allows you to change the behaviour of +the app depending on what argument has been passed. You can lookup arguments by calling +the `Args` function on `cli.Command`, e.g.: + + +```go +package main + +import ( + "fmt" + "log" + "os" + "context" + + "github.com/urfave/cli/v3" +) + +func main() { + cmd := &cli.Command{ + Action: func(ctx context.Context, cmd *cli.Command) error { + fmt.Printf("Hello %q", cmd.Args().Get(0)) + return nil + }, + } + + if err := cmd.Run(context.Background(), os.Args); err != nil { + log.Fatal(err) + } +} +``` + +Running this program with an argument gives the following output + +```sh-session +$ greet friend +Hello "Friend" +``` + +Any number of arguments can be passed to the greeter app. We can get the number of arguments +and each argument using the `Args` + + +```go +package main + +import ( + "fmt" + "log" + "os" + "context" + + "github.com/urfave/cli/v3" +) + +func main() { + cmd := &cli.Command{ + Action: func(ctx context.Context, cmd *cli.Command) error { + fmt.Printf("Number of args : %d\n", cmd.Args().Len()) + var out string + for i := 0; i < cmd.Args().Len(); i++ { + out = out + fmt.Sprintf(" %v", cmd.Args().Get(i)) + } + fmt.Printf("Hello%v", out) + return nil + }, + } + + if err := cmd.Run(context.Background(), os.Args); err != nil { + log.Fatal(err) + } +} +``` + +Running this program with an argument gives the following output + +```sh-session +$ greet Friend 1 bar 2.0 +Number of args : 4 +Hello Friend 1 bar 2.0 +``` diff --git a/docs/v3/examples/shell-completions.md b/docs/v3/examples/completions/shell-completions.md similarity index 52% rename from docs/v3/examples/shell-completions.md rename to docs/v3/examples/completions/shell-completions.md index a8aa8d789b..0d3225defc 100644 --- a/docs/v3/examples/shell-completions.md +++ b/docs/v3/examples/completions/shell-completions.md @@ -5,10 +5,113 @@ search: boost: 2 --- -You can enable completion commands by setting the `EnableShellCompletion` flag on -the `Command` object to `true`. By default, this setting will allow auto-completion -for an command's subcommands, but you can write your own completion methods for the -command or its subcommands as well. +The urfave/cli v3 library supports programmable completion for apps utilizing its framework. This means +that the completion is generated dynamically at runtime by invokiong the app itself with a special hidden +flag. The urfave/cli searches for this flag and activates a different flow for command paths than regular flow +The following shells are supported + - bash + - zsh + - fish + - powershell + +Enabling auto complete requires 2 things + - Setting the `EnableShellCompletion` field on root `Command` object to `true`. + - Sourcing the completion script for that particular shell. + + The completion script for a particular shell can be retrieved by running the "completion" subcommand + on the app after the `EnableShellCompletion` field on root `Command` object has been set to `true`. + +Consider the following program + + ```go +package main + +import ( + "fmt" + "log" + "os" + "context" + + "github.com/urfave/cli/v3" +) + +func main() { + cmd := &cli.Command{ + Name: "greet", + EnableShellCompletion: true, + Commands: []*cli.Command{ + { + Name: "add", + Aliases: []string{"a"}, + Usage: "add a task to the list", + Action: func(ctx context.Context, cmd *cli.Command) error { + fmt.Println("added task: ", cmd.Args().First()) + return nil + }, + }, + { + Name: "complete", + Aliases: []string{"c"}, + Usage: "complete a task on the list", + Action: func(ctx context.Context, cmd *cli.Command) error { + fmt.Println("completed task: ", cmd.Args().First()) + return nil + }, + }, + { + Name: "template", + Aliases: []string{"t"}, + Usage: "options for task templates", + Commands: []*cli.Command{ + { + Name: "add", + Usage: "add a new template", + Action: func(ctx context.Context, cmd *cli.Command) error { + fmt.Println("new task template: ", cmd.Args().First()) + return nil + }, + }, + { + Name: "remove", + Usage: "remove an existing template", + Action: func(ctx context.Context, cmd *cli.Command) error { + fmt.Println("removed task template: ", cmd.Args().First()) + return nil + }, + }, + }, + }, + }, + } + + if err := cmd.Run(context.Background(), os.Args); err != nil { + log.Fatal(err) + } +} +``` + +After compiling this app as `greet` we can generate the autocompletion as following +in bash script + +```sh-session +$ greet completion bash +``` + +This file can be saved to /etc/bash_completion.d/greet or $HOME/.bash_completion.d/greet +where it will be automatically picked in new bash shells. For the current shell these +can be sourced either using filename or from generation command directly + +```sh-session +$ source ~/.bash_completion.d/greet +``` + +```sh-session +$ source <(greet completion bash) +``` + +The procedure for other shells is similar to bash though the specific paths for each of the +shells may vary. Some of the sections below detail the setup need for other shells as +well as examples in those shells. #### Default auto-completion @@ -77,7 +180,7 @@ func main() { } } ``` -![](../images/default-bash-autocomplete.gif) +![](../../images/default-bash-autocomplete.gif) #### Custom auto-completion diff --git a/docs/v3/examples/flags.md b/docs/v3/examples/flags/advanced.md similarity index 55% rename from docs/v3/examples/flags.md rename to docs/v3/examples/flags/advanced.md index b311e6a897..4c6566f8b0 100644 --- a/docs/v3/examples/flags.md +++ b/docs/v3/examples/flags/advanced.md @@ -5,16 +5,19 @@ search: boost: 2 --- -Setting and querying flags is simple. +#### Alternate Names + +You can set alternate (or short) names for flags by providing a list of strings for `Aliases` +e.g. ```go package main import ( - "fmt" "log" "os" "context" @@ -26,23 +29,12 @@ func main() { cmd := &cli.Command{ Flags: []cli.Flag{ &cli.StringFlag{ - Name: "lang", - Value: "english", - Usage: "language for the greeting", + Name: "lang", + Aliases: []string{"l"}, + Value: "english", + Usage: "language for the greeting", }, }, - Action: func(ctx context.Context, cmd *cli.Command) error { - name := "Nefertiti" - if cmd.NArg() > 0 { - name = cmd.Args().Get(0) - } - if cmd.String("lang") == "spanish" { - fmt.Println("Hola", name) - } else { - fmt.Println("Hello", name) - } - return nil - }, } if err := cmd.Run(context.Background(), os.Args); err != nil { @@ -51,12 +43,27 @@ func main() { } ``` -You can also set a destination variable for a flag, to which the content will be -scanned. Note that if the `Value` is set for the flag, it will be shown as default, -and destination will be set to this value before parsing flag on the command line. +That flag can then be set with `--lang spanish` or `-l spanish`. Note that +giving two different forms of the same flag in the same command invocation is an +error. + +#### Multiple Values per Single Flag + +As noted in the basics for flag, the simple flags allow only one value per flag and only the last +entered value on command line will be returned to user on query. + +`urfave/cli` also supports multi-value flags called slice flags. These flags can take multiple values of same type. +In addition they can be invoked multiple times on the command line and values will be appended to original value +of the flag and returned to the user as a slice + +- `UintSliceFlag` +- `IntSliceFlag` +- `StringSliceFlag` +- `FloatSliceFlag` ```go package main @@ -65,33 +72,22 @@ import ( "fmt" "log" "os" + "strings" "context" "github.com/urfave/cli/v3" ) func main() { - var language string - cmd := &cli.Command{ Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "lang", - Value: "english", - Usage: "language for the greeting", - Destination: &language, + &cli.StringSliceFlag{ + Name: "greeting", + Usage: "Pass multiple greetings", }, }, Action: func(ctx context.Context, cmd *cli.Command) error { - name := "someone" - if cmd.NArg() > 0 { - name = cmd.Args().Get(0) - } - if language == "spanish" { - fmt.Println("Hola", name) - } else { - fmt.Println("Hello", name) - } + fmt.Println(strings.Join(cmd.StringSlice("greeting"), `, `)) return nil }, } @@ -102,7 +98,9 @@ func main() { } ``` -See full list of flags at https://pkg.go.dev/github.com/urfave/cli/v3 +Multiple values need to be passed as separate, repeating flags, e.g. `--greeting Hello --greeting Hola`. + +#### Count for bool flag For bool flags you can specify the flag multiple times to get a count(e.g -v -v -v or -vvv) @@ -199,94 +197,6 @@ Will result in help output like: Note that only the first placeholder is used. Subsequent back-quoted words will be left as-is. -#### Alternate Names - -You can set alternate (or short) names for flags by providing a comma-delimited -list for the `Name`. e.g. - - -```go -package main - -import ( - "log" - "os" - "context" - - "github.com/urfave/cli/v3" -) - -func main() { - cmd := &cli.Command{ - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "lang", - Aliases: []string{"l"}, - Value: "english", - Usage: "language for the greeting", - }, - }, - } - - if err := cmd.Run(context.Background(), os.Args); err != nil { - log.Fatal(err) - } -} -``` - -That flag can then be set with `--lang spanish` or `-l spanish`. Note that -giving two different forms of the same flag in the same command invocation is an -error. - -#### Multiple Values per Single Flag - -Using a slice flag allows you to pass multiple values for a single flag; the values will be provided as a slice: - -- `UintSliceFlag` -- `IntSliceFlag` -- `StringSliceFlag` - - -```go -package main - -import ( - "fmt" - "log" - "os" - "strings" - "context" - - "github.com/urfave/cli/v3" -) - -func main() { - cmd := &cli.Command{ - Flags: []cli.Flag{ - &cli.StringSliceFlag{ - Name: "greeting", - Usage: "Pass multiple greetings", - }, - }, - Action: func(ctx context.Context, cmd *cli.Command) error { - fmt.Println(strings.Join(cmd.StringSlice("greeting"), `, `)) - return nil - }, - } - - if err := cmd.Run(context.Background(), os.Args); err != nil { - log.Fatal(err) - } -} -``` - -Multiple values need to be passed as separate, repeating flags, e.g. `--greeting Hello --greeting Hola`. #### Ordering @@ -362,202 +272,9 @@ Will result in help output like: --lang value, -l value Language for the greeting (default: "english") ``` -#### Values from the Environment - -You can also have the default value set from the environment via `cli.EnvVars`. e.g. - - -```go -package main - -import ( - "log" - "os" - "context" - - "github.com/urfave/cli/v3" -) - -func main() { - cmd := &cli.Command{ - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "lang", - Aliases: []string{"l"}, - Value: "english", - Usage: "language for the greeting", - Sources: cli.EnvVars("APP_LANG"), - }, - }, - } - - if err := cmd.Run(context.Background(), os.Args); err != nil { - log.Fatal(err) - } -} -``` - -If `cli.EnvVars` contains more than one string, the first environment variable that -resolves is used. - - -```go -package main - -import ( - "log" - "os" - "context" - - "github.com/urfave/cli/v3" -) - -func main() { - cmd := &cli.Command{ - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "lang", - Aliases: []string{"l"}, - Value: "english", - Usage: "language for the greeting", - Sources: cli.EnvVars("LEGACY_COMPAT_LANG", "APP_LANG", "LANG"), - }, - }, - } - - if err := cmd.Run(context.Background(), os.Args); err != nil { - log.Fatal(err) - } -} -``` - -#### Values from files - -You can also have the default value set from file via `cli.File`. e.g. - - -```go -package main - -import ( - "log" - "os" - "context" - - "github.com/urfave/cli/v3" -) - -func main() { - cmd := &cli.Command{ - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "password", - Aliases: []string{"p"}, - Usage: "password for the mysql database", - Sources: cli.Files("/etc/mysql/password"), - }, - }, - } - - if err := cmd.Run(context.Background(), os.Args); err != nil { - log.Fatal(err) - } -} -``` - -Note that default values are set in the same order as they are defined in the -`Sources` param. This allows the user to choose order of priority - -#### Values from alternate input sources (YAML, TOML, and others) - -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 - -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 - // --- >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 - // --- >8 --- - command.Before = func(ctx context.Context, cmd *Command) (context.Context, error) { - return ctx, altsrc.InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - } -``` - -The code above will use the "load" string as a flag name to get the file name of -a yaml file from the cli.Context. It will then use that file name to initialize -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. - -Here is a more complete sample of a command using YAML support: - - -```go -package main - -import ( - "context" - "fmt" - "os" - - altsrc "github.com/urfave/cli-altsrc/v3" - "github.com/urfave/cli/v3" -) - -func main() { - flags := []cli.Flag{ - &cli.IntFlag{ - Name: "test", - Sources: altsrc.YAML("key", "/path/to/file"), - }, - &cli.StringFlag{Name: "load"}, - } - - cmd := &cli.Command{ - Action: func(context.Context, *cli.Command) error { - fmt.Println("--test value.*default: 0") - return nil - }, - Flags: flags, - } - - cmd.Run(context.Background(), os.Args) -} -``` - #### Required Flags -You can make a flag required by setting the `Required` field to `true`. If a user +You can mark a flag as *required* by setting the `Required` field to `true`. If a user does not provide a required flag, they will be shown an error message. Take for example this app that requires the `lang` flag: diff --git a/docs/v3/examples/flags/basics.md b/docs/v3/examples/flags/basics.md new file mode 100644 index 0000000000..01671e7088 --- /dev/null +++ b/docs/v3/examples/flags/basics.md @@ -0,0 +1,293 @@ +--- +tags: + - v3 +search: + boost: 2 +--- + +Flags, also called options, can be used to control various behaviour of the app +by turning on/off capabilities or setting some configuration and so on. +Setting and querying flags is done using the ```cmd.()``` +function + +Here is an example of using a StringFlag which accepts a string as its option value + + +```go +package main + +import ( + "fmt" + "log" + "os" + "context" + + "github.com/urfave/cli/v3" +) + +func main() { + cmd := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "lang", + Value: "english", + Usage: "language for the greeting", + }, + }, + Action: func(ctx context.Context, cmd *cli.Command) error { + name := "Nefertiti" + if cmd.NArg() > 0 { + name = cmd.Args().Get(0) + } + if cmd.String("lang") == "spanish" { + fmt.Println("Hola", name) + } else { + fmt.Println("Hello", name) + } + return nil + }, + } + + if err := cmd.Run(context.Background(), os.Args); err != nil { + log.Fatal(err) + } +} +``` + +This very simple program gives a lot of outputs depending on the value of the flag set. +```sh-session +$ greet +Hello Nefertiti +``` +Note that the Value for the flag is the default value that will be used when the flag +is not set on the command line. Since in the above invocation no flag was specified the +value of the "lang" flag was default to "english". Now lets change the language + +```sh-session +$ greet --lang spanish +Hola Nefertiti +``` + +Flag values can be provided with a space after the flag name or using the ```=``` sign +```sh-session +$ greet --lang=spanish +Hola Nefertiti +$ greet --lang=spanish my-friend +Hola my-friend +``` + +While the value of any flag can be retrieved using ```command.``` sometimes +it is convenient to have the value of the flag automatically stored in a destination +variable for a flag. If the `Value` is set for the flag, it will be shown as default, +and destination will be set to this value before parsing flag on the command line. + + +```go +package main + +import ( + "fmt" + "log" + "os" + "context" + + "github.com/urfave/cli/v3" +) + +func main() { + var language string + + cmd := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "lang", + Value: "english", + Usage: "language for the greeting", + Destination: &language, + }, + }, + Action: func(ctx context.Context, cmd *cli.Command) error { + name := "someone" + if cmd.NArg() > 0 { + name = cmd.Args().Get(0) + } + if language == "spanish" { + fmt.Println("Hola", name) + } else { + fmt.Println("Hello", name) + } + return nil + }, + } + + if err := cmd.Run(context.Background(), os.Args); err != nil { + log.Fatal(err) + } +} +``` + +Note that most flag can be invoked multiple times but only the last value entered for the flag +will be provided to the user(with some exceptions. See flags-advanced.md) + +The following basic flags are supported +* IntFlag +* UintFlag +* BoolFlag +* DurationFlag +* FloatFlag +* StringFlag +* TimestampFlag + +For full list of flags see https://pkg.go.dev/github.com/urfave/cli/v3 + +### Timestamp Flag ### + +Using the timestamp flag is similar to other flags but special attention is need +for the format to be provided to the flag . Please refer to +[`time.Parse`](https://golang.org/pkg/time/#example_Parse) to get possible +formats. + + +```go +package main + +import ( + "fmt" + "log" + "os" + "context" + + "github.com/urfave/cli/v3" +) + +func main() { + cmd := &cli.Command{ + Flags: []cli.Flag{ + &cli.TimestampFlag{ + Name: "meeting", + Config: cli.TimestampConfig{ + Layouts: []string{"2006-01-02T15:04:05"}, + }, + }, + }, + Action: func(ctx context.Context, cmd *cli.Command) error { + fmt.Printf("%s", cmd.Timestamp("meeting").String()) + return nil + }, + } + + if err := cmd.Run(context.Background(), os.Args); err != nil { + log.Fatal(err) + } +} +``` + +In this example the flag could be used like this: + +```sh-session +$ myapp --meeting 2019-08-12T15:04:05 +``` + +When the layout doesn't contain timezones, timestamp will render with UTC. To +change behavior, a default timezone can be provided with flag definition: + +```go +cmd := &cli.Command{ + Flags: []cli.Flag{ + &cli.TimestampFlag{ + Name: "meeting", + Config: cli.TimestampConfig{ + Timezone: time.Local, + AvailableLayouts: []string{"2006-01-02T15:04:05"}, + }, + }, + }, +} +``` + +(time.Local contains the system's local time zone.) + +Side note: quotes may be necessary around the date depending on your layout (if +you have spaces for instance) + +### Version Flags ### + +A default version flag (`-v/--version`) is provided as `cli.VersionFlag`, which +is checked by the cli internals in order to print the `Command.Version` via +`cli.VersionPrinter` and break execution. + +#### Customization + +The default flag may be customized to something other than `-v/--version` by +setting fields of `cli.VersionFlag`, e.g.: + + +```go +package main + +import ( + "os" + "context" + + "github.com/urfave/cli/v3" +) + +func main() { + cli.VersionFlag = &cli.BoolFlag{ + Name: "print-version", + Aliases: []string{"V"}, + Usage: "print only the version", + } + + cmd := &cli.Command{ + Name: "partay", + Version: "v19.99.0", + } + cmd.Run(context.Background(), os.Args) +} +``` + +Alternatively, the version printer at `cli.VersionPrinter` may be overridden, +e.g.: + + +```go +package main + +import ( + "fmt" + "os" + "context" + + "github.com/urfave/cli/v3" +) + +var ( + Revision = "fafafaf" +) + +func main() { + cli.VersionPrinter = func(cmd *cli.Command) { + fmt.Printf("version=%s revision=%s\n", cmd.Root().Version, Revision) + } + + cmd := &cli.Command{ + Name: "partay", + Version: "v19.99.0", + } + cmd.Run(context.Background(), os.Args) +} +``` diff --git a/docs/v3/examples/combining-short-options.md b/docs/v3/examples/flags/short-options.md similarity index 100% rename from docs/v3/examples/combining-short-options.md rename to docs/v3/examples/flags/short-options.md diff --git a/docs/v3/examples/flags/value-sources.md b/docs/v3/examples/flags/value-sources.md new file mode 100644 index 0000000000..8627396d8c --- /dev/null +++ b/docs/v3/examples/flags/value-sources.md @@ -0,0 +1,213 @@ +--- +tags: + - v3 +search: + boost: 2 +--- + +Flags can have their default values set from different sources. The following sources are +provided by default with `urfave/cli` + - Environment + - Text Files + +The library also provides a framework for users to plugin their own implementation of value sources +to be fetched via other mechanisms(http and so on). + +In addition there is a `urfave/cli-altsrc` repo which hosts some common value sources to read + - YAML + - JSON + - TOML +from files or via http/https. + +#### Values from the Environment + +To set a value from the environment use `cli.EnvVars`. e.g. + + +```go +package main + +import ( + "log" + "os" + "context" + + "github.com/urfave/cli/v3" +) + +func main() { + cmd := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "lang", + Aliases: []string{"l"}, + Value: "english", + Usage: "language for the greeting", + Sources: cli.EnvVars("APP_LANG"), + }, + }, + } + + if err := cmd.Run(context.Background(), os.Args); err != nil { + log.Fatal(err) + } +} +``` + +If `cli.EnvVars` contains more than one string, the first environment variable that +resolves is used. + + +```go +package main + +import ( + "log" + "os" + "context" + + "github.com/urfave/cli/v3" +) + +func main() { + cmd := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "lang", + Aliases: []string{"l"}, + Value: "english", + Usage: "language for the greeting", + Sources: cli.EnvVars("LEGACY_COMPAT_LANG", "APP_LANG", "LANG"), + }, + }, + } + + if err := cmd.Run(context.Background(), os.Args); err != nil { + log.Fatal(err) + } +} +``` + +#### Values from files + +You can also have the default value set from file via `cli.File`. e.g. + + +```go +package main + +import ( + "log" + "os" + "context" + + "github.com/urfave/cli/v3" +) + +func main() { + cmd := &cli.Command{ + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "password", + Aliases: []string{"p"}, + Usage: "password for the mysql database", + Sources: cli.Files("/etc/mysql/password"), + }, + }, + } + + if err := cmd.Run(context.Background(), os.Args); err != nil { + log.Fatal(err) + } +} +``` + +Note that default values are set in the same order as they are defined in the +`Sources` param. This allows the user to choose order of priority + +#### Values from alternate input sources (YAML, TOML, and others) + +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 + +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 + // --- >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 + // --- >8 --- + command.Before = func(ctx context.Context, cmd *Command) (context.Context, error) { + return ctx, altsrc.InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) + } +``` + +The code above will use the "load" string as a flag name to get the file name of +a yaml file from the cli.Context. It will then use that file name to initialize +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. + +Here is a more complete sample of a command using YAML support: + + +```go +package main + +import ( + "context" + "fmt" + "os" + + altsrc "github.com/urfave/cli-altsrc/v3" + "github.com/urfave/cli/v3" +) + +func main() { + flags := []cli.Flag{ + &cli.IntFlag{ + Name: "test", + Sources: altsrc.YAML("key", "/path/to/file"), + }, + &cli.StringFlag{Name: "load"}, + } + + cmd := &cli.Command{ + Action: func(context.Context, *cli.Command) error { + fmt.Println("--test value.*default: 0") + return nil + }, + Flags: flags, + } + + cmd.Run(context.Background(), os.Args) +} +``` \ No newline at end of file diff --git a/docs/v3/examples/full-api-example.md b/docs/v3/examples/full-api-example.md index edf97c58ec..7442732223 100644 --- a/docs/v3/examples/full-api-example.md +++ b/docs/v3/examples/full-api-example.md @@ -141,9 +141,20 @@ func main() { &cli.BoolFlag{Value: true, Name: "fancier"}, &cli.DurationFlag{Name: "howlong", Aliases: []string{"H"}, Value: time.Second * 3}, &cli.FloatFlag{Name: "howmuch"}, - &cli.IntFlag{Name: "longdistance"}, + &cli.IntFlag{Name: "longdistance", Validator: func (t int) error { + if t < 10 { + return fmt.Errorf("10 miles isnt long distance!!!!") + } + return nil + }}, &cli.IntSliceFlag{Name: "intervals"}, - &cli.StringFlag{Name: "dance-move", Aliases: []string{"d"}}, + &cli.StringFlag{Name: "dance-move", Aliases: []string{"d"}, Validator: func(move string) error { + moves := []string{"salsa", "tap", "two-step", "lock-step"} + if !slices.Contains(moves, move) { + return fmt.Errorf("Havent learnt %s move yet", move) + } + return nil + }}, &cli.StringSliceFlag{Name: "names", Aliases: []string{"N"}}, &cli.UintFlag{Name: "age"}, }, diff --git a/docs/v3/examples/greet.md b/docs/v3/examples/greet.md index 586a3d48a6..c43f791978 100644 --- a/docs/v3/examples/greet.md +++ b/docs/v3/examples/greet.md @@ -60,6 +60,18 @@ cli also generates neat help text: ```sh-session $ greet help +NAME: + greet - fight the loneliness! + +USAGE: + greet [global options] + +GLOBAL OPTIONS: + --help, -h show help +``` + +In general a full help with flags and subcommands would give something like this +``` NAME: greet - fight the loneliness! diff --git a/docs/v3/examples/generated-help-text.md b/docs/v3/examples/help/generated-help-text.md similarity index 100% rename from docs/v3/examples/generated-help-text.md rename to docs/v3/examples/help/generated-help-text.md diff --git a/docs/v3/examples/suggestions.md b/docs/v3/examples/help/suggestions.md similarity index 100% rename from docs/v3/examples/suggestions.md rename to docs/v3/examples/help/suggestions.md diff --git a/docs/v3/examples/subcommands.md b/docs/v3/examples/subcommands/basics.md similarity index 100% rename from docs/v3/examples/subcommands.md rename to docs/v3/examples/subcommands/basics.md diff --git a/docs/v3/examples/subcommands-categories.md b/docs/v3/examples/subcommands/categories.md similarity index 100% rename from docs/v3/examples/subcommands-categories.md rename to docs/v3/examples/subcommands/categories.md diff --git a/docs/v3/examples/timestamp-flag.md b/docs/v3/examples/timestamp-flag.md deleted file mode 100644 index 0513977949..0000000000 --- a/docs/v3/examples/timestamp-flag.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -tags: - - v3 -search: - boost: 2 ---- - -Using the timestamp flag is simple. Please refer to -[`time.Parse`](https://golang.org/pkg/time/#example_Parse) to get possible -formats. - - -```go -package main - -import ( - "fmt" - "log" - "os" - "context" - - "github.com/urfave/cli/v3" -) - -func main() { - cmd := &cli.Command{ - Flags: []cli.Flag{ - &cli.TimestampFlag{ - Name: "meeting", - Config: cli.TimestampConfig{ - Layouts: []string{"2006-01-02T15:04:05"}, - }, - }, - }, - Action: func(ctx context.Context, cmd *cli.Command) error { - fmt.Printf("%s", cmd.Timestamp("meeting").String()) - return nil - }, - } - - if err := cmd.Run(context.Background(), os.Args); err != nil { - log.Fatal(err) - } -} -``` - -In this example the flag could be used like this: - -```sh-session -$ myapp --meeting 2019-08-12T15:04:05 -``` - -When the layout doesn't contain timezones, timestamp will render with UTC. To -change behavior, a default timezone can be provided with flag definition: - -```go -cmd := &cli.Command{ - Flags: []cli.Flag{ - &cli.TimestampFlag{ - Name: "meeting", - Config: cli.TimestampConfig{ - Timezone: time.Local, - AvailableLayouts: []string{"2006-01-02T15:04:05"}, - }, - }, - }, -} -``` - -(time.Local contains the system's local time zone.) - -Side note: quotes may be necessary around the date depending on your layout (if -you have spaces for instance) diff --git a/docs/v3/examples/version-flag.md b/docs/v3/examples/version-flag.md deleted file mode 100644 index 143cecc23e..0000000000 --- a/docs/v3/examples/version-flag.md +++ /dev/null @@ -1,79 +0,0 @@ ---- -tags: - - v3 -search: - boost: 2 ---- - -The default version flag (`-v/--version`) is defined as `cli.VersionFlag`, which -is checked by the cli internals in order to print the `Command.Version` via -`cli.VersionPrinter` and break execution. - -#### Customization - -The default flag may be customized to something other than `-v/--version` by -setting `cli.VersionFlag`, e.g.: - - -```go -package main - -import ( - "os" - "context" - - "github.com/urfave/cli/v3" -) - -func main() { - cli.VersionFlag = &cli.BoolFlag{ - Name: "print-version", - Aliases: []string{"V"}, - Usage: "print only the version", - } - - cmd := &cli.Command{ - Name: "partay", - Version: "v19.99.0", - } - cmd.Run(context.Background(), os.Args) -} -``` - -Alternatively, the version printer at `cli.VersionPrinter` may be overridden, -e.g.: - - -```go -package main - -import ( - "fmt" - "os" - "context" - - "github.com/urfave/cli/v3" -) - -var ( - Revision = "fafafaf" -) - -func main() { - cli.VersionPrinter = func(cmd *cli.Command) { - fmt.Printf("version=%s revision=%s\n", cmd.Root().Version, Revision) - } - - cmd := &cli.Command{ - Name: "partay", - Version: "v19.99.0", - } - cmd.Run(context.Background(), os.Args) -} -``` diff --git a/docs/v3/getting-started.md b/docs/v3/getting-started.md index 3d7fb0267d..8c4eac17f7 100644 --- a/docs/v3/getting-started.md +++ b/docs/v3/getting-started.md @@ -37,13 +37,10 @@ NAME: hello - A new cli application USAGE: - hello [global options] [command [command options]] [arguments...] - -COMMANDS: - help, h Shows a list of commands or help for one command + hello [global options] GLOBAL OPTIONS: - --help, -h show help (default: false) + --help, -h show help ``` Let's add an action to execute and some help documentation: @@ -78,6 +75,11 @@ func main() { } } ``` +The output of above code is + +``` +boom! I say! +``` Running this already gives you a ton of functionality, plus support for things -like subcommands and flags, which are covered below. +like subcommands and flags, which are covered in a separate section. diff --git a/mkdocs.yml b/mkdocs.yml index 14c688895e..51ca24c3e0 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -22,17 +22,24 @@ nav: - Migrating From Older Releases: v3/migrating-from-older-releases.md - Examples: - Greet: v3/examples/greet.md - - Arguments: v3/examples/arguments.md - - Flags: v3/examples/flags.md - - Subcommands: v3/examples/subcommands.md - - Subcommands Categories: v3/examples/subcommands-categories.md - - Exit Codes: v3/examples/exit-codes.md - - Combining Short Options: v3/examples/combining-short-options.md - - Shell Completions: v3/examples/shell-completions.md - - Generated Help Text: v3/examples/generated-help-text.md - - Version Flag: v3/examples/version-flag.md - - Timestamp Flag: v3/examples/timestamp-flag.md - - Suggestions: v3/examples/suggestions.md + - Flags: + - Basics: v3/examples/flags/basics.md + - Value Sources: v3/examples/flags/value-sources.md + - Short Options: v3/examples/flags/short-options.md + - Advanced: v3/examples/flags/advanced.md + - Arguments: + - Basics: v3/examples/arguments/basics.md + - Advanced: v3/examples/arguments/advanced.md + - Subcommands: + - Basics: v3/examples/subcommands/basics.md + - Categories: v3/examples/subcommands/categories.md + - Completions: + - Shell Completions: v3/examples/completions/shell-completions.md + - Help Text: + - Generated Help Text: v3/examples/help/generated-help-text.md + - Suggestions: v3/examples/help/suggestions.md + - Error Handling: + - Exit Codes: v3/examples/exit-codes.md - Full API Example: v3/examples/full-api-example.md - v2 Manual: - Getting Started: v2/getting-started.md @@ -93,7 +100,7 @@ plugins: - search - redirects: redirect_maps: - 'v3/examples/bash-completions.md': 'v3/examples/shell-completions.md' + 'v3/examples/bash-completions.md': 'v3/examples/completions/shell-completions.md' - tags # NOTE: this is the recommended configuration from