Merge pull request #1453 from hay-kot/main
docs: split documentation into individual pages
This commit is contained in:
commit
7357e10160
2
.github/workflows/cli.yml
vendored
2
.github/workflows/cli.yml
vendored
@ -89,7 +89,7 @@ jobs:
|
||||
chmod +x "${GITHUB_WORKSPACE}/.local/bin/gfmrun"
|
||||
|
||||
- name: gfmrun
|
||||
run: go run internal/build/build.go gfmrun docs/v2/manual.md
|
||||
run: go run internal/build/build.go gfmrun --walk docs/v2/
|
||||
|
||||
- name: diff check
|
||||
run: |
|
||||
|
@ -12,7 +12,7 @@ an expressive way.
|
||||
These are the guides for each major supported version:
|
||||
|
||||
- [`v2`](./v2/)
|
||||
- [`v1`](./v1/)
|
||||
- [`v1`](./v1/getting-started)
|
||||
|
||||
In addition to the version-specific guides, these other documents are available:
|
||||
|
||||
|
35
docs/v1/examples/arguments.md
Normal file
35
docs/v1/examples/arguments.md
Normal file
@ -0,0 +1,35 @@
|
||||
---
|
||||
tags:
|
||||
- v1
|
||||
---
|
||||
|
||||
You can lookup arguments by calling the `Args` function on `cli.Context`, e.g.:
|
||||
|
||||
<!-- {
|
||||
"output": "Hello \""
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Action = func(c *cli.Context) error {
|
||||
fmt.Printf("Hello %q", c.Args().Get(0))
|
||||
return nil
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
119
docs/v1/examples/bash-completions.md
Normal file
119
docs/v1/examples/bash-completions.md
Normal file
@ -0,0 +1,119 @@
|
||||
---
|
||||
tags:
|
||||
- v1
|
||||
---
|
||||
|
||||
You can enable completion commands by setting the `EnableBashCompletion`
|
||||
flag on the `App` object. By default, this setting will only auto-complete to
|
||||
show an app's subcommands, but you can write your own completion methods for
|
||||
the App or its subcommands.
|
||||
|
||||
<!-- {
|
||||
"args": ["complete", "--generate-bash-completion"],
|
||||
"output": "laundry"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
tasks := []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
|
||||
|
||||
app := cli.NewApp()
|
||||
app.EnableBashCompletion = true
|
||||
app.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)
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Enabling
|
||||
|
||||
Source the `autocomplete/bash_autocomplete` file in your `.bashrc` file while
|
||||
setting the `PROG` variable to the name of your program:
|
||||
|
||||
`PROG=myprogram source /.../cli/autocomplete/bash_autocomplete`
|
||||
|
||||
#### Distribution
|
||||
|
||||
Copy `autocomplete/bash_autocomplete` into `/etc/bash_completion.d/` and rename
|
||||
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 to make it active in the current shell.
|
||||
|
||||
```
|
||||
sudo cp src/bash_autocomplete /etc/bash_completion.d/<myprogram>
|
||||
source /etc/bash_completion.d/<myprogram>
|
||||
```
|
||||
|
||||
Alternatively, you can just document that users should source the generic
|
||||
`autocomplete/bash_autocomplete` in their bash configuration with `$PROG` set
|
||||
to the name of their program (as above).
|
||||
|
||||
#### Customization
|
||||
|
||||
The default bash completion flag (`--generate-bash-completion`) is defined as
|
||||
`cli.BashCompletionFlag`, and may be redefined if desired, e.g.:
|
||||
|
||||
<!-- {
|
||||
"args": ["--compgen"],
|
||||
"output": "wat\nhelp\nh"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cli.BashCompletionFlag = cli.BoolFlag{
|
||||
Name: "compgen",
|
||||
Hidden: true,
|
||||
}
|
||||
|
||||
app := cli.NewApp()
|
||||
app.EnableBashCompletion = true
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "wat",
|
||||
},
|
||||
}
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
72
docs/v1/examples/combining-short-options.md
Normal file
72
docs/v1/examples/combining-short-options.md
Normal file
@ -0,0 +1,72 @@
|
||||
---
|
||||
tags:
|
||||
- v1
|
||||
---
|
||||
|
||||
Traditional use of options using their shortnames look like this:
|
||||
|
||||
```
|
||||
$ cmd -s -o -m "Some message"
|
||||
```
|
||||
|
||||
Suppose you want users to be able to combine options with their shortnames. This
|
||||
can be done using the `UseShortOptionHandling` bool in your app configuration,
|
||||
or for individual commands by attaching it to the command configuration. For
|
||||
example:
|
||||
|
||||
<!-- {
|
||||
"args": ["short", "-som", "Some message"],
|
||||
"output": "serve: true\noption: true\nmessage: Some message\n"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.UseShortOptionHandling = true
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "short",
|
||||
Usage: "complete a task on the list",
|
||||
Flags: []cli.Flag{
|
||||
cli.BoolFlag{Name: "serve, s"},
|
||||
cli.BoolFlag{Name: "option, o"},
|
||||
cli.StringFlag{Name: "message, 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
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If your program has any number of bool flags such as `serve` and `option`, and
|
||||
optionally one non-bool flag `message`, with the short options of `-s`, `-o`,
|
||||
and `-m` respectively, setting `UseShortOptionHandling` will also support the
|
||||
following syntax:
|
||||
|
||||
```
|
||||
$ cmd -som "Some message"
|
||||
```
|
||||
|
||||
If you enable `UseShortOptionHandling`, then you must not use any flags that
|
||||
have a single leading `-` or this will result in failures. For example,
|
||||
`-option` can no longer be used. Flags with two leading dashes (such as
|
||||
`--options`) are still valid.
|
43
docs/v1/examples/exit-codes.md
Normal file
43
docs/v1/examples/exit-codes.md
Normal file
@ -0,0 +1,43 @@
|
||||
---
|
||||
tags:
|
||||
- v1
|
||||
---
|
||||
|
||||
Calling `App.Run` will not automatically call `os.Exit`, which means that by
|
||||
default the exit code will "fall through" to being `0`. An explicit exit code
|
||||
may be set by returning a non-nil error that fulfills `cli.ExitCoder`, *or* a
|
||||
`cli.MultiError` that includes an error that fulfills `cli.ExitCoder`, e.g.:
|
||||
<!-- {
|
||||
"error": "Ginger croutons are not in the soup"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Flags = []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "ginger-crouton",
|
||||
Usage: "Add ginger croutons to the soup",
|
||||
},
|
||||
}
|
||||
app.Action = func(ctx *cli.Context) error {
|
||||
if !ctx.Bool("ginger-crouton") {
|
||||
return cli.NewExitError("Ginger croutons are not in the soup", 86)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
462
docs/v1/examples/flags.md
Normal file
462
docs/v1/examples/flags.md
Normal file
@ -0,0 +1,462 @@
|
||||
---
|
||||
tags:
|
||||
- v1
|
||||
---
|
||||
|
||||
Setting and querying flags is simple.
|
||||
|
||||
<!-- {
|
||||
"output": "Hello Nefertiti"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Flags = []cli.Flag {
|
||||
cli.StringFlag{
|
||||
Name: "lang",
|
||||
Value: "english",
|
||||
Usage: "language for the greeting",
|
||||
},
|
||||
}
|
||||
|
||||
app.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
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also set a destination variable for a flag, to which the content will be
|
||||
scanned.
|
||||
|
||||
<!-- {
|
||||
"output": "Hello someone"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"fmt"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var language string
|
||||
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Flags = []cli.Flag {
|
||||
cli.StringFlag{
|
||||
Name: "lang",
|
||||
Value: "english",
|
||||
Usage: "language for the greeting",
|
||||
Destination: &language,
|
||||
},
|
||||
}
|
||||
|
||||
app.Action = func(c *cli.Context) error {
|
||||
name := "someone"
|
||||
if c.NArg() > 0 {
|
||||
name = c.Args()[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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See full list of flags at http://godoc.org/github.com/urfave/cli
|
||||
|
||||
#### Placeholder Values
|
||||
|
||||
Sometimes it's useful to specify a flag's value within the usage string itself.
|
||||
Such placeholders are indicated with back quotes.
|
||||
|
||||
For example this:
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": "--config FILE, -c FILE"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "config, c",
|
||||
Usage: "Load configuration from `FILE`",
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Will result in help output like:
|
||||
|
||||
```
|
||||
--config FILE, -c FILE Load configuration from FILE
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": "--lang value, -l value.*language for the greeting.*default: \"english\""
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Flags = []cli.Flag {
|
||||
cli.StringFlag{
|
||||
Name: "lang, l",
|
||||
Value: "english",
|
||||
Usage: "language for the greeting",
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if 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.
|
||||
|
||||
#### Ordering
|
||||
|
||||
Flags for the application and commands are shown in the order they are defined.
|
||||
However, it's possible to sort them from outside this library by using `FlagsByName`
|
||||
or `CommandsByName` with `sort`.
|
||||
|
||||
For example this:
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": "add a task to the list\n.*complete a task on the list\n.*\n\n.*\n.*Load configuration from FILE\n.*Language for the greeting.*"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Flags = []cli.Flag {
|
||||
cli.StringFlag{
|
||||
Name: "lang, l",
|
||||
Value: "english",
|
||||
Usage: "Language for the greeting",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "config, c",
|
||||
Usage: "Load configuration from `FILE`",
|
||||
},
|
||||
}
|
||||
|
||||
app.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
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
sort.Sort(cli.FlagsByName(app.Flags))
|
||||
sort.Sort(cli.CommandsByName(app.Commands))
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Will result in help output like:
|
||||
|
||||
```
|
||||
--config FILE, -c FILE Load configuration from FILE
|
||||
--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 `EnvVar`. e.g.
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": "language for the greeting.*APP_LANG"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Flags = []cli.Flag {
|
||||
cli.StringFlag{
|
||||
Name: "lang, l",
|
||||
Value: "english",
|
||||
Usage: "language for the greeting",
|
||||
EnvVar: "APP_LANG",
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `EnvVar` may also be given as a comma-delimited "cascade", where the first
|
||||
environment variable that resolves is used as the default.
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": "language for the greeting.*LEGACY_COMPAT_LANG.*APP_LANG.*LANG"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Flags = []cli.Flag {
|
||||
cli.StringFlag{
|
||||
Name: "lang, l",
|
||||
Value: "english",
|
||||
Usage: "language for the greeting",
|
||||
EnvVar: "LEGACY_COMPAT_LANG,APP_LANG,LANG",
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Values from files
|
||||
|
||||
You can also have the default value set from file via `FilePath`. e.g.
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": "password for the mysql database"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Flags = []cli.Flag {
|
||||
cli.StringFlag{
|
||||
Name: "password, p",
|
||||
Usage: "password for the mysql database",
|
||||
FilePath: "/etc/mysql/password",
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that default values set from file (e.g. `FilePath`) take precedence over
|
||||
default values set from the environment (e.g. `EnvVar`).
|
||||
|
||||
#### 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
|
||||
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
|
||||
command.Before = 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:
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": "--test value.*default: 0"
|
||||
} -->
|
||||
``` go
|
||||
package notmain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
"github.com/urfave/cli/altsrc"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
|
||||
flags := []cli.Flag{
|
||||
altsrc.NewIntFlag(cli.IntFlag{Name: "test"}),
|
||||
cli.StringFlag{Name: "load"},
|
||||
}
|
||||
|
||||
app.Action = func(c *cli.Context) error {
|
||||
fmt.Println("yaml ist rad")
|
||||
return nil
|
||||
}
|
||||
|
||||
app.Before = altsrc.InitInputSourceWithContext(flags, altsrc.NewYamlSourceFromFlagFunc("load"))
|
||||
app.Flags = flags
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Precedence
|
||||
|
||||
The precedence for flag value sources is as follows (highest to lowest):
|
||||
|
||||
0. Command line flag value from user
|
||||
0. Environment variable (if specified)
|
||||
0. Configuration file (if specified)
|
||||
0. Default defined on the flag
|
106
docs/v1/examples/generated-help-text.md
Normal file
106
docs/v1/examples/generated-help-text.md
Normal file
@ -0,0 +1,106 @@
|
||||
---
|
||||
tags:
|
||||
- v1
|
||||
---
|
||||
|
||||
The default help flag (`-h/--help`) is defined as `cli.HelpFlag` and is checked
|
||||
by the cli internals in order to print generated help text for the app, command,
|
||||
or subcommand, and break execution.
|
||||
|
||||
#### Customization
|
||||
|
||||
All of the help text generation may be customized, and at multiple levels. The
|
||||
templates are exposed as variables `AppHelpTemplate`, `CommandHelpTemplate`, and
|
||||
`SubcommandHelpTemplate` which may be reassigned or augmented, and full override
|
||||
is possible by assigning a compatible func to the `cli.HelpPrinter` variable,
|
||||
e.g.:
|
||||
|
||||
<!-- {
|
||||
"output": "Ha HA. I pwnd the help!!1"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// EXAMPLE: Append to an existing template
|
||||
cli.AppHelpTemplate = fmt.Sprintf(`%s
|
||||
|
||||
WEBSITE: http://awesometown.example.com
|
||||
|
||||
SUPPORT: support@awesometown.example.com
|
||||
|
||||
`, cli.AppHelpTemplate)
|
||||
|
||||
// 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}}
|
||||
{{if len .Authors}}
|
||||
AUTHOR:
|
||||
{{range .Authors}}{{ . }}{{end}}
|
||||
{{end}}{{if .Commands}}
|
||||
COMMANDS:
|
||||
{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
|
||||
GLOBAL OPTIONS:
|
||||
{{range .VisibleFlags}}{{.}}
|
||||
{{end}}{{end}}{{if .Copyright }}
|
||||
COPYRIGHT:
|
||||
{{.Copyright}}
|
||||
{{end}}{{if .Version}}
|
||||
VERSION:
|
||||
{{.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")
|
||||
}
|
||||
|
||||
err := cli.NewApp().Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The default flag may be customized to something other than `-h/--help` by
|
||||
setting `cli.HelpFlag`, e.g.:
|
||||
|
||||
<!-- {
|
||||
"args": ["--halp"],
|
||||
"output": "haaaaalp.*HALP"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cli.HelpFlag = cli.BoolFlag{
|
||||
Name: "halp, haaaaalp",
|
||||
Usage: "HALP",
|
||||
EnvVar: "SHOW_HALP,HALPPLZ",
|
||||
}
|
||||
|
||||
err := cli.NewApp().Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
74
docs/v1/examples/greet.md
Normal file
74
docs/v1/examples/greet.md
Normal file
@ -0,0 +1,74 @@
|
||||
---
|
||||
tags:
|
||||
- v1
|
||||
---
|
||||
|
||||
Being a programmer can be a lonely job. Thankfully by the power of automation
|
||||
that is not the case! Let's create a greeter app to fend off our demons of
|
||||
loneliness!
|
||||
|
||||
Start by creating a directory named `greet`, and within it, add a file,
|
||||
`greet.go` with the following code in it:
|
||||
|
||||
<!-- {
|
||||
"output": "Hello friend!"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "greet"
|
||||
app.Usage = "fight the loneliness!"
|
||||
app.Action = func(c *cli.Context) error {
|
||||
fmt.Println("Hello friend!")
|
||||
return nil
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Install our command to the `$GOPATH/bin` directory:
|
||||
|
||||
```
|
||||
$ go install
|
||||
```
|
||||
|
||||
Finally run our new command:
|
||||
|
||||
```
|
||||
$ greet
|
||||
Hello friend!
|
||||
```
|
||||
|
||||
cli also generates neat help text:
|
||||
|
||||
```
|
||||
$ greet help
|
||||
NAME:
|
||||
greet - fight the loneliness!
|
||||
|
||||
USAGE:
|
||||
greet [global options] command [command options] [arguments...]
|
||||
|
||||
VERSION:
|
||||
0.0.0
|
||||
|
||||
COMMANDS:
|
||||
help, h Shows a list of commands or help for one command
|
||||
|
||||
GLOBAL OPTIONS
|
||||
--version Shows version information
|
||||
```
|
55
docs/v1/examples/subcommands-categories.md
Normal file
55
docs/v1/examples/subcommands-categories.md
Normal file
@ -0,0 +1,55 @@
|
||||
---
|
||||
tags:
|
||||
- v1
|
||||
---
|
||||
|
||||
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.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "noop",
|
||||
},
|
||||
{
|
||||
Name: "add",
|
||||
Category: "Template actions",
|
||||
},
|
||||
{
|
||||
Name: "remove",
|
||||
Category: "Template actions",
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Will include:
|
||||
|
||||
```
|
||||
COMMANDS:
|
||||
noop
|
||||
|
||||
Template actions:
|
||||
add
|
||||
remove
|
||||
```
|
75
docs/v1/examples/subcommands.md
Normal file
75
docs/v1/examples/subcommands.md
Normal file
@ -0,0 +1,75 @@
|
||||
---
|
||||
tags:
|
||||
- v1
|
||||
---
|
||||
|
||||
Subcommands can be defined for a more git-like command line app.
|
||||
|
||||
<!-- {
|
||||
"args": ["template", "add"],
|
||||
"output": "new task template: .+"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
|
||||
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
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
363
docs/v1/examples/version-flag.md
Normal file
363
docs/v1/examples/version-flag.md
Normal file
@ -0,0 +1,363 @@
|
||||
---
|
||||
tags:
|
||||
- v1
|
||||
---
|
||||
|
||||
The default version flag (`-v/--version`) is defined as `cli.VersionFlag`, which
|
||||
is checked by the cli internals in order to print the `App.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.:
|
||||
|
||||
<!-- {
|
||||
"args": ["--print-version"],
|
||||
"output": "partay version 19\\.99\\.0"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cli.VersionFlag = cli.BoolFlag{
|
||||
Name: "print-version, V",
|
||||
Usage: "print only the version",
|
||||
}
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Name = "partay"
|
||||
app.Version = "19.99.0"
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, the version printer at `cli.VersionPrinter` may be overridden, e.g.:
|
||||
|
||||
<!-- {
|
||||
"args": ["--version"],
|
||||
"output": "version=19\\.99\\.0 revision=fafafaf"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
var (
|
||||
Revision = "fafafaf"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cli.VersionPrinter = func(c *cli.Context) {
|
||||
fmt.Printf("version=%s revision=%s\n", c.App.Version, Revision)
|
||||
}
|
||||
|
||||
app := cli.NewApp()
|
||||
app.Name = "partay"
|
||||
app.Version = "19.99.0"
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Full API Example
|
||||
|
||||
**Notice**: This is a contrived (functioning) example meant strictly for API
|
||||
demonstration purposes. Use of one's imagination is encouraged.
|
||||
|
||||
<!-- {
|
||||
"output": "made it!\nPhew!"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func init() {
|
||||
cli.AppHelpTemplate += "\nCUSTOMIZED: you bet ur muffins\n"
|
||||
cli.CommandHelpTemplate += "\nYMMV\n"
|
||||
cli.SubcommandHelpTemplate += "\nor something\n"
|
||||
|
||||
cli.HelpFlag = cli.BoolFlag{Name: "halp"}
|
||||
cli.BashCompletionFlag = cli.BoolFlag{Name: "compgen", Hidden: true}
|
||||
cli.VersionFlag = cli.BoolFlag{Name: "print-version, 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.GetName())
|
||||
}
|
||||
}
|
||||
|
||||
type hexWriter struct{}
|
||||
|
||||
func (w *hexWriter) Write(p []byte) (int, error) {
|
||||
for _, b := range p {
|
||||
fmt.Printf("%x", b)
|
||||
}
|
||||
fmt.Printf("\n")
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
type genericType struct{
|
||||
s string
|
||||
}
|
||||
|
||||
func (g *genericType) Set(value string) error {
|
||||
g.s = value
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *genericType) String() string {
|
||||
return g.s
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "kənˈtrīv"
|
||||
app.Version = "19.99.0"
|
||||
app.Compiled = time.Now()
|
||||
app.Authors = []cli.Author{
|
||||
cli.Author{
|
||||
Name: "Example Human",
|
||||
Email: "human@example.com",
|
||||
},
|
||||
}
|
||||
app.Copyright = "(c) 1999 Serious Enterprise"
|
||||
app.HelpName = "contrive"
|
||||
app.Usage = "demonstrate available API"
|
||||
app.UsageText = "contrive - demonstrating the available API"
|
||||
app.ArgsUsage = "[args and such]"
|
||||
app.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, forevvarr"},
|
||||
},
|
||||
Subcommands: cli.Commands{
|
||||
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
|
||||
},
|
||||
},
|
||||
}
|
||||
app.Flags = []cli.Flag{
|
||||
cli.BoolFlag{Name: "fancy"},
|
||||
cli.BoolTFlag{Name: "fancier"},
|
||||
cli.DurationFlag{Name: "howlong, 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, d"},
|
||||
cli.StringSliceFlag{Name: "names, N"},
|
||||
cli.UintFlag{Name: "age"},
|
||||
cli.Uint64Flag{Name: "bigage"},
|
||||
}
|
||||
app.EnableBashCompletion = true
|
||||
app.UseShortOptionHandling = true
|
||||
app.HideHelp = false
|
||||
app.HideVersion = false
|
||||
app.BashComplete = func(c *cli.Context) {
|
||||
fmt.Fprintf(c.App.Writer, "lipstick\nkiss\nme\nlipstick\nringo\n")
|
||||
}
|
||||
app.Before = func(c *cli.Context) error {
|
||||
fmt.Fprintf(c.App.Writer, "HEEEERE GOES\n")
|
||||
return nil
|
||||
}
|
||||
app.After = func(c *cli.Context) error {
|
||||
fmt.Fprintf(c.App.Writer, "Phew!\n")
|
||||
return nil
|
||||
}
|
||||
app.CommandNotFound = func(c *cli.Context, command string) {
|
||||
fmt.Fprintf(c.App.Writer, "Thar be no %q here.\n", command)
|
||||
}
|
||||
app.OnUsageError = func(c *cli.Context, err error, isSubcommand bool) error {
|
||||
if isSubcommand {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Fprintf(c.App.Writer, "WRONG: %#v\n", err)
|
||||
return nil
|
||||
}
|
||||
app.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)
|
||||
|
||||
categories := c.App.Categories()
|
||||
categories.AddCommand("sounds", cli.Command{
|
||||
Name: "bloop",
|
||||
})
|
||||
|
||||
for _, category := range c.App.Categories() {
|
||||
fmt.Fprintf(c.App.Writer, "%s\n", category.Name)
|
||||
fmt.Fprintf(c.App.Writer, "%#v\n", category.Commands)
|
||||
fmt.Fprintf(c.App.Writer, "%#v\n", category.VisibleCommands())
|
||||
}
|
||||
|
||||
fmt.Printf("%#v\n", c.App.Command("doo"))
|
||||
if c.Bool("infinite") {
|
||||
c.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())
|
||||
|
||||
fmt.Printf("%#v\n", c.Args().First())
|
||||
if len(c.Args()) > 0 {
|
||||
fmt.Printf("%#v\n", c.Args()[1])
|
||||
}
|
||||
fmt.Printf("%#v\n", c.Args().Present())
|
||||
fmt.Printf("%#v\n", c.Args().Tail())
|
||||
|
||||
set := flag.NewFlagSet("contrive", 0)
|
||||
nc := cli.NewContext(c.App, set, c)
|
||||
|
||||
fmt.Printf("%#v\n", nc.Args())
|
||||
fmt.Printf("%#v\n", nc.Bool("nope"))
|
||||
fmt.Printf("%#v\n", nc.BoolT("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.GlobalBool("global-nope"))
|
||||
fmt.Printf("%#v\n", nc.GlobalBoolT("global-nerp"))
|
||||
fmt.Printf("%#v\n", nc.GlobalDuration("global-howlong"))
|
||||
fmt.Printf("%#v\n", nc.GlobalFloat64("global-hay"))
|
||||
fmt.Printf("%#v\n", nc.GlobalGeneric("global-bloop"))
|
||||
fmt.Printf("%#v\n", nc.GlobalInt("global-bips"))
|
||||
fmt.Printf("%#v\n", nc.GlobalIntSlice("global-blups"))
|
||||
fmt.Printf("%#v\n", nc.GlobalString("global-snurt"))
|
||||
fmt.Printf("%#v\n", nc.GlobalStringSlice("global-snurkles"))
|
||||
|
||||
fmt.Printf("%#v\n", nc.FlagNames())
|
||||
fmt.Printf("%#v\n", nc.GlobalFlagNames())
|
||||
fmt.Printf("%#v\n", nc.GlobalIsSet("wat"))
|
||||
fmt.Printf("%#v\n", nc.GlobalSet("wat", "nope"))
|
||||
fmt.Printf("%#v\n", nc.NArg())
|
||||
fmt.Printf("%#v\n", nc.NumFlags())
|
||||
fmt.Printf("%#v\n", nc.Parent())
|
||||
|
||||
nc.Set("wat", "also-nope")
|
||||
|
||||
ec := cli.NewExitError("ohwell", 86)
|
||||
fmt.Fprintf(c.App.Writer, "%d", ec.ExitCode())
|
||||
fmt.Printf("made it!\n")
|
||||
return nil
|
||||
}
|
||||
|
||||
if os.Getenv("HEXY") != "" {
|
||||
app.Writer = &hexWriter{}
|
||||
app.ErrWriter = &hexWriter{}
|
||||
}
|
||||
|
||||
app.Metadata = map[string]interface{}{
|
||||
"layers": "many",
|
||||
"explicable": false,
|
||||
"whatever-values": 19.99,
|
||||
}
|
||||
|
||||
|
||||
// ignore error so we don't exit non-zero and break gfmrun README example tests
|
||||
_ = app.Run(os.Args)
|
||||
}
|
||||
|
||||
func wopAction(c *cli.Context) error {
|
||||
fmt.Fprintf(c.App.Writer, ":wave: over here, eh\n")
|
||||
return nil
|
||||
}
|
||||
```
|
65
docs/v1/getting-started.md
Normal file
65
docs/v1/getting-started.md
Normal file
@ -0,0 +1,65 @@
|
||||
---
|
||||
tags:
|
||||
- v1
|
||||
---
|
||||
|
||||
One of the philosophies behind cli is that an API should be playful and full of
|
||||
discovery. So a cli app can be as little as one line of code in `main()`.
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": "A new cli application"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := cli.NewApp().Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This app will run and show help text, but is not very useful. Let's give an
|
||||
action to execute and some help documentation:
|
||||
|
||||
<!-- {
|
||||
"output": "boom! I say!"
|
||||
} -->
|
||||
``` go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "boom"
|
||||
app.Usage = "make an explosive entrance"
|
||||
app.Action = func(c *cli.Context) error {
|
||||
fmt.Println("boom! I say!")
|
||||
return nil
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Running this already gives you a ton of functionality, plus support for things
|
||||
like subcommands and flags, which are covered below.
|
@ -1 +0,0 @@
|
||||
manual.md
|
1455
docs/v1/manual.md
1455
docs/v1/manual.md
File diff suppressed because it is too large
Load Diff
9
docs/v1/migrating-to-v2.md
Normal file
9
docs/v1/migrating-to-v2.md
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
tags:
|
||||
- v1
|
||||
---
|
||||
|
||||
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).
|
36
docs/v2/examples/arguments.md
Normal file
36
docs/v2/examples/arguments.md
Normal file
@ -0,0 +1,36 @@
|
||||
---
|
||||
tags:
|
||||
- v2
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
You can lookup arguments by calling the `Args` function on `cli.Context`, e.g.:
|
||||
|
||||
<!-- {
|
||||
"output": "Hello \""
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Action: func(cCtx *cli.Context) error {
|
||||
fmt.Printf("Hello %q", cCtx.Args().Get(0))
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
257
docs/v2/examples/bash-completions.md
Normal file
257
docs/v2/examples/bash-completions.md
Normal file
@ -0,0 +1,257 @@
|
||||
---
|
||||
tags:
|
||||
- v2
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
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.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: "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
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
![](/docs/v2/images/default-bash-autocomplete.gif)
|
||||
|
||||
#### Custom auto-completion
|
||||
<!-- {
|
||||
"args": ["complete", "--generate-bash-completion"],
|
||||
"output": "laundry"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
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(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)
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
![](/docs/v2/images/custom-bash-autocomplete.gif)
|
||||
|
||||
#### Enabling
|
||||
|
||||
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.
|
||||
|
||||
For example, if your cli program is called `myprogram`:
|
||||
|
||||
```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.
|
||||
|
||||
#### Distribution and Persistent Autocompletion
|
||||
|
||||
Copy `autocomplete/bash_autocomplete` into `/etc/bash_completion.d/` and rename
|
||||
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.
|
||||
|
||||
```sh-session
|
||||
$ sudo cp path/to/autocomplete/bash_autocomplete /etc/bash_completion.d/<myprogram>
|
||||
$ source /etc/bash_completion.d/<myprogram>
|
||||
```
|
||||
|
||||
Alternatively, you can just document that users should `source` the generic
|
||||
`autocomplete/bash_autocomplete` and set `$PROG` within their bash configuration
|
||||
file, adding these lines:
|
||||
|
||||
```sh-session
|
||||
$ PROG=<myprogram>
|
||||
$ 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=<program1>
|
||||
$ source path/to/cli/autocomplete/bash_autocomplete
|
||||
|
||||
$ PROG=<program2>
|
||||
$ source path/to/cli/autocomplete/bash_autocomplete
|
||||
```
|
||||
|
||||
#### Customization
|
||||
|
||||
The default shell completion flag (`--generate-bash-completion`) is defined as
|
||||
`cli.EnableBashCompletion`, and may be redefined if desired, e.g.:
|
||||
|
||||
<!-- {
|
||||
"args": ["--generate-bash-completion"],
|
||||
"output": "wat\nhelp\nh"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
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:
|
||||
|
||||
```sh-session
|
||||
$ PROG=<myprogram>
|
||||
$ 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 `<my program>.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 activate it, enter:
|
||||
|
||||
```powershell
|
||||
& path/to/autocomplete/<my program>.ps1
|
||||
```
|
||||
|
||||
To persist across new shells, open the PowerShell profile (with `code $profile`
|
||||
or `notepad $profile`) and add the line:
|
||||
|
||||
```powershell
|
||||
& path/to/autocomplete/<my program>.ps1
|
||||
```
|
74
docs/v2/examples/combining-short-options.md
Normal file
74
docs/v2/examples/combining-short-options.md
Normal file
@ -0,0 +1,74 @@
|
||||
---
|
||||
tags:
|
||||
- v2
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
Traditional use of options using their shortnames look like this:
|
||||
|
||||
```sh-session
|
||||
$ cmd -s -o -m "Some message"
|
||||
```
|
||||
|
||||
Suppose you want users to be able to combine options with their shortnames. This
|
||||
can be done using the `UseShortOptionHandling` bool in your app configuration,
|
||||
or for individual commands by attaching it to the command configuration. For
|
||||
example:
|
||||
|
||||
<!-- {
|
||||
"args": ["short", "-som", "Some message"],
|
||||
"output": "serve: true\noption: true\nmessage: Some message\n"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
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
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If your program has any number of bool flags such as `serve` and `option`, and
|
||||
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"
|
||||
```
|
||||
|
||||
If you enable `UseShortOptionHandling`, then you must not use any flags that
|
||||
have a single leading `-` or this will result in failures. For example,
|
||||
`-option` can no longer be used. Flags with two leading dashes (such as
|
||||
`--options`) are still valid.
|
45
docs/v2/examples/exit-codes.md
Normal file
45
docs/v2/examples/exit-codes.md
Normal file
@ -0,0 +1,45 @@
|
||||
---
|
||||
tags:
|
||||
- v2
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
Calling `App.Run` will not automatically call `os.Exit`, which means that by
|
||||
default the exit code will "fall through" to being `0`. An explicit exit code
|
||||
may be set by returning a non-nil error that fulfills `cli.ExitCoder`, *or* a
|
||||
`cli.MultiError` that includes an error that fulfills `cli.ExitCoder`, e.g.:
|
||||
<!-- {
|
||||
"error": "Ginger croutons are not in the soup"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"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
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
557
docs/v2/examples/flags.md
Normal file
557
docs/v2/examples/flags.md
Normal file
@ -0,0 +1,557 @@
|
||||
---
|
||||
tags:
|
||||
- v2
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
Setting and querying flags is simple.
|
||||
|
||||
<!-- {
|
||||
"output": "Hello Nefertiti"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"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(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
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
You can also set a destination variable for a flag, to which the content will be
|
||||
scanned.
|
||||
|
||||
<!-- {
|
||||
"output": "Hello someone"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var language string
|
||||
|
||||
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
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
See full list of flags at https://pkg.go.dev/github.com/urfave/cli/v2
|
||||
|
||||
#### Placeholder Values
|
||||
|
||||
Sometimes it's useful to specify a flag's value within the usage string itself.
|
||||
Such placeholders are indicated with back quotes.
|
||||
|
||||
For example this:
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": "--config FILE, -c FILE"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"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`",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Will result in help output like:
|
||||
|
||||
```
|
||||
--config FILE, -c FILE Load configuration from FILE
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": "--lang value, -l value.*language for the greeting.*default: \"english\""
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"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",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(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.
|
||||
|
||||
#### Ordering
|
||||
|
||||
Flags for the application and commands are shown in the order they are defined.
|
||||
However, it's possible to sort them from outside this library by using `FlagsByName`
|
||||
or `CommandsByName` with `sort`.
|
||||
|
||||
For example this:
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": ".*Load configuration from FILE\n.*\n.*Language for the greeting.*"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"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(*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))
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Will result in help output like:
|
||||
|
||||
```
|
||||
--config FILE, -c FILE Load configuration from FILE
|
||||
--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 `EnvVars`. e.g.
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": "language for the greeting.*APP_LANG"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"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"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If `EnvVars` contains more than one string, the first environment variable that
|
||||
resolves is used.
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": "language for the greeting.*LEGACY_COMPAT_LANG.*APP_LANG.*LANG"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"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"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Values from files
|
||||
|
||||
You can also have the default value set from file via `FilePath`. e.g.
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": "password for the mysql database"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Flags: []cli.Flag{
|
||||
&cli.StringFlag{
|
||||
Name: "password",
|
||||
Aliases: []string{"p"},
|
||||
Usage: "password for the mysql database",
|
||||
FilePath: "/etc/mysql/password",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that default values set from file (e.g. `FilePath`) take precedence over
|
||||
default values set from the environment (e.g. `EnvVar`).
|
||||
|
||||
#### 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 = 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:
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": "--test value.*default: 0"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"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"},
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
```
|
||||
|
||||
#### Required Flags
|
||||
|
||||
You can make a flag 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:
|
||||
|
||||
<!-- {
|
||||
"error": "Required flag \"lang\" not set"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
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
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
If the app is run without the `lang` flag, the user will see the following message
|
||||
|
||||
```
|
||||
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.
|
||||
|
||||
For example this:
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": "--port value"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"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",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Will result in help output like:
|
||||
|
||||
```
|
||||
--port value Use a randomized port (default: random)
|
||||
```
|
||||
|
||||
#### Precedence
|
||||
|
||||
The precedence for flag value sources is as follows (highest to lowest):
|
||||
|
||||
0. Command line flag value from user
|
||||
0. Environment variable (if specified)
|
||||
0. Configuration file (if specified)
|
||||
0. Default defined on the flag
|
262
docs/v2/examples/full-api-example.md
Normal file
262
docs/v2/examples/full-api-example.md
Normal file
@ -0,0 +1,262 @@
|
||||
---
|
||||
tags:
|
||||
- v2
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
**Notice**: This is a contrived (functioning) example meant strictly for API
|
||||
demonstration purposes. Use of one's imagination is encouraged.
|
||||
|
||||
<!-- {
|
||||
"output": "made it!\nPhew!"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"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.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(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")
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
type genericType struct {
|
||||
s string
|
||||
}
|
||||
|
||||
func (g *genericType) Set(value string) error {
|
||||
g.s = value
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *genericType) String() string {
|
||||
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(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(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", cCtx.App.Command("doo"))
|
||||
if cCtx.Bool("infinite") {
|
||||
cCtx.App.Run([]string{"app", "doo", "wop"})
|
||||
}
|
||||
|
||||
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", 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(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.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(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{}
|
||||
}
|
||||
|
||||
app.Run(os.Args)
|
||||
}
|
||||
|
||||
func wopAction(cCtx *cli.Context) error {
|
||||
fmt.Fprintf(cCtx.App.Writer, ":wave: over here, eh\n")
|
||||
return nil
|
||||
}
|
||||
```
|
101
docs/v2/examples/generated-help-text.md
Normal file
101
docs/v2/examples/generated-help-text.md
Normal file
@ -0,0 +1,101 @@
|
||||
---
|
||||
tags:
|
||||
- v2
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
The default help flag (`-h/--help`) is defined as `cli.HelpFlag` and is checked
|
||||
by the cli internals in order to print generated help text for the app, command,
|
||||
or subcommand, and break execution.
|
||||
|
||||
#### Customization
|
||||
|
||||
All of the help text generation may be customized, and at multiple levels. The
|
||||
templates are exposed as variables `AppHelpTemplate`, `CommandHelpTemplate`, and
|
||||
`SubcommandHelpTemplate` which may be reassigned or augmented, and full override
|
||||
is possible by assigning a compatible func to the `cli.HelpPrinter` variable,
|
||||
e.g.:
|
||||
|
||||
<!-- {
|
||||
"output": "Ha HA. I pwnd the help!!1"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// EXAMPLE: Append to an existing template
|
||||
cli.AppHelpTemplate = fmt.Sprintf(`%s
|
||||
|
||||
WEBSITE: http://awesometown.example.com
|
||||
|
||||
SUPPORT: support@awesometown.example.com
|
||||
|
||||
`, cli.AppHelpTemplate)
|
||||
|
||||
// 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}}
|
||||
{{if len .Authors}}
|
||||
AUTHOR:
|
||||
{{range .Authors}}{{ . }}{{end}}
|
||||
{{end}}{{if .Commands}}
|
||||
COMMANDS:
|
||||
{{range .Commands}}{{if not .HideHelp}} {{join .Names ", "}}{{ "\t"}}{{.Usage}}{{ "\n" }}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
|
||||
GLOBAL OPTIONS:
|
||||
{{range .VisibleFlags}}{{.}}
|
||||
{{end}}{{end}}{{if .Copyright }}
|
||||
COPYRIGHT:
|
||||
{{.Copyright}}
|
||||
{{end}}{{if .Version}}
|
||||
VERSION:
|
||||
{{.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")
|
||||
}
|
||||
|
||||
(&cli.App{}).Run(os.Args)
|
||||
}
|
||||
```
|
||||
|
||||
The default flag may be customized to something other than `-h/--help` by
|
||||
setting `cli.HelpFlag`, e.g.:
|
||||
|
||||
<!-- {
|
||||
"args": ["--halp"],
|
||||
"output": "haaaaalp.*HALP"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
cli.HelpFlag = &cli.BoolFlag{
|
||||
Name: "haaaaalp",
|
||||
Aliases: []string{"halp"},
|
||||
Usage: "HALP",
|
||||
EnvVars: []string{"SHOW_HALP", "HALPPLZ"},
|
||||
}
|
||||
|
||||
(&cli.App{}).Run(os.Args)
|
||||
}
|
||||
```
|
73
docs/v2/examples/greet.md
Normal file
73
docs/v2/examples/greet.md
Normal file
@ -0,0 +1,73 @@
|
||||
---
|
||||
tags:
|
||||
- v2
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
Being a programmer can be a lonely job. Thankfully by the power of automation
|
||||
that is not the case! Let's create a greeter app to fend off our demons of
|
||||
loneliness!
|
||||
|
||||
Start by creating a directory named `greet`, and within it, add a file,
|
||||
`greet.go` with the following code in it:
|
||||
|
||||
<!-- {
|
||||
"output": "Hello friend!"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "greet",
|
||||
Usage: "fight the loneliness!",
|
||||
Action: func(*cli.Context) error {
|
||||
fmt.Println("Hello friend!")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
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!
|
||||
|
||||
USAGE:
|
||||
greet [global options] command [command options] [arguments...]
|
||||
|
||||
COMMANDS:
|
||||
help, h Shows a list of commands or help for one command
|
||||
|
||||
GLOBAL OPTIONS
|
||||
--help, -h show help (default: false)
|
||||
```
|
54
docs/v2/examples/subcommands-categories.md
Normal file
54
docs/v2/examples/subcommands-categories.md
Normal file
@ -0,0 +1,54 @@
|
||||
---
|
||||
tags:
|
||||
- v2
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
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.:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Commands: []*cli.Command{
|
||||
{
|
||||
Name: "noop",
|
||||
},
|
||||
{
|
||||
Name: "add",
|
||||
Category: "template",
|
||||
},
|
||||
{
|
||||
Name: "remove",
|
||||
Category: "template",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Will include:
|
||||
|
||||
```
|
||||
COMMANDS:
|
||||
noop
|
||||
|
||||
Template actions:
|
||||
add
|
||||
remove
|
||||
```
|
76
docs/v2/examples/subcommands.md
Normal file
76
docs/v2/examples/subcommands.md
Normal file
@ -0,0 +1,76 @@
|
||||
---
|
||||
tags:
|
||||
- v2
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
Subcommands can be defined for a more git-like command line app.
|
||||
|
||||
<!-- {
|
||||
"args": ["template", "add"],
|
||||
"output": "new task template: .+"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"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(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
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
11
docs/v2/examples/suggestions.md
Normal file
11
docs/v2/examples/suggestions.md
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
tags:
|
||||
- v2
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
To enable flag and command suggestions, set `app.Suggest = true`. If the suggest
|
||||
feature is enabled, then the help output of the corresponding command will
|
||||
provide an appropriate suggestion for the provided flag or subcommand if
|
||||
available.
|
64
docs/v2/examples/timestamp-flag.md
Normal file
64
docs/v2/examples/timestamp-flag.md
Normal file
@ -0,0 +1,64 @@
|
||||
---
|
||||
tags:
|
||||
- v2
|
||||
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.
|
||||
|
||||
<!-- {
|
||||
"args": ["--meeting", "2019-08-12T15:04:05"],
|
||||
"output": "2019\\-08\\-12 15\\:04\\:05 \\+0000 UTC"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"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(cCtx *cli.Context) error {
|
||||
fmt.Printf("%s", cCtx.Timestamp("meeting").String())
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(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
|
||||
app := &cli.App{
|
||||
Flags: []cli.Flag{
|
||||
&cli.TimestampFlag{Name: "meeting", Layout: "2006-01-02T15:04:05", Timezone: time.Local},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
(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)
|
77
docs/v2/examples/version-flag.md
Normal file
77
docs/v2/examples/version-flag.md
Normal file
@ -0,0 +1,77 @@
|
||||
---
|
||||
tags:
|
||||
- v2
|
||||
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 `App.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.:
|
||||
|
||||
<!-- {
|
||||
"args": ["--print-version"],
|
||||
"output": "partay version v19\\.99\\.0"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
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)
|
||||
}
|
||||
```
|
||||
|
||||
Alternatively, the version printer at `cli.VersionPrinter` may be overridden,
|
||||
e.g.:
|
||||
|
||||
<!-- {
|
||||
"args": ["--version"],
|
||||
"output": "version=v19\\.99\\.0 revision=fafafaf"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
Revision = "fafafaf"
|
||||
)
|
||||
|
||||
func main() {
|
||||
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)
|
||||
}
|
||||
```
|
63
docs/v2/getting-started.md
Normal file
63
docs/v2/getting-started.md
Normal file
@ -0,0 +1,63 @@
|
||||
---
|
||||
tags:
|
||||
- v2
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
One of the philosophies behind cli is that an API should be playful and full of
|
||||
discovery. So a cli app can be as little as one line of code in `main()`.
|
||||
|
||||
<!-- {
|
||||
"args": ["--help"],
|
||||
"output": "A new cli application"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
(&cli.App{}).Run(os.Args)
|
||||
}
|
||||
```
|
||||
|
||||
This app will run and show help text, but is not very useful. Let's give an
|
||||
action to execute and some help documentation:
|
||||
|
||||
<!-- {
|
||||
"output": "boom! I say!"
|
||||
} -->
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := &cli.App{
|
||||
Name: "boom",
|
||||
Usage: "make an explosive entrance",
|
||||
Action: func(*cli.Context) error {
|
||||
fmt.Println("boom! I say!")
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Running this already gives you a ton of functionality, plus support for things
|
||||
like subcommands and flags, which are covered below.
|
@ -1 +0,0 @@
|
||||
manual.md
|
1703
docs/v2/manual.md
1703
docs/v2/manual.md
File diff suppressed because it is too large
Load Diff
12
docs/v2/migrating-from-older-releases.md
Normal file
12
docs/v2/migrating-from-older-releases.md
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
tags:
|
||||
- v2
|
||||
search:
|
||||
boost: 2
|
||||
---
|
||||
|
||||
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.
|
@ -60,7 +60,14 @@ func main() {
|
||||
Action: TestActionFunc,
|
||||
},
|
||||
{
|
||||
Name: "gfmrun",
|
||||
Name: "gfmrun",
|
||||
Flags: []cli.Flag{
|
||||
&cli.BoolFlag{
|
||||
Name: "walk",
|
||||
Value: false,
|
||||
Usage: "Walk the specified directory and perform validation on all markdown files",
|
||||
},
|
||||
},
|
||||
Action: GfmrunActionFunc,
|
||||
},
|
||||
{
|
||||
@ -215,36 +222,75 @@ func GfmrunActionFunc(cCtx *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
filename := cCtx.Args().Get(0)
|
||||
if filename == "" {
|
||||
filename = "README.md"
|
||||
dirPath := cCtx.Args().Get(0)
|
||||
if dirPath == "" {
|
||||
dirPath = "README.md"
|
||||
}
|
||||
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
walk := cCtx.Bool("walk")
|
||||
sources := []string{}
|
||||
|
||||
if walk {
|
||||
// Walk the directory and find all markdown files.
|
||||
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if filepath.Ext(path) != ".md" {
|
||||
return nil
|
||||
}
|
||||
|
||||
sources = append(sources, path)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
sources = append(sources, dirPath)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var counter int
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
if strings.Contains(scanner.Text(), "package main") {
|
||||
counter++
|
||||
|
||||
for _, src := range sources {
|
||||
file, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
if strings.Contains(scanner.Text(), "package main") {
|
||||
counter++
|
||||
}
|
||||
}
|
||||
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = scanner.Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = file.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
gfmArgs := []string{
|
||||
"--count",
|
||||
fmt.Sprint(counter),
|
||||
}
|
||||
for _, src := range sources {
|
||||
gfmArgs = append(gfmArgs, "--sources", src)
|
||||
}
|
||||
|
||||
err = scanner.Err()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := runCmd("gfmrun", "-c", fmt.Sprint(counter), "-s", filename); err != nil {
|
||||
if err := runCmd("gfmrun", gfmArgs...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
51
mkdocs.yml
51
mkdocs.yml
@ -9,9 +9,45 @@ site_url: https://cli.urfave.org/
|
||||
repo_url: https://github.com/urfave/cli
|
||||
edit_uri: edit/main/docs/
|
||||
nav:
|
||||
- Home: index.md
|
||||
- v2 Manual: v2/index.md
|
||||
- v1 Manual: v1/index.md
|
||||
- Home:
|
||||
- Welcome: index.md
|
||||
- Contributing: CONTRIBUTING.md
|
||||
- Code of Conduct: CODE_OF_CONDUCT.md
|
||||
- Releasing: RELEASING.md
|
||||
- Security: SECURITY.md
|
||||
- Migrate v1 to v2: migrate-v1-to-v2.md
|
||||
- v2 Manual:
|
||||
- Getting Started: v2/getting-started.md
|
||||
- Migrating From Older Releases: v2/migrating-from-older-releases.md
|
||||
- Examples:
|
||||
- Greet: v2/examples/greet.md
|
||||
- Arguments: v2/examples/arguments.md
|
||||
- Flags: v2/examples/flags.md
|
||||
- Subcommands: v2/examples/subcommands.md
|
||||
- Subcommands Categories: v2/examples/subcommands-categories.md
|
||||
- Exit Codes: v2/examples/exit-codes.md
|
||||
- Combining Short Options: v2/examples/combining-short-options.md
|
||||
- Bash Completions: v2/examples/bash-completions.md
|
||||
- Generated Help Text: v2/examples/generated-help-text.md
|
||||
- Version Flag: v2/examples/version-flag.md
|
||||
- Timestamp Flag: v2/examples/timestamp-flag.md
|
||||
- Suggestions: v2/examples/suggestions.md
|
||||
- Full API Example: v2/examples/full-api-example.md
|
||||
- v1 Manual:
|
||||
- Getting Started: v1/getting-started.md
|
||||
- Migrating to v2: v1/migrating-to-v2.md
|
||||
- Examples:
|
||||
- Greet: v1/examples/greet.md
|
||||
- Arguments: v1/examples/arguments.md
|
||||
- Flags: v1/examples/flags.md
|
||||
- Subcommands: v1/examples/subcommands.md
|
||||
- Subcommands (Categories): v1/examples/subcommands-categories.md
|
||||
- Exit Codes: v1/examples/exit-codes.md
|
||||
- Combining Short Options: v1/examples/combining-short-options.md
|
||||
- Bash Completions: v1/examples/bash-completions.md
|
||||
- Generated Help Text: v1/examples/generated-help-text.md
|
||||
- Version Flag: v1/examples/version-flag.md
|
||||
|
||||
theme:
|
||||
name: material
|
||||
palette:
|
||||
@ -25,9 +61,18 @@ theme:
|
||||
toggle:
|
||||
icon: material/brightness-7
|
||||
name: light mode
|
||||
features:
|
||||
- content.code.annotate
|
||||
- navigation.top
|
||||
- navigation.instant
|
||||
- navigation.expand
|
||||
- navigation.sections
|
||||
- navigation.tabs
|
||||
- navigation.tabs.sticky
|
||||
plugins:
|
||||
- git-revision-date-localized
|
||||
- search
|
||||
- tags
|
||||
# NOTE: this is the recommended configuration from
|
||||
# https://squidfunk.github.io/mkdocs-material/setup/extensions/#recommended-configuration
|
||||
markdown_extensions:
|
||||
|
Loading…
Reference in New Issue
Block a user