diff --git a/command.go b/command.go index c05fed45a..218560296 100644 --- a/command.go +++ b/command.go @@ -257,6 +257,23 @@ type Command struct { // SuggestionsMinimumDistance defines minimum levenshtein distance to display suggestions. // Must be > 0. SuggestionsMinimumDistance int + + // helpExitCode is the exit code to return when the help flag is used. + // If 0 (default), the program returns nil error (exit code 0). + helpExitCode int +} +type ExitError struct { + Code int +} + +func (e *ExitError) Error() string { + return fmt.Sprintf("command requested exit with code %d", e.Code) +} + +// SetHelpExitCode sets the exit code to return when help is shown. +// If set to a non-zero value, Execute() will return an ExitError wrapping this code. +func (c *Command) SetHelpExitCode(code int) { + c.helpExitCode = code } // Context returns underlying command context. If command was executed @@ -1151,6 +1168,10 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { // effect if errors.Is(err, flag.ErrHelp) { cmd.HelpFunc()(cmd, args) + // If the user set a custom exit code for help, return a structured ExitError + if cmd.helpExitCode != 0 { + return cmd, &ExitError{Code: cmd.helpExitCode} + } return cmd, nil } diff --git a/command_test.go b/command_test.go index a86e57f0a..52c481277 100644 --- a/command_test.go +++ b/command_test.go @@ -17,6 +17,7 @@ package cobra import ( "bytes" "context" + "errors" "fmt" "io" "os" @@ -2952,3 +2953,35 @@ func TestHelpFuncExecuted(t *testing.T) { checkStringContains(t, output, helpText) } + +// command_test.go + +func TestHelpExitCode(t *testing.T) { + // 1. Create a dummy command + cmd := &Command{Use: "test", Run: func(*Command, []string) {}} + + // 2. Set the custom exit code + expectedCode := 42 + cmd.SetHelpExitCode(expectedCode) + + // 3. Force help flag + cmd.SetArgs([]string{"--help"}) + + // 4. Run it + _, err := cmd.ExecuteC() + + // 5. Verify we got an error + if err == nil { + t.Fatal("Expected an error because SetHelpExitCode(42) was called, but got nil") + } + + // 6. Verify the error is the correct TYPE and contains the correct CODE + var exitErr *ExitError + if errors.As(err, &exitErr) { + if exitErr.Code != expectedCode { + t.Errorf("Expected exit code %d, got %d", expectedCode, exitErr.Code) + } + } else { + t.Errorf("Expected error to be of type *ExitError, got %T: %v", err, err) + } +}