mirror of
https://github.com/docker/cli.git
synced 2026-11-06 13:10:41 -04:00
fix: propagate leading --help to subcommands with a local help flag
- --help before a subcommand is parsed as the root's persistent flag and stripped from args, which works for most subcommands since they share the same flag object with root - run/create register their own local help flag (no -h, to avoid a shorthand clash with --hostname), which shadows root's and never saw the value, so the container ran instead of printing usage - propagate the parsed value into a subcommand's local help flag when one shadows the root's, in HandleGlobalFlags Fixes #7473 Signed-off-by: Nick Sieger <nick@nicksieger.com>
This commit is contained in:
+19
-1
@@ -160,7 +160,25 @@ func (tcmd *TopLevelCommand) HandleGlobalFlags() (*cobra.Command, []string, erro
|
||||
return nil, nil, cmd.FlagErrorFunc()(cmd, err)
|
||||
}
|
||||
|
||||
return cmd, flags.Args(), nil
|
||||
args := flags.Args()
|
||||
|
||||
// A leading "--help"/"-h" is consumed above as the root's persistent
|
||||
// help flag and stripped from args, so the target subcommand never sees
|
||||
// it. That's fine for most subcommands, which inherit the same help
|
||||
// flag instance, but "run" and "create" register their own local
|
||||
// "help" flag (to avoid a shorthand clash with "-h"/"--hostname"),
|
||||
// which shadows the persistent one and is left false. Propagate the
|
||||
// value so "docker --help run <image>" shows help like "docker run
|
||||
// --help" does, instead of running the container.
|
||||
if help, _ := flags.GetBool("help"); help {
|
||||
if sub, _, err := cmd.Find(args); err == nil && sub != cmd {
|
||||
if sub.Flags().Lookup("help") != nil {
|
||||
_ = sub.Flags().Set("help", "true")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cmd, args, nil
|
||||
}
|
||||
|
||||
// Initialize finalises global option parsing and initializes the docker client.
|
||||
|
||||
@@ -108,3 +108,45 @@ func TestDecoratedName(t *testing.T) {
|
||||
topLevelCommand.Annotations = map[string]string{metadata.CommandAnnotationPlugin: "true"}
|
||||
assert.Equal(t, decoratedName(topLevelCommand), "pluginTopLevelCommand*")
|
||||
}
|
||||
|
||||
// TestHandleGlobalFlagsHelpPropagation covers https://github.com/docker/cli/issues/7473:
|
||||
// a leading "--help" is consumed as the root's persistent help flag and
|
||||
// stripped from args, so a subcommand that registers its own local "help"
|
||||
// flag (as "run" and "create" do, to avoid a shorthand clash with "-h") never
|
||||
// sees it and runs instead of showing help.
|
||||
func TestHandleGlobalFlagsHelpPropagation(t *testing.T) {
|
||||
newRootCmd := func() (root, sub *cobra.Command) {
|
||||
root = &cobra.Command{Use: "docker"}
|
||||
root.PersistentFlags().BoolP("help", "h", false, "Print usage")
|
||||
|
||||
sub = &cobra.Command{Use: "run", RunE: func(cmd *cobra.Command, args []string) error { return nil }}
|
||||
sub.Flags().Bool("help", false, "Print usage")
|
||||
root.AddCommand(sub)
|
||||
return root, sub
|
||||
}
|
||||
|
||||
t.Run("propagates to the shadowing local help flag", func(t *testing.T) {
|
||||
root, sub := newRootCmd()
|
||||
tcmd := &TopLevelCommand{cmd: root, args: []string{"--help", "run", "hello-world"}}
|
||||
|
||||
_, args, err := tcmd.HandleGlobalFlags()
|
||||
assert.NilError(t, err)
|
||||
assert.DeepEqual(t, args, []string{"run", "hello-world"})
|
||||
|
||||
help, err := sub.Flags().GetBool("help")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, help, true)
|
||||
})
|
||||
|
||||
t.Run("leaves the local help flag alone without --help", func(t *testing.T) {
|
||||
root, sub := newRootCmd()
|
||||
tcmd := &TopLevelCommand{cmd: root, args: []string{"run", "hello-world"}}
|
||||
|
||||
_, _, err := tcmd.HandleGlobalFlags()
|
||||
assert.NilError(t, err)
|
||||
|
||||
help, err := sub.Flags().GetBool("help")
|
||||
assert.NilError(t, err)
|
||||
assert.Equal(t, help, false)
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user