Backward compatibility

An error is only thrown if there are arguments to a valid command.
Without any arguments the help is printed as before.
An invalid subcommand will return an error.
This commit is contained in:
Andre Mueller 2024-10-25 11:09:09 +02:00
parent ae6c026135
commit 41b8340b2b
3 changed files with 33 additions and 19 deletions

View file

@ -68,7 +68,7 @@ var EnableTraverseRunHooks = defaultTraverseRunHooks
// EnableErrorOnUnknownSubcommand controls the behavior of subcommand handling. // EnableErrorOnUnknownSubcommand controls the behavior of subcommand handling.
// When the flag is set true the behavior of Command.Execute() will change: // When the flag is set true the behavior of Command.Execute() will change:
// If a sub-subcommand is not found ErrUnknownSubcommand will be returned on calling // If a sub-subcommand is not found an error will be returned on calling
// Command.Exec() instead of the old behavior where a nil error was sent. // Command.Exec() instead of the old behavior where a nil error was sent.
// If the flag is false (default) the old behavior is performed. // If the flag is false (default) the old behavior is performed.
// For this behavior the child subcommand must be nil. // For this behavior the child subcommand must be nil.

View file

@ -44,10 +44,6 @@ type Group struct {
Title string Title string
} }
// ErrUnknownSubcommand is returned by Command.Execute() when the subcommand was not found.
// Hereto, the ErrorOnUnknownSubcommand flag must be set true.
var ErrUnknownSubcommand = errors.New("subcommand is unknown")
// Command is just that, a command for your application. // Command is just that, a command for your application.
// E.g. 'go run ...' - 'run' is the command. Cobra requires // E.g. 'go run ...' - 'run' is the command. Cobra requires
// you to define the usage and description as part of your command // you to define the usage and description as part of your command
@ -927,9 +923,14 @@ func (c *Command) execute(a []string) (err error) {
} }
} }
argWoFlags := c.Flags().Args()
if c.DisableFlagParsing {
argWoFlags = a
}
if !c.Runnable() { if !c.Runnable() {
if EnableErrorOnUnknownSubcommand { if EnableErrorOnUnknownSubcommand && len(argWoFlags) > 0 {
return ErrUnknownSubcommand return fmt.Errorf("unknown command %q for %q%s", argWoFlags[0], c.CommandPath(), c.findSuggestions(argWoFlags[0]))
} else { } else {
return flag.ErrHelp return flag.ErrHelp
} }
@ -938,11 +939,6 @@ func (c *Command) execute(a []string) (err error) {
defer c.postRun() defer c.postRun()
argWoFlags := c.Flags().Args()
if c.DisableFlagParsing {
argWoFlags = a
}
if err := c.ValidateArgs(argWoFlags); err != nil { if err := c.ValidateArgs(argWoFlags); err != nil {
return err return err
} }

View file

@ -17,7 +17,6 @@ package cobra
import ( import (
"bytes" "bytes"
"context" "context"
"errors"
"fmt" "fmt"
"io" "io"
"os" "os"
@ -224,7 +223,8 @@ func TestSubcommandExecuteMissingSubcommand(t *testing.T) {
} }
func TestSubcommandExecuteMissingSubcommandWithErrorOnUnknownSubcommand(t *testing.T) { func TestSubcommandExecuteMissingSubcommandWithErrorOnUnknownSubcommand(t *testing.T) {
rootCmd := &Command{Use: "root", Run: emptyRun} const rootName = "root"
rootCmd := &Command{Use: rootName, Run: emptyRun}
const childName = "child" const childName = "child"
const grandchildName = "grandchild" const grandchildName = "grandchild"
EnableErrorOnUnknownSubcommand = true EnableErrorOnUnknownSubcommand = true
@ -236,10 +236,10 @@ func TestSubcommandExecuteMissingSubcommandWithErrorOnUnknownSubcommand(t *testi
// test existing command // test existing command
c, output, err := executeCommandC(rootCmd, childName) c, output, err := executeCommandC(rootCmd, childName)
if !strings.HasPrefix(output, "Error:") { if strings.HasPrefix(output, "Error:") {
t.Errorf("Unexpected output: %v", output) t.Errorf("Unexpected output: %v", output)
} }
if !errors.Is(err, ErrUnknownSubcommand) { if err != nil {
t.Errorf("Unexpected error: %v", err) t.Errorf("Unexpected error: %v", err)
} }
if c.Name() != childName { if c.Name() != childName {
@ -258,12 +258,30 @@ func TestSubcommandExecuteMissingSubcommandWithErrorOnUnknownSubcommand(t *testi
t.Errorf(`invalid command returned from ExecuteC: expected "child"', got: %q`, c.Name()) t.Errorf(`invalid command returned from ExecuteC: expected "child"', got: %q`, c.Name())
} }
// now test a command which does not exist, we expect an error because of the ErrorOnUnknownSubcommand flag // test a child command which does not exist, we expect an error because of the ErrorOnUnknownSubcommand flag
c, output, err = executeCommandC(rootCmd, childName, "unknownChild") c, output, err = executeCommandC(rootCmd, "unknownChild")
if !strings.HasPrefix(output, "Error:") { if !strings.HasPrefix(output, "Error:") {
t.Errorf("Unexpected output: %v", output) t.Errorf("Unexpected output: %v", output)
} }
if !errors.Is(err, ErrUnknownSubcommand) { if err == nil {
t.Error("Expected error")
}
if err != nil && !strings.HasPrefix(err.Error(), "unknown command") {
t.Errorf("Unexpected error: %v", err)
}
if c.Name() != rootName {
t.Errorf(`invalid command returned from ExecuteC: expected "child"', got: %q`, c.Name())
}
// test a grandchild command which does not exist, we expect an error because of the ErrorOnUnknownSubcommand flag
c, output, err = executeCommandC(rootCmd, childName, "unknownGrandChild")
if !strings.HasPrefix(output, "Error:") {
t.Errorf("Unexpected output: %v", output)
}
if err == nil {
t.Error("Expected error")
}
if err != nil && !strings.HasPrefix(err.Error(), "unknown command") {
t.Errorf("Unexpected error: %v", err) t.Errorf("Unexpected error: %v", err)
} }
if c.Name() != childName { if c.Name() != childName {