commit
9001f738f4
17
README.md
17
README.md
@ -1,16 +1,19 @@
|
|||||||
[![Coverage](http://gocover.io/_badge/github.com/codegangsta/cli?0)](http://gocover.io/github.com/codegangsta/cli)
|
[![Coverage](http://gocover.io/_badge/github.com/codegangsta/cli?0)](http://gocover.io/github.com/codegangsta/cli)
|
||||||
[![Build Status](https://travis-ci.org/codegangsta/cli.png?branch=master)](https://travis-ci.org/codegangsta/cli)
|
[![Build Status](https://travis-ci.org/codegangsta/cli.svg?branch=master)](https://travis-ci.org/codegangsta/cli)
|
||||||
[![GoDoc](https://godoc.org/github.com/codegangsta/cli?status.svg)](https://godoc.org/github.com/codegangsta/cli)
|
[![GoDoc](https://godoc.org/github.com/codegangsta/cli?status.svg)](https://godoc.org/github.com/codegangsta/cli)
|
||||||
|
|
||||||
# cli.go
|
# cli.go
|
||||||
|
|
||||||
`cli.go` is simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way.
|
`cli.go` is simple, fast, and fun package for building command line apps in Go. The goal is to enable developers to write fast and distributable command line applications in an expressive way.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Command line apps are usually so tiny that there is absolutely no reason why your code should *not* be self-documenting. Things like generating help text and parsing command flags/options should not hinder productivity when writing a command line app.
|
Command line apps are usually so tiny that there is absolutely no reason why your code should *not* be self-documenting. Things like generating help text and parsing command flags/options should not hinder productivity when writing a command line app.
|
||||||
|
|
||||||
**This is where `cli.go` comes into play.** `cli.go` makes command line programming fun, organized, and expressive!
|
**This is where `cli.go` comes into play.** `cli.go` makes command line programming fun, organized, and expressive!
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Make sure you have a working Go environment (go 1.1+ is *required*). [See the install instructions](http://golang.org/doc/install.html).
|
Make sure you have a working Go environment (go 1.1+ is *required*). [See the install instructions](http://golang.org/doc/install.html).
|
||||||
|
|
||||||
To install `cli.go`, simply run:
|
To install `cli.go`, simply run:
|
||||||
@ -24,6 +27,7 @@ export PATH=$PATH:$GOPATH/bin
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Getting Started
|
## Getting Started
|
||||||
|
|
||||||
One of the philosophies behind `cli.go` is that an API should be playful and full of discovery. So a `cli.go` app can be as little as one line of code in `main()`.
|
One of the philosophies behind `cli.go` is that an API should be playful and full of discovery. So a `cli.go` app can be as little as one line of code in `main()`.
|
||||||
|
|
||||||
``` go
|
``` go
|
||||||
@ -123,6 +127,7 @@ GLOBAL OPTIONS
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Arguments
|
### Arguments
|
||||||
|
|
||||||
You can lookup arguments by calling the `Args` function on `cli.Context`.
|
You can lookup arguments by calling the `Args` function on `cli.Context`.
|
||||||
|
|
||||||
``` go
|
``` go
|
||||||
@ -134,7 +139,9 @@ app.Action = func(c *cli.Context) {
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Flags
|
### Flags
|
||||||
|
|
||||||
Setting and querying flags is simple.
|
Setting and querying flags is simple.
|
||||||
|
|
||||||
``` go
|
``` go
|
||||||
...
|
...
|
||||||
app.Flags = []cli.Flag {
|
app.Flags = []cli.Flag {
|
||||||
@ -159,6 +166,7 @@ app.Action = func(c *cli.Context) {
|
|||||||
```
|
```
|
||||||
|
|
||||||
You can also set a destination variable for a flag, to which the content will be scanned.
|
You can also set a destination variable for a flag, to which the content will be scanned.
|
||||||
|
|
||||||
``` go
|
``` go
|
||||||
...
|
...
|
||||||
var language string
|
var language string
|
||||||
@ -233,6 +241,7 @@ app.Flags = []cli.Flag {
|
|||||||
### Subcommands
|
### Subcommands
|
||||||
|
|
||||||
Subcommands can be defined for a more git-like command line app.
|
Subcommands can be defined for a more git-like command line app.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
...
|
...
|
||||||
app.Commands = []cli.Command{
|
app.Commands = []cli.Command{
|
||||||
@ -283,6 +292,7 @@ You can enable completion commands by setting the `EnableBashCompletion`
|
|||||||
flag on the `App` object. By default, this setting will only auto-complete to
|
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
|
show an app's subcommands, but you can write your own completion methods for
|
||||||
the App or its subcommands.
|
the App or its subcommands.
|
||||||
|
|
||||||
```go
|
```go
|
||||||
...
|
...
|
||||||
var tasks = []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
|
var tasks = []string{"cook", "clean", "laundry", "eat", "sleep", "code"}
|
||||||
@ -325,8 +335,8 @@ 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.
|
to source the file to make it active in the current shell.
|
||||||
|
|
||||||
```
|
```
|
||||||
sudo cp src/bash_autocomplete /etc/bash_completion.d/<myprogram>
|
sudo cp src/bash_autocomplete /etc/bash_completion.d/<myprogram>
|
||||||
source /etc/bash_completion.d/<myprogram>
|
source /etc/bash_completion.d/<myprogram>
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, you can just document that users should source the generic
|
Alternatively, you can just document that users should source the generic
|
||||||
@ -334,6 +344,7 @@ Alternatively, you can just document that users should source the generic
|
|||||||
to the name of their program (as above).
|
to the name of their program (as above).
|
||||||
|
|
||||||
## Contribution Guidelines
|
## Contribution Guidelines
|
||||||
|
|
||||||
Feel free to put up a pull request to fix a bug or maybe add a feature. I will give it a code review and make sure that it does not break backwards compatibility. If I or any other collaborators agree that it is in line with the vision of the project, we will work with you to get the code into a mergeable state and merge it into the master branch.
|
Feel free to put up a pull request to fix a bug or maybe add a feature. I will give it a code review and make sure that it does not break backwards compatibility. If I or any other collaborators agree that it is in line with the vision of the project, we will work with you to get the code into a mergeable state and merge it into the master branch.
|
||||||
|
|
||||||
If you have contributed something significant to the project, I will most likely add you as a collaborator. As a collaborator you are given the ability to merge others pull requests. It is very important that new code does not break existing code, so be careful about what code you do choose to merge. If you have any questions feel free to link @codegangsta to the issue in question and we can review it together.
|
If you have contributed something significant to the project, I will most likely add you as a collaborator. As a collaborator you are given the ability to merge others pull requests. It is very important that new code does not break existing code, so be careful about what code you do choose to merge. If you have any questions feel free to link @codegangsta to the issue in question and we can review it together.
|
||||||
|
60
app.go
60
app.go
@ -5,18 +5,21 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// App is the main structure of a cli application. It is recomended that
|
// App is the main structure of a cli application. It is recomended that
|
||||||
// an app be created with the cli.NewApp() function
|
// an app be created with the cli.NewApp() function
|
||||||
type App struct {
|
type App struct {
|
||||||
// The name of the program. Defaults to os.Args[0]
|
// The name of the program. Defaults to path.Base(os.Args[0])
|
||||||
Name string
|
Name string
|
||||||
// Full name of command for help, defaults to Name
|
// Full name of command for help, defaults to Name
|
||||||
HelpName string
|
HelpName string
|
||||||
// Description of the program.
|
// Description of the program.
|
||||||
Usage string
|
Usage string
|
||||||
|
// Text to override the USAGE section of help
|
||||||
|
UsageText string
|
||||||
// Description of the program argument format.
|
// Description of the program argument format.
|
||||||
ArgsUsage string
|
ArgsUsage string
|
||||||
// Version of the program
|
// Version of the program
|
||||||
@ -43,6 +46,10 @@ type App struct {
|
|||||||
Action func(context *Context)
|
Action func(context *Context)
|
||||||
// Execute this function if the proper command cannot be found
|
// Execute this function if the proper command cannot be found
|
||||||
CommandNotFound func(context *Context, command string)
|
CommandNotFound func(context *Context, command string)
|
||||||
|
// Execute this function, if an usage error occurs. This is useful for displaying customized usage error messages.
|
||||||
|
// This function is able to replace the original error messages.
|
||||||
|
// If this function is not set, the "Incorrect usage" is displayed and the execution is interrupted.
|
||||||
|
OnUsageError func(context *Context, err error, isSubcommand bool) error
|
||||||
// Compilation date
|
// Compilation date
|
||||||
Compiled time.Time
|
Compiled time.Time
|
||||||
// List of all authors who contributed
|
// List of all authors who contributed
|
||||||
@ -70,9 +77,10 @@ func compileTime() time.Time {
|
|||||||
// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action.
|
// Creates a new cli Application with some reasonable defaults for Name, Usage, Version and Action.
|
||||||
func NewApp() *App {
|
func NewApp() *App {
|
||||||
return &App{
|
return &App{
|
||||||
Name: os.Args[0],
|
Name: path.Base(os.Args[0]),
|
||||||
HelpName: os.Args[0],
|
HelpName: path.Base(os.Args[0]),
|
||||||
Usage: "A new cli application",
|
Usage: "A new cli application",
|
||||||
|
UsageText: "",
|
||||||
Version: "0.0.0",
|
Version: "0.0.0",
|
||||||
BashComplete: DefaultAppComplete,
|
BashComplete: DefaultAppComplete,
|
||||||
Action: helpCommand.Action,
|
Action: helpCommand.Action,
|
||||||
@ -118,25 +126,28 @@ func (a *App) Run(arguments []string) (err error) {
|
|||||||
set.SetOutput(ioutil.Discard)
|
set.SetOutput(ioutil.Discard)
|
||||||
err = set.Parse(arguments[1:])
|
err = set.Parse(arguments[1:])
|
||||||
nerr := normalizeFlags(a.Flags, set)
|
nerr := normalizeFlags(a.Flags, set)
|
||||||
|
context := NewContext(a, set, nil)
|
||||||
if nerr != nil {
|
if nerr != nil {
|
||||||
fmt.Fprintln(a.Writer, nerr)
|
fmt.Fprintln(a.Writer, nerr)
|
||||||
context := NewContext(a, set, nil)
|
|
||||||
ShowAppHelp(context)
|
ShowAppHelp(context)
|
||||||
return nerr
|
return nerr
|
||||||
}
|
}
|
||||||
context := NewContext(a, set, nil)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(a.Writer, "Incorrect Usage.")
|
|
||||||
fmt.Fprintln(a.Writer)
|
|
||||||
ShowAppHelp(context)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if checkCompletions(context) {
|
if checkCompletions(context) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if a.OnUsageError != nil {
|
||||||
|
err := a.OnUsageError(context, err, false)
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
|
||||||
|
ShowAppHelp(context)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !a.HideHelp && checkHelp(context) {
|
if !a.HideHelp && checkHelp(context) {
|
||||||
ShowAppHelp(context)
|
ShowAppHelp(context)
|
||||||
return nil
|
return nil
|
||||||
@ -149,8 +160,7 @@ func (a *App) Run(arguments []string) (err error) {
|
|||||||
|
|
||||||
if a.After != nil {
|
if a.After != nil {
|
||||||
defer func() {
|
defer func() {
|
||||||
afterErr := a.After(context)
|
if afterErr := a.After(context); afterErr != nil {
|
||||||
if afterErr != nil {
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = NewMultiError(err, afterErr)
|
err = NewMultiError(err, afterErr)
|
||||||
} else {
|
} else {
|
||||||
@ -161,8 +171,10 @@ func (a *App) Run(arguments []string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if a.Before != nil {
|
if a.Before != nil {
|
||||||
err := a.Before(context)
|
err = a.Before(context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
fmt.Fprintf(a.Writer, "%v\n\n", err)
|
||||||
|
ShowAppHelp(context)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -233,17 +245,21 @@ func (a *App) RunAsSubcommand(ctx *Context) (err error) {
|
|||||||
return nerr
|
return nerr
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintln(a.Writer, "Incorrect Usage.")
|
|
||||||
fmt.Fprintln(a.Writer)
|
|
||||||
ShowSubcommandHelp(context)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if checkCompletions(context) {
|
if checkCompletions(context) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if a.OnUsageError != nil {
|
||||||
|
err = a.OnUsageError(context, err, true)
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(a.Writer, "%s\n\n", "Incorrect Usage.")
|
||||||
|
ShowSubcommandHelp(context)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(a.Commands) > 0 {
|
if len(a.Commands) > 0 {
|
||||||
if checkSubcommandHelp(context) {
|
if checkSubcommandHelp(context) {
|
||||||
return nil
|
return nil
|
||||||
|
87
app_test.go
87
app_test.go
@ -5,9 +5,11 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExampleApp_Run() {
|
func ExampleApp_Run() {
|
||||||
@ -22,6 +24,7 @@ func ExampleApp_Run() {
|
|||||||
app.Action = func(c *Context) {
|
app.Action = func(c *Context) {
|
||||||
fmt.Printf("Hello %v\n", c.String("name"))
|
fmt.Printf("Hello %v\n", c.String("name"))
|
||||||
}
|
}
|
||||||
|
app.UsageText = "app [first_arg] [second_arg]"
|
||||||
app.Author = "Harrison"
|
app.Author = "Harrison"
|
||||||
app.Email = "harrison@lolwut.com"
|
app.Email = "harrison@lolwut.com"
|
||||||
app.Authors = []Author{Author{Name: "Oliver Allen", Email: "oliver@toyshop.com"}}
|
app.Authors = []Author{Author{Name: "Oliver Allen", Email: "oliver@toyshop.com"}}
|
||||||
@ -248,6 +251,24 @@ func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) {
|
|||||||
expect(t, args[2], "--notARealFlag")
|
expect(t, args[2], "--notARealFlag")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApp_CommandWithDash(t *testing.T) {
|
||||||
|
var args []string
|
||||||
|
|
||||||
|
app := NewApp()
|
||||||
|
command := Command{
|
||||||
|
Name: "cmd",
|
||||||
|
Action: func(c *Context) {
|
||||||
|
args = c.Args()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
app.Commands = []Command{command}
|
||||||
|
|
||||||
|
app.Run([]string{"", "cmd", "my-arg", "-"})
|
||||||
|
|
||||||
|
expect(t, args[0], "my-arg")
|
||||||
|
expect(t, args[1], "-")
|
||||||
|
}
|
||||||
|
|
||||||
func TestApp_CommandWithNoFlagBeforeTerminator(t *testing.T) {
|
func TestApp_CommandWithNoFlagBeforeTerminator(t *testing.T) {
|
||||||
var args []string
|
var args []string
|
||||||
|
|
||||||
@ -556,6 +577,7 @@ func TestAppNoHelpFlag(t *testing.T) {
|
|||||||
HelpFlag = BoolFlag{}
|
HelpFlag = BoolFlag{}
|
||||||
|
|
||||||
app := NewApp()
|
app := NewApp()
|
||||||
|
app.Writer = ioutil.Discard
|
||||||
err := app.Run([]string{"test", "-h"})
|
err := app.Run([]string{"test", "-h"})
|
||||||
|
|
||||||
if err != flag.ErrHelp {
|
if err != flag.ErrHelp {
|
||||||
@ -940,6 +962,11 @@ func TestApp_Run_SubcommandDoesNotOverwriteErrorFromBefore(t *testing.T) {
|
|||||||
app := NewApp()
|
app := NewApp()
|
||||||
app.Commands = []Command{
|
app.Commands = []Command{
|
||||||
Command{
|
Command{
|
||||||
|
Subcommands: []Command{
|
||||||
|
Command{
|
||||||
|
Name: "sub",
|
||||||
|
},
|
||||||
|
},
|
||||||
Name: "bar",
|
Name: "bar",
|
||||||
Before: func(c *Context) error { return fmt.Errorf("before error") },
|
Before: func(c *Context) error { return fmt.Errorf("before error") },
|
||||||
After: func(c *Context) error { return fmt.Errorf("after error") },
|
After: func(c *Context) error { return fmt.Errorf("after error") },
|
||||||
@ -958,3 +985,63 @@ func TestApp_Run_SubcommandDoesNotOverwriteErrorFromBefore(t *testing.T) {
|
|||||||
t.Errorf("expected text of error from After method, but got none in \"%v\"", err)
|
t.Errorf("expected text of error from After method, but got none in \"%v\"", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestApp_OnUsageError_WithWrongFlagValue(t *testing.T) {
|
||||||
|
app := NewApp()
|
||||||
|
app.Flags = []Flag{
|
||||||
|
IntFlag{Name: "flag"},
|
||||||
|
}
|
||||||
|
app.OnUsageError = func(c *Context, err error, isSubcommand bool) error {
|
||||||
|
if isSubcommand {
|
||||||
|
t.Errorf("Expect no subcommand")
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") {
|
||||||
|
t.Errorf("Expect an invalid value error, but got \"%v\"", err)
|
||||||
|
}
|
||||||
|
return errors.New("intercepted: " + err.Error())
|
||||||
|
}
|
||||||
|
app.Commands = []Command{
|
||||||
|
Command{
|
||||||
|
Name: "bar",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := app.Run([]string{"foo", "--flag=wrong"})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected to receive error from Run, got none")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(err.Error(), "intercepted: invalid value") {
|
||||||
|
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestApp_OnUsageError_WithWrongFlagValue_ForSubcommand(t *testing.T) {
|
||||||
|
app := NewApp()
|
||||||
|
app.Flags = []Flag{
|
||||||
|
IntFlag{Name: "flag"},
|
||||||
|
}
|
||||||
|
app.OnUsageError = func(c *Context, err error, isSubcommand bool) error {
|
||||||
|
if isSubcommand {
|
||||||
|
t.Errorf("Expect subcommand")
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") {
|
||||||
|
t.Errorf("Expect an invalid value error, but got \"%v\"", err)
|
||||||
|
}
|
||||||
|
return errors.New("intercepted: " + err.Error())
|
||||||
|
}
|
||||||
|
app.Commands = []Command{
|
||||||
|
Command{
|
||||||
|
Name: "bar",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := app.Run([]string{"foo", "--flag=wrong", "bar"})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected to receive error from Run, got none")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(err.Error(), "intercepted: invalid value") {
|
||||||
|
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
108
command.go
108
command.go
@ -16,6 +16,8 @@ type Command struct {
|
|||||||
Aliases []string
|
Aliases []string
|
||||||
// A short description of the usage of this command
|
// A short description of the usage of this command
|
||||||
Usage string
|
Usage string
|
||||||
|
// Custom text to show on USAGE section of help
|
||||||
|
UsageText string
|
||||||
// A longer explanation of how the command works
|
// A longer explanation of how the command works
|
||||||
Description string
|
Description string
|
||||||
// A short description of the arguments of this command
|
// A short description of the arguments of this command
|
||||||
@ -30,6 +32,10 @@ type Command struct {
|
|||||||
After func(context *Context) error
|
After func(context *Context) error
|
||||||
// The function to call when this command is invoked
|
// The function to call when this command is invoked
|
||||||
Action func(context *Context)
|
Action func(context *Context)
|
||||||
|
// Execute this function, if an usage error occurs. This is useful for displaying customized usage error messages.
|
||||||
|
// This function is able to replace the original error messages.
|
||||||
|
// If this function is not set, the "Incorrect usage" is displayed and the execution is interrupted.
|
||||||
|
OnUsageError func(context *Context, err error) error
|
||||||
// List of child commands
|
// List of child commands
|
||||||
Subcommands []Command
|
Subcommands []Command
|
||||||
// List of flags to parse
|
// List of flags to parse
|
||||||
@ -54,8 +60,8 @@ func (c Command) FullName() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Invokes the command given the context, parses ctx.Args() to generate command-specific flags
|
// Invokes the command given the context, parses ctx.Args() to generate command-specific flags
|
||||||
func (c Command) Run(ctx *Context) error {
|
func (c Command) Run(ctx *Context) (err error) {
|
||||||
if len(c.Subcommands) > 0 || c.Before != nil || c.After != nil {
|
if len(c.Subcommands) > 0 {
|
||||||
return c.startApp(ctx)
|
return c.startApp(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,41 +80,54 @@ func (c Command) Run(ctx *Context) error {
|
|||||||
set := flagSet(c.Name, c.Flags)
|
set := flagSet(c.Name, c.Flags)
|
||||||
set.SetOutput(ioutil.Discard)
|
set.SetOutput(ioutil.Discard)
|
||||||
|
|
||||||
firstFlagIndex := -1
|
if !c.SkipFlagParsing {
|
||||||
terminatorIndex := -1
|
firstFlagIndex := -1
|
||||||
for index, arg := range ctx.Args() {
|
terminatorIndex := -1
|
||||||
if arg == "--" {
|
for index, arg := range ctx.Args() {
|
||||||
terminatorIndex = index
|
if arg == "--" {
|
||||||
break
|
terminatorIndex = index
|
||||||
} else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 {
|
break
|
||||||
firstFlagIndex = index
|
} else if arg == "-" {
|
||||||
|
// Do nothing. A dash alone is not really a flag.
|
||||||
|
continue
|
||||||
|
} else if strings.HasPrefix(arg, "-") && firstFlagIndex == -1 {
|
||||||
|
firstFlagIndex = index
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
if firstFlagIndex > -1 {
|
||||||
if firstFlagIndex > -1 && !c.SkipFlagParsing {
|
args := ctx.Args()
|
||||||
args := ctx.Args()
|
regularArgs := make([]string, len(args[1:firstFlagIndex]))
|
||||||
regularArgs := make([]string, len(args[1:firstFlagIndex]))
|
copy(regularArgs, args[1:firstFlagIndex])
|
||||||
copy(regularArgs, args[1:firstFlagIndex])
|
|
||||||
|
|
||||||
var flagArgs []string
|
var flagArgs []string
|
||||||
if terminatorIndex > -1 {
|
if terminatorIndex > -1 {
|
||||||
flagArgs = args[firstFlagIndex:terminatorIndex]
|
flagArgs = args[firstFlagIndex:terminatorIndex]
|
||||||
regularArgs = append(regularArgs, args[terminatorIndex:]...)
|
regularArgs = append(regularArgs, args[terminatorIndex:]...)
|
||||||
|
} else {
|
||||||
|
flagArgs = args[firstFlagIndex:]
|
||||||
|
}
|
||||||
|
|
||||||
|
err = set.Parse(append(flagArgs, regularArgs...))
|
||||||
} else {
|
} else {
|
||||||
flagArgs = args[firstFlagIndex:]
|
err = set.Parse(ctx.Args().Tail())
|
||||||
}
|
}
|
||||||
|
|
||||||
err = set.Parse(append(flagArgs, regularArgs...))
|
|
||||||
} else {
|
} else {
|
||||||
err = set.Parse(ctx.Args().Tail())
|
if c.SkipFlagParsing {
|
||||||
|
err = set.Parse(append([]string{"--"}, ctx.Args().Tail()...))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.")
|
if c.OnUsageError != nil {
|
||||||
fmt.Fprintln(ctx.App.Writer)
|
err := c.OnUsageError(ctx, err)
|
||||||
ShowCommandHelp(ctx, c.Name)
|
return err
|
||||||
return err
|
} else {
|
||||||
|
fmt.Fprintln(ctx.App.Writer, "Incorrect Usage.")
|
||||||
|
fmt.Fprintln(ctx.App.Writer)
|
||||||
|
ShowCommandHelp(ctx, c.Name)
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nerr := normalizeFlags(c.Flags, set)
|
nerr := normalizeFlags(c.Flags, set)
|
||||||
@ -127,6 +146,30 @@ func (c Command) Run(ctx *Context) error {
|
|||||||
if checkCommandHelp(context, c.Name) {
|
if checkCommandHelp(context, c.Name) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if c.After != nil {
|
||||||
|
defer func() {
|
||||||
|
afterErr := c.After(context)
|
||||||
|
if afterErr != nil {
|
||||||
|
if err != nil {
|
||||||
|
err = NewMultiError(err, afterErr)
|
||||||
|
} else {
|
||||||
|
err = afterErr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Before != nil {
|
||||||
|
err := c.Before(context)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(ctx.App.Writer, err)
|
||||||
|
fmt.Fprintln(ctx.App.Writer)
|
||||||
|
ShowCommandHelp(ctx, c.Name)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
context.Command = c
|
context.Command = c
|
||||||
c.Action(context)
|
c.Action(context)
|
||||||
return nil
|
return nil
|
||||||
@ -160,7 +203,7 @@ func (c Command) startApp(ctx *Context) error {
|
|||||||
if c.HelpName == "" {
|
if c.HelpName == "" {
|
||||||
app.HelpName = c.HelpName
|
app.HelpName = c.HelpName
|
||||||
} else {
|
} else {
|
||||||
app.HelpName = fmt.Sprintf("%s %s", ctx.App.Name, c.Name)
|
app.HelpName = app.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Description != "" {
|
if c.Description != "" {
|
||||||
@ -199,12 +242,9 @@ func (c Command) startApp(ctx *Context) error {
|
|||||||
app.Action = helpSubcommand.Action
|
app.Action = helpSubcommand.Action
|
||||||
}
|
}
|
||||||
|
|
||||||
var newCmds []Command
|
for index, cc := range app.Commands {
|
||||||
for _, cc := range app.Commands {
|
app.Commands[index].commandNamePath = []string{c.Name, cc.Name}
|
||||||
cc.commandNamePath = []string{c.Name, cc.Name}
|
|
||||||
newCmds = append(newCmds, cc)
|
|
||||||
}
|
}
|
||||||
app.Commands = newCmds
|
|
||||||
|
|
||||||
return app.RunAsSubcommand(ctx)
|
return app.RunAsSubcommand(ctx)
|
||||||
}
|
}
|
||||||
|
114
command_test.go
114
command_test.go
@ -1,47 +1,97 @@
|
|||||||
package cli
|
package cli
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestCommandDoNotIgnoreFlags(t *testing.T) {
|
func TestCommandFlagParsing(t *testing.T) {
|
||||||
app := NewApp()
|
cases := []struct {
|
||||||
set := flag.NewFlagSet("test", 0)
|
testArgs []string
|
||||||
test := []string{"blah", "blah", "-break"}
|
skipFlagParsing bool
|
||||||
set.Parse(test)
|
expectedErr error
|
||||||
|
}{
|
||||||
c := NewContext(app, set, nil)
|
{[]string{"blah", "blah", "-break"}, false, errors.New("flag provided but not defined: -break")}, // Test normal "not ignoring flags" flow
|
||||||
|
{[]string{"blah", "blah"}, true, nil}, // Test SkipFlagParsing without any args that look like flags
|
||||||
command := Command{
|
{[]string{"blah", "-break"}, true, nil}, // Test SkipFlagParsing with random flag arg
|
||||||
Name: "test-cmd",
|
{[]string{"blah", "-help"}, true, nil}, // Test SkipFlagParsing with "special" help flag arg
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(_ *Context) {},
|
|
||||||
}
|
}
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err.Error(), "flag provided but not defined: -break")
|
for _, c := range cases {
|
||||||
|
app := NewApp()
|
||||||
|
app.Writer = ioutil.Discard
|
||||||
|
set := flag.NewFlagSet("test", 0)
|
||||||
|
set.Parse(c.testArgs)
|
||||||
|
|
||||||
|
context := NewContext(app, set, nil)
|
||||||
|
|
||||||
|
command := Command{
|
||||||
|
Name: "test-cmd",
|
||||||
|
Aliases: []string{"tc"},
|
||||||
|
Usage: "this is for testing",
|
||||||
|
Description: "testing",
|
||||||
|
Action: func(_ *Context) {},
|
||||||
|
}
|
||||||
|
|
||||||
|
command.SkipFlagParsing = c.skipFlagParsing
|
||||||
|
|
||||||
|
err := command.Run(context)
|
||||||
|
|
||||||
|
expect(t, err, c.expectedErr)
|
||||||
|
expect(t, []string(context.Args()), c.testArgs)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCommandIgnoreFlags(t *testing.T) {
|
func TestCommand_Run_DoesNotOverwriteErrorFromBefore(t *testing.T) {
|
||||||
app := NewApp()
|
app := NewApp()
|
||||||
set := flag.NewFlagSet("test", 0)
|
app.Commands = []Command{
|
||||||
test := []string{"blah", "blah", "-break"}
|
Command{
|
||||||
set.Parse(test)
|
Name: "bar",
|
||||||
|
Before: func(c *Context) error { return fmt.Errorf("before error") },
|
||||||
c := NewContext(app, set, nil)
|
After: func(c *Context) error { return fmt.Errorf("after error") },
|
||||||
|
},
|
||||||
command := Command{
|
|
||||||
Name: "test-cmd",
|
|
||||||
Aliases: []string{"tc"},
|
|
||||||
Usage: "this is for testing",
|
|
||||||
Description: "testing",
|
|
||||||
Action: func(_ *Context) {},
|
|
||||||
SkipFlagParsing: true,
|
|
||||||
}
|
}
|
||||||
err := command.Run(c)
|
|
||||||
|
|
||||||
expect(t, err, nil)
|
err := app.Run([]string{"foo", "bar"})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected to receive error from Run, got none")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(err.Error(), "before error") {
|
||||||
|
t.Errorf("expected text of error from Before method, but got none in \"%v\"", err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), "after error") {
|
||||||
|
t.Errorf("expected text of error from After method, but got none in \"%v\"", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCommand_OnUsageError_WithWrongFlagValue(t *testing.T) {
|
||||||
|
app := NewApp()
|
||||||
|
app.Commands = []Command{
|
||||||
|
Command{
|
||||||
|
Name: "bar",
|
||||||
|
Flags: []Flag{
|
||||||
|
IntFlag{Name: "flag"},
|
||||||
|
},
|
||||||
|
OnUsageError: func(c *Context, err error) error {
|
||||||
|
if !strings.HasPrefix(err.Error(), "invalid value \"wrong\"") {
|
||||||
|
t.Errorf("Expect an invalid value error, but got \"%v\"", err)
|
||||||
|
}
|
||||||
|
return errors.New("intercepted: " + err.Error())
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
err := app.Run([]string{"foo", "bar", "--flag=wrong"})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("expected to receive error from Run, got none")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(err.Error(), "intercepted: invalid value") {
|
||||||
|
t.Errorf("Expect an intercepted error, but got \"%v\"", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -163,7 +163,7 @@ func (c *Context) GlobalIsSet(name string) bool {
|
|||||||
// Returns a slice of flag names used in this context.
|
// Returns a slice of flag names used in this context.
|
||||||
func (c *Context) FlagNames() (names []string) {
|
func (c *Context) FlagNames() (names []string) {
|
||||||
for _, flag := range c.Command.Flags {
|
for _, flag := range c.Command.Flags {
|
||||||
name := strings.Split(flag.getName(), ",")[0]
|
name := strings.Split(flag.GetName(), ",")[0]
|
||||||
if name == "help" {
|
if name == "help" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -175,7 +175,7 @@ func (c *Context) FlagNames() (names []string) {
|
|||||||
// Returns a slice of global flag names used by the app.
|
// Returns a slice of global flag names used by the app.
|
||||||
func (c *Context) GlobalFlagNames() (names []string) {
|
func (c *Context) GlobalFlagNames() (names []string) {
|
||||||
for _, flag := range c.App.Flags {
|
for _, flag := range c.App.Flags {
|
||||||
name := strings.Split(flag.getName(), ",")[0]
|
name := strings.Split(flag.GetName(), ",")[0]
|
||||||
if name == "help" || name == "version" {
|
if name == "help" || name == "version" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -360,7 +360,7 @@ func normalizeFlags(flags []Flag, set *flag.FlagSet) error {
|
|||||||
visited[f.Name] = true
|
visited[f.Name] = true
|
||||||
})
|
})
|
||||||
for _, f := range flags {
|
for _, f := range flags {
|
||||||
parts := strings.Split(f.getName(), ",")
|
parts := strings.Split(f.GetName(), ",")
|
||||||
if len(parts) == 1 {
|
if len(parts) == 1 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
59
flag.go
59
flag.go
@ -7,6 +7,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// This flag enables bash-completion for all commands and subcommands
|
// This flag enables bash-completion for all commands and subcommands
|
||||||
@ -35,7 +36,7 @@ type Flag interface {
|
|||||||
fmt.Stringer
|
fmt.Stringer
|
||||||
// Apply Flag settings to the given flag set
|
// Apply Flag settings to the given flag set
|
||||||
Apply(*flag.FlagSet)
|
Apply(*flag.FlagSet)
|
||||||
getName() string
|
GetName() string
|
||||||
}
|
}
|
||||||
|
|
||||||
func flagSet(name string, flags []Flag) *flag.FlagSet {
|
func flagSet(name string, flags []Flag) *flag.FlagSet {
|
||||||
@ -73,7 +74,18 @@ type GenericFlag struct {
|
|||||||
// help text to the user (uses the String() method of the generic flag to show
|
// help text to the user (uses the String() method of the generic flag to show
|
||||||
// the value)
|
// the value)
|
||||||
func (f GenericFlag) String() string {
|
func (f GenericFlag) String() string {
|
||||||
return withEnvHint(f.EnvVar, fmt.Sprintf("%s%s \"%v\"\t%v", prefixFor(f.Name), f.Name, f.Value, f.Usage))
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s %v\t%v", prefixedNames(f.Name), f.FormatValueHelp(), f.Usage))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f GenericFlag) FormatValueHelp() string {
|
||||||
|
if f.Value == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
s := f.Value.String()
|
||||||
|
if len(s) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("\"%s\"", s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply takes the flagset and calls Set on the generic flag with the value
|
// Apply takes the flagset and calls Set on the generic flag with the value
|
||||||
@ -95,7 +107,7 @@ func (f GenericFlag) Apply(set *flag.FlagSet) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f GenericFlag) getName() string {
|
func (f GenericFlag) GetName() string {
|
||||||
return f.Name
|
return f.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,7 +171,7 @@ func (f StringSliceFlag) Apply(set *flag.FlagSet) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f StringSliceFlag) getName() string {
|
func (f StringSliceFlag) GetName() string {
|
||||||
return f.Name
|
return f.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +243,7 @@ func (f IntSliceFlag) Apply(set *flag.FlagSet) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f IntSliceFlag) getName() string {
|
func (f IntSliceFlag) GetName() string {
|
||||||
return f.Name
|
return f.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -273,7 +285,7 @@ func (f BoolFlag) Apply(set *flag.FlagSet) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f BoolFlag) getName() string {
|
func (f BoolFlag) GetName() string {
|
||||||
return f.Name
|
return f.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,7 +328,7 @@ func (f BoolTFlag) Apply(set *flag.FlagSet) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f BoolTFlag) getName() string {
|
func (f BoolTFlag) GetName() string {
|
||||||
return f.Name
|
return f.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -331,16 +343,15 @@ type StringFlag struct {
|
|||||||
|
|
||||||
// String returns the usage
|
// String returns the usage
|
||||||
func (f StringFlag) String() string {
|
func (f StringFlag) String() string {
|
||||||
var fmtString string
|
return withEnvHint(f.EnvVar, fmt.Sprintf("%s %v\t%v", prefixedNames(f.Name), f.FormatValueHelp(), f.Usage))
|
||||||
fmtString = "%s %v\t%v"
|
}
|
||||||
|
|
||||||
if len(f.Value) > 0 {
|
func (f StringFlag) FormatValueHelp() string {
|
||||||
fmtString = "%s \"%v\"\t%v"
|
s := f.Value
|
||||||
} else {
|
if len(s) == 0 {
|
||||||
fmtString = "%s %v\t%v"
|
return ""
|
||||||
}
|
}
|
||||||
|
return fmt.Sprintf("\"%s\"", s)
|
||||||
return withEnvHint(f.EnvVar, fmt.Sprintf(fmtString, prefixedNames(f.Name), f.Value, f.Usage))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply populates the flag given the flag set and environment
|
// Apply populates the flag given the flag set and environment
|
||||||
@ -364,7 +375,7 @@ func (f StringFlag) Apply(set *flag.FlagSet) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f StringFlag) getName() string {
|
func (f StringFlag) GetName() string {
|
||||||
return f.Name
|
return f.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,7 +418,7 @@ func (f IntFlag) Apply(set *flag.FlagSet) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f IntFlag) getName() string {
|
func (f IntFlag) GetName() string {
|
||||||
return f.Name
|
return f.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -450,7 +461,7 @@ func (f DurationFlag) Apply(set *flag.FlagSet) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f DurationFlag) getName() string {
|
func (f DurationFlag) GetName() string {
|
||||||
return f.Name
|
return f.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,7 +503,7 @@ func (f Float64Flag) Apply(set *flag.FlagSet) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Float64Flag) getName() string {
|
func (f Float64Flag) GetName() string {
|
||||||
return f.Name
|
return f.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -521,7 +532,15 @@ func prefixedNames(fullName string) (prefixed string) {
|
|||||||
func withEnvHint(envVar, str string) string {
|
func withEnvHint(envVar, str string) string {
|
||||||
envText := ""
|
envText := ""
|
||||||
if envVar != "" {
|
if envVar != "" {
|
||||||
envText = fmt.Sprintf(" [$%s]", strings.Join(strings.Split(envVar, ","), ", $"))
|
prefix := "$"
|
||||||
|
suffix := ""
|
||||||
|
sep := ", $"
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
prefix = "%"
|
||||||
|
suffix = "%"
|
||||||
|
sep = "%, %"
|
||||||
|
}
|
||||||
|
envText = fmt.Sprintf(" [%s%s%s]", prefix, strings.Join(strings.Split(envVar, ","), sep), suffix)
|
||||||
}
|
}
|
||||||
return str + envText
|
return str + envText
|
||||||
}
|
}
|
||||||
|
57
flag_test.go
57
flag_test.go
@ -6,6 +6,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
var boolFlagTests = []struct {
|
var boolFlagTests = []struct {
|
||||||
@ -58,8 +59,12 @@ func TestStringFlagWithEnvVarHelpOutput(t *testing.T) {
|
|||||||
flag := StringFlag{Name: test.name, Value: test.value, EnvVar: "APP_FOO"}
|
flag := StringFlag{Name: test.name, Value: test.value, EnvVar: "APP_FOO"}
|
||||||
output := flag.String()
|
output := flag.String()
|
||||||
|
|
||||||
if !strings.HasSuffix(output, " [$APP_FOO]") {
|
expectedSuffix := " [$APP_FOO]"
|
||||||
t.Errorf("%s does not end with [$APP_FOO]", output)
|
if runtime.GOOS == "windows" {
|
||||||
|
expectedSuffix = " [%APP_FOO%]"
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(output, expectedSuffix) {
|
||||||
|
t.Errorf("%s does not end with" + expectedSuffix, output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,8 +115,12 @@ func TestStringSliceFlagWithEnvVarHelpOutput(t *testing.T) {
|
|||||||
flag := StringSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_QWWX"}
|
flag := StringSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_QWWX"}
|
||||||
output := flag.String()
|
output := flag.String()
|
||||||
|
|
||||||
if !strings.HasSuffix(output, " [$APP_QWWX]") {
|
expectedSuffix := " [$APP_QWWX]"
|
||||||
t.Errorf("%q does not end with [$APP_QWWX]", output)
|
if runtime.GOOS == "windows" {
|
||||||
|
expectedSuffix = " [%APP_QWWX%]"
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(output, expectedSuffix) {
|
||||||
|
t.Errorf("%q does not end with" + expectedSuffix, output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,8 +152,12 @@ func TestIntFlagWithEnvVarHelpOutput(t *testing.T) {
|
|||||||
flag := IntFlag{Name: test.name, EnvVar: "APP_BAR"}
|
flag := IntFlag{Name: test.name, EnvVar: "APP_BAR"}
|
||||||
output := flag.String()
|
output := flag.String()
|
||||||
|
|
||||||
if !strings.HasSuffix(output, " [$APP_BAR]") {
|
expectedSuffix := " [$APP_BAR]"
|
||||||
t.Errorf("%s does not end with [$APP_BAR]", output)
|
if runtime.GOOS == "windows" {
|
||||||
|
expectedSuffix = " [%APP_BAR%]"
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(output, expectedSuffix) {
|
||||||
|
t.Errorf("%s does not end with" + expectedSuffix, output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -176,8 +189,12 @@ func TestDurationFlagWithEnvVarHelpOutput(t *testing.T) {
|
|||||||
flag := DurationFlag{Name: test.name, EnvVar: "APP_BAR"}
|
flag := DurationFlag{Name: test.name, EnvVar: "APP_BAR"}
|
||||||
output := flag.String()
|
output := flag.String()
|
||||||
|
|
||||||
if !strings.HasSuffix(output, " [$APP_BAR]") {
|
expectedSuffix := " [$APP_BAR]"
|
||||||
t.Errorf("%s does not end with [$APP_BAR]", output)
|
if runtime.GOOS == "windows" {
|
||||||
|
expectedSuffix = " [%APP_BAR%]"
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(output, expectedSuffix) {
|
||||||
|
t.Errorf("%s does not end with" + expectedSuffix, output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,8 +233,12 @@ func TestIntSliceFlagWithEnvVarHelpOutput(t *testing.T) {
|
|||||||
flag := IntSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"}
|
flag := IntSliceFlag{Name: test.name, Value: test.value, EnvVar: "APP_SMURF"}
|
||||||
output := flag.String()
|
output := flag.String()
|
||||||
|
|
||||||
if !strings.HasSuffix(output, " [$APP_SMURF]") {
|
expectedSuffix := " [$APP_SMURF]"
|
||||||
t.Errorf("%q does not end with [$APP_SMURF]", output)
|
if runtime.GOOS == "windows" {
|
||||||
|
expectedSuffix = " [%APP_SMURF%]"
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(output, expectedSuffix) {
|
||||||
|
t.Errorf("%q does not end with" + expectedSuffix, output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -249,8 +270,12 @@ func TestFloat64FlagWithEnvVarHelpOutput(t *testing.T) {
|
|||||||
flag := Float64Flag{Name: test.name, EnvVar: "APP_BAZ"}
|
flag := Float64Flag{Name: test.name, EnvVar: "APP_BAZ"}
|
||||||
output := flag.String()
|
output := flag.String()
|
||||||
|
|
||||||
if !strings.HasSuffix(output, " [$APP_BAZ]") {
|
expectedSuffix := " [$APP_BAZ]"
|
||||||
t.Errorf("%s does not end with [$APP_BAZ]", output)
|
if runtime.GOOS == "windows" {
|
||||||
|
expectedSuffix = " [%APP_BAZ%]"
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(output, expectedSuffix) {
|
||||||
|
t.Errorf("%s does not end with" + expectedSuffix, output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -283,8 +308,12 @@ func TestGenericFlagWithEnvVarHelpOutput(t *testing.T) {
|
|||||||
flag := GenericFlag{Name: test.name, EnvVar: "APP_ZAP"}
|
flag := GenericFlag{Name: test.name, EnvVar: "APP_ZAP"}
|
||||||
output := flag.String()
|
output := flag.String()
|
||||||
|
|
||||||
if !strings.HasSuffix(output, " [$APP_ZAP]") {
|
expectedSuffix := " [$APP_ZAP]"
|
||||||
t.Errorf("%s does not end with [$APP_ZAP]", output)
|
if runtime.GOOS == "windows" {
|
||||||
|
expectedSuffix = " [%APP_ZAP%]"
|
||||||
|
}
|
||||||
|
if !strings.HasSuffix(output, expectedSuffix) {
|
||||||
|
t.Errorf("%s does not end with" + expectedSuffix, output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
6
help.go
6
help.go
@ -15,7 +15,7 @@ var AppHelpTemplate = `NAME:
|
|||||||
{{.Name}} - {{.Usage}}
|
{{.Name}} - {{.Usage}}
|
||||||
|
|
||||||
USAGE:
|
USAGE:
|
||||||
{{.HelpName}} {{if .Flags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}
|
{{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}} {{if .Flags}}[global options]{{end}}{{if .Commands}} command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}
|
||||||
{{if .Version}}
|
{{if .Version}}
|
||||||
VERSION:
|
VERSION:
|
||||||
{{.Version}}
|
{{.Version}}
|
||||||
@ -180,7 +180,9 @@ func printHelp(out io.Writer, templ string, data interface{}) {
|
|||||||
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
|
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
|
||||||
err := t.Execute(w, data)
|
err := t.Execute(w, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
// If the writer is closed, t.Execute will fail, and there's nothing
|
||||||
|
// we can do to recover. We could send this to os.Stderr if we need.
|
||||||
|
return
|
||||||
}
|
}
|
||||||
w.Flush()
|
w.Flush()
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,13 @@ import (
|
|||||||
|
|
||||||
/* Test Helpers */
|
/* Test Helpers */
|
||||||
func expect(t *testing.T, a interface{}, b interface{}) {
|
func expect(t *testing.T, a interface{}, b interface{}) {
|
||||||
if a != b {
|
if !reflect.DeepEqual(a, b) {
|
||||||
t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
t.Errorf("Expected %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func refute(t *testing.T, a interface{}, b interface{}) {
|
func refute(t *testing.T, a interface{}, b interface{}) {
|
||||||
if a == b {
|
if reflect.DeepEqual(a, b) {
|
||||||
t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
t.Errorf("Did not expect %v (type %v) - Got %v (type %v)", b, reflect.TypeOf(b), a, reflect.TypeOf(a))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user