From b3917f15b395a5dbc5f541aa98d60d954b5410f8 Mon Sep 17 00:00:00 2001 From: Chris Williams Date: Mon, 26 Feb 2018 10:52:51 -0800 Subject: [PATCH 1/4] Return an error if not runnable --- command.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/command.go b/command.go index 29675b336..d7d37ac15 100644 --- a/command.go +++ b/command.go @@ -17,6 +17,7 @@ package cobra import ( "bytes" + "errors" "fmt" "io" "os" @@ -713,7 +714,7 @@ func (c *Command) execute(a []string) (err error) { } if !c.Runnable() { - return flag.ErrHelp + return errors.New("Subcommand required.") } c.preRun() From ddad852e889906070c1e7d43d50c0067aa876467 Mon Sep 17 00:00:00 2001 From: Chris Williams Date: Mon, 26 Feb 2018 11:11:58 -0800 Subject: [PATCH 2/4] Maintain identical output --- command.go | 14 ++++++++++++-- command_test.go | 4 ++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/command.go b/command.go index d7d37ac15..0668560a0 100644 --- a/command.go +++ b/command.go @@ -23,11 +23,13 @@ import ( "os" "path/filepath" "sort" - "strings" flag "github.com/spf13/pflag" + "strings" ) +var NotRunnable = errors.New("Command not runnable; need subcommand.") + // Command is just that, a command for your application. // E.g. 'go run ...' - 'run' is the command. Cobra requires // you to define the usage and description as part of your command @@ -714,7 +716,7 @@ func (c *Command) execute(a []string) (err error) { } if !c.Runnable() { - return errors.New("Subcommand required.") + return NotRunnable } c.preRun() @@ -850,6 +852,14 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { return cmd, nil } + // If command wasn't runnable, show full help, but do return the error. + // This will result in apps by default returning a non-success exit code, but also gives them the option to + // handle specially. + if err == NotRunnable { + cmd.HelpFunc()(cmd, args) + return cmd, err + } + // If root command has SilentErrors flagged, // all subcommands should respect it if !cmd.SilenceErrors && !c.SilenceErrors { diff --git a/command_test.go b/command_test.go index d874a9a51..c1b87496e 100644 --- a/command_test.go +++ b/command_test.go @@ -836,8 +836,8 @@ func TestHelpExecutedOnNonRunnableChild(t *testing.T) { rootCmd.AddCommand(childCmd) output, err := executeCommand(rootCmd, "child") - if err != nil { - t.Errorf("Unexpected error: %v", err) + if err != NotRunnable { + t.Error("Expected error for missing subcommand.") } checkStringContains(t, output, childCmd.Long) From 487d155ee241dcb769795a965ea9234a8b1c56f4 Mon Sep 17 00:00:00 2001 From: Chris Williams Date: Mon, 26 Feb 2018 11:27:49 -0800 Subject: [PATCH 3/4] Help belongs on stderr --- command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command.go b/command.go index 0668560a0..f65d43253 100644 --- a/command.go +++ b/command.go @@ -311,7 +311,7 @@ func (c *Command) HelpFunc() func(*Command, []string) { } return func(c *Command, a []string) { c.mergePersistentFlags() - err := tmpl(c.OutOrStdout(), c.HelpTemplate(), c) + err := tmpl(c.OutOrStderr(), c.HelpTemplate(), c) if err != nil { c.Println(err) } From 6e93d49a674ce35166b05f08276595aca48a7496 Mon Sep 17 00:00:00 2001 From: Chris Williams Date: Tue, 27 Feb 2018 16:15:26 -0800 Subject: [PATCH 4/4] Fix import ordering --- command.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/command.go b/command.go index f65d43253..2f35efaa2 100644 --- a/command.go +++ b/command.go @@ -23,9 +23,9 @@ import ( "os" "path/filepath" "sort" + "strings" flag "github.com/spf13/pflag" - "strings" ) var NotRunnable = errors.New("Command not runnable; need subcommand.")