14 changeLogURL = "https://github.com/urfave/cli/blob/master/CHANGELOG.md"
15 appActionDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-action-signature", changeLogURL)
16 runAndExitOnErrorDeprecationURL = fmt.Sprintf("%s#deprecated-cli-app-runandexitonerror", changeLogURL)
18 contactSysadmin = "This is an error in the application. Please contact the distributor of this application if this is not you."
20 errInvalidActionType = NewExitError("ERROR invalid Action type. "+
21 fmt.Sprintf("Must be `func(*Context`)` or `func(*Context) error). %s", contactSysadmin)+
22 fmt.Sprintf("See %s", appActionDeprecationURL), 2)
25 // App is the main structure of a cli application. It is recommended that
26 // an app be created with the cli.NewApp() function
28 // The name of the program. Defaults to path.Base(os.Args[0])
30 // Full name of command for help, defaults to Name
32 // Description of the program.
34 // Text to override the USAGE section of help
36 // Description of the program argument format.
38 // Version of the program
40 // Description of the program
42 // List of commands to execute
44 // List of flags to parse
46 // Boolean to enable bash completion commands
47 EnableBashCompletion bool
48 // Boolean to hide built-in help command
50 // Boolean to hide built-in version flag and the VERSION section of help
52 // Populate on app startup, only gettable through method Categories()
53 categories CommandCategories
54 // An action to execute when the bash-completion flag is set
55 BashComplete BashCompleteFunc
56 // An action to execute before any subcommands are run, but after the context is ready
57 // If a non-nil error is returned, no subcommands are run
59 // An action to execute after any subcommands are run, but after the subcommand has finished
60 // It is run even if Action() panics
63 // The action to execute when no subcommands are specified
64 // Expects a `cli.ActionFunc` but will accept the *deprecated* signature of `func(*cli.Context) {}`
65 // *Note*: support for the deprecated `Action` signature will be removed in a future version
68 // Execute this function if the proper command cannot be found
69 CommandNotFound CommandNotFoundFunc
70 // Execute this function if an usage error occurs
71 OnUsageError OnUsageErrorFunc
74 // List of all authors who contributed
76 // Copyright of the binary if any
78 // Name of Author (Note: Use App.Authors, this is deprecated)
80 // Email of Author (Note: Use App.Authors, this is deprecated)
82 // Writer writer to write output to
84 // ErrWriter writes error output
87 Metadata map[string]interface{}
92 // Tries to find out when this binary was compiled.
93 // Returns the current time if it fails to find it.
94 func compileTime() time.Time {
95 info, err := os.Stat(os.Args[0])
102 // NewApp creates a new cli Application with some reasonable defaults for Name,
103 // Usage, Version and Action.
106 Name: filepath.Base(os.Args[0]),
107 HelpName: filepath.Base(os.Args[0]),
108 Usage: "A new cli application",
111 BashComplete: DefaultAppComplete,
112 Action: helpCommand.Action,
113 Compiled: compileTime(),
118 // Setup runs initialization code to ensure all data structures are ready for
119 // `Run` or inspection prior to `Run`. It is internally called by `Run`, but
120 // will return early if setup has already happened.
121 func (a *App) Setup() {
128 if a.Author != "" || a.Email != "" {
129 a.Authors = append(a.Authors, Author{Name: a.Author, Email: a.Email})
132 newCmds := []Command{}
133 for _, c := range a.Commands {
134 if c.HelpName == "" {
135 c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
137 newCmds = append(newCmds, c)
141 if a.Command(helpCommand.Name) == nil && !a.HideHelp {
142 a.Commands = append(a.Commands, helpCommand)
143 if (HelpFlag != BoolFlag{}) {
144 a.appendFlag(HelpFlag)
149 a.appendFlag(VersionFlag)
152 a.categories = CommandCategories{}
153 for _, command := range a.Commands {
154 a.categories = a.categories.AddCommand(command.Category, command)
156 sort.Sort(a.categories)
158 if a.Metadata == nil {
159 a.Metadata = make(map[string]interface{})
167 // Run is the entry point to the cli app. Parses the arguments slice and routes
168 // to the proper flag/args combination
169 func (a *App) Run(arguments []string) (err error) {
172 // handle the completion flag separately from the flagset since
173 // completion could be attempted after a flag, but before its value was put
174 // on the command line. this causes the flagset to interpret the completion
175 // flag name as the value of the flag before it which is undesirable
176 // note that we can only do this because the shell autocomplete function
177 // always appends the completion flag at the end of the command
178 shellComplete, arguments := checkShellCompleteFlag(a, arguments)
181 set, err := flagSet(a.Name, a.Flags)
186 set.SetOutput(ioutil.Discard)
187 err = set.Parse(arguments[1:])
188 nerr := normalizeFlags(a.Flags, set)
189 context := NewContext(a, set, nil)
191 fmt.Fprintln(a.Writer, nerr)
195 context.shellComplete = shellComplete
197 if checkCompletions(context) {
202 if a.OnUsageError != nil {
203 err := a.OnUsageError(context, err, false)
207 fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
212 if !a.HideHelp && checkHelp(context) {
217 if !a.HideVersion && checkVersion(context) {
224 if afterErr := a.After(context); afterErr != nil {
226 err = NewMultiError(err, afterErr)
235 beforeErr := a.Before(context)
236 if beforeErr != nil {
237 fmt.Fprintf(a.Writer, "%v\n\n", beforeErr)
239 HandleExitCoder(beforeErr)
245 args := context.Args()
250 return c.Run(context)
255 a.Action = helpCommand.Action
258 // Run default Action
259 err = HandleAction(a.Action, context)
265 // RunAndExitOnError calls .Run() and exits non-zero if an error was returned
267 // Deprecated: instead you should return an error that fulfills cli.ExitCoder
268 // to cli.App.Run. This will cause the application to exit with the given eror
269 // code in the cli.ExitCoder
270 func (a *App) RunAndExitOnError() {
271 if err := a.Run(os.Args); err != nil {
272 fmt.Fprintln(a.errWriter(), err)
277 // RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to
278 // generate command-specific flags
279 func (a *App) RunAsSubcommand(ctx *Context) (err error) {
280 // append help to commands
281 if len(a.Commands) > 0 {
282 if a.Command(helpCommand.Name) == nil && !a.HideHelp {
283 a.Commands = append(a.Commands, helpCommand)
284 if (HelpFlag != BoolFlag{}) {
285 a.appendFlag(HelpFlag)
290 newCmds := []Command{}
291 for _, c := range a.Commands {
292 if c.HelpName == "" {
293 c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name)
295 newCmds = append(newCmds, c)
300 set, err := flagSet(a.Name, a.Flags)
305 set.SetOutput(ioutil.Discard)
306 err = set.Parse(ctx.Args().Tail())
307 nerr := normalizeFlags(a.Flags, set)
308 context := NewContext(a, set, ctx)
311 fmt.Fprintln(a.Writer, nerr)
312 fmt.Fprintln(a.Writer)
313 if len(a.Commands) > 0 {
314 ShowSubcommandHelp(context)
316 ShowCommandHelp(ctx, context.Args().First())
321 if checkCompletions(context) {
326 if a.OnUsageError != nil {
327 err = a.OnUsageError(context, err, true)
331 fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error())
332 ShowSubcommandHelp(context)
336 if len(a.Commands) > 0 {
337 if checkSubcommandHelp(context) {
341 if checkCommandHelp(ctx, context.Args().First()) {
348 afterErr := a.After(context)
352 err = NewMultiError(err, afterErr)
361 beforeErr := a.Before(context)
362 if beforeErr != nil {
363 HandleExitCoder(beforeErr)
369 args := context.Args()
374 return c.Run(context)
378 // Run default Action
379 err = HandleAction(a.Action, context)
385 // Command returns the named command on App. Returns nil if the command does not exist
386 func (a *App) Command(name string) *Command {
387 for _, c := range a.Commands {
396 // Categories returns a slice containing all the categories with the commands they contain
397 func (a *App) Categories() CommandCategories {
401 // VisibleCategories returns a slice of categories and commands that are
403 func (a *App) VisibleCategories() []*CommandCategory {
404 ret := []*CommandCategory{}
405 for _, category := range a.categories {
406 if visible := func() *CommandCategory {
407 for _, command := range category.Commands {
413 }(); visible != nil {
414 ret = append(ret, visible)
420 // VisibleCommands returns a slice of the Commands with Hidden=false
421 func (a *App) VisibleCommands() []Command {
423 for _, command := range a.Commands {
425 ret = append(ret, command)
431 // VisibleFlags returns a slice of the Flags with Hidden=false
432 func (a *App) VisibleFlags() []Flag {
433 return visibleFlags(a.Flags)
436 func (a *App) hasFlag(flag Flag) bool {
437 for _, f := range a.Flags {
446 func (a *App) errWriter() io.Writer {
448 // When the app ErrWriter is nil use the package level one.
449 if a.ErrWriter == nil {
456 func (a *App) appendFlag(flag Flag) {
457 if !a.hasFlag(flag) {
458 a.Flags = append(a.Flags, flag)
462 // Author represents someone who has contributed to a cli project.
464 Name string // The Authors name
465 Email string // The Authors email
468 // String makes Author comply to the Stringer interface, to allow an easy print in the templating process
469 func (a Author) String() string {
472 e = " <" + a.Email + ">"
475 return fmt.Sprintf("%v%v", a.Name, e)
478 // HandleAction attempts to figure out which Action signature was used. If
479 // it's an ActionFunc or a func with the legacy signature for Action, the func
481 func HandleAction(action interface{}, context *Context) (err error) {
482 if a, ok := action.(ActionFunc); ok {
484 } else if a, ok := action.(func(*Context) error); ok {
486 } else if a, ok := action.(func(*Context)); ok { // deprecated function signature
490 return errInvalidActionType