Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Cobra completions for run and create #5580

Merged
merged 15 commits into from
Nov 14, 2024

Conversation

albers
Copy link
Collaborator

@albers albers commented Oct 28, 2024

- What I did
Added Cobra completion for all options of docker run and docker create.

- How I did it
Provided FlagCompletionFuncs for all options except for

  • boolean flags (default completion works)
  • flags with file completions (default completion works)
  • flags that already had a completion function.

The reference for my work was the legacy bash completion.
Features that I did not recreate:

  • completion for --user
  • completion for the individual log driver options
  • completion of non-built-in drivers

- How to verify it

  1. In a dev container, issue make completion, then try completions of various docker run and docker create commands.
  2. In another terminal, exec into the container, install the legacy completion with . ./contrib/completion/bash/docker. then execute the same completions for reference.

- Description for the changelog
Improved completions for docker run and docker create.

Ported some completions from the bash completion to the new cobra based completion.

@albers albers marked this pull request as ready for review October 28, 2024 22:55
@albers albers requested a review from thaJeztah October 28, 2024 22:55
@codecov-commenter
Copy link

codecov-commenter commented Oct 28, 2024

Codecov Report

Attention: Patch coverage is 43.50282% with 100 lines in your changes missing coverage. Please review.

Project coverage is 59.50%. Comparing base (61baf2a) to head (06260e6).
Report is 82 commits behind head on master.

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #5580      +/-   ##
==========================================
+ Coverage   59.21%   59.50%   +0.28%     
==========================================
  Files         342      346       +4     
  Lines       29083    29365     +282     
==========================================
+ Hits        17222    17474     +252     
- Misses      10889    10916      +27     
- Partials      972      975       +3     

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is amazing work, thank you!! I left quite some comments, but welcome your feedback/thoughts.

