From f03a2c5110974ea036d84ed4ec2ea81394cc81a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Pedersen?= Date: Sat, 17 Oct 2015 19:22:43 +0200 Subject: [PATCH] Silence some usage and error logging in Execute `RunE` was introduced on `Command` to support centralized error handling. The errors returned from `RunE` is, however, still logged by Cobra, and there is no easy way to toogle the usage logging on a case-by-case basis (other than the brutal `os.Exit(-1)`. This commit introduces two small interfaces that enables end users to signal the behavior they want from Cobra in this area. --- cobra_test.go | 78 +++++++++++++++++++++++++++++++++++++++++++++++++-- command.go | 28 ++++++++++++++++-- 2 files changed, 102 insertions(+), 4 deletions(-) diff --git a/cobra_test.go b/cobra_test.go index f4814ead..29239b07 100644 --- a/cobra_test.go +++ b/cobra_test.go @@ -2,15 +2,15 @@ package cobra import ( "bytes" + "errors" "fmt" + "github.com/spf13/pflag" "os" "reflect" "runtime" "strings" "testing" "text/template" - - "github.com/spf13/pflag" ) var _ = fmt.Println @@ -25,6 +25,22 @@ var globalFlag1 bool var flagEcho, rootcalled bool var versionUsed int +type silentErr int + +func (silentErr) Error() string { return "" } +func (silentErr) ShouldLogError() bool { return false } +func (silentErr) ShouldLogUsage() bool { return false } + +type noErrorLogging int + +func (noErrorLogging) Error() string { return "" } +func (noErrorLogging) ShouldLogError() bool { return false } + +type noUsageLogging int + +func (noUsageLogging) Error() string { return "" } +func (noUsageLogging) ShouldLogUsage() bool { return false } + const strtwoParentHelp = "help message for parent flag strtwo" const strtwoChildHelp = "help message for child flag strtwo" @@ -582,6 +598,64 @@ func TestSubcommandArgEvaluation(t *testing.T) { } } +func TestSilentError(t *testing.T) { + rootCmd := initializeWithRootCmd() + + tests := []struct { + cmd *Command + logError bool + logUsage bool + }{ + { + &Command{ + Use: "silent", + RunE: func(cmd *Command, args []string) error { + return silentErr(1) + }}, false, false}, + { + &Command{ + Use: "nousage", + RunE: func(cmd *Command, args []string) error { + return noUsageLogging(2) + }}, true, false}, + { + &Command{ + Use: "noerror", + RunE: func(cmd *Command, args []string) error { + return noErrorLogging(3) + }}, false, true}, + { + &Command{ + Use: "regular", + RunE: func(cmd *Command, args []string) error { + return errors.New("Failed") + }}, true, true}, + } + + for _, tc := range tests { + rootCmd.AddCommand(tc.cmd) + } + + for i, tc := range tests { + result := fullTester(rootCmd, tc.cmd.Use) + if result.Error == nil { + t.Fatalf("[%d] Err was nil", i) + } + hasError := strings.Contains(result.Output, "Error:") + hasUsage := strings.Contains(result.Output, "Usage:") + + if hasError != tc.logError { + t.Errorf("[%d] Error was logged: %s", i, result.Output) + } + + if hasUsage != tc.logUsage { + t.Errorf("[%d] Usage was logged: %s", i, result.Output) + } + + } + +} + func TestPersistentFlags(t *testing.T) { fullSetupTest("echo -s something -p more here") diff --git a/command.go b/command.go index 20412439..14ed11be 100644 --- a/command.go +++ b/command.go @@ -115,6 +115,16 @@ type Command struct { SuggestionsMinimumDistance int } +// LogError can be implemented to toggle error logging when error is returned from RunE. +type LogError interface { + ShouldLogError() bool +} + +// LogUsage can be implemented to toggle usage logging when error is returned from RunE. +type LogUsage interface { + ShouldLogUsage() bool +} + // os.Args[1:] by default, if desired, can be overridden // particularly useful when testing. func (c *Command) SetArgs(a []string) { @@ -637,8 +647,22 @@ func (c *Command) Execute() (err error) { cmd.HelpFunc()(cmd, args) return nil } - c.Println(cmd.UsageString()) - c.Println("Error:", err.Error()) + + logError := true + logUsage := true + + if le, ok := err.(LogError); ok { + logError = le.ShouldLogError() + } + if lu, ok := err.(LogUsage); ok { + logUsage = lu.ShouldLogUsage() + } + if logUsage { + c.Println(cmd.UsageString()) + } + if logError { + c.Println("Error:", err.Error()) + } } return