why does the following CLI program using the cobra package throw a stack overflow error when ran with go run /tmp/test.go branch leaf, but does not error when when leaf subcommand is directly connected to root(as commented in the main function)?
This suggests that I'm not using the cobra PersistenRun* functions properly. My understanding of the PersistenRun* functions is that they only apply to command's children. The issue seems that a command's parent has been somehow set to the command itself.
package main
import (
"fmt"
"os"
"path"
"github.com/spf13/cobra"
)
var programName = path.Base(os.Args[0])
var rootCmd = &cobra.Command{
Use: programName,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
fmt.Println("in root pre run")
},
}
var branchCmd = &cobra.Command{
Use: "branch",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
cmd.Parent().PersistentPreRun(cmd, args)
fmt.Println("in branch pre run")
},
}
var leafCmd = &cobra.Command{
Use: "leaf",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
cmd.Parent().PersistentPreRun(cmd, args)
fmt.Println("in leaf pre run")
},
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("in leaf run")
},
}
func main() {
branchCmd.AddCommand(leafCmd)
rootCmd.AddCommand(branchCmd)
rootCmd.Execute()
// If I connect the root to the leaf directly, like the following, then
// the program no longer stack overflow
// rootCmd.AddCommand(leafCmd)
// rootCmd.Execute()
}
NVM, I figured it out.
Intead of
PersistentPreRun: func(cmd *cobra.Command, args []string) {
cmd.Parent().PersistentPreRun(cmd, args)
fmt.Println("in * pre run")
},
It should be:
PersistentPreRun: func(cmd *cobra.Command, args []string) {
cmd.Parent().PersistentPreRun(cmd.Parent(), args)
fmt.Println("in * pre run")
},
Related
I'm moving my cobra command flags inside a function so I can use it in other commands. I can able to see the commands but when I type the flage it always returns false.
Following is my code:
func NewCommand(ctx context.Context) *cobra.Command {
var opts ListOptions
cmd := &cobra.Command{
Use: "list",
Short: "List",
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Println(args) // []
opts.refs = args
return List(ctx, gh, opts, os.Stdout)
},
}
cmd = GetCommandFlags(cmd, opts)
return cmd
}
// GetListCommandFlags for list
func GetCommandFlags(cmd *cobra.Command, opts ListOptions) *cobra.Command {
flags := cmd.Flags()
flags.BoolVar(&opts.IgnoreLatest, "ignore-latest", false, "Do not display latest")
flags.BoolVar(&opts.IgnoreOld, "ignore-old", false, "Do not display old data")
return cmd
}
So when I type the following command
data-check list --ignore-latest
The the flag value of --ignore-latest should be true but I get false as a value in RunE args. Am I missing something here?
GetCommandFlags is something I want to use it in other commands I don't want to repeat the same flags.
You should use func GetCommandFlags(cmd *cobra.Command, opts *ListOptions) and call the func like cmd = GetCommandFlags(cmd, &opts).
You can print opts.IgnoreLatest and opts.IgnoreOld to see the changed value.
Works fine for me. Hope it will work for you too.
func NewCommand(ctx context.Context) *cobra.Command {
var opts ListOptions
cmd := &cobra.Command{
Use: "list",
Short: "List",
RunE: func(cmd *cobra.Command, args []string) error {
// fmt.Println(args) // []
fmt.Println(opts.IgnoreLatest, ", ", opts.IgnoreOld)
opts.refs = args
return List(ctx, gh, opts, os.Stdout)
},
}
cmd = GetCommandFlags(cmd, &opts)
return cmd
}
// GetListCommandFlags for list
func GetCommandFlags(cmd *cobra.Command, opts *ListOptions) *cobra.Command {
flags := cmd.Flags()
flags.BoolVar(&opts.IgnoreLatest, "ignore-latest", false, "Do not display latest")
flags.BoolVar(&opts.IgnoreOld, "ignore-old", false, "Do not display old data")
return cmd
}
You are passing opts to GetCommandFlags by value. You should pass a pointer to it, so the addresses registered for the flags use the opts declared in the calling function.
func GetCommandFlags(cmd *cobra.Command, opts *ListOptions) *cobra.Command {
...
}
You are passing value parameter not a pointer parameter.
Try someting like:
cmd = GetCommandFlags(cmd, &opts, "")
I use the following code which need to pass value from functions root, value
var cfBuild = &cobra.Command{
Use: "build",
Short: "Build",
Run: func(cmd *cobra.Command, args []string) {
root, value := Build(target)
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
//Here I need to use root, value
}
}
I can do it by using global variable but is there a nice way to avoid global in this case ?
This is the repo, I didnt find any nice way to do it ...
https://github.com/spf13/cobra
Btw there is option to use viper
like ...
Run: func(cmd *cobra.Command, args []string) {
root, value := Build(target)
viper.Set("root", root)
viper.Set("value", value)
and then get it in the other method...
Is it good direction?
you don't need Viper for that.
Just create 2 singletons (variables with global scope in the command file) and you can assign them to your Build function returns.
Example
package cmd
var (
root,
value string
)
var cfBuild = &cobra.Command{
Use: "build",
Short: "Build",
Run: func(cmd *cobra.Command, args []string) {
root, value = Build(target)
},
PersistentPostRun: func(cmd *cobra.Command, args []string) {
fmt.Printf("%s, %s", root, value)
}
}
I'm using cobra with my Golang application. How can I get the list of commands and values that I have registered with Cobra.
If I add a root command and then a DisplayName command.
var Name = "sample_"
var rootCmd = &cobra.Command{Use: "Use help to find out more options"}
rootCmd.AddCommand(cmd.DisplayNameCommand(Name))
Will I be able to know what is the value in Name from within my program by using some Cobra function? Ideally I want to access this value in Name and use it for checking some logic.
You can use the value stored in the Name variable for performing operations within your program. An example usage of cobra is:
var Name = "sample_"
var rootCmd = &cobra.Command{
Use: "hello",
Short: "Example short description",
Run: func(cmd *cobra.Command, args []string) {
// Do Stuff Here
},
}
var echoCmd = &cobra.Command{
Use: "echo",
Short: "Echo description",
Run: func(cmd *cobra.Command, args []string) {
fmt.Printf("hello %s", Name)
},
}
func init() {
rootCmd.AddCommand(echoCmd)
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
`
In the above code, you can see that hello is the root command and echo is a sub command. If you do hello echo, it'll echo the value sample_ which is stored in the Name variable.
You can also do something like this:
var echoCmd = &cobra.Command{
Use: "echo",
Short: "Echo description",
Run: func(cmd *cobra.Command, args []string) {
// Perform some logical operations
if Name == "sample_" {
fmt.Printf("hello %s", Name)
} else {
fmt.Println("Name did not match")
}
},
}
For knowing more about how to use cobra, you can also view my project from the below link.
https://github.com/bharath-srinivas/nephele
Hope this helps.
I am using Cobra to make some cli updated to my app. I want to make this command required, meaning the application should fail if it doesn't find the argument it is looking for.
package commands
import (
"github.com/spf13/cobra"
"errors"
"fmt"
)
var (
Env string
)
var RootCmd = &cobra.Command{
Use: "myapp",
Short: "tool",
Long: `tool`,
Run: func(cmd *cobra.Command, args []string) {
// Root command does nothing
},
}
func init() {
RootCmd.AddCommand(Environment)
}
var Environment = &cobra.Command{
Use: "env",
Short: "Specify Environment to run against",
Long: `Can be dev or prod`,
Args: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return errors.New("requires at least one arg")
}
if args[0] == "dev" || args[0] == "prod" {
return nil
}else {
return errors.New("input can only be dev or prod")
}
return fmt.Errorf("invalid env specified: %s", args[0])
},
PreRunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return fmt.Errorf("env is required")
}
return nil
},
Run: func(cmd *cobra.Command, args []string) {
Env = args[0]
},
}
and main package is
package main
import (
"fmt"
"log"
"os"
"util"
"commands"
)
func main() {
log.Println("Executing")
if err := commands.RootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
log.Println("Executing")
}
Now if I run this as ./myApp without any env, it doesn't complain about it. However if I use env after myapp then it activates the function and runs all the validations.
You can just make the body of the function handle it, perhaps by printing help and exiting as non-successful:
Run: func(cmd *cobra.Command, args []string) {
// Root command does nothing
cmd.Help()
os.Exit(1)
},
Omitting the Run (and RunE) field from the cobra.Command will make it a requirement for a valid subcommand to be given:
var RootCmd = &cobra.Command{
Use: "myapp",
Short: "tool",
Long: `tool long help...`,
}
If no subcommand is given on the command line, Cobra will print out the command's Help() text , which will include the root command's Long help text and the autogenerated usage help for all subcommands.
In cobra I've create a command of commands:
myapp zip -directory "xzy" -output="zipname"
myapp upload -filename="abc"
I want to make a zipAndUpload command and reuse the existing commands, i.e.,
myapp zipup -directory "xzy" -outout="zipname"
Here the zipup would first call the "zip" command and then use the output name from the "zip" command as the "filename" flag for the "upload" command.
How can I execute this without a lot of code duplication?
The "shared" switches make as global.
The "run" sections of the sub-commands, convert to functions.
To do this you must define the commands manually:
var (
cfgDirectory string
cfgFilename string
cfgOutput string
)
var rootCmd = &cobra.Command{
Use: "root",
Short: "root",
Long: "root",
Run: func(cmd *cobra.Command, args []string) {
// something
},
}
var uploadCmd = &cobra.Command{
Use: 'upload',
Short: 'upload',
Long: `upload`,
Run: func(cmd *cobra.Command, args []string) {
Upload()
},
}
var zipCmd = &cobra.Command{
Use: "zip",
Short: "zip",
Long: "zip",
Run: func(cmd *cobra.Command, args []string) {
Zip()
},
}
var zipupCmd = &cobra.Command{
Use: "zipup",
Short: "zipup",
Long: "zipup",
Run: func(cmd *cobra.Command, args []string) {
Zip()
Upload()
},
}
func setFlags() {
rootCmd.PersistentFlags().StringVar(&cfgDirectory, "directory", "", "explanation")
rootCmd.PersistentFlags().StringVar(&cfgFilename, "filename", "", "explanation")
rootCmd.PersistentFlags().StringVar(&cfgOutput, "output", "", "explanation")
}
func Upload() {
// you know what to do
}
func Zip() {
// you know what to do
}
...
// Add subcommands
rootCmd.AddCommand(zipCmd)
rootCmd.AddCommand(uploadCmd)
rootCmd.AddCommand(zipupCmd)
Hope this helps, this is the best I could do without any example code.
When you create new commands from the cobra CLI, it usually puts them into separate files. If you want to run a command from a different command file, it would look something like this:
package cmd
import "github.com/spf13/cobra"
// DirectoryFlag specifies the directory
var DirectoryFlag string
// OutputFlag specifies the output
var OutputFlag string
// zipupCmd represents the "zipup" command
var zipupCmd = &cobra.Command{
Use: "zipup",
Short: "Zip & Upload",
Long: `Zip & Upload`,
Run: func(cmd *cobra.Command, args []string) {
anyArgs := "whatever"
zipCmd.Run(cmd, []string{anyArgs})
uploadCmd.Run(cmd, []string{anyArgs})
},
}
func init() {
rootCmd.AddCommand(zipupCmd)
zipupCmd.Flags().StringVarP(&DirectoryFlag, "directory", "d", "", "set the directory")
zipupCmd.Flags().StringVarP(&OutputFlag, "output", "o", "", "set the output")
}