2016-04-19 12:59:48 -04:00
|
|
|
package cli
|
|
|
|
|
|
|
|
import (
|
2017-03-09 13:23:45 -05:00
|
|
|
"github.com/pkg/errors"
|
2016-04-19 12:59:48 -04:00
|
|
|
"github.com/spf13/cobra"
|
|
|
|
)
|
|
|
|
|
2016-07-03 08:47:39 -04:00
|
|
|
// NoArgs validates args and returns an error if there are any args
|
2016-05-26 17:57:31 -04:00
|
|
|
func NoArgs(cmd *cobra.Command, args []string) error {
|
|
|
|
if len(args) == 0 {
|
2016-04-19 12:59:48 -04:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-05-26 17:57:31 -04:00
|
|
|
if cmd.HasSubCommands() {
|
cli: improve output and consistency for unknown (sub)commands
Before this patch, output for invalid top-level and sub-commands differed.
For top-level commands, the CLI would print an error-message and a suggestion
to use `--help`. For missing *subcommands*, we would hit a different code-path,
and different output, which includes full "usage" / "help" output.
While it is a common convention to show usage output, and may have been
a nice gesture when docker was still young and only had a few commands
and options ("you did something wrong; here's an overview of what you
can use"), that's no longer the case, and many commands have a _very_
long output.
The result of this is that the error message, which is the relevant
information in this case - "You mis-typed something" - is lost in the
output, and hard to find (sometimes even requiring scrolling back).
The output is also confusing, because it _looks_ like something ran
successfully (most of the output is not about the error!).
Even further; the suggested resolution (try `--help` to see the correct
options) is rather redundant, because running teh command with `--help`
produces _exactly_ the same output as was just showh, baring the error
message. As a fun fact, due to the usage output being printed, the
output even contains not one, but _two_ "call to actions";
- `See 'docker volume --help'.` (under the erro message)
- `Run 'docker volume COMMAND --help' for more information on a command.`
(under the usage output)
In short; the output is too verbose, confusing, and doesn't provide
a good UX. Let's reduce the output produced so that the focus is on the
important information.
This patch:
- Changes the usage to the short-usage.
- Changes the error-message to mention the _full_ command instead of only
the command after `docker` (so `docker no-such-command` instead of
`no-such-command`).
- Prefixes the error message with the binary / root-command name
(usually `docker:`); this is something we can still decide on, but
it's a pattern we already use in some places. The motivation for this
is that `docker` commands can often produce output that's a combination
of output from the CLI itself, output from the daemon, and even output
from the container. The `docker:` prefix helps to distinguish where
the message originated from (the `docker` CLI in this case).
- Adds an empty line between the error-message and the "call to action"
(`Run 'docker volume --help'...` in the example below). This helps
separating the error message ("unkown flag") from the call-to-action.
Before this patch:
Unknown top-level command:
docker nosuchcommand foo
docker: 'nosuchcommand' is not a docker command.
See 'docker --help'
Unknown sub-command:
docker volume nosuchcommand foo
Usage: docker volume COMMAND
Manage volumes
Commands:
create Create a volume
inspect Display detailed information on one or more volumes
ls List volumes
prune Remove unused local volumes
rm Remove one or more volumes
update Update a volume (cluster volumes only)
Run 'docker volume COMMAND --help' for more information on a command.
After this patch:
Unknown top-level command:
docker nosuchcommand foo
docker: unknown command: docker nosuchcommand
Run 'docker --help' for more information
Unknown sub-command:
docker volume nosuchcommand foo
docker: unknown command: 'docker volume nosuchcommand'
Usage: docker volume COMMAND
Run 'docker volume --help' for more information
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-07-04 18:57:07 -04:00
|
|
|
return errors.Errorf(
|
|
|
|
"%[1]s: unknown command: %[2]s %[3]s\n\nUsage: %[4]s\n\nRun '%[2]s --help' for more information",
|
|
|
|
binName(cmd),
|
|
|
|
cmd.CommandPath(),
|
|
|
|
args[0],
|
|
|
|
cmd.UseLine(),
|
|
|
|
)
|
2016-05-16 17:20:29 -04:00
|
|
|
}
|
|
|
|
|
2017-03-09 13:23:45 -05:00
|
|
|
return errors.Errorf(
|
2024-07-04 20:49:31 -04:00
|
|
|
"%[1]s: '%[2]s' accepts no arguments\n\nUsage: %[3]s\n\nRun '%[2]s --help' for more information",
|
|
|
|
binName(cmd),
|
2016-05-16 17:20:29 -04:00
|
|
|
cmd.CommandPath(),
|
|
|
|
cmd.UseLine(),
|
|
|
|
)
|
|
|
|
}
|
2016-05-26 17:57:31 -04:00
|
|
|
|
|
|
|
// RequiresMinArgs returns an error if there is not at least min args
|
2024-08-26 07:55:28 -04:00
|
|
|
func RequiresMinArgs(minArgs int) cobra.PositionalArgs {
|
2016-05-26 17:57:31 -04:00
|
|
|
return func(cmd *cobra.Command, args []string) error {
|
2024-08-26 07:55:28 -04:00
|
|
|
if len(args) >= minArgs {
|
2016-05-26 17:57:31 -04:00
|
|
|
return nil
|
|
|
|
}
|
2017-03-09 13:23:45 -05:00
|
|
|
return errors.Errorf(
|
2024-07-04 20:49:31 -04:00
|
|
|
"%[1]s: '%[2]s' requires at least %[3]d %[4]s\n\nUsage: %[5]s\n\nSee '%[2]s --help' for more information",
|
|
|
|
binName(cmd),
|
2016-05-26 17:57:31 -04:00
|
|
|
cmd.CommandPath(),
|
2024-08-26 07:55:28 -04:00
|
|
|
minArgs,
|
|
|
|
pluralize("argument", minArgs),
|
2016-05-26 17:57:31 -04:00
|
|
|
cmd.UseLine(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2016-06-03 13:50:01 -04:00
|
|
|
|
2016-06-05 23:55:47 -04:00
|
|
|
// RequiresMaxArgs returns an error if there is not at most max args
|
2024-08-26 07:55:28 -04:00
|
|
|
func RequiresMaxArgs(maxArgs int) cobra.PositionalArgs {
|
2016-06-05 23:55:47 -04:00
|
|
|
return func(cmd *cobra.Command, args []string) error {
|
2024-08-26 07:55:28 -04:00
|
|
|
if len(args) <= maxArgs {
|
2016-06-05 23:55:47 -04:00
|
|
|
return nil
|
|
|
|
}
|
2017-03-09 13:23:45 -05:00
|
|
|
return errors.Errorf(
|
2024-07-04 20:49:31 -04:00
|
|
|
"%[1]s: '%[2]s' requires at most %[3]d %[4]s\n\nUsage: %[5]s\n\nSRun '%[2]s --help' for more information",
|
|
|
|
binName(cmd),
|
2016-06-05 23:55:47 -04:00
|
|
|
cmd.CommandPath(),
|
2024-08-26 07:55:28 -04:00
|
|
|
maxArgs,
|
|
|
|
pluralize("argument", maxArgs),
|
2016-06-05 23:55:47 -04:00
|
|
|
cmd.UseLine(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-06 10:57:44 -04:00
|
|
|
// RequiresRangeArgs returns an error if there is not at least min args and at most max args
|
2024-08-26 07:55:28 -04:00
|
|
|
func RequiresRangeArgs(minArgs int, maxArgs int) cobra.PositionalArgs {
|
2016-06-05 19:48:26 -04:00
|
|
|
return func(cmd *cobra.Command, args []string) error {
|
2024-08-26 07:55:28 -04:00
|
|
|
if len(args) >= minArgs && len(args) <= maxArgs {
|
2016-06-05 19:48:26 -04:00
|
|
|
return nil
|
|
|
|
}
|
2017-03-09 13:23:45 -05:00
|
|
|
return errors.Errorf(
|
2024-07-04 20:49:31 -04:00
|
|
|
"%[1]s: '%[2]s' requires at least %[3]d and at most %[4]d %[5]s\n\nUsage: %[6]s\n\nRun '%[2]s --help' for more information",
|
|
|
|
binName(cmd),
|
2016-06-05 19:48:26 -04:00
|
|
|
cmd.CommandPath(),
|
2024-08-26 07:55:28 -04:00
|
|
|
minArgs,
|
|
|
|
maxArgs,
|
|
|
|
pluralize("argument", maxArgs),
|
2016-06-05 19:48:26 -04:00
|
|
|
cmd.UseLine(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-03 13:50:01 -04:00
|
|
|
// ExactArgs returns an error if there is not the exact number of args
|
|
|
|
func ExactArgs(number int) cobra.PositionalArgs {
|
|
|
|
return func(cmd *cobra.Command, args []string) error {
|
|
|
|
if len(args) == number {
|
|
|
|
return nil
|
|
|
|
}
|
2017-03-09 13:23:45 -05:00
|
|
|
return errors.Errorf(
|
2024-07-04 20:49:31 -04:00
|
|
|
"%[1]s: '%[2]s' requires %[3]d %[4]s\n\nUsage: %[5]s\n\nRun '%[2]s --help' for more information",
|
|
|
|
binName(cmd),
|
2016-06-03 13:50:01 -04:00
|
|
|
cmd.CommandPath(),
|
|
|
|
number,
|
2017-08-12 12:25:38 -04:00
|
|
|
pluralize("argument", number),
|
2016-06-03 13:50:01 -04:00
|
|
|
cmd.UseLine(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2017-08-12 12:25:38 -04:00
|
|
|
|
cli: improve output and consistency for unknown (sub)commands
Before this patch, output for invalid top-level and sub-commands differed.
For top-level commands, the CLI would print an error-message and a suggestion
to use `--help`. For missing *subcommands*, we would hit a different code-path,
and different output, which includes full "usage" / "help" output.
While it is a common convention to show usage output, and may have been
a nice gesture when docker was still young and only had a few commands
and options ("you did something wrong; here's an overview of what you
can use"), that's no longer the case, and many commands have a _very_
long output.
The result of this is that the error message, which is the relevant
information in this case - "You mis-typed something" - is lost in the
output, and hard to find (sometimes even requiring scrolling back).
The output is also confusing, because it _looks_ like something ran
successfully (most of the output is not about the error!).
Even further; the suggested resolution (try `--help` to see the correct
options) is rather redundant, because running teh command with `--help`
produces _exactly_ the same output as was just showh, baring the error
message. As a fun fact, due to the usage output being printed, the
output even contains not one, but _two_ "call to actions";
- `See 'docker volume --help'.` (under the erro message)
- `Run 'docker volume COMMAND --help' for more information on a command.`
(under the usage output)
In short; the output is too verbose, confusing, and doesn't provide
a good UX. Let's reduce the output produced so that the focus is on the
important information.
This patch:
- Changes the usage to the short-usage.
- Changes the error-message to mention the _full_ command instead of only
the command after `docker` (so `docker no-such-command` instead of
`no-such-command`).
- Prefixes the error message with the binary / root-command name
(usually `docker:`); this is something we can still decide on, but
it's a pattern we already use in some places. The motivation for this
is that `docker` commands can often produce output that's a combination
of output from the CLI itself, output from the daemon, and even output
from the container. The `docker:` prefix helps to distinguish where
the message originated from (the `docker` CLI in this case).
- Adds an empty line between the error-message and the "call to action"
(`Run 'docker volume --help'...` in the example below). This helps
separating the error message ("unkown flag") from the call-to-action.
Before this patch:
Unknown top-level command:
docker nosuchcommand foo
docker: 'nosuchcommand' is not a docker command.
See 'docker --help'
Unknown sub-command:
docker volume nosuchcommand foo
Usage: docker volume COMMAND
Manage volumes
Commands:
create Create a volume
inspect Display detailed information on one or more volumes
ls List volumes
prune Remove unused local volumes
rm Remove one or more volumes
update Update a volume (cluster volumes only)
Run 'docker volume COMMAND --help' for more information on a command.
After this patch:
Unknown top-level command:
docker nosuchcommand foo
docker: unknown command: docker nosuchcommand
Run 'docker --help' for more information
Unknown sub-command:
docker volume nosuchcommand foo
docker: unknown command: 'docker volume nosuchcommand'
Usage: docker volume COMMAND
Run 'docker volume --help' for more information
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2024-07-04 18:57:07 -04:00
|
|
|
// binName returns the name of the binary / root command (usually 'docker').
|
|
|
|
func binName(cmd *cobra.Command) string {
|
|
|
|
return cmd.Root().Name()
|
|
|
|
}
|
|
|
|
|
2022-07-13 06:29:49 -04:00
|
|
|
//nolint:unparam
|
2017-08-12 12:25:38 -04:00
|
|
|
func pluralize(word string, number int) string {
|
|
|
|
if number == 1 {
|
|
|
|
return word
|
|
|
|
}
|
|
|
|
return word + "s"
|
|
|
|
}
|