@@ -105,6 +105,21 @@ type Command struct {
105
105
// * PersistentPostRun()
106
106
// All functions get the same args, the arguments after the command name.
107
107
//
108
+ // When TraverseChildrenHooks is set, PersistentPreRun and
109
+ // PersistentPostRun are chained together so that each child
110
+ // PersistentPreRun is ran, and the PersistentPostRun are ran in reverse
111
+ // order. For example:
112
+ //
113
+ // Commands: root -> subcommand-a -> subcommand-b
114
+ //
115
+ // root - PersistentPreRun
116
+ // subcommand-a - PersistentPreRun
117
+ // subcommand-b - PersistentPreRun
118
+ // subcommand-b - Run
119
+ // subcommand-b - PersistentPostRun
120
+ // subcommand-a - PersistentPostRun
121
+ // root - PersistentPostRun
122
+ //
108
123
// PersistentPreRun: children of this command will inherit and execute.
109
124
PersistentPreRun func (cmd * Command , args []string )
110
125
// PersistentPreRunE: PersistentPreRun but returns an error.
@@ -154,6 +169,11 @@ type Command struct {
154
169
// TraverseChildren parses flags on all parents before executing child command.
155
170
TraverseChildren bool
156
171
172
+ // TraverseChildrenHooks will have each subcommand's PersistentPreRun and
173
+ // PersistentPostRun instead of overriding. It should be set on the root
174
+ // command.
175
+ TraverseChildrenHooks bool
176
+
157
177
// FParseErrWhitelist flag parse errors to be ignored
158
178
FParseErrWhitelist FParseErrWhitelist
159
179
@@ -824,55 +844,130 @@ func (c *Command) execute(a []string) (err error) {
824
844
return err
825
845
}
826
846
827
- for p := c ; p != nil ; p = p .Parent () {
828
- if p .PersistentPreRunE != nil {
829
- if err := p .PersistentPreRunE (c , argWoFlags ); err != nil {
830
- return err
847
+ // Look to see if TraverseChildrenHooks is set on the root command.
848
+ if _ , err := c .runTree (c , argWoFlags , c .traverseChildrenHooks ()); err != nil {
849
+ return err
850
+ }
851
+
852
+ return nil
853
+ }
854
+
855
+ func (c * Command ) traverseChildrenHooks () bool {
856
+ if c .HasParent () {
857
+ return c .Parent ().traverseChildrenHooks ()
858
+ }
859
+
860
+ return c .TraverseChildrenHooks
861
+ }
862
+
863
+ func (c * Command ) runTree (
864
+ cmd * Command ,
865
+ args []string ,
866
+ traverseChildrenHooks bool ,
867
+ ) (
868
+ persistentPostRunEs []func (cmd * Command , args []string ) error ,
869
+ err error ,
870
+ ) {
871
+ if c == nil {
872
+ return nil , nil
873
+ }
874
+
875
+ // Traverse command tree and save the PersistentPostRun{,E} functions.
876
+ persistentPostRunEs , err = c .Parent ().runTree (cmd , args , traverseChildrenHooks )
877
+ if err != nil {
878
+ return nil , err
879
+ }
880
+
881
+ if traverseChildrenHooks || c == cmd {
882
+ // PersistentPreRun/PersistentPreRunE
883
+ switch {
884
+ case c .PersistentPreRun != nil :
885
+ c .PersistentPreRun (cmd , args )
886
+ case c .PersistentPreRunE != nil :
887
+ if err := c .PersistentPreRunE (cmd , args ); err != nil {
888
+ return nil , err
831
889
}
832
- break
833
- } else if p .PersistentPreRun != nil {
834
- p .PersistentPreRun (c , argWoFlags )
835
- break
890
+ default :
891
+ // Doesn't have a registered PersistentPreRun{,E}. Move on...
892
+ }
893
+
894
+ // PersistentPostRun/PersistentPostRunE
895
+ switch {
896
+ case c .PersistentPostRun != nil :
897
+ persistentPostRunEs = append (
898
+ persistentPostRunEs ,
899
+ func (cmd * Command , args []string ) error {
900
+ c .PersistentPostRun (cmd , args )
901
+ return nil
902
+ },
903
+ )
904
+ case c .PersistentPostRunE != nil :
905
+ persistentPostRunEs = append (
906
+ persistentPostRunEs ,
907
+ c .PersistentPostRunE ,
908
+ )
909
+ default :
910
+ // Doesn't have a registered PersistentPostRun{,E}. Move on...
836
911
}
837
912
}
838
- if c .PreRunE != nil {
839
- if err := c .PreRunE (c , argWoFlags ); err != nil {
840
- return err
913
+
914
+ if c != cmd {
915
+ // Don't run a parent command.
916
+ return persistentPostRunEs , nil
917
+ }
918
+
919
+ // PreRun/PreRunE
920
+ switch {
921
+ case c .PreRun != nil :
922
+ c .PreRun (cmd , args )
923
+ case c .PreRunE != nil :
924
+ if err := c .PreRunE (cmd , args ); err != nil {
925
+ return nil , err
841
926
}
842
- } else if c . PreRun != nil {
843
- c . PreRun ( c , argWoFlags )
927
+ default :
928
+ // Doesn't have a registered PreRun{,E}. Move on...
844
929
}
845
930
846
931
if err := c .validateRequiredFlags (); err != nil {
847
- return err
932
+ return nil , err
848
933
}
849
- if c .RunE != nil {
850
- if err := c .RunE (c , argWoFlags ); err != nil {
851
- return err
934
+
935
+ // Run/RunE
936
+ switch {
937
+ case c .RunE != nil :
938
+ if err := c .RunE (cmd , args ); err != nil {
939
+ return nil , err
852
940
}
853
- } else {
854
- c .Run (c , argWoFlags )
855
- }
856
- if c .PostRunE != nil {
857
- if err := c .PostRunE (c , argWoFlags ); err != nil {
858
- return err
941
+ case c .Run != nil :
942
+ c .Run (cmd , args )
943
+ default :
944
+ // Both RunE and Run are nil...
945
+ panic (fmt .Sprintf ("command %q does not have a non-nil RunE or Run function" , c .Use ))
946
+ }
947
+
948
+ // PostRun/PostRunE
949
+ switch {
950
+ case c .PostRun != nil :
951
+ c .PostRun (cmd , args )
952
+ case c .PostRunE != nil :
953
+ if err := c .PostRunE (cmd , args ); err != nil {
954
+ return nil , err
859
955
}
860
- } else if c . PostRun != nil {
861
- c . PostRun ( c , argWoFlags )
956
+ default :
957
+ // Doesn't have a registered PostRun{,E}. Move on...
862
958
}
863
- for p := c ; p != nil ; p = p .Parent () {
864
- if p .PersistentPostRunE != nil {
865
- if err := p .PersistentPostRunE (c , argWoFlags ); err != nil {
866
- return err
867
- }
868
- break
869
- } else if p .PersistentPostRun != nil {
870
- p .PersistentPostRun (c , argWoFlags )
871
- break
959
+
960
+ // PersistentPostRun/PersistentPostRunE
961
+ // Iterate through the list in reverse order. Similar to a defer, allow
962
+ // the topmost commands to cleanup first.
963
+ for i := range persistentPostRunEs {
964
+ r := persistentPostRunEs [len (persistentPostRunEs )- 1 - i ]
965
+ if err := r (cmd , args ); err != nil {
966
+ return nil , err
872
967
}
873
968
}
874
969
875
- return nil
970
+ return nil , nil
876
971
}
877
972
878
973
func (c * Command ) preRun () {
0 commit comments