diff --git a/command.go b/command.go index 4794e5ebe..a08cf342e 100644 --- a/command.go +++ b/command.go @@ -138,6 +138,8 @@ type Command struct { RunE func(cmd *Command, args []string) error // PostRun: run after the Run command. PostRun func(cmd *Command, args []string) + // Finalize: run at the end of the Run command, even if it panics. + Finalize func(cmd *Command, args []string) // PostRunE: PostRun but returns an error. PostRunE func(cmd *Command, args []string) error // PersistentPostRun: children of this command will inherit and execute after PostRun. @@ -961,6 +963,7 @@ func (c *Command) execute(a []string) (err error) { defer c.postRun() argWoFlags := c.Flags().Args() + if c.DisableFlagParsing { argWoFlags = a } @@ -981,6 +984,13 @@ func (c *Command) execute(a []string) (err error) { parents = append(parents, p) } } + defer func() { + for _, p := range parents { + if p.Finalize != nil { + p.Finalize(c, argWoFlags) + } + } + }() for _, p := range parents { if p.PersistentPreRunE != nil { if err := p.PersistentPreRunE(c, argWoFlags); err != nil { diff --git a/command_test.go b/command_test.go index 156df9eb6..082cac949 100644 --- a/command_test.go +++ b/command_test.go @@ -2952,3 +2952,37 @@ func TestHelpFuncExecuted(t *testing.T) { checkStringContains(t, output, helpText) } + +func TestFinalizeOfChildAndParentCalledOnPanic(t *testing.T) { + parentFinalizeCalls := 0 + childFinalizeCalls := 0 + defer func() { + if recover() == nil { + t.Error("The code should have panicked due to panicking run") + } + if parentFinalizeCalls != 1 { + t.Errorf("finalize() of parent command called %d times, want 1", parentFinalizeCalls) + } + if childFinalizeCalls != 1 { + t.Errorf("finalize() of child command called %d times, want 1", childFinalizeCalls) + } + }() + rootCmd := &Command{ + Use: "root", + Run: emptyRun, + Finalize: func(cmd *Command, args []string) { + parentFinalizeCalls++ + }, + } + subCmd := &Command{ + Use: "sub", + Run: func(cmd *Command, args []string) { + panic("should panic") + }, + Finalize: func(cmd *Command, args []string) { + childFinalizeCalls++ + }, + } + rootCmd.AddCommand(subCmd) + executeCommand(rootCmd, "sub") +}