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

Fix shell completion by adding missing usage message required by spf13/cobra #1688

Merged
merged 1 commit into from
Mar 22, 2023
Merged

Fix shell completion by adding missing usage message required by spf13/cobra #1688

merged 1 commit into from
Mar 22, 2023

Conversation

DanHam
Copy link
Contributor

@DanHam DanHam commented Mar 22, 2023

Attempts a fix for #962.

All credit to @cschug - see this comment

As pointed out in that comment, the spf13/cobra library writes out the shell completion script using a fmt.Sprintf here.

The %[1]s in that string seem to be replaced with the first command in the Use: field of the cobra.Command struct. Currently this field is not set so the %[1]s expand to an empty string resulting in a broken shell completion script.

Adding the field (following the same method used by grype - here) gives the spf13/cobra library the info it needs to correctly generate the shell completion script.

Testing locally seems to show everything is working OK. The shell completion now works as expected after running source <(syft completion bash)

Fixes: #962

@kzantow
Copy link
Contributor

kzantow commented Mar 22, 2023

I'm having a bit of a hard time getting this working. If I try this on my mac with zsh, it simply seems to do nothing. If I try this in a bash container (golang:latest), when I type syft p<tab>, I get this:

bash: _get_comp_words_by_ref: command not found

Is there a particular container I could use to see this work properly?

Also: you will need to sign-off your commits.