I think the most notable one that could reduce a lot of boiler plating, is the comment on (perhaps could be a utility) setting a "default" NoComplete on all flags; that way we wouldn't have to set the NoComplete for all flags that don't have one. (Although maybe there's something to be said for explicitly setting "no completions here)

@@ -57,6 +57,7 @@ var restartPolicies = []string{

// addCompletions adds the completions that `run` and `create` have in common.
func addCompletions(cmd *cobra.Command, dockerCli command.Cli) {
_ = cmd.RegisterFlagCompletionFunc("add-host", completion.NoComplete)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ugh, yes, this is something I was looking at as well. I was considering if we could set a default, then optionally override, but Cobra currently doesn't allow for a completion to be replaced once set;

if _, exists := flagCompletionFunctions[flag]; exists {
return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' already registered", flagName)
}
flagCompletionFunctions[flag] = f

In that case, if the flag has no completion, it returns here;

if !finalCmd.DisableFlagParsing {
// If DisableFlagParsing==false, we have completed the flags as known by Cobra;
// we can return what we found.
// If DisableFlagParsing==true, Cobra may not be aware of all flags, so we
// let the logic continue to see if ValidArgsFunction needs to be called.
return finalCmd, completions, directive, nil
}

Which returns with ShellCompDirectiveNoFileComp

directive = ShellCompDirectiveNoFileComp

I think ideally Cobra would allow setting a custom default for flags, but perhaps a somewhat dirty, but functional one would be to do it ourselves, and set a default at the very end of functions that constructed a command, e.g. at;

flags.VisitAll(func(flag *pflag.Flag) {
	// Set a default completion function if none was set. We don't look
	// up if it does already have one set, because Cobra does this for
	// us, and returns an error (which we ignore for this reason).
	_ = cmd.RegisterFlagCompletionFunc(flag.Name, completion.NoComplete)
})

return cmd

I was a bit on the fence on using VisitAll, but it looks like there's many places where Cobra already does, so maybe not too bad, WDYT?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not find a way to set a default ShellCompDirective for silencing the numerouns flags with inapproptiate file completions.

IMHO this should be handled in Cobra. I'll open a feature request for this.

Your workaround looks very promising, I'll give it a try.

cli/command/container/completion.go Outdated Show resolved Hide resolved
@@ -59,6 +59,7 @@ var restartPolicies = []string{
func addCompletions(cmd *cobra.Command, dockerCli command.Cli) {
_ = cmd.RegisterFlagCompletionFunc("add-host", completion.NoComplete)
_ = cmd.RegisterFlagCompletionFunc("annotation", completion.NoComplete)
_ = cmd.RegisterFlagCompletionFunc("attach", completion.FromList("stderr", "stdin", "stdout"))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh; I was working on this one, when I discovered completion doesn't work for specifying --attach multiple times.

I started looking "why???" because it does work for some other flags we define multiple times, which is when I discovered the reason; Cobra has some "magic" values for flag "types"; if the flag-type ends with Slice or Array it considers that it can be used multiple times. If not, it considers that the flag can only be set once and it removes it from the suggestions;

if !flag.Changed ||
strings.Contains(flag.Value.Type(), "Slice") ||
strings.Contains(flag.Value.Type(), "Array") {
// If the flag is not already present, or if it can be specified multiple times (Array or Slice)
// we suggest it as a completion
completions = append(completions, getFlagNameCompletions(flag, toComplete)...)
}

In our case, some types use list as name, which isn't matched. I'm considering contributing a patch to Cobra to make it somewhat smarter;

  • make the matching case-insensitive
  • possibly add list to the magic strings

But also to have it match SliceValue, which is an interface defined by the pflag module used by Cobra for flags. I think it's an omission that it doesn't consider that interface;

type SliceValue interface {
// Append adds the specified value to the end of the flag value list.
Append(string) error
// Replace will fully overwrite any data currently in the flag value list.
Replace([]string) error
// GetSlice returns the flag value list as an array of strings.
GetSlice() []string
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, now I understand.
I was also wondering why the events --filter completion only works once.

cli/command/container/completion.go Outdated Show resolved Hide resolved
Comment on lines 82 to 84
_ = cmd.RegisterFlagCompletionFunc("dns", completion.NoComplete)
_ = cmd.RegisterFlagCompletionFunc("dns-option", completion.NoComplete)
_ = cmd.RegisterFlagCompletionFunc("dns-search", completion.NoComplete)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly "note to self" - I think it's fine to disable completion for these for now.

  • For dns-option we could at some point consider adding the list from https://man7.org/linux/man-pages/man5/resolv.conf.5.html
  • For dns-search I wonder how much sense it would make to complete based on the current host's settings. That would not make sense for a remote daemon, but wondering if it's still useful for situations where the daemon runs locally.
  • (same for dns)

cli/command/container/completion.go Outdated Show resolved Hide resolved
cli/command/container/completion.go Outdated Show resolved Hide resolved
@@ -138,6 +138,11 @@ func addCompletions(cmd *cobra.Command, dockerCli command.Cli) {
_ = cmd.RegisterFlagCompletionFunc("workdir", completion.NoComplete)
}

// completeDetachKeys implements shell completion for the `--detach-keys` option of `run` and `create`.
func completeDetachKeys(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{"ctrl-"}, cobra.ShellCompDirectiveNoSpace
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to self; I need to lookup if there's other special ones we define (meta-, alt-, possibly others)

cli/command/container/completion_test.go Show resolved Hide resolved
cli/command/container/completion.go Outdated Show resolved Hide resolved
@thaJeztah thaJeztah added this to the 28.0.0 milestone Oct 29, 2024
@albers albers changed the title Improve cobra completions for run and create Improve Cobra completions for run and create Oct 30, 2024
@albers
Copy link
Collaborator Author

albers commented Oct 30, 2024

@thaJeztah Thanks very much for your mentoring. It's very much appreciated.

I think I'm through with your review comments, please take another look.

@albers albers requested a review from thaJeztah October 30, 2024 20:23
Copy link
Member

@Benehiko Benehiko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@albers
Copy link
Collaborator Author

albers commented Nov 6, 2024

Let me know when I can squash the commits into a more relevant set of commits.

@laurazard
Copy link
Member

laurazard commented Nov 7, 2024

Thank you so much for this @albers!

I'm thinking we could squash 4378ab4 (#5580) together with all the "disable file completions for X" commit, and also take care of 33d3246 (#5580) – and whatever else there is – but overall looks good!

Signed-off-by: Harald Albers <[email protected]>
Signed-off-by: Harald Albers <[email protected]>
Signed-off-by: Harald Albers <[email protected]>
Signed-off-by: Harald Albers <[email protected]>
Credits to thaJeztah

Signed-off-by: Harald Albers <[email protected]>
@albers
Copy link
Collaborator Author

albers commented Nov 8, 2024

@laurazard The commits are now squashed to a sequence of meaningful, self-contained commits.

@thaJeztah would you like to take another look at this PR?

Copy link
Member

@laurazard laurazard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-LGTM. Thanks!

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Sorry thought I already did 🙈 🙈 🙈

@thaJeztah thaJeztah merged commit 4adbb18 into docker:master Nov 14, 2024
89 checks passed
@albers albers deleted the container-completions branch November 14, 2024 20:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants