Merge branch 'master' into default-writer
This commit is contained in:
commit
61db37e7ae
@ -12,6 +12,7 @@ go:
|
|||||||
- 1.4.2
|
- 1.4.2
|
||||||
- 1.5.x
|
- 1.5.x
|
||||||
- 1.6.x
|
- 1.6.x
|
||||||
|
- 1.7.x
|
||||||
- master
|
- master
|
||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
@ -20,6 +21,8 @@ matrix:
|
|||||||
include:
|
include:
|
||||||
- go: 1.6.x
|
- go: 1.6.x
|
||||||
os: osx
|
os: osx
|
||||||
|
- go: 1.7.x
|
||||||
|
os: osx
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- go get github.com/urfave/gfmrun/... || true
|
- go get github.com/urfave/gfmrun/... || true
|
||||||
|
51
README.md
51
README.md
@ -30,6 +30,7 @@ applications in an expressive way.
|
|||||||
* [Flags](#flags)
|
* [Flags](#flags)
|
||||||
+ [Placeholder Values](#placeholder-values)
|
+ [Placeholder Values](#placeholder-values)
|
||||||
+ [Alternate Names](#alternate-names)
|
+ [Alternate Names](#alternate-names)
|
||||||
|
+ [Ordering](#ordering)
|
||||||
+ [Values from the Environment](#values-from-the-environment)
|
+ [Values from the Environment](#values-from-the-environment)
|
||||||
+ [Values from alternate input sources (YAML, TOML, and others)](#values-from-alternate-input-sources-yaml-toml-and-others)
|
+ [Values from alternate input sources (YAML, TOML, and others)](#values-from-alternate-input-sources-yaml-toml-and-others)
|
||||||
* [Subcommands](#subcommands)
|
* [Subcommands](#subcommands)
|
||||||
@ -450,6 +451,56 @@ 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
|
giving two different forms of the same flag in the same command invocation is an
|
||||||
error.
|
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`
|
||||||
|
with `sort`.
|
||||||
|
|
||||||
|
For example this:
|
||||||
|
|
||||||
|
<!-- {
|
||||||
|
"args": ["--help"],
|
||||||
|
"output": "Load configuration from FILE\n.*Language for the greeting.*"
|
||||||
|
} -->
|
||||||
|
``` go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"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`",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Sort(cli.FlagsByName(app.Flags))
|
||||||
|
|
||||||
|
app.Run(os.Args)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
#### Values from the Environment
|
||||||
|
|
||||||
You can also have the default value set from the environment via `EnvVar`. e.g.
|
You can also have the default value set from the environment via `EnvVar`. e.g.
|
||||||
|
10
app.go
10
app.go
@ -42,6 +42,8 @@ type App struct {
|
|||||||
ArgsUsage string
|
ArgsUsage string
|
||||||
// Version of the program
|
// Version of the program
|
||||||
Version string
|
Version string
|
||||||
|
// Description of the program
|
||||||
|
Description string
|
||||||
// List of commands to execute
|
// List of commands to execute
|
||||||
Commands []Command
|
Commands []Command
|
||||||
// List of flags to parse
|
// List of flags to parse
|
||||||
@ -198,7 +200,7 @@ func (a *App) Run(arguments []string) (err error) {
|
|||||||
HandleExitCoder(err)
|
HandleExitCoder(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
|
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
|
||||||
ShowAppHelp(context)
|
ShowAppHelp(context)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -319,7 +321,7 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
|||||||
HandleExitCoder(err)
|
HandleExitCoder(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
|
fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
|
||||||
ShowSubcommandHelp(context)
|
ShowSubcommandHelp(context)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -460,10 +462,10 @@ type Author struct {
|
|||||||
func (a Author) String() string {
|
func (a Author) String() string {
|
||||||
e := ""
|
e := ""
|
||||||
if a.Email != "" {
|
if a.Email != "" {
|
||||||
e = "<" + a.Email + "> "
|
e = " <" + a.Email + ">"
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Sprintf("%v %v", a.Name, e)
|
return fmt.Sprintf("%v%v", a.Name, e)
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleAction uses ✧✧✧reflection✧✧✧ to figure out if the given Action is an
|
// HandleAction uses ✧✧✧reflection✧✧✧ to figure out if the given Action is an
|
||||||
|
74
app_test.go
74
app_test.go
@ -90,7 +90,62 @@ func ExampleApp_Run_subcommand() {
|
|||||||
// Hello, Jeremy
|
// Hello, Jeremy
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleApp_Run_help() {
|
func ExampleApp_Run_appHelp() {
|
||||||
|
// set args for examples sake
|
||||||
|
os.Args = []string{"greet", "help"}
|
||||||
|
|
||||||
|
app := NewApp()
|
||||||
|
app.Name = "greet"
|
||||||
|
app.Version = "0.1.0"
|
||||||
|
app.Description = "This is how we describe greet the app"
|
||||||
|
app.Authors = []Author{
|
||||||
|
{Name: "Harrison", Email: "harrison@lolwut.com"},
|
||||||
|
{Name: "Oliver Allen", Email: "oliver@toyshop.com"},
|
||||||
|
}
|
||||||
|
app.Flags = []Flag{
|
||||||
|
StringFlag{Name: "name", Value: "bob", Usage: "a name to say"},
|
||||||
|
}
|
||||||
|
app.Commands = []Command{
|
||||||
|
{
|
||||||
|
Name: "describeit",
|
||||||
|
Aliases: []string{"d"},
|
||||||
|
Usage: "use it to see a description",
|
||||||
|
Description: "This is how we describe describeit the function",
|
||||||
|
Action: func(c *Context) error {
|
||||||
|
fmt.Printf("i like to describe things")
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
app.Run(os.Args)
|
||||||
|
// Output:
|
||||||
|
// NAME:
|
||||||
|
// greet - A new cli application
|
||||||
|
//
|
||||||
|
// USAGE:
|
||||||
|
// greet [global options] command [command options] [arguments...]
|
||||||
|
//
|
||||||
|
// VERSION:
|
||||||
|
// 0.1.0
|
||||||
|
//
|
||||||
|
// DESCRIPTION:
|
||||||
|
// This is how we describe greet the app
|
||||||
|
//
|
||||||
|
// AUTHORS:
|
||||||
|
// Harrison <harrison@lolwut.com>
|
||||||
|
// Oliver Allen <oliver@toyshop.com>
|
||||||
|
//
|
||||||
|
// COMMANDS:
|
||||||
|
// describeit, d use it to see a description
|
||||||
|
// help, h Shows a list of commands or help for one command
|
||||||
|
//
|
||||||
|
// GLOBAL OPTIONS:
|
||||||
|
// --name value a name to say (default: "bob")
|
||||||
|
// --help, -h show help
|
||||||
|
// --version, -v print the version
|
||||||
|
}
|
||||||
|
|
||||||
|
func ExampleApp_Run_commandHelp() {
|
||||||
// set args for examples sake
|
// set args for examples sake
|
||||||
os.Args = []string{"greet", "h", "describeit"}
|
os.Args = []string{"greet", "h", "describeit"}
|
||||||
|
|
||||||
@ -258,6 +313,23 @@ func TestApp_RunAsSubcommandParseFlags(t *testing.T) {
|
|||||||
expect(t, context.String("lang"), "spanish")
|
expect(t, context.String("lang"), "spanish")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApp_RunAsSubCommandIncorrectUsage(t *testing.T) {
|
||||||
|
a := App{
|
||||||
|
Flags: []Flag{
|
||||||
|
StringFlag{Name: "--foo"},
|
||||||
|
},
|
||||||
|
Writer: bytes.NewBufferString(""),
|
||||||
|
}
|
||||||
|
|
||||||
|
set := flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
|
set.Parse([]string{"", "---foo"})
|
||||||
|
c := &Context{flagSet: set}
|
||||||
|
|
||||||
|
err := a.RunAsSubcommand(c)
|
||||||
|
|
||||||
|
expect(t, err, errors.New("bad flag syntax: ---foo"))
|
||||||
|
}
|
||||||
|
|
||||||
func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) {
|
func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) {
|
||||||
var parsedOption string
|
var parsedOption string
|
||||||
var args []string
|
var args []string
|
||||||
|
@ -138,7 +138,7 @@ func (c Command) Run(ctx *Context) (err error) {
|
|||||||
HandleExitCoder(err)
|
HandleExitCoder(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.")
|
fmt.Fprintln(ctx.App.Writer, "Incorrect Usage:", err.Error())
|
||||||
fmt.Fprintln(ctx.App.Writer)
|
fmt.Fprintln(ctx.App.Writer)
|
||||||
ShowCommandHelp(ctx, c.Name)
|
ShowCommandHelp(ctx, c.Name)
|
||||||
return err
|
return err
|
||||||
|
15
flag.go
15
flag.go
@ -37,6 +37,21 @@ var HelpFlag = BoolFlag{
|
|||||||
// to display a flag.
|
// to display a flag.
|
||||||
var FlagStringer FlagStringFunc = stringifyFlag
|
var FlagStringer FlagStringFunc = stringifyFlag
|
||||||
|
|
||||||
|
// FlagsByName is a slice of Flag.
|
||||||
|
type FlagsByName []Flag
|
||||||
|
|
||||||
|
func (f FlagsByName) Len() int {
|
||||||
|
return len(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FlagsByName) Less(i, j int) bool {
|
||||||
|
return f[i].GetName() < f[j].GetName()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f FlagsByName) Swap(i, j int) {
|
||||||
|
f[i], f[j] = f[j], f[i]
|
||||||
|
}
|
||||||
|
|
||||||
// Flag is a common interface related to parsing flags in cli.
|
// Flag is a common interface related to parsing flags in cli.
|
||||||
// For more advanced flag parsing techniques, it is recommended that
|
// For more advanced flag parsing techniques, it is recommended that
|
||||||
// this interface be implemented.
|
// this interface be implemented.
|
||||||
|
30
help.go
30
help.go
@ -16,24 +16,28 @@ var AppHelpTemplate = `NAME:
|
|||||||
{{.Name}} - {{.Usage}}
|
{{.Name}} - {{.Usage}}
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}
|
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .VisibleFlags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Version}}{{if not .HideVersion}}
|
||||||
{{if .Version}}{{if not .HideVersion}}
|
|
||||||
VERSION:
|
VERSION:
|
||||||
{{.Version}}
|
{{.Version}}{{end}}{{end}}{{if .Description}}
|
||||||
{{end}}{{end}}{{if len .Authors}}
|
|
||||||
AUTHOR(S):
|
DESCRIPTION:
|
||||||
{{range .Authors}}{{.}}{{end}}
|
{{.Description}}{{end}}{{if len .Authors}}
|
||||||
{{end}}{{if .VisibleCommands}}
|
|
||||||
|
AUTHOR{{with $length := len .Authors}}{{if ne 1 $length}}S{{end}}{{end}}:
|
||||||
|
{{range $index, $author := .Authors}}{{if $index}}
|
||||||
|
{{end}}{{$author}}{{end}}{{end}}{{if .VisibleCommands}}
|
||||||
|
|
||||||
COMMANDS:{{range .VisibleCategories}}{{if .Name}}
|
COMMANDS:{{range .VisibleCategories}}{{if .Name}}
|
||||||
{{.Name}}:{{end}}{{range .VisibleCommands}}
|
{{.Name}}:{{end}}{{range .VisibleCommands}}
|
||||||
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}
|
{{join .Names ", "}}{{"\t"}}{{.Usage}}{{end}}{{end}}{{end}}{{if .VisibleFlags}}
|
||||||
{{end}}{{end}}{{if .VisibleFlags}}
|
|
||||||
GLOBAL OPTIONS:
|
GLOBAL OPTIONS:
|
||||||
{{range .VisibleFlags}}{{.}}
|
{{range $index, $option := .VisibleFlags}}{{if $index}}
|
||||||
{{end}}{{end}}{{if .Copyright}}
|
{{end}}{{$option}}{{end}}{{end}}{{if .Copyright}}
|
||||||
|
|
||||||
COPYRIGHT:
|
COPYRIGHT:
|
||||||
{{.Copyright}}
|
{{.Copyright}}{{end}}
|
||||||
{{end}}
|
|
||||||
`
|
`
|
||||||
|
|
||||||
// CommandHelpTemplate is the text template for the command help topic.
|
// CommandHelpTemplate is the text template for the command help topic.
|
||||||
|
Loading…
Reference in New Issue
Block a user