@@ -163,8 +163,11 @@ func newDockerCommand(dockerCli *command.DockerCli) *cli.TopLevelCommand {
163163 cmd .SetOut (dockerCli .Out ())
164164 commands .AddCommands (cmd , dockerCli )
165165
166- cli .DisableFlagsInUseLine (cmd )
167- setValidateArgs (dockerCli , cmd )
166+ visitAll (cmd ,
167+ setValidateArgs (dockerCli ),
168+ // prevent adding "[flags]" to the end of the usage line.
169+ func (c * cobra.Command ) { c .DisableFlagsInUseLine = true },
170+ )
168171
169172 // flags must be the top-level command flags, not cmd.Flags()
170173 return cli .NewTopLevelCommand (cmd , dockerCli , opts , cmd .Flags ())
@@ -265,14 +268,29 @@ func setHelpFunc(dockerCli command.Cli, cmd *cobra.Command) {
265268 })
266269}
267270
268- func setValidateArgs (dockerCli command.Cli , cmd * cobra.Command ) {
269- // The Args is handled by ValidateArgs in cobra, which does not allows a pre-hook.
270- // As a result, here we replace the existing Args validation func to a wrapper,
271- // where the wrapper will check to see if the feature is supported or not.
272- // The Args validation error will only be returned if the feature is supported.
273- cli .VisitAll (cmd , func (ccmd * cobra.Command ) {
271+ // visitAll traverses all commands from the root.
272+ func visitAll (root * cobra.Command , fns ... func (* cobra.Command )) {
273+ for _ , cmd := range root .Commands () {
274+ visitAll (cmd , fns ... )
275+ }
276+ for _ , fn := range fns {
277+ fn (root )
278+ }
279+ }
280+
281+ // The Args is handled by ValidateArgs in cobra, which does not allows a pre-hook.
282+ // As a result, here we replace the existing Args validation func to a wrapper,
283+ // where the wrapper will check to see if the feature is supported or not.
284+ // The Args validation error will only be returned if the feature is supported.
285+ func setValidateArgs (dockerCLI versionDetails ) func (* cobra.Command ) {
286+ return func (ccmd * cobra.Command ) {
274287 // if there is no tags for a command or any of its parent,
275288 // there is no need to wrap the Args validation.
289+ //
290+ // FIXME(thaJeztah): can we memoize properties of the parent?
291+ // visitAll traverses root -> all childcommands, and hasTags
292+ // goes the reverse (cmd -> visit all parents), so we may
293+ // end traversing two directions.
276294 if ! hasTags (ccmd ) {
277295 return
278296 }
@@ -283,12 +301,12 @@ func setValidateArgs(dockerCli command.Cli, cmd *cobra.Command) {
283301
284302 cmdArgs := ccmd .Args
285303 ccmd .Args = func (cmd * cobra.Command , args []string ) error {
286- if err := isSupported (cmd , dockerCli ); err != nil {
304+ if err := isSupported (cmd , dockerCLI ); err != nil {
287305 return err
288306 }
289307 return cmdArgs (cmd , args )
290308 }
291- })
309+ }
292310}
293311
294312func tryPluginRun (ctx context.Context , dockerCli command.Cli , cmd * cobra.Command , subcommand string , envs []string ) error {
@@ -321,19 +339,19 @@ func tryPluginRun(ctx context.Context, dockerCli command.Cli, cmd *cobra.Command
321339 // signals to the subprocess because the shared
322340 // pgid makes the TTY a controlling terminal.
323341 //
324- // The plugin should have it's own copy of this
342+ // The plugin should have its own copy of this
325343 // termination logic, and exit after 3 retries
326- // on it's own.
344+ // on its own.
327345 if dockerCli .Out ().IsTerminal () {
328346 return
329347 }
330348
331- // Terminate the plugin server, which will
332- // close all connections with plugin
333- // subprocesses, and signal them to exit.
349+ // Terminate the plugin server, which closes
350+ // all connections with plugin subprocesses,
351+ // and signal them to exit.
334352 //
335- // Repeated invocations will result in EINVAL,
336- // or EBADF; but that is fine for our purposes.
353+ // Repeated invocations result in EINVAL or EBADF ,
354+ // but that is fine for our purposes.
337355 if srv != nil {
338356 _ = srv .Close ()
339357 }
@@ -351,15 +369,15 @@ func tryPluginRun(ctx context.Context, dockerCli command.Cli, cmd *cobra.Command
351369
352370 go func () {
353371 retries := 0
354- force := false
355372 // catch the first signal through context cancellation
356373 <- ctx .Done ()
357- tryTerminatePlugin (force )
374+ tryTerminatePlugin (false )
358375
359376 // register subsequent signals
360377 signals := make (chan os.Signal , exitLimit )
361378 signal .Notify (signals , platformsignals .TerminationSignals ... )
362379
380+ force := false
363381 for range signals {
364382 retries ++
365383 // If we're still running after 3 interruptions
@@ -440,7 +458,7 @@ func runDocker(ctx context.Context, dockerCli *command.DockerCli) error {
440458 }
441459 }()
442460 } else {
443- fmt .Fprint (dockerCli .Err (), "Warning: Unexpected OTEL error, metrics may not be flushed" )
461+ _ , _ = fmt .Fprint (dockerCli .Err (), "Warning: Unexpected OTEL error, metrics may not be flushed" )
444462 }
445463
446464 dockerCli .InstrumentCobraCommands (ctx , cmd )
@@ -451,12 +469,11 @@ func runDocker(ctx context.Context, dockerCli *command.DockerCli) error {
451469 return err
452470 }
453471
454- if cli . HasCompletionArg (args ) {
472+ if hasCompletionArg (args ) {
455473 // We add plugin command stubs early only for completion. We don't
456474 // want to add them for normal command execution as it would cause
457475 // a significant performance hit.
458- err = pluginmanager .AddPluginCommandStubs (dockerCli , cmd )
459- if err != nil {
476+ if err := pluginmanager .AddPluginCommandStubs (dockerCli , cmd ); err != nil {
460477 return err
461478 }
462479 }
@@ -504,6 +521,16 @@ func runDocker(ctx context.Context, dockerCli *command.DockerCli) error {
504521 return err
505522}
506523
524+ // hasCompletionArg returns true if a cobra completion arg request is found.
525+ func hasCompletionArg (args []string ) bool {
526+ for _ , arg := range args {
527+ if arg == cobra .ShellCompRequestCmd || arg == cobra .ShellCompNoDescRequestCmd {
528+ return true
529+ }
530+ }
531+ return false
532+ }
533+
507534type versionDetails interface {
508535 CurrentVersion () string
509536 ServerInfo () command.ServerInfo
0 commit comments