-
Notifications
You must be signed in to change notification settings - Fork 1
Commands
This page first introduces you to declaring commands, either trees or groups of them.
We start with the following struct:
type Root struct {}
This struct will be root parser (or cobra.Command) of our application.
The name of the cobra command will thus be the os.Args[0]
value.
It has no execution method by default.
If we generate our CLI from this command, we get an empty -but working-
cobra command, which we would use and run like this in a main
package:
rootCmd := flags.Generate(&Root{})
rootCmd.Execute()
We now declare another command type, which now includes an execution method. This subcommand does not do anything, as its runner is empty.
type Create struct {}
// Execute - The actual command implementation. The `args` parameter is not the args
// of the command itself, but any aditional arguments that `flags` will not have
// parsed into the command positional arguments' struct fields.
func (c *Create) Execute(args []string) (err error) {
return
}
We then add this subcommand to our root:
type Root struct {
Create `command:"create" desc:"a subcommand that creates something"`
}
Note: See the section Runners Interfaces for all command interfaces that are automatically searched for and mapped to the generated cobra.Command.
Forwarding a little, now you have various command structs ready. You can either
add them below the Create
command, or you can group them together into an another
struct, which will act as a group holder for these commands. This enables better
help usage formatting, as well as completions formats for shell supporting it.
A group struct is actually identical to a root command: it's just a struct.
// Comms is a group of commands for communicating things.
type Comms struct {
Dial `command:"dial" desc:"this command dials a host"`
Send `command:"create" desc:"a subcommand that sends data"`
Recv `command:"receive" desc:"this command receives data"`
}
We would then include it in our root parser like this:
type Root struct {
Create `command:"create" alias:"make" desc:"a subcommand that creates something"`
Comms `commands:"comms"`
}
Compile this program and run it with --help
, and you will see a structured ouput.
An invocation of our program would look like:
./program --help
./program create ...
./program dial ...
The following functions can be implemented by your struct type and will be automatically bound to the generated cobra.Command:
func PreRun(args []string)
func PreRunE(args []string) error
// Runners
func Execute(args []string) (err error) // This has precedence
func RunE(args []string) error // over this one.
func Run(args []string) // Cobra only executes this is both previous are nil
// Post runners
func PostRun(args []string)
func PostRunE(args []string) error
If we stayed only with native Go code to declare the commands' functionality and specify various behavior stuff, we would be quite stuck. Additionally, there would be no way for a child command to know about the state of any parent.
In order to solve this, you can still access the cobra API. And we might have multiple reasons to do so.
As said in the introduction page, declaring specs through tags has its limits.
We might want to assing longer help strings, PreRun
and PostRun
functions of all
sorts, etc.
The following briefly shows how we would do this for our program dial
subcommand:
// Our newly generated cobra tree.
rootCmd := flags.Generate(&Root{})
// Get the dial subcommand
dial, _, _ := rootCmd.Find([]string{"dial"})
// And specify/bind additional behavior.
dial.Long = `a very long and multiline string...`
dial.PreRunE = func(cmd *cobra.Command, args []string) error { return nil }
When executing our command, and from within our Execute(args []string)
method,
we might need to query something in the cobra application. (Note, though, that
one of the interests of this library is actually to use flags/args native types
as is, not through the cobra API).
Now familiar with ways to avoid circular dependencies, you might declare your root command in a common package, and you would assign the generated command tree to it:
package common
var CLI *cobra.Command
...
package main
func main() {
common.CLI = flags.Generate(&Root{}) // Root is the struct of the beginning.
}
Then you can freely query your cobra CLI from within the Dial
execution method, had you written one:
type Dial struct {}
func (c *Dial) Execute(args []string) (err error) {
cmdOfInterest, _, _ := common.CLI.Find([]string{"send"})
// ... do stuff here
return
}
You might have understood, at this point, that you don't even have to write such an Execute
method
if you dont want to, and you just declare this naked struct, while binding a Run(cmd *cobra.Command)
runner to it via the generated cobra command.
But this would have no interest if your command would declare positional arguments.