diff --git a/cmd/docker/docker.go b/cmd/docker/docker.go index 2341570b91a1..2d65ea4d79bb 100644 --- a/cmd/docker/docker.go +++ b/cmd/docker/docker.go @@ -202,10 +202,40 @@ func setFlagErrorFunc(dockerCli command.Cli, cmd *cobra.Command) { if err := isSupported(cmd, dockerCli); err != nil { return err } + if cmd == cmd.Root() { + if rewritten := unknownCommandInsteadOfFlag(cmd, err); rewritten != nil { + return rewritten + } + } return flagErrorFunc(cmd, err) }) } +// unknownCommandInsteadOfFlag rewrites "unknown flag" to "unknown command" +// when the first leftover arg isn't a docker subcommand. That way +// `docker iamges --filter` matches `docker iamges` instead of complaining +// about --filter. `--help` after a bogus name still works because it is a +// known flag and never reaches FlagErrorFunc. +func unknownCommandInsteadOfFlag(cmd *cobra.Command, err error) error { + msg := err.Error() + if !strings.HasPrefix(msg, "unknown flag:") && !strings.HasPrefix(msg, "unknown shorthand flag:") { + return nil + } + args := cmd.Flags().Args() + if len(args) == 0 { + return nil + } + name := args[0] + if strings.HasPrefix(name, "-") { + return nil + } + found, _, _ := cmd.Find([]string{name}) + if found != nil && found != cmd { + return nil + } + return fmt.Errorf("docker: unknown command: docker %s\n\nRun 'docker --help' for more information", name) +} + func setupHelpCommand(dockerCli command.Cli, rootCmd, helpCmd *cobra.Command) { origRun := helpCmd.Run origRunE := helpCmd.RunE diff --git a/cmd/docker/docker_test.go b/cmd/docker/docker_test.go index e68ba48db92b..58004f4af511 100644 --- a/cmd/docker/docker_test.go +++ b/cmd/docker/docker_test.go @@ -92,6 +92,22 @@ func TestExitStatusForInvalidSubcommand(t *testing.T) { assert.Check(t, is.ErrorContains(err, "docker: unknown command: docker invalid")) } +func TestUnknownCommandWithFlag(t *testing.T) { + err := runCliCommand(t, discard, nil, "iamges", "--filter") + assert.Check(t, is.ErrorContains(err, "docker: unknown command: docker iamges")) + assert.Check(t, is.ErrorContains(err, "docker --help")) + + err = runCliCommand(t, discard, nil, "iamges", "-f") + assert.Check(t, is.ErrorContains(err, "docker: unknown command: docker iamges")) +} + +func TestUnknownCommandWithHelpFlag(t *testing.T) { + var b bytes.Buffer + err := runCliCommand(t, discard, &b, "nonexistent", "--help") + assert.NilError(t, err) + assert.Check(t, is.Contains(b.String(), "Usage:")) +} + func TestVersion(t *testing.T) { var b bytes.Buffer err := runCliCommand(t, nil, &b, "--version")