Skip to content

Commit

Permalink
Pass arguments to the help function
Browse files Browse the repository at this point in the history
Signed-off-by: Marc Khouzam <marc.khouzam@gmail.com>
  • Loading branch information
marckhouzam committed Jun 9, 2024
1 parent e94f6d0 commit 8e51700
Show file tree
Hide file tree
Showing 2 changed files with 201 additions and 5 deletions.
19 changes: 14 additions & 5 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -470,8 +470,11 @@ func (c *Command) HelpFunc() func(*Command, []string) {
}

// Help puts out the help for the command.
// Used when a user calls help [command].
// Can be defined by user by overriding HelpFunc.
// Kept for backwards compatibility.
// No longer used because it does not allow
// to pass arguments to the help command.
// Can be a simple way to trigger the help
// if arguments are not needed.
func (c *Command) Help() error {
c.HelpFunc()(c, []string{})
return nil
Expand Down Expand Up @@ -1119,7 +1122,13 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
// Always show help if requested, even if SilenceErrors is in
// effect
if errors.Is(err, flag.ErrHelp) {
cmd.HelpFunc()(cmd, args)
// The call to execute() above has parsed the flags.
// We therefore only pass the remaining arguments to the help function.
argWoFlags := cmd.Flags().Args()
if cmd.DisableFlagParsing {
argWoFlags = flags
}
cmd.HelpFunc()(cmd, argWoFlags)
return cmd, nil
}

Expand Down Expand Up @@ -1260,14 +1269,14 @@ Simply type ` + c.displayName() + ` help [path to command] for full details.`,
return completions, ShellCompDirectiveNoFileComp
},
Run: func(c *Command, args []string) {
cmd, _, e := c.Root().Find(args)
cmd, remainingArgs, e := c.Root().Find(args)
if cmd == nil || e != nil {
c.Printf("Unknown help topic %#q\n", args)
CheckErr(c.Root().Usage())
} else {
cmd.InitDefaultHelpFlag() // make possible 'help' flag to be shown
cmd.InitDefaultVersionFlag() // make possible 'version' flag to be shown
CheckErr(cmd.Help())
cmd.HelpFunc()(cmd, remainingArgs)
}
},
GroupID: c.helpCommandGroupID,
Expand Down
187 changes: 187 additions & 0 deletions command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

//nolint:goconst
package cobra

import (
Expand Down Expand Up @@ -1079,6 +1080,192 @@ func TestHelpExecutedOnNonRunnableChild(t *testing.T) {
checkStringContains(t, output, childCmd.Long)
}

func TestHelpOverrideOnRoot(t *testing.T) {
var helpCalled bool
rootCmd := &Command{Use: "root"}
rootCmd.SetHelpFunc(func(c *Command, args []string) {
helpCalled = true
if c.Name() != "root" {
t.Errorf(`Expected command name: "root", got %q`, c.Name())
}
if len(args) != 2 || args[0] != "arg1" || args[1] != "arg2" {
t.Errorf("Expected args [args1 arg2], got %v", args)
}
})

_, err := executeCommand(rootCmd, "arg1", "arg2", "--help")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}

if !helpCalled {
t.Error("Overridden help function not called")
}
}

func TestHelpOverrideOnChild(t *testing.T) {
var helpCalled bool
rootCmd := &Command{Use: "root"}
subCmd := &Command{Use: "child"}
rootCmd.AddCommand(subCmd)

subCmd.SetHelpFunc(func(c *Command, args []string) {
helpCalled = true
if c.Name() != "child" {
t.Errorf(`Expected command name: "child", got %q`, c.Name())
}
if len(args) != 2 || args[0] != "arg1" || args[1] != "arg2" {
t.Errorf("Expected args [args1 arg2], got %v", args)
}
})

_, err := executeCommand(rootCmd, "child", "arg1", "arg2", "--help")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}

if !helpCalled {
t.Error("Overridden help function not called")
}
}

func TestHelpOverrideOnRootWithChild(t *testing.T) {
var helpCalled bool
rootCmd := &Command{Use: "root"}
subCmd := &Command{Use: "child"}
rootCmd.AddCommand(subCmd)

rootCmd.SetHelpFunc(func(c *Command, args []string) {
helpCalled = true
if c.Name() != "child" {
t.Errorf(`Expected command name: "child", got %q`, c.Name())
}
if len(args) != 2 || args[0] != "arg1" || args[1] != "arg2" {
t.Errorf("Expected args [args1 arg2], got %v", args)
}
})

_, err := executeCommand(rootCmd, "child", "arg1", "arg2", "--help")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}

if !helpCalled {
t.Error("Overridden help function not called")
}
}

func TestHelpOverrideOnRootWithChildAndFlags(t *testing.T) {
var helpCalled bool
rootCmd := &Command{Use: "root"}
subCmd := &Command{Use: "child"}
rootCmd.AddCommand(subCmd)

var myFlag bool
subCmd.Flags().BoolVar(&myFlag, "myflag", false, "")

rootCmd.SetHelpFunc(func(c *Command, args []string) {
helpCalled = true
if c.Name() != "child" {
t.Errorf(`Expected command name: "child", got %q`, c.Name())
}
if len(args) != 2 || args[0] != "arg1" || args[1] != "arg2" {
t.Errorf("Expected args [args1 arg2], got %v", args)
}
})

_, err := executeCommand(rootCmd, "child", "arg1", "--myflag", "arg2", "--help")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}

if !helpCalled {
t.Error("Overridden help function not called")
}
}

func TestHelpOverrideOnRootWithChildAndFlagsButParsingDisabled(t *testing.T) {
var helpCalled bool
rootCmd := &Command{Use: "root"}
subCmd := &Command{Use: "child", DisableFlagParsing: true}
rootCmd.AddCommand(subCmd)

var myFlag bool
subCmd.Flags().BoolVar(&myFlag, "myflag", false, "")

rootCmd.SetHelpFunc(func(c *Command, args []string) {
helpCalled = true
if c.Name() != "child" {
t.Errorf(`Expected command name: "child", got %q`, c.Name())
}
if len(args) != 4 ||
args[0] != "arg1" || args[1] != "--myflag" || args[2] != "arg2" || args[3] != "--help" {
t.Errorf("Expected args [args1 --myflag arg2 --help], got %v", args)
}
})

_, err := executeCommand(rootCmd, "child", "arg1", "--myflag", "arg2", "--help")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}

if !helpCalled {
t.Error("Overridden help function not called")
}
}

func TestHelpCommandOverrideOnChild(t *testing.T) {
var helpCalled bool
rootCmd := &Command{Use: "root"}
subCmd := &Command{Use: "child"}
rootCmd.AddCommand(subCmd)

subCmd.SetHelpFunc(func(c *Command, args []string) {
helpCalled = true
if c.Name() != "child" {
t.Errorf(`Expected command name: "child", got %q`, c.Name())
}
if len(args) != 2 || args[0] != "arg1" || args[1] != "arg2" {
t.Errorf("Expected args [args1 arg2], got %v", args)
}
})

_, err := executeCommand(rootCmd, "help", "child", "arg1", "arg2")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}

if !helpCalled {
t.Error("Overridden help function not called")
}
}

func TestHelpCommandOverrideOnRootWithChild(t *testing.T) {
var helpCalled bool
rootCmd := &Command{Use: "root"}
subCmd := &Command{Use: "child"}
rootCmd.AddCommand(subCmd)

rootCmd.SetHelpFunc(func(c *Command, args []string) {
helpCalled = true
if c.Name() != "child" {
t.Errorf(`Expected command name: "child", got %q`, c.Name())
}
if len(args) != 2 || args[0] != "arg1" || args[1] != "arg2" {
t.Errorf("Expected args [args1 arg2], got %v", args)
}
})

_, err := executeCommand(rootCmd, "help", "child", "arg1", "arg2")
if err != nil {
t.Errorf("Unexpected error: %v", err)
}

if !helpCalled {
t.Error("Overridden help function not called")
}
}

func TestVersionFlagExecuted(t *testing.T) {
rootCmd := &Command{Use: "root", Version: "1.0.0", Run: emptyRun}

Expand Down

0 comments on commit 8e51700

Please sign in to comment.