spf13 / cobra

A Commander for modern Go CLI interactions
https://cobra.dev
Apache License 2.0
38.41k stars 2.86k forks source link

Setting "Use" field in Command creation and then calling "Help" on Command object doesn't use the "Use" value #2166

Closed davidmichaelkarr closed 4 months ago

davidmichaelkarr commented 4 months ago

I'm using Cobra for a CLI app, but I'm doing some nonstandard things with it that make perfect sense for what I'm doing. Specifically, the command line for the application has the application name, and then a single parameter value is required before the subcommand name. This isn't an "option" with a dashed character marker, but simply a value that is used by all of the subcommands. That value is read in "root.go" and stored in a variable.

Functionally, this all works fine. However, I wanted to get the help info working properly, and I'm finding that even though I've changed the "Use" value in the Command creation, the manual call to "Help()" that I'm making is still showing the default value.

This code excerpt shows what I'm doing:

var rootCmd = &cobra.Command{
    Use:   "idpk <contextName> [subcommand]",
    Short: "Facade for IDP cluster diagnostics",
    Long:  `This tool provides a number of IDP-specific diagnostics for a Kubernetes cluster.`,
}

func Execute() {
    if len(os.Args) < 2 {
        fmt.Println("Please provide a context name.")
        fmt.Println()
        fmt.Printf("use[%s]\n", rootCmd.Use)
        rootCmd.Help()
        os.Exit(0)
    }

This is what I see in the output:

Please provide a context name.

use[idpk <contextName> [subcommand]]
This tool provides a number of IDP-specific diagnostics for a Kubernetes cluster.

Usage:
  idpk [command]

Available Commands:
    ....

Use "idpk [command] --help" for more information about a command.

This shows that by the time I call "rootCmd.Help()", the value of "Use" has been changed, but it's not being used in the "Help()" output.

I tried stepping through the code, but I'm not sure what I should be looking for.

marckhouzam commented 4 months ago

You can look at UsageTemplate(). Try adding a Run() or RunE() function to your command; I expect this will change the behaviour you are seeing.

davidmichaelkarr commented 4 months ago

I've been distracted with other issues until now, and I'm trying to get back to this now.

Looking at "UsageTemplate()", I see that it references "{{.UseLine}}". It's not clear what that is. The only property with a similar name is "Use". I tried stepping through this code, and it went into many twisty passages.

This is in the "root" command, which doesn't have a Run function. I can't imagine how that could change how the usage string is generated.

davidmichaelkarr commented 4 months ago

Actually, I do see now that in the template, whether a "Run" method exists makes the template vary its output, so I tried adding an empty "Run" method in the root command. This sort of helps, but it's still broken. Now I get this:

  idpk <contextName> [subcommand]
  idpk [command]

Available Commands:

So it provides both the "Use" value that I set, and also the default value.

This seems so hard to do, I'm considering just giving up and changing the structure so I simply expect that first parameter in all subcommands, instead of expecting it before the subcommand.

davidmichaelkarr commented 4 months ago

Problem solved. I gave up on this custom approach. Help info now works fine.

marckhouzam commented 4 months ago

Go templating can be pretty hard to read. For tanzu plugins we’ve switched to using plain go code to generate the help. We do this by having a super simple template {{ printHelp . }} and have all the help printing logic written in plain Go within printHelp()

You can see the starting point here: https://github.com/vmware-tanzu/tanzu-plugin-runtime/blob/6196605b3672069ba2600c443aee4c0d92db9782/plugin/usage.go#L54