diff --git a/README.md b/README.md index 66311c46..a87a63c0 100644 --- a/README.md +++ b/README.md @@ -641,15 +641,22 @@ command.SetUsageTemplate(s string) ## PreRun or PostRun Hooks -It is possible to run functions before or after the main `Run` function of your command. The `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun` and `PostRun` will be executed after `Run`. The `Persistent*Run` functions will be inherrited by children if they do not declare their own. These function are run in the following order: +It is possible to run functions before or after the main `Run` function of your command. The `PreRunChain`, `PersistentPreRun` and `PreRun` functions will be executed before `Run`. `PersistentPostRun`, `PostRun` and `PostRunChain` will be executed after `Run`. The `Persistent*Run` functions will be inherrited by children if they do not declare their own. These function are run in the following order: +- `PreRunChain` - `PersistentPreRun` - `PreRun` - `Run` - `PostRun` - `PersistentPostRun` +- `PostRunChain` -An example of two commands which use all of these features is below. When the subcommand is executed, it will run the root command's `PersistentPreRun` but not the root command's `PersistentPostRun`: + +`PreRunChain` executes all PreRunChain functions from the root to the child. `PostRunChain` executes all PostRunChain functions from the child to the root. These +are useful in cases such as setting the logger and profiling at the root level in `PreRunChain`, and stopping the profiler once the proram finishes in the `PostRunChain`, while still allowing subcommand `PreRunChain` and `PostRunChain` commands to execute for their children. + +An example of two commands which use all of these features is below. When the subcommand is executed, it will run the root command's `PersistentPreRun` but not the root command's `PersistentPostRun` - because the subcommand has overridden the root command's `PersistentPostRun`. However, it will still run the root command's `PreRunChain` and `PostRunChain` in addition to the subcommand's `PreRunChain` and `PostRunChain`, because chains are supposed to execute all defined +chain functions, and not just the overridden one. ```go package main @@ -665,6 +672,9 @@ func main() { var rootCmd = &cobra.Command{ Use: "root [sub]", Short: "My root command", + PreRunChain: func(cmd *cobra.Command, args []string) { + fmt.Printf("Inside rootCmd PreRunChain with args: %v\n", args) + }, PersistentPreRun: func(cmd *cobra.Command, args []string) { fmt.Printf("Inside rootCmd PersistentPreRun with args: %v\n", args) }, @@ -680,11 +690,17 @@ func main() { PersistentPostRun: func(cmd *cobra.Command, args []string) { fmt.Printf("Inside rootCmd PersistentPostRun with args: %v\n", args) }, + PostRunChain: func(cmd *cobra.Command, args []string) { + fmt.Printf("Inside rootCmd PostRunChain with args: %v\n", args) + }, } var subCmd = &cobra.Command{ Use: "sub [no options!]", Short: "My subcommand", + PreRunChain: func(cmd *cobra.Command, args []string) { + fmt.Printf("Inside subCmd PreRunChain with args: %v\n", args) + }, PreRun: func(cmd *cobra.Command, args []string) { fmt.Printf("Inside subCmd PreRun with args: %v\n", args) }, @@ -697,6 +713,9 @@ func main() { PersistentPostRun: func(cmd *cobra.Command, args []string) { fmt.Printf("Inside subCmd PersistentPostRun with args: %v\n", args) }, + PostRunChain: func(cmd *cobra.Command, args []string) { + fmt.Printf("Inside subCmd PostRunChain with args: %v\n", args) + }, } rootCmd.AddCommand(subCmd) @@ -714,11 +733,13 @@ func main() { Cobra also has functions where the return signature is an error. This allows for errors to bubble up to the top, providing a way to handle the errors in one location. The current list of functions that return an error is: +* PreRunChainE * PersistentPreRunE * PreRunE * RunE * PostRunE * PersistentPostRunE +* PostRunChainE **Example Usage using RunE:** diff --git a/command.go b/command.go index 9b7a006c..b7ef09d4 100644 --- a/command.go +++ b/command.go @@ -64,12 +64,18 @@ type Command struct { // Silence Usage is an option to silence usage when an error occurs. SilenceUsage bool // The *Run functions are executed in the following order: + // * PreRunChain() // * PersistentPreRun() // * PreRun() // * Run() // * PostRun() // * PersistentPostRun() + // * PostRunChain() // All functions get the same args, the arguments after the command name + // PreRunChain: runs all PreRunChain functions from root to child sequentially + PreRunChain func(cmd *Command, args []string) + // PreRunChainE: PreRunChain but returns an error + PreRunChainE func(cmd *Command, args []string) error // PersistentPreRun: children of this command will inherit and execute PersistentPreRun func(cmd *Command, args []string) // PersistentPreRunE: PersistentPreRun but returns an error @@ -90,6 +96,10 @@ type Command struct { PersistentPostRun func(cmd *Command, args []string) // PersistentPostRunE: PersistentPostRun but returns an error PersistentPostRunE func(cmd *Command, args []string) error + // PostRunChain: runs all PostRunChain functions from child to root sequentially + PostRunChain func(cmd *Command, args []string) + // PostRunChainE: PostRunChain but returns an error + PostRunChainE func(cmd *Command, args []string) error // DisableAutoGenTag remove DisableAutoGenTag bool // Commands is the list of commands supported by this program. @@ -542,6 +552,10 @@ func (c *Command) execute(a []string) (err error) { c.preRun() argWoFlags := c.Flags().Args() + if err := c.executePreRunChain(c, argWoFlags); err != nil { + return err + } + for p := c; p != nil; p = p.Parent() { if p.PersistentPreRunE != nil { if err := p.PersistentPreRunE(c, argWoFlags); err != nil { @@ -587,6 +601,10 @@ func (c *Command) execute(a []string) (err error) { } } + if err := c.executePostRunChain(c, argWoFlags); err != nil { + return err + } + return nil } @@ -596,6 +614,49 @@ func (c *Command) preRun() { } } +// executePreRunChain executes all the PreRunChain functions from +// the root to the child. +func (c *Command) executePreRunChain(cmd *Command, args []string) error { + parent := c.Parent() + if parent != nil { + if err := parent.executePreRunChain(cmd, args); err != nil { + return err + } + } + + var err error = nil + switch { + case c.PreRunChainE != nil: + err = c.PreRunChainE(cmd, args) + case c.PreRunChain != nil: + c.PreRunChain(cmd, args) + } + + return err +} + +// executePostRunChain executes all the PostRunChain functions from +// the child to the root. +func (c *Command) executePostRunChain(cmd *Command, args []string) error { + var err error = nil + switch { + case c.PostRunChainE != nil: + err = c.PostRunChainE(cmd, args) + case c.PostRunChain != nil: + c.PostRunChain(cmd, args) + } + + if err != nil { + return err + } + + parent := c.Parent() + if parent != nil { + return parent.executePostRunChain(cmd, args) + } + return nil +} + func (c *Command) errorMsgFromParse() string { s := c.flagErrorBuf.String()