From 9334a46bd6b3887f3561d705440038ec93b7f62e Mon Sep 17 00:00:00 2001 From: Bruce Downs Date: Mon, 29 Jul 2019 23:36:50 -0700 Subject: [PATCH] Return an error in the case of unrunnable subcommand * credit to @chriswhelix for initial commit --- command.go | 16 ++++++++++++++-- command_test.go | 4 ++-- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/command.go b/command.go index c7e8983..05fd9f3 100644 --- a/command.go +++ b/command.go @@ -17,6 +17,7 @@ package cobra import ( "bytes" + "errors" "fmt" "io" "os" @@ -27,6 +28,9 @@ import ( flag "github.com/spf13/pflag" ) +// NotRunnable defines subcommand error +var NotRunnable = errors.New("subcommand is required") + // FParseErrWhitelist configures Flag parse errors to be ignored type FParseErrWhitelist flag.ParseErrorsWhitelist @@ -369,7 +373,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) } @@ -786,7 +790,7 @@ func (c *Command) execute(a []string) (err error) { } if !c.Runnable() { - return flag.ErrHelp + return NotRunnable } c.preRun() @@ -920,6 +924,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 2fa2003..e7961c8 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.Errorf("Expected error") } checkStringContains(t, output, childCmd.Long)