@@ -55,6 +55,7 @@ func New() (*cobra.Command, error) {

// rootCmd is currently an alias for the packages command
rootCmd := &cobra.Command{
Use: fmt.Sprintf("%s package [SOURCE]", internal.ApplicationName),
Copy link
Contributor

Choose a reason for hiding this comment

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

package is not a valid command, and this is the root command, so this should probably be:

fmt.Sprintf("%s [SOURCE]", internal.ApplicationName)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That should have been fmt.Sprintf("%s packages [SOURCE]", ...

Copy link
Contributor

Choose a reason for hiding this comment

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

Even so this is the root command, not the packages command (even though they are duplicates), so it should display usage text as the root command.

Copy link
Contributor Author

@DanHam DanHam Mar 22, 2023

Choose a reason for hiding this comment

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

Using fmt.Sprintf("%s packages [SOURCE]",... causes the following to be output for the 'Usage'

$ syft
Generate a packaged-based Software Bill Of Materials (SBOM) from container images and filesystems

Usage:
  syft packages [SOURCE] [flags]
  syft [command]

Examples:
  syft packages alpin...

Happy to change that to what ever you prefer. The important bit (for our fix) is that we have syft at the start of the command. That is what the spf13/cobra library needs to generate a working shell completion script (for bash at least!!)

Copy link
Contributor

@kzantow kzantow Mar 22, 2023

Choose a reason for hiding this comment

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

Right, but since a user can run syft without the packages command, I think we should leave this as "%s [SOURCE]", this leaves syft as the first part of the usage, which is what you need.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hmmm... So the [SOURCE] bit of that usage command is only really relevant when running syft packages ...

$ syft packages
Generate a packaged-based Software Bill Of Materials (SBOM) from container images and filesystems

Usage:
  syft packages [SOURCE] [flags]

Perhaps we should just have fmt.Sprintf("%s [command] [flags]", ... as the usage for the main syft command?

This would just give:

$ syft
Generate a packaged-based Software Bill Of Materials (SBOM) from container images and filesystems

Usage:
  syft [command] [flags]
  syft [command]

Examples:
  syft packages alpine:latest                                a summary of discovered packages
  syft packages alpine:la...

...which seems to make a bit more sense. Are you OK with that?

Copy link
Contributor

Choose a reason for hiding this comment

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

There's a bit of a mismatch between the help text and the command itself (this should be fixed, but I would not ask you to change this as part of this PR).

You can run Syft like:

syft alpine:latest

without the packages command keyword, because the root command duplicates the functionality of the packages command.

For this reason, I would prefer that the root help text indicates how to run the root command, rather than indicating packages or some sub-command is necessary. It's a very small detail.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ahh... OK. That makes sense now. The penny finally dropped!! 😄

@DanHam
Copy link
Contributor Author

DanHam commented Mar 22, 2023

Hi Keith

I'm having a bit of a hard time getting this working. If I try this on my mac with zsh...

The original issue (#962) was with shell completion for bash. I don't use zsh so I've never tried the completions that are generated for that particular shell...

EDIT: FWIW I've just tried to get the completions working for zsh and haven't had much luck either (note that I'm a complete noob with zsh). Perhaps the zsh completions need fixing as well?

Is there a particular container I could use to see this work properly?

I can see this working using the golang:latest image. Note: my arch is amd64:

  1. Start: docker run -it -v ${PWD}:/usr/src/syft -w /usr/src/syft --name syft-build golang:latest /bin/bash
  2. Fix dubious ownership warning: git config --global --add safe.directory /usr/src/syft
  3. Install build deps: make bootstrap
  4. Build: make build
  5. Install syft for my os/arch: install snapshot/linux-build_linux_amd64_v1/syft /usr/local/bin
  6. Verify: syft --version # Outputs: syft 0.0.0-SNAPSHOT-4867698
  7. The golang:latest image does not have the bash-completion package installed so lets add that: apt update && apt install bash-completion
  8. Source the main bash-completion script: source /usr/share/bash-completion/bash_completion
  9. Typing ls --<tab><tab> should now show completions e.g. --all etc
  10. Source the syft completions script: source <(syft completion bash)
  11. Typing syft <tab><tab> should now show completions

@DanHam
Copy link
Contributor Author

DanHam commented Mar 22, 2023

Also: you will need to sign-off your commits.

Shall I fix that typo (package -> packages) and force push with a signed commit?

@kzantow
Copy link
Contributor

kzantow commented Mar 22, 2023

Shall I fix that typo (package -> packages) and force push with a signed commit?

Again, I think this should be removed. And force-push a signed-off commit, yes 👍

@kzantow
Copy link
Contributor

kzantow commented Mar 22, 2023

Ahh, this was the magic sauce:

apt update && apt install bash-completion
source /usr/share/bash-completion/bash_completion
install snapshot/linux-build_linux_amd64_v1/syft /usr/local/bin

I think we can move forward after the aforementioned changes are done, thanks @DanHam

@DanHam
Copy link
Contributor Author

DanHam commented Mar 22, 2023

Thanks Keith,

I've now pushed up a signed commit with changes.

I've set the usage message to fmt.Sprintf('%s [command] [flags]",...) as this seems to make the most sense for the main syft command (see my comment above - #1688 (comment))

Please let me know if you require any additional changes.

@kzantow
Copy link
Contributor

kzantow commented Mar 22, 2023

@DanHam I hope this explanation makes sense why I still feel this should be "%s [SOURCE]": #1688 (comment)

…3/cobra

The spf13/cobra library requires and uses the first command given in the
usage to correctly generate the shell completion script.

Signed-off-by: DanHam <DanHam@users.noreply.github.com>
Copy link
Contributor

@kzantow kzantow left a comment

Choose a reason for hiding this comment

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

👍 thanks for sticking through this, @DanHam !

@DanHam
Copy link
Contributor Author

DanHam commented Mar 22, 2023

I hope this explanation makes sense...

It does! Thank you for your patience!!!

Hopefully, that last push should be OK.

@kzantow
Copy link
Contributor

kzantow commented Mar 22, 2023

FYI -- I tested this out with bash based on the instructions @DanHam provided:

$ syft <tab>
attest      (Generate an SBOM as an attestation for the given [SOURCE] container image)
completion  (Generate the autocompletion script for the specified shell)
convert     (Convert between SBOM formats)
help        (Help about any command)
login       (Log in to a registry)
packages    (Generate a package SBOM)
version     (show the version)
$ syft completion <tab>
bash        (Generate the autocompletion script for bash)
fish        (Generate the autocompletion script for fish)
powershell  (Generate the autocompletion script for powershell)
zsh         (Generate the autocompletion script for zsh)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Broken shell completion - Bash
2 participants