diff --git a/shared/cmd/flags.go b/shared/cmd/flags.go index 7aef06561d3d..129b0b959110 100644 --- a/shared/cmd/flags.go +++ b/shared/cmd/flags.go @@ -257,13 +257,28 @@ func LoadFlagsFromConfig(cliCtx *cli.Context, flags []cli.Flag) error { // ValidateNoArgs insures that the application is not run with erroneous arguments or flags. // This function should be used in the app.Before, whenever the application supports a default command. func ValidateNoArgs(ctx *cli.Context) error { + commandList := ctx.App.Commands for _, a := range ctx.Args().Slice() { if strings.HasPrefix(a, "-") { continue } - if c := ctx.App.Command(a); c == nil { + c := checkCommandList(commandList, a) + if c == nil { return fmt.Errorf("unrecognized argument: %s", a) } + // Set the command list as the subcommand's + // from the current selected parent command. + commandList = c.Subcommands + } + return nil +} + +// verifies that the provided command is in the command list. +func checkCommandList(commands []*cli.Command, name string) *cli.Command { + for _, c := range commands { + if c.Name == name { + return c + } } return nil } diff --git a/shared/cmd/flags_test.go b/shared/cmd/flags_test.go index a4af7dde8d0a..363d6f4c0c34 100644 --- a/shared/cmd/flags_test.go +++ b/shared/cmd/flags_test.go @@ -55,6 +55,24 @@ func TestValidateNoArgs(t *testing.T) { Commands: []*cli.Command{ { Name: "bar", + Subcommands: []*cli.Command{ + { + Name: "subComm1", + Subcommands: []*cli.Command{ + { + Name: "subComm3", + }, + }, + }, + { + Name: "subComm2", + Subcommands: []*cli.Command{ + { + Name: "subComm4", + }, + }, + }, + }, }, }, } @@ -71,4 +89,39 @@ func TestValidateNoArgs(t *testing.T) { // It should fail on unregistered flag (default logic in urfave/cli). err = app.Run([]string{"command", "bar", "--baz"}) require.ErrorContains(t, "flag provided but not defined", err) + + // Handle Nested Subcommands + + err = app.Run([]string{"command", "bar", "subComm1"}) + require.NoError(t, err) + + err = app.Run([]string{"command", "bar", "subComm2"}) + require.NoError(t, err) + + // Should fail from unknown subcommands. + err = app.Run([]string{"command", "bar", "subComm3"}) + require.ErrorContains(t, "unrecognized argument: subComm3", err) + + err = app.Run([]string{"command", "bar", "subComm4"}) + require.ErrorContains(t, "unrecognized argument: subComm4", err) + + // Should fail with invalid double nested subcommands. + err = app.Run([]string{"command", "bar", "subComm1", "subComm2"}) + require.ErrorContains(t, "unrecognized argument: subComm2", err) + + err = app.Run([]string{"command", "bar", "subComm1", "subComm4"}) + require.ErrorContains(t, "unrecognized argument: subComm4", err) + + err = app.Run([]string{"command", "bar", "subComm2", "subComm1"}) + require.ErrorContains(t, "unrecognized argument: subComm1", err) + + err = app.Run([]string{"command", "bar", "subComm2", "subComm3"}) + require.ErrorContains(t, "unrecognized argument: subComm3", err) + + // Should pass with correct nested double subcommands. + err = app.Run([]string{"command", "bar", "subComm1", "subComm3"}) + require.NoError(t, err) + + err = app.Run([]string{"command", "bar", "subComm2", "subComm4"}) + require.NoError(t, err) }