3

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()
}
Shang Jian Ding
  • 1,931
  • 11
  • 24

1 Answers1

4

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")
       },

Shang Jian Ding
  • 1,931
  • 11
  • 24
  • Note that this per-child pre-run simply does what cobra would do if there were no child pre-run. The only reason to call the parent's pre-run directly like this is if you provide a pre-run function; providing a pre-run function that just calls the parent's pre-run isn't *wrong*, but is not necessary. – torek Sep 24 '20 at 23:16
  • @torek - note that each child PersistentPreRun calls the parent's PersistentPreRun and then does some additional work. – Shang Jian Ding Sep 25 '20 at 13:58
  • 3
    If you mean that your sample has a `fmt.Println`, yes, that's certainly some additional work. :-) I'm just thinking that someone reading this answer might think that they have to put in a one-line function with a call to the parent's PersisentPreRun. – torek Sep 25 '20 at 20:41
  • A parent `persistentPreRun` will not run if the child command also implements `persistentPreRun`. In this case you do actually need to invoke it manually from the child. See the [PreRun and PostRun Hooks](https://umarcor.github.io/cobra/) example output from the cobra documentation – geg Feb 06 '23 at 17:16