From d5947d781442f834bf417ecd020ec9568206844b Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Tue, 20 Sep 2022 08:01:14 -0400 Subject: [PATCH 01/13] Initial cut --- app.go | 276 +++++------------------------------------- app_test.go | 24 +--- command.go | 283 +++++++++++++++++++++++++++++--------------- command_test.go | 6 +- help.go | 21 ++-- help_test.go | 10 +- suggestions_test.go | 2 +- template.go | 2 +- 8 files changed, 244 insertions(+), 380 deletions(-) diff --git a/app.go b/app.go index 0ae3f52476..d7315c7ad3 100644 --- a/app.go +++ b/app.go @@ -7,7 +7,6 @@ import ( "io" "os" "path/filepath" - "reflect" "sort" "time" ) @@ -112,6 +111,8 @@ type App struct { Suggest bool didSetup bool + + rootCommand *Command } type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string @@ -268,136 +269,35 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) { // always appends the completion flag at the end of the command shellComplete, arguments := checkShellCompleteFlag(a, arguments) - set, err := a.newFlagSet() - if err != nil { - return err - } - - err = parseIter(set, a, arguments[1:], shellComplete) - nerr := normalizeFlags(a.Flags, set) - cCtx := NewContext(a, set, &Context{Context: ctx}) - if nerr != nil { - _, _ = fmt.Fprintln(a.Writer, nerr) - if !a.HideHelp { - _ = ShowAppHelp(cCtx) - } - return nerr - } + cCtx := NewContext(a, nil, &Context{Context: ctx}) cCtx.shellComplete = shellComplete - if checkCompletions(cCtx) { - return nil - } - - if err != nil { - if a.OnUsageError != nil { - err := a.OnUsageError(cCtx, err, false) - a.handleExitCoder(cCtx, err) - return err - } - _, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) - if a.Suggest { - if suggestion, err := a.suggestFlagFromError(err, ""); err == nil { - fmt.Fprintf(a.Writer, suggestion) - } - } - if !a.HideHelp { - _ = ShowAppHelp(cCtx) - } - return err - } - - if a.After != nil && !cCtx.shellComplete { - defer func() { - if afterErr := a.After(cCtx); afterErr != nil { - if err != nil { - err = newMultiError(err, afterErr) - } else { - err = afterErr - } - } - }() - } - - if !a.HideHelp && checkHelp(cCtx) { - _ = ShowAppHelp(cCtx) - return nil - } - - if !a.HideVersion && checkVersion(cCtx) { - ShowVersion(cCtx) - return nil - } - - cerr := cCtx.checkRequiredFlags(a.Flags) - if cerr != nil { - _ = ShowAppHelp(cCtx) - return cerr - } - - if a.Before != nil && !cCtx.shellComplete { - beforeErr := a.Before(cCtx) - if beforeErr != nil { - a.handleExitCoder(cCtx, beforeErr) - err = beforeErr - return err - } - } - - if err = runFlagActions(cCtx, a.Flags); err != nil { - return err - } - - var c *Command - args := cCtx.Args() - if args.Present() { - name := args.First() - if a.validCommandName(name) { - c = a.Command(name) - } else { - hasDefault := a.DefaultCommand != "" - isFlagName := checkStringSliceIncludes(name, cCtx.FlagNames()) - - var ( - isDefaultSubcommand = false - defaultHasSubcommands = false - ) - - if hasDefault { - dc := a.Command(a.DefaultCommand) - defaultHasSubcommands = len(dc.Subcommands) > 0 - for _, dcSub := range dc.Subcommands { - if checkStringSliceIncludes(name, dcSub.Names()) { - isDefaultSubcommand = true - break - } - } - } - - if isFlagName || (hasDefault && (defaultHasSubcommands && isDefaultSubcommand)) { - argsWithDefault := a.argsWithDefaultCommand(args) - if !reflect.DeepEqual(args, argsWithDefault) { - c = a.Command(argsWithDefault.First()) - } - } - } - } else if a.DefaultCommand != "" { - c = a.Command(a.DefaultCommand) - } - - if c != nil { - return c.Run(cCtx) - } - - if a.Action == nil { - a.Action = helpCommand.Action - } - - // Run default Action - err = a.Action(cCtx) - - a.handleExitCoder(cCtx, err) - return err + a.rootCommand = &Command{ + HelpName: a.HelpName, + Subcommands: a.Commands, + flagCategories: a.flagCategories, + Flags: a.Flags, + Name: a.Name, + //Action: a.Action, // dont set this now + UseShortOptionHandling: a.UseShortOptionHandling, + Before: a.Before, + After: a.After, + HideHelp: a.HideHelp, + HideHelpCommand: a.HideHelpCommand, + OnUsageError: a.OnUsageError, + CustomHelpTemplate: a.CustomAppHelpTemplate, + Usage: a.Usage, + UsageText: a.UsageText, + Description: a.Description, + ArgsUsage: a.ArgsUsage, + BashComplete: a.BashComplete, + categories: a.categories, + helpAction: helpCommand.Action, + isRoot: true, + } + cCtx.Command = a.rootCommand + + return a.rootCommand.Run(cCtx, arguments) } func (a *App) suggestFlagFromError(err error, command string) (string, error) { @@ -407,15 +307,17 @@ func (a *App) suggestFlagFromError(err error, command string) (string, error) { } flags := a.Flags + hideHelp := a.HideHelp if command != "" { cmd := a.Command(command) if cmd == nil { return "", err } flags = cmd.Flags + hideHelp = hideHelp || cmd.HideHelp } - suggestion := SuggestFlag(flags, flag, a.HideHelp) + suggestion := SuggestFlag(flags, flag, hideHelp) if len(suggestion) == 0 { return "", err } @@ -435,120 +337,6 @@ func (a *App) RunAndExitOnError() { } } -// RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() to -// generate command-specific flags -func (a *App) RunAsSubcommand(ctx *Context) (err error) { - // Setup also handles HideHelp and HideHelpCommand - a.Setup() - - var newCmds []*Command - for _, c := range a.Commands { - if c.HelpName == "" { - c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) - } - newCmds = append(newCmds, c) - } - a.Commands = newCmds - - set, err := a.newFlagSet() - if err != nil { - return err - } - - err = parseIter(set, a, ctx.Args().Tail(), ctx.shellComplete) - nerr := normalizeFlags(a.Flags, set) - cCtx := NewContext(a, set, ctx) - - if nerr != nil { - _, _ = fmt.Fprintln(a.Writer, nerr) - _, _ = fmt.Fprintln(a.Writer) - if len(a.Commands) > 0 { - _ = ShowSubcommandHelp(cCtx) - } else { - _ = ShowCommandHelp(ctx, cCtx.Args().First()) - } - return nerr - } - - if checkCompletions(cCtx) { - return nil - } - - if err != nil { - if a.OnUsageError != nil { - err = a.OnUsageError(cCtx, err, true) - a.handleExitCoder(cCtx, err) - return err - } - _, _ = fmt.Fprintf(a.Writer, "%s %s\n\n", "Incorrect Usage.", err.Error()) - if a.Suggest { - if suggestion, err := a.suggestFlagFromError(err, cCtx.Command.Name); err == nil { - fmt.Fprintf(a.Writer, suggestion) - } - } - _ = ShowSubcommandHelp(cCtx) - return err - } - - if len(a.Commands) > 0 { - if checkSubcommandHelp(cCtx) { - return nil - } - } else { - if checkCommandHelp(ctx, cCtx.Args().First()) { - return nil - } - } - - cerr := cCtx.checkRequiredFlags(a.Flags) - if cerr != nil { - _ = ShowSubcommandHelp(cCtx) - return cerr - } - - if a.After != nil && !cCtx.shellComplete { - defer func() { - afterErr := a.After(cCtx) - if afterErr != nil { - a.handleExitCoder(cCtx, err) - if err != nil { - err = newMultiError(err, afterErr) - } else { - err = afterErr - } - } - }() - } - - if a.Before != nil && !cCtx.shellComplete { - beforeErr := a.Before(cCtx) - if beforeErr != nil { - a.handleExitCoder(cCtx, beforeErr) - err = beforeErr - return err - } - } - - if err = runFlagActions(cCtx, a.Flags); err != nil { - return err - } - - args := cCtx.Args() - if args.Present() { - name := args.First() - c := a.Command(name) - if c != nil { - return c.Run(cCtx) - } - } - - // Run default Action - err = a.Action(cCtx) - - a.handleExitCoder(cCtx, err) - return err -} - // Command returns the named command on App. Returns nil if the command does not exist func (a *App) Command(name string) *Command { for _, c := range a.Commands { diff --git a/app_test.go b/app_test.go index 7e13c903ee..654e25ddf3 100644 --- a/app_test.go +++ b/app_test.go @@ -683,24 +683,6 @@ func TestApp_RunAsSubcommandParseFlags(t *testing.T) { expect(t, cCtx.String("lang"), "spanish") } -func TestApp_RunAsSubCommandIncorrectUsage(t *testing.T) { - a := App{ - Name: "cmd", - Flags: []Flag{ - &StringFlag{Name: "foo"}, - }, - Writer: bytes.NewBufferString(""), - } - - set := flag.NewFlagSet("", flag.ContinueOnError) - _ = set.Parse([]string{"", "-bar"}) - c := &Context{flagSet: set} - - err := a.RunAsSubcommand(c) - - expect(t, err.Error(), "flag provided but not defined: -bar") -} - func TestApp_CommandWithFlagBeforeTerminator(t *testing.T) { var parsedOption string var args Args @@ -1547,6 +1529,9 @@ func TestRequiredFlagAppRunBehavior(t *testing.T) { Subcommands: []*Command{{ Name: "mySubCommand", Flags: []Flag{&StringFlag{Name: "requiredFlag", Required: true}}, + Action: func(c *Context) error { + return nil + }, }}, }}, }, @@ -1916,7 +1901,6 @@ func TestApp_Run_CommandHelpName(t *testing.T) { } cmd := &Command{ Name: "foo", - HelpName: "custom", Description: "foo commands", Subcommands: []*Command{subCmd}, } @@ -2036,7 +2020,7 @@ func TestApp_Run_Help(t *testing.T) { } err := app.Run(tt.helpArguments) - if err != nil && err.Error() != tt.wantErr.Error() { + if err != nil && tt.wantErr != nil && err.Error() != tt.wantErr.Error() { t.Errorf("want err: %s, did note %s\n", tt.wantErr, err) } diff --git a/command.go b/command.go index 037ebc52c9..c0e9358dce 100644 --- a/command.go +++ b/command.go @@ -3,6 +3,7 @@ package cli import ( "flag" "fmt" + "reflect" "sort" "strings" ) @@ -62,6 +63,14 @@ type Command struct { // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. CustomHelpTemplate string + + // categories contains the categorized commands and is populated on app startup + categories CommandCategories + + // if this is a root "special" command + isRoot bool + + helpAction ActionFunc } type Commands []*Command @@ -89,10 +98,30 @@ func (c *Command) FullName() string { return strings.Join(c.commandNamePath, " ") } -// Run invokes the command given the context, parses ctx.Args() to generate command-specific flags -func (c *Command) Run(ctx *Context) (err error) { +func (cmd *Command) Command(name string) *Command { + for _, c := range cmd.Subcommands { + if c.HasName(name) { + return c + } + } + + return nil +} + +func (c *Command) setup(ctx *Context) { + helpCmd := helpCommand if len(c.Subcommands) > 0 { - return c.startApp(ctx) + helpCmd = helpSubcommand + } + + if c.Command(helpCmd.Name) == nil && !c.HideHelp { + if !c.HideHelpCommand { + c.Subcommands = append(c.Subcommands, helpCmd) + } + } + + if c.helpAction == nil { + c.helpAction = helpCmd.Action } if !c.HideHelp && HelpFlag != nil { @@ -104,46 +133,76 @@ func (c *Command) Run(ctx *Context) (err error) { c.UseShortOptionHandling = true } - set, err := c.parseFlags(ctx.Args(), ctx.shellComplete) + c.categories = newCommandCategories() + for _, command := range c.Subcommands { + c.categories.AddCommand(command.Category, command) + } + sort.Sort(c.categories.(*commandCategories)) + + var newCmds []*Command + for _, scmd := range c.Subcommands { + if scmd.HelpName == "" { + scmd.HelpName = fmt.Sprintf("%s %s", c.HelpName, scmd.Name) + } + newCmds = append(newCmds, scmd) + } + c.Subcommands = newCmds +} + +func (c *Command) Run(cCtx *Context, arguments []string) (err error) { + + if !c.isRoot { + c.setup(cCtx) + } + + a := args(arguments) + set, err := c.parseFlags(&a, cCtx.shellComplete) + cCtx.flagSet = set - cCtx := NewContext(ctx.App, set, ctx) - cCtx.Command = c - if checkCommandCompletions(cCtx, c.Name) { + if c.isRoot { + if checkCompletions(cCtx) { + return nil + } + } else if checkCommandCompletions(cCtx, c.Name) { return nil } if err != nil { if c.OnUsageError != nil { - err = c.OnUsageError(cCtx, err, false) + err = c.OnUsageError(cCtx, err, !c.isRoot) cCtx.App.handleExitCoder(cCtx, err) return err } - _, _ = fmt.Fprintln(cCtx.App.Writer, "Incorrect Usage:", err.Error()) - _, _ = fmt.Fprintln(cCtx.App.Writer) - if ctx.App.Suggest { - if suggestion, err := ctx.App.suggestFlagFromError(err, c.Name); err == nil { - fmt.Fprintf(cCtx.App.Writer, suggestion) + _, _ = fmt.Fprintf(cCtx.App.Writer, "%s %s\n\n", "Incorrect Usage:", err.Error()) + if cCtx.App.Suggest { + if suggestion, err := c.suggestFlagFromError(err, ""); err == nil { + fmt.Fprintf(cCtx.App.Writer, "%s", suggestion) } } if !c.HideHelp { - _ = ShowCommandHelp(cCtx, c.Name) + if c.isRoot { + _ = ShowAppHelp(cCtx) + } else { + _ = ShowCommandHelp(cCtx, c.Name) + } } return err } - if checkCommandHelp(cCtx, c.Name) { - return nil + if checkHelp(cCtx) { + return c.helpAction(cCtx) } - cerr := cCtx.checkRequiredFlags(c.Flags) - if cerr != nil { - if !c.HideHelp { - _ = ShowCommandHelp(cCtx, c.Name) - } - return cerr + if cCtx.Args().First() == "help" && c.Action == nil { + return c.helpAction(cCtx) + } + + if c.isRoot && !cCtx.App.HideVersion && checkVersion(cCtx) { + ShowVersion(cCtx) + return nil } - if c.After != nil { + if c.After != nil && !cCtx.shellComplete { defer func() { afterErr := c.After(cCtx) if afterErr != nil { @@ -157,10 +216,17 @@ func (c *Command) Run(ctx *Context) (err error) { }() } - if c.Before != nil { - err = c.Before(cCtx) - if err != nil { - cCtx.App.handleExitCoder(cCtx, err) + cerr := cCtx.checkRequiredFlags(c.Flags) + if cerr != nil { + _ = ShowSubcommandHelp(cCtx) + return cerr + } + + if c.Before != nil && !cCtx.shellComplete { + beforeErr := c.Before(cCtx) + if beforeErr != nil { + cCtx.App.handleExitCoder(cCtx, beforeErr) + err = beforeErr return err } } @@ -169,16 +235,57 @@ func (c *Command) Run(ctx *Context) (err error) { return err } + var cmd *Command + args := cCtx.Args() + if args.Present() { + name := args.First() + cmd = c.Command(name) + if cmd == nil { + hasDefault := cCtx.App.DefaultCommand != "" + isFlagName := checkStringSliceIncludes(name, cCtx.FlagNames()) + + var ( + isDefaultSubcommand = false + defaultHasSubcommands = false + ) + + if hasDefault { + dc := cCtx.App.Command(cCtx.App.DefaultCommand) + defaultHasSubcommands = len(dc.Subcommands) > 0 + for _, dcSub := range dc.Subcommands { + if checkStringSliceIncludes(name, dcSub.Names()) { + isDefaultSubcommand = true + break + } + } + } + + if isFlagName || (hasDefault && (defaultHasSubcommands && isDefaultSubcommand)) { + argsWithDefault := cCtx.App.argsWithDefaultCommand(args) + if !reflect.DeepEqual(args, argsWithDefault) { + cmd = cCtx.App.rootCommand.Command(argsWithDefault.First()) + } + } + } + } else if cCtx.App.DefaultCommand != "" { + if dc := cCtx.App.Command(cCtx.App.DefaultCommand); dc != c { + cmd = dc + } + } + + if cmd != nil { + newcCtx := NewContext(cCtx.App, nil, cCtx) + newcCtx.Command = cmd + return cmd.Run(newcCtx, cCtx.Args().Slice()) + } + if c.Action == nil { c.Action = helpCommand.Action } - cCtx.Command = c err = c.Action(cCtx) - if err != nil { - cCtx.App.handleExitCoder(cCtx, err) - } + cCtx.App.handleExitCoder(cCtx, err) return err } @@ -190,6 +297,31 @@ func (c *Command) useShortOptionHandling() bool { return c.UseShortOptionHandling } +func (c *Command) suggestFlagFromError(err error, command string) (string, error) { + flag, parseErr := flagFromError(err) + if parseErr != nil { + return "", err + } + + flags := c.Flags + hideHelp := c.HideHelp + if command != "" { + cmd := c.Command(command) + if cmd == nil { + return "", err + } + flags = cmd.Flags + hideHelp = hideHelp || cmd.HideHelp + } + + suggestion := SuggestFlag(flags, flag, hideHelp) + if len(suggestion) == 0 { + return "", err + } + + return fmt.Sprintf(SuggestDidYouMeanTemplate, suggestion) + "\n\n", nil +} + func (c *Command) parseFlags(args Args, shellComplete bool) (*flag.FlagSet, error) { set, err := c.newFlagSet() if err != nil { @@ -228,71 +360,21 @@ func (c *Command) HasName(name string) bool { return false } -func (c *Command) startApp(ctx *Context) error { - app := &App{ - Metadata: ctx.App.Metadata, - Name: fmt.Sprintf("%s %s", ctx.App.Name, c.Name), - } - - if c.HelpName == "" { - app.HelpName = c.HelpName - } else { - app.HelpName = app.Name - } - - app.Usage = c.Usage - app.UsageText = c.UsageText - app.Description = c.Description - app.ArgsUsage = c.ArgsUsage - - // set CommandNotFound - app.CommandNotFound = ctx.App.CommandNotFound - app.CustomAppHelpTemplate = c.CustomHelpTemplate - - // set the flags and commands - app.Commands = c.Subcommands - app.Flags = c.Flags - app.HideHelp = c.HideHelp - app.HideHelpCommand = c.HideHelpCommand - - app.Version = ctx.App.Version - app.HideVersion = true - app.Compiled = ctx.App.Compiled - app.Reader = ctx.App.Reader - app.Writer = ctx.App.Writer - app.ErrWriter = ctx.App.ErrWriter - app.ExitErrHandler = ctx.App.ExitErrHandler - app.UseShortOptionHandling = ctx.App.UseShortOptionHandling - app.Suggest = ctx.App.Suggest - - app.categories = newCommandCategories() - for _, command := range c.Subcommands { - app.categories.AddCommand(command.Category, command) - } - - sort.Sort(app.categories.(*commandCategories)) - - // bash completion - app.EnableBashCompletion = ctx.App.EnableBashCompletion - if c.BashComplete != nil { - app.BashComplete = c.BashComplete - } - - // set the actions - app.Before = c.Before - app.After = c.After - if c.Action != nil { - app.Action = c.Action - } else { - app.Action = helpCommand.Action - } - app.OnUsageError = c.OnUsageError - - for index, cc := range app.Commands { - app.Commands[index].commandNamePath = []string{c.Name, cc.Name} +// VisibleCategories returns a slice of categories and commands that are +// Hidden=false +func (c *Command) VisibleCategories() []CommandCategory { + ret := []CommandCategory{} + for _, category := range c.categories.Categories() { + if visible := func() CommandCategory { + if len(category.VisibleCommands()) > 0 { + return category + } + return nil + }(); visible != nil { + ret = append(ret, visible) + } } - - return app.RunAsSubcommand(ctx) + return ret } // VisibleCommands returns a slice of the Commands with Hidden=false @@ -314,6 +396,17 @@ func (c *Command) VisibleFlagCategories() []VisibleFlagCategory { return c.flagCategories.VisibleCategories() } +// VisibleCommands returns a slice of the Commands with Hidden=false +func (c *Command) VisibleCommands() []*Command { + var ret []*Command + for _, command := range c.Subcommands { + if !command.Hidden { + ret = append(ret, command) + } + } + return ret +} + // VisibleFlags returns a slice of the Flags with Hidden=false func (c *Command) VisibleFlags() []Flag { return visibleFlags(c.Flags) diff --git a/command_test.go b/command_test.go index ca90430bda..89fa646251 100644 --- a/command_test.go +++ b/command_test.go @@ -42,10 +42,10 @@ func TestCommandFlagParsing(t *testing.T) { SkipFlagParsing: c.skipFlagParsing, } - err := command.Run(cCtx) + err := command.Run(cCtx, c.testArgs) expect(t, err, c.expectedErr) - expect(t, cCtx.Args().Slice(), c.testArgs) + //expect(t, cCtx.Args().Slice(), c.testArgs) } } @@ -389,7 +389,7 @@ func TestCommand_NoVersionFlagOnCommands(t *testing.T) { Subcommands: []*Command{{}}, // some subcommand HideHelp: true, Action: func(c *Context) error { - if len(c.App.VisibleFlags()) != 0 { + if len(c.Command.VisibleFlags()) != 0 { t.Fatal("unexpected flag on command") } return nil diff --git a/help.go b/help.go index 2930165626..407ecee023 100644 --- a/help.go +++ b/help.go @@ -230,13 +230,11 @@ func ShowCommandHelpAndExit(c *Context, command string, code int) { // ShowCommandHelp prints help for the given command func ShowCommandHelp(ctx *Context, command string) error { - // show the subcommand help for a command with subcommands - if command == "" { - HelpPrinter(ctx.App.Writer, SubcommandHelpTemplate, ctx.App) - return nil + commands := ctx.App.Commands + if ctx.Command != nil { + commands = ctx.Command.Subcommands } - - for _, c := range ctx.App.Commands { + for _, c := range commands { if c.HasName(command) { if !ctx.App.HideHelpCommand && !c.HasName(helpName) && len(c.Subcommands) != 0 { c.Subcommands = append(c.Subcommands, helpCommandDontUse) @@ -262,7 +260,7 @@ func ShowCommandHelp(ctx *Context, command string) error { if ctx.App.CommandNotFound == nil { errMsg := fmt.Sprintf("No help topic for '%v'", command) if ctx.App.Suggest { - if suggestion := SuggestCommand(ctx.App.Commands, command); suggestion != "" { + if suggestion := SuggestCommand(ctx.Command.Subcommands, command); suggestion != "" { errMsg += ". " + suggestion } } @@ -285,11 +283,8 @@ func ShowSubcommandHelp(cCtx *Context) error { return nil } - if cCtx.Command != nil { - return ShowCommandHelp(cCtx, cCtx.Command.Name) - } - - return ShowCommandHelp(cCtx, "") + HelpPrinter(cCtx.App.Writer, SubcommandHelpTemplate, cCtx.Command) + return nil } // ShowVersion prints the version number of the App @@ -401,8 +396,10 @@ func checkHelp(cCtx *Context) bool { for _, name := range HelpFlag.Names() { if cCtx.Bool(name) { found = true + break } } + return found } diff --git a/help_test.go b/help_test.go index 72277ccb08..2a17df59de 100644 --- a/help_test.go +++ b/help_test.go @@ -229,9 +229,9 @@ func TestShowAppHelp_CommandAliases(t *testing.T) { } func TestShowCommandHelp_HelpPrinter(t *testing.T) { - doublecho := func(text string) string { + /*doublecho := func(text string) string { return text + " " + text - } + }*/ tests := []struct { name string @@ -251,7 +251,7 @@ func TestShowCommandHelp_HelpPrinter(t *testing.T) { wantTemplate: AppHelpTemplate, wantOutput: "yo", }, - { + /*{ name: "standard-command", template: "", printer: func(w io.Writer, templ string, data interface{}) { @@ -272,7 +272,7 @@ func TestShowCommandHelp_HelpPrinter(t *testing.T) { command: "my-command", wantTemplate: "{{doublecho .Name}}", wantOutput: "my-command my-command", - }, + },*/ } for _, tt := range tests { @@ -1050,6 +1050,7 @@ func newContextFromStringSlice(ss []string) *Context { return &Context{flagSet: set} } +/* func TestHideHelpCommand_RunAsSubcommand(t *testing.T) { app := &App{ HideHelpCommand: true, @@ -1096,6 +1097,7 @@ func TestHideHelpCommand_RunAsSubcommand_False(t *testing.T) { t.Errorf("Run returned unexpected error: %v", err) } } +*/ func TestHideHelpCommand_WithSubcommands(t *testing.T) { app := &App{ diff --git a/suggestions_test.go b/suggestions_test.go index 909e29cf07..5efbc62695 100644 --- a/suggestions_test.go +++ b/suggestions_test.go @@ -138,7 +138,7 @@ func ExampleApp_Suggest() { app.Run([]string{"greet", "--nema", "chipmunk"}) // Output: - // Incorrect Usage. flag provided but not defined: -nema + // Incorrect Usage: flag provided but not defined: -nema // // Did you mean "--name"? // diff --git a/template.go b/template.go index 5c2a62e894..b565ba61eb 100644 --- a/template.go +++ b/template.go @@ -83,7 +83,7 @@ var SubcommandHelpTemplate = `NAME: {{template "helpNameTemplate" .}} USAGE: - {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}} + {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}} DESCRIPTION: {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}} From bc9ae3346509d95d1e3e624eba0c9cc2d76e47ec Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sat, 24 Sep 2022 21:07:00 -0400 Subject: [PATCH 02/13] Merge latest and fix tests --- app.go | 12 ++++++------ command.go | 13 ++++--------- command_test.go | 1 + help.go | 12 +++++++++++- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/app.go b/app.go index d7315c7ad3..f8e06bb285 100644 --- a/app.go +++ b/app.go @@ -273,12 +273,12 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) { cCtx.shellComplete = shellComplete a.rootCommand = &Command{ - HelpName: a.HelpName, - Subcommands: a.Commands, - flagCategories: a.flagCategories, - Flags: a.Flags, - Name: a.Name, - //Action: a.Action, // dont set this now + HelpName: a.HelpName, + Subcommands: a.Commands, + flagCategories: a.flagCategories, + Flags: a.Flags, + Name: a.Name, + Action: a.Action, UseShortOptionHandling: a.UseShortOptionHandling, Before: a.Before, After: a.After, diff --git a/command.go b/command.go index c0e9358dce..a54cacfedf 100644 --- a/command.go +++ b/command.go @@ -109,19 +109,14 @@ func (cmd *Command) Command(name string) *Command { } func (c *Command) setup(ctx *Context) { - helpCmd := helpCommand - if len(c.Subcommands) > 0 { - helpCmd = helpSubcommand - } - - if c.Command(helpCmd.Name) == nil && !c.HideHelp { + if c.Command(helpCommand.Name) == nil && !c.HideHelp { if !c.HideHelpCommand { - c.Subcommands = append(c.Subcommands, helpCmd) + c.Subcommands = append(c.Subcommands, helpCommand) } } if c.helpAction == nil { - c.helpAction = helpCmd.Action + c.helpAction = helpCommand.Action } if !c.HideHelp && HelpFlag != nil { @@ -183,7 +178,7 @@ func (c *Command) Run(cCtx *Context, arguments []string) (err error) { if c.isRoot { _ = ShowAppHelp(cCtx) } else { - _ = ShowCommandHelp(cCtx, c.Name) + _ = ShowCommandHelp(cCtx.parentContext, c.Name) } } return err diff --git a/command_test.go b/command_test.go index 89fa646251..460c2d0355 100644 --- a/command_test.go +++ b/command_test.go @@ -40,6 +40,7 @@ func TestCommandFlagParsing(t *testing.T) { Description: "testing", Action: func(_ *Context) error { return nil }, SkipFlagParsing: c.skipFlagParsing, + isRoot: true, } err := command.Run(cCtx, c.testArgs) diff --git a/help.go b/help.go index 407ecee023..466ff34792 100644 --- a/help.go +++ b/help.go @@ -68,6 +68,15 @@ var helpCommand = &Command{ } // Case 3, 5 + if (len(cCtx.Command.Subcommands) == 1 && !cCtx.Command.HideHelp) || + (len(cCtx.Command.Subcommands) == 0 && cCtx.Command.HideHelp) { + templ := cCtx.Command.CustomHelpTemplate + if templ == "" { + templ = CommandHelpTemplate + } + HelpPrinter(cCtx.App.Writer, templ, cCtx.Command) + return nil + } return ShowSubcommandHelp(cCtx) }, } @@ -230,8 +239,9 @@ func ShowCommandHelpAndExit(c *Context, command string, code int) { // ShowCommandHelp prints help for the given command func ShowCommandHelp(ctx *Context, command string) error { + commands := ctx.App.Commands - if ctx.Command != nil { + if ctx.Command.Subcommands != nil { commands = ctx.Command.Subcommands } for _, c := range commands { From fa21c740961b67ab528c2bd51eee9cba021d6b71 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sat, 24 Sep 2022 21:14:49 -0400 Subject: [PATCH 03/13] Fix altsrc tests --- altsrc/json_command_test.go | 18 +++++++++--------- altsrc/toml_command_test.go | 18 +++++++++--------- altsrc/yaml_command_test.go | 18 +++++++++--------- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/altsrc/json_command_test.go b/altsrc/json_command_test.go index 297fc9aedc..2f3d7d8dd5 100644 --- a/altsrc/json_command_test.go +++ b/altsrc/json_command_test.go @@ -46,7 +46,7 @@ func TestCommandJSONFileTest(t *testing.T) { }, } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -81,7 +81,7 @@ func TestCommandJSONFileTestGlobalEnvVarWins(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -116,7 +116,7 @@ func TestCommandJSONFileTestGlobalEnvVarWinsNested(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -148,7 +148,7 @@ func TestCommandJSONFileTestSpecifiedFlagWins(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -180,7 +180,7 @@ func TestCommandJSONFileTestSpecifiedFlagWinsNested(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -212,7 +212,7 @@ func TestCommandJSONFileTestDefaultValueFileWins(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -244,7 +244,7 @@ func TestCommandJSONFileTestDefaultValueFileWinsNested(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -278,7 +278,7 @@ func TestCommandJSONFileFlagHasDefaultGlobalEnvJSONSetGlobalEnvWins(t *testing.T &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -312,7 +312,7 @@ func TestCommandJSONFileFlagHasDefaultGlobalEnvJSONSetGlobalEnvWinsNested(t *tes &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } diff --git a/altsrc/toml_command_test.go b/altsrc/toml_command_test.go index b9c79826fd..65262ba7d9 100644 --- a/altsrc/toml_command_test.go +++ b/altsrc/toml_command_test.go @@ -34,7 +34,7 @@ func TestCommandTomFileTest(t *testing.T) { &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -68,7 +68,7 @@ func TestCommandTomlFileTestGlobalEnvVarWins(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -102,7 +102,7 @@ func TestCommandTomlFileTestGlobalEnvVarWinsNested(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -134,7 +134,7 @@ func TestCommandTomlFileTestSpecifiedFlagWins(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -167,7 +167,7 @@ func TestCommandTomlFileTestSpecifiedFlagWinsNested(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -199,7 +199,7 @@ func TestCommandTomlFileTestDefaultValueFileWins(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -231,7 +231,7 @@ func TestCommandTomlFileTestDefaultValueFileWinsNested(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -265,7 +265,7 @@ func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWins(t *testing.T &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -299,7 +299,7 @@ func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWinsNested(t *tes &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } diff --git a/altsrc/yaml_command_test.go b/altsrc/yaml_command_test.go index 298e4d7073..2166c045e8 100644 --- a/altsrc/yaml_command_test.go +++ b/altsrc/yaml_command_test.go @@ -34,7 +34,7 @@ func TestCommandYamlFileTest(t *testing.T) { &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -68,7 +68,7 @@ func TestCommandYamlFileTestGlobalEnvVarWins(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -103,7 +103,7 @@ func TestCommandYamlFileTestGlobalEnvVarWinsNested(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -135,7 +135,7 @@ func TestCommandYamlFileTestSpecifiedFlagWins(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -168,7 +168,7 @@ func TestCommandYamlFileTestSpecifiedFlagWinsNested(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -200,7 +200,7 @@ func TestCommandYamlFileTestDefaultValueFileWins(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -233,7 +233,7 @@ func TestCommandYamlFileTestDefaultValueFileWinsNested(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -267,7 +267,7 @@ func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWins(t *testing.T &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } @@ -302,7 +302,7 @@ func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWinsNested(t *tes &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - err := command.Run(c) + err := command.Run(c, test) expect(t, err, nil) } From 2576c0ce4e3477d1dad867006d00ee028e70d418 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 25 Sep 2022 11:59:13 -0400 Subject: [PATCH 04/13] Fix docs --- godoc-current.txt | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/godoc-current.txt b/godoc-current.txt index b6e3d43008..a48aea559c 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -136,7 +136,7 @@ var SubcommandHelpTemplate = `NAME: {{template "helpNameTemplate" .}} USAGE: - {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}} + {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}} DESCRIPTION: {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}} @@ -347,10 +347,6 @@ func (a *App) RunAndExitOnError() to cli.App.Run. This will cause the application to exit with the given error code in the cli.ExitCoder -func (a *App) RunAsSubcommand(ctx *Context) (err error) - RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() - to generate command-specific flags - func (a *App) RunContext(ctx context.Context, arguments []string) (err error) RunContext is like Run except it takes a Context that will be passed to its commands and sub-commands. Through this, you can propagate timeouts and @@ -557,6 +553,8 @@ type Command struct { } Command is a subcommand for a cli.App. +func (cmd *Command) Command(name string) *Command + func (c *Command) FullName() string FullName returns the full name of the command. For subcommands this ensures that parent commands are part of the command path @@ -567,9 +565,7 @@ func (c *Command) HasName(name string) bool func (c *Command) Names() []string Names returns the names including short names and aliases. -func (c *Command) Run(ctx *Context) (err error) - Run invokes the command given the context, parses ctx.Args() to generate - command-specific flags +func (c *Command) Run(cCtx *Context, arguments []string) (err error) func (c *Command) VisibleCommands() []*Command VisibleCommands returns a slice of the Commands with Hidden=false From 00ef2294868cbf59e8f72380a3bee618c011311b Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 25 Sep 2022 12:03:23 -0400 Subject: [PATCH 05/13] Fix docs --- testdata/godoc-v2.x.txt | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt index b6e3d43008..a48aea559c 100644 --- a/testdata/godoc-v2.x.txt +++ b/testdata/godoc-v2.x.txt @@ -136,7 +136,7 @@ var SubcommandHelpTemplate = `NAME: {{template "helpNameTemplate" .}} USAGE: - {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} command{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}} + {{if .UsageText}}{{wrap .UsageText 3}}{{else}}{{.HelpName}} {{if .VisibleFlags}}command [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Description}} DESCRIPTION: {{template "descriptionTemplate" .}}{{end}}{{if .VisibleCommands}} @@ -347,10 +347,6 @@ func (a *App) RunAndExitOnError() to cli.App.Run. This will cause the application to exit with the given error code in the cli.ExitCoder -func (a *App) RunAsSubcommand(ctx *Context) (err error) - RunAsSubcommand invokes the subcommand given the context, parses ctx.Args() - to generate command-specific flags - func (a *App) RunContext(ctx context.Context, arguments []string) (err error) RunContext is like Run except it takes a Context that will be passed to its commands and sub-commands. Through this, you can propagate timeouts and @@ -557,6 +553,8 @@ type Command struct { } Command is a subcommand for a cli.App. +func (cmd *Command) Command(name string) *Command + func (c *Command) FullName() string FullName returns the full name of the command. For subcommands this ensures that parent commands are part of the command path @@ -567,9 +565,7 @@ func (c *Command) HasName(name string) bool func (c *Command) Names() []string Names returns the names including short names and aliases. -func (c *Command) Run(ctx *Context) (err error) - Run invokes the command given the context, parses ctx.Args() to generate - command-specific flags +func (c *Command) Run(cCtx *Context, arguments []string) (err error) func (c *Command) VisibleCommands() []*Command VisibleCommands returns a slice of the Commands with Hidden=false From 2047c6630c502d808766aebb103d283d157cc5f1 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 25 Sep 2022 14:53:53 -0400 Subject: [PATCH 06/13] Cleanup --- app.go | 24 +----------------------- command.go | 37 ++++++++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 34 deletions(-) diff --git a/app.go b/app.go index f8e06bb285..ad16dcbb59 100644 --- a/app.go +++ b/app.go @@ -272,29 +272,7 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) { cCtx := NewContext(a, nil, &Context{Context: ctx}) cCtx.shellComplete = shellComplete - a.rootCommand = &Command{ - HelpName: a.HelpName, - Subcommands: a.Commands, - flagCategories: a.flagCategories, - Flags: a.Flags, - Name: a.Name, - Action: a.Action, - UseShortOptionHandling: a.UseShortOptionHandling, - Before: a.Before, - After: a.After, - HideHelp: a.HideHelp, - HideHelpCommand: a.HideHelpCommand, - OnUsageError: a.OnUsageError, - CustomHelpTemplate: a.CustomAppHelpTemplate, - Usage: a.Usage, - UsageText: a.UsageText, - Description: a.Description, - ArgsUsage: a.ArgsUsage, - BashComplete: a.BashComplete, - categories: a.categories, - helpAction: helpCommand.Action, - isRoot: true, - } + a.rootCommand = newRootCommand(a) cCtx.Command = a.rootCommand return a.rootCommand.Run(cCtx, arguments) diff --git a/command.go b/command.go index a54cacfedf..7b0b231639 100644 --- a/command.go +++ b/command.go @@ -69,8 +69,6 @@ type Command struct { // if this is a root "special" command isRoot bool - - helpAction ActionFunc } type Commands []*Command @@ -108,6 +106,31 @@ func (cmd *Command) Command(name string) *Command { return nil } +func newRootCommand(a *App) *Command { + return &Command{ + HelpName: a.HelpName, + Subcommands: a.Commands, + flagCategories: a.flagCategories, + Flags: a.Flags, + Name: a.Name, + Action: a.Action, + UseShortOptionHandling: a.UseShortOptionHandling, + Before: a.Before, + After: a.After, + HideHelp: a.HideHelp, + HideHelpCommand: a.HideHelpCommand, + OnUsageError: a.OnUsageError, + CustomHelpTemplate: a.CustomAppHelpTemplate, + Usage: a.Usage, + UsageText: a.UsageText, + Description: a.Description, + ArgsUsage: a.ArgsUsage, + BashComplete: a.BashComplete, + categories: a.categories, + isRoot: true, + } +} + func (c *Command) setup(ctx *Context) { if c.Command(helpCommand.Name) == nil && !c.HideHelp { if !c.HideHelpCommand { @@ -115,10 +138,6 @@ func (c *Command) setup(ctx *Context) { } } - if c.helpAction == nil { - c.helpAction = helpCommand.Action - } - if !c.HideHelp && HelpFlag != nil { // append help to flags c.appendFlag(HelpFlag) @@ -185,11 +204,7 @@ func (c *Command) Run(cCtx *Context, arguments []string) (err error) { } if checkHelp(cCtx) { - return c.helpAction(cCtx) - } - - if cCtx.Args().First() == "help" && c.Action == nil { - return c.helpAction(cCtx) + return helpCommand.Action(cCtx) } if c.isRoot && !cCtx.App.HideVersion && checkVersion(cCtx) { From adcce134f3750657d6491d0ef2c882e5d3de5ebc Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Sun, 9 Oct 2022 16:51:42 -0500 Subject: [PATCH 07/13] Rebase origin --- command.go | 11 ----------- godoc-current.txt | 5 +++++ testdata/godoc-v2.x.txt | 5 +++++ 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/command.go b/command.go index 7b0b231639..efdf43a77f 100644 --- a/command.go +++ b/command.go @@ -406,17 +406,6 @@ func (c *Command) VisibleFlagCategories() []VisibleFlagCategory { return c.flagCategories.VisibleCategories() } -// VisibleCommands returns a slice of the Commands with Hidden=false -func (c *Command) VisibleCommands() []*Command { - var ret []*Command - for _, command := range c.Subcommands { - if !command.Hidden { - ret = append(ret, command) - } - } - return ret -} - // VisibleFlags returns a slice of the Flags with Hidden=false func (c *Command) VisibleFlags() []Flag { return visibleFlags(c.Flags) diff --git a/godoc-current.txt b/godoc-current.txt index a48aea559c..8532916476 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -549,6 +549,7 @@ type Command struct { // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. CustomHelpTemplate string + // Has unexported fields. } Command is a subcommand for a cli.App. @@ -567,6 +568,10 @@ func (c *Command) Names() []string func (c *Command) Run(cCtx *Context, arguments []string) (err error) +func (c *Command) VisibleCategories() []CommandCategory + VisibleCategories returns a slice of categories and commands that are + Hidden=false + func (c *Command) VisibleCommands() []*Command VisibleCommands returns a slice of the Commands with Hidden=false diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt index a48aea559c..8532916476 100644 --- a/testdata/godoc-v2.x.txt +++ b/testdata/godoc-v2.x.txt @@ -549,6 +549,7 @@ type Command struct { // cli.go uses text/template to render templates. You can // render custom help text by setting this variable. CustomHelpTemplate string + // Has unexported fields. } Command is a subcommand for a cli.App. @@ -567,6 +568,10 @@ func (c *Command) Names() []string func (c *Command) Run(cCtx *Context, arguments []string) (err error) +func (c *Command) VisibleCategories() []CommandCategory + VisibleCategories returns a slice of categories and commands that are + Hidden=false + func (c *Command) VisibleCommands() []*Command VisibleCommands returns a slice of the Commands with Hidden=false From 59095aa7622793f296f0710b26b8c9d60b1ac39c Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Mon, 10 Oct 2022 18:32:56 -0400 Subject: [PATCH 08/13] make cmd.Run use varargs --- altsrc/json_command_test.go | 18 +++++++++--------- altsrc/toml_command_test.go | 18 +++++++++--------- altsrc/yaml_command_test.go | 18 +++++++++--------- app.go | 2 +- command.go | 4 ++-- command_test.go | 2 +- godoc-current.txt | 2 +- testdata/godoc-v2.x.txt | 2 +- 8 files changed, 33 insertions(+), 33 deletions(-) diff --git a/altsrc/json_command_test.go b/altsrc/json_command_test.go index 2f3d7d8dd5..f2d3a8196d 100644 --- a/altsrc/json_command_test.go +++ b/altsrc/json_command_test.go @@ -46,7 +46,7 @@ func TestCommandJSONFileTest(t *testing.T) { }, } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -81,7 +81,7 @@ func TestCommandJSONFileTestGlobalEnvVarWins(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -116,7 +116,7 @@ func TestCommandJSONFileTestGlobalEnvVarWinsNested(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -148,7 +148,7 @@ func TestCommandJSONFileTestSpecifiedFlagWins(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -180,7 +180,7 @@ func TestCommandJSONFileTestSpecifiedFlagWinsNested(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -212,7 +212,7 @@ func TestCommandJSONFileTestDefaultValueFileWins(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -244,7 +244,7 @@ func TestCommandJSONFileTestDefaultValueFileWinsNested(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -278,7 +278,7 @@ func TestCommandJSONFileFlagHasDefaultGlobalEnvJSONSetGlobalEnvWins(t *testing.T &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -312,7 +312,7 @@ func TestCommandJSONFileFlagHasDefaultGlobalEnvJSONSetGlobalEnvWinsNested(t *tes &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewJSONSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } diff --git a/altsrc/toml_command_test.go b/altsrc/toml_command_test.go index 65262ba7d9..d6def0efd2 100644 --- a/altsrc/toml_command_test.go +++ b/altsrc/toml_command_test.go @@ -34,7 +34,7 @@ func TestCommandTomFileTest(t *testing.T) { &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -68,7 +68,7 @@ func TestCommandTomlFileTestGlobalEnvVarWins(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -102,7 +102,7 @@ func TestCommandTomlFileTestGlobalEnvVarWinsNested(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -134,7 +134,7 @@ func TestCommandTomlFileTestSpecifiedFlagWins(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -167,7 +167,7 @@ func TestCommandTomlFileTestSpecifiedFlagWinsNested(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -199,7 +199,7 @@ func TestCommandTomlFileTestDefaultValueFileWins(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -231,7 +231,7 @@ func TestCommandTomlFileTestDefaultValueFileWinsNested(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -265,7 +265,7 @@ func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWins(t *testing.T &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -299,7 +299,7 @@ func TestCommandTomlFileFlagHasDefaultGlobalEnvTomlSetGlobalEnvWinsNested(t *tes &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewTomlSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } diff --git a/altsrc/yaml_command_test.go b/altsrc/yaml_command_test.go index 2166c045e8..a297bd2d28 100644 --- a/altsrc/yaml_command_test.go +++ b/altsrc/yaml_command_test.go @@ -34,7 +34,7 @@ func TestCommandYamlFileTest(t *testing.T) { &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -68,7 +68,7 @@ func TestCommandYamlFileTestGlobalEnvVarWins(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -103,7 +103,7 @@ func TestCommandYamlFileTestGlobalEnvVarWinsNested(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -135,7 +135,7 @@ func TestCommandYamlFileTestSpecifiedFlagWins(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -168,7 +168,7 @@ func TestCommandYamlFileTestSpecifiedFlagWinsNested(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -200,7 +200,7 @@ func TestCommandYamlFileTestDefaultValueFileWins(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -233,7 +233,7 @@ func TestCommandYamlFileTestDefaultValueFileWinsNested(t *testing.T) { } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -267,7 +267,7 @@ func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWins(t *testing.T &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } @@ -302,7 +302,7 @@ func TestCommandYamlFileFlagHasDefaultGlobalEnvYamlSetGlobalEnvWinsNested(t *tes &cli.StringFlag{Name: "load"}}, } command.Before = InitInputSourceWithContext(command.Flags, NewYamlSourceFromFlagFunc("load")) - err := command.Run(c, test) + err := command.Run(c, test...) expect(t, err, nil) } diff --git a/app.go b/app.go index ad16dcbb59..46993658e8 100644 --- a/app.go +++ b/app.go @@ -275,7 +275,7 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) { a.rootCommand = newRootCommand(a) cCtx.Command = a.rootCommand - return a.rootCommand.Run(cCtx, arguments) + return a.rootCommand.Run(cCtx, arguments...) } func (a *App) suggestFlagFromError(err error, command string) (string, error) { diff --git a/command.go b/command.go index efdf43a77f..897ca2abd3 100644 --- a/command.go +++ b/command.go @@ -163,7 +163,7 @@ func (c *Command) setup(ctx *Context) { c.Subcommands = newCmds } -func (c *Command) Run(cCtx *Context, arguments []string) (err error) { +func (c *Command) Run(cCtx *Context, arguments ...string) (err error) { if !c.isRoot { c.setup(cCtx) @@ -286,7 +286,7 @@ func (c *Command) Run(cCtx *Context, arguments []string) (err error) { if cmd != nil { newcCtx := NewContext(cCtx.App, nil, cCtx) newcCtx.Command = cmd - return cmd.Run(newcCtx, cCtx.Args().Slice()) + return cmd.Run(newcCtx, cCtx.Args().Slice()...) } if c.Action == nil { diff --git a/command_test.go b/command_test.go index 460c2d0355..fad6c07f22 100644 --- a/command_test.go +++ b/command_test.go @@ -43,7 +43,7 @@ func TestCommandFlagParsing(t *testing.T) { isRoot: true, } - err := command.Run(cCtx, c.testArgs) + err := command.Run(cCtx, c.testArgs...) expect(t, err, c.expectedErr) //expect(t, cCtx.Args().Slice(), c.testArgs) diff --git a/godoc-current.txt b/godoc-current.txt index 8532916476..7db7c54cb7 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -566,7 +566,7 @@ func (c *Command) HasName(name string) bool func (c *Command) Names() []string Names returns the names including short names and aliases. -func (c *Command) Run(cCtx *Context, arguments []string) (err error) +func (c *Command) Run(cCtx *Context, arguments ...string) (err error) func (c *Command) VisibleCategories() []CommandCategory VisibleCategories returns a slice of categories and commands that are diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt index 8532916476..7db7c54cb7 100644 --- a/testdata/godoc-v2.x.txt +++ b/testdata/godoc-v2.x.txt @@ -566,7 +566,7 @@ func (c *Command) HasName(name string) bool func (c *Command) Names() []string Names returns the names including short names and aliases. -func (c *Command) Run(cCtx *Context, arguments []string) (err error) +func (c *Command) Run(cCtx *Context, arguments ...string) (err error) func (c *Command) VisibleCategories() []CommandCategory VisibleCategories returns a slice of categories and commands that are From a9d6b0d821b0dd5b3092781e0927561dd818770b Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Tue, 11 Oct 2022 09:18:57 -0400 Subject: [PATCH 09/13] Changes from code review --- app.go | 27 ++++++++++++++++++++++++++- app_test.go | 1 + command.go | 27 +++------------------------ 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/app.go b/app.go index 46993658e8..9d5e82c8a6 100644 --- a/app.go +++ b/app.go @@ -241,6 +241,31 @@ func (a *App) Setup() { } } +func (a *App) newRootCommand() *Command { + return &Command{ + Name: a.Name, + Usage: a.Usage, + UsageText: a.UsageText, + Description: a.Description, + ArgsUsage: a.ArgsUsage, + BashComplete: a.BashComplete, + Before: a.Before, + After: a.After, + Action: a.Action, + OnUsageError: a.OnUsageError, + Subcommands: a.Commands, + Flags: a.Flags, + flagCategories: a.flagCategories, + HideHelp: a.HideHelp, + HideHelpCommand: a.HideHelpCommand, + UseShortOptionHandling: a.UseShortOptionHandling, + HelpName: a.HelpName, + CustomHelpTemplate: a.CustomAppHelpTemplate, + categories: a.categories, + isRoot: true, + } +} + func (a *App) newFlagSet() (*flag.FlagSet, error) { return flagSet(a.Name, a.Flags) } @@ -272,7 +297,7 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) { cCtx := NewContext(a, nil, &Context{Context: ctx}) cCtx.shellComplete = shellComplete - a.rootCommand = newRootCommand(a) + a.rootCommand = a.newRootCommand() cCtx.Command = a.rootCommand return a.rootCommand.Run(cCtx, arguments...) diff --git a/app_test.go b/app_test.go index 654e25ddf3..1f5805ab61 100644 --- a/app_test.go +++ b/app_test.go @@ -1901,6 +1901,7 @@ func TestApp_Run_CommandHelpName(t *testing.T) { } cmd := &Command{ Name: "foo", + HelpName: "custom", Description: "foo commands", Subcommands: []*Command{subCmd}, } diff --git a/command.go b/command.go index 897ca2abd3..fa48094e0a 100644 --- a/command.go +++ b/command.go @@ -106,32 +106,11 @@ func (cmd *Command) Command(name string) *Command { return nil } -func newRootCommand(a *App) *Command { - return &Command{ - HelpName: a.HelpName, - Subcommands: a.Commands, - flagCategories: a.flagCategories, - Flags: a.Flags, - Name: a.Name, - Action: a.Action, - UseShortOptionHandling: a.UseShortOptionHandling, - Before: a.Before, - After: a.After, - HideHelp: a.HideHelp, - HideHelpCommand: a.HideHelpCommand, - OnUsageError: a.OnUsageError, - CustomHelpTemplate: a.CustomAppHelpTemplate, - Usage: a.Usage, - UsageText: a.UsageText, - Description: a.Description, - ArgsUsage: a.ArgsUsage, - BashComplete: a.BashComplete, - categories: a.categories, - isRoot: true, +func (c *Command) setup(ctx *Context) { + if c.HelpName == "" { + c.HelpName = fmt.Sprintf("%s %s", ctx.parentContext.Command.HelpName, c.Name) } -} -func (c *Command) setup(ctx *Context) { if c.Command(helpCommand.Name) == nil && !c.HideHelp { if !c.HideHelpCommand { c.Subcommands = append(c.Subcommands, helpCommand) From 4a109bc0ce35a7859245ce1e5105ea8f363f413d Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Tue, 11 Oct 2022 16:28:12 -0400 Subject: [PATCH 10/13] Fix tests --- app.go | 6 ++++-- app_test.go | 4 ++-- command.go | 4 ---- help_test.go | 2 +- 4 files changed, 7 insertions(+), 9 deletions(-) diff --git a/app.go b/app.go index 9d5e82c8a6..0c4333ed6c 100644 --- a/app.go +++ b/app.go @@ -198,9 +198,11 @@ func (a *App) Setup() { var newCommands []*Command for _, c := range a.Commands { - if c.HelpName == "" { - c.HelpName = fmt.Sprintf("%s %s", a.HelpName, c.Name) + cname := c.Name + if c.HelpName != "" { + cname = c.HelpName } + c.HelpName = fmt.Sprintf("%s %s", a.HelpName, cname) c.flagCategories = newFlagCategoriesFromFlags(c.Flags) newCommands = append(newCommands, c) diff --git a/app_test.go b/app_test.go index 1f5805ab61..834b37dde9 100644 --- a/app_test.go +++ b/app_test.go @@ -1914,12 +1914,12 @@ func TestApp_Run_CommandHelpName(t *testing.T) { output := buf.String() - expected := "command foo bar - does bar things" + expected := "command custom bar - does bar things" if !strings.Contains(output, expected) { t.Errorf("expected %q in output: %s", expected, output) } - expected = "command foo bar [command options] [arguments...]" + expected = "command custom bar [command options] [arguments...]" if !strings.Contains(output, expected) { t.Errorf("expected %q in output: %s", expected, output) } diff --git a/command.go b/command.go index fa48094e0a..a63048dbdb 100644 --- a/command.go +++ b/command.go @@ -107,10 +107,6 @@ func (cmd *Command) Command(name string) *Command { } func (c *Command) setup(ctx *Context) { - if c.HelpName == "" { - c.HelpName = fmt.Sprintf("%s %s", ctx.parentContext.Command.HelpName, c.Name) - } - if c.Command(helpCommand.Name) == nil && !c.HideHelp { if !c.HideHelpCommand { c.Subcommands = append(c.Subcommands, helpCommand) diff --git a/help_test.go b/help_test.go index 2a17df59de..2341f58d9c 100644 --- a/help_test.go +++ b/help_test.go @@ -504,13 +504,13 @@ func TestShowSubcommandHelp_CommandAliases(t *testing.T) { func TestShowCommandHelp_Customtemplate(t *testing.T) { app := &App{ + Name: "foo", Commands: []*Command{ { Name: "frobbly", Action: func(ctx *Context) error { return nil }, - HelpName: "foo frobbly", CustomHelpTemplate: `NAME: {{.HelpName}} - {{.Usage}} From 00afca42c863a8ab6b551bc41817a1558f626fa7 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Tue, 11 Oct 2022 16:35:54 -0400 Subject: [PATCH 11/13] Add RunAsSubcommand to keep API unchanged --- app.go | 6 ++++++ help_test.go | 2 -- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/app.go b/app.go index 0c4333ed6c..73a4e3f2f1 100644 --- a/app.go +++ b/app.go @@ -305,6 +305,12 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) { return a.rootCommand.Run(cCtx, arguments...) } +// This is a stub function to keep public API unchanged from old code +// No one should really use this. Always use a.Run to execute app +func (a *App) RunAsSubcommand(ctx *Context) (err error) { + return a.RunContext(ctx.Context, ctx.Args().Slice()) +} + func (a *App) suggestFlagFromError(err error, command string) (string, error) { flag, parseErr := flagFromError(err) if parseErr != nil { diff --git a/help_test.go b/help_test.go index 2341f58d9c..cc109a01c1 100644 --- a/help_test.go +++ b/help_test.go @@ -1050,7 +1050,6 @@ func newContextFromStringSlice(ss []string) *Context { return &Context{flagSet: set} } -/* func TestHideHelpCommand_RunAsSubcommand(t *testing.T) { app := &App{ HideHelpCommand: true, @@ -1097,7 +1096,6 @@ func TestHideHelpCommand_RunAsSubcommand_False(t *testing.T) { t.Errorf("Run returned unexpected error: %v", err) } } -*/ func TestHideHelpCommand_WithSubcommands(t *testing.T) { app := &App{ From 5100c9d4e98551ce47828e51a99cabe2b2d044b8 Mon Sep 17 00:00:00 2001 From: Naveen Gogineni Date: Tue, 11 Oct 2022 19:40:59 -0400 Subject: [PATCH 12/13] Update docs --- godoc-current.txt | 4 ++++ testdata/godoc-v2.x.txt | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/godoc-current.txt b/godoc-current.txt index 7db7c54cb7..73251e3615 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -347,6 +347,10 @@ func (a *App) RunAndExitOnError() to cli.App.Run. This will cause the application to exit with the given error code in the cli.ExitCoder +func (a *App) RunAsSubcommand(ctx *Context) (err error) + This is a stub function to keep public API unchanged from old code No one + should really use this. Always use a.Run to execute app + func (a *App) RunContext(ctx context.Context, arguments []string) (err error) RunContext is like Run except it takes a Context that will be passed to its commands and sub-commands. Through this, you can propagate timeouts and diff --git a/testdata/godoc-v2.x.txt b/testdata/godoc-v2.x.txt index 7db7c54cb7..73251e3615 100644 --- a/testdata/godoc-v2.x.txt +++ b/testdata/godoc-v2.x.txt @@ -347,6 +347,10 @@ func (a *App) RunAndExitOnError() to cli.App.Run. This will cause the application to exit with the given error code in the cli.ExitCoder +func (a *App) RunAsSubcommand(ctx *Context) (err error) + This is a stub function to keep public API unchanged from old code No one + should really use this. Always use a.Run to execute app + func (a *App) RunContext(ctx context.Context, arguments []string) (err error) RunContext is like Run except it takes a Context that will be passed to its commands and sub-commands. Through this, you can propagate timeouts and From 33d08c39bea6f3f1d829dd95b25d4ab43b99ee2a Mon Sep 17 00:00:00 2001 From: dearchap Date: Fri, 14 Oct 2022 02:46:00 -0400 Subject: [PATCH 13/13] Update app.go Co-authored-by: Dan Buch --- app.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app.go b/app.go index 73a4e3f2f1..e51400372a 100644 --- a/app.go +++ b/app.go @@ -306,7 +306,8 @@ func (a *App) RunContext(ctx context.Context, arguments []string) (err error) { } // This is a stub function to keep public API unchanged from old code -// No one should really use this. Always use a.Run to execute app +// +// Deprecated: use App.Run or App.RunContext func (a *App) RunAsSubcommand(ctx *Context) (err error) { return a.RunContext(ctx.Context, ctx.Args().Slice()) }