Skip to content

Commit 7768c06

Browse files
authored
Merge pull request #1838 from trungutt/fix/persistent-pre-run-ancestor-chain
fix: walk full ancestor chain in addGatewayFlags PersistentPreRunE
2 parents a08f88f + c113e81 commit 7768c06

2 files changed

Lines changed: 39 additions & 2 deletions

File tree

cmd/root/flags.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,14 @@ func addGatewayFlags(cmd *cobra.Command, runConfig *config.RuntimeConfig) {
9898
if persistentPreRunE != nil {
9999
return persistentPreRunE(cmd, args)
100100
}
101-
if cmd.Parent() != nil && cmd.Parent().PersistentPreRunE != nil {
102-
return cmd.Parent().PersistentPreRunE(cmd, args)
101+
// Walk up the ancestor chain to find and call the nearest PersistentPreRunE.
102+
// A single cmd.Parent() check is not sufficient when this command is nested
103+
// more than one level deep (e.g. root → serve → api): the immediate parent
104+
// may have no PersistentPreRunE, but a grandparent (such as root) might.
105+
for p := cmd.Parent(); p != nil; p = p.Parent() {
106+
if p.PersistentPreRunE != nil {
107+
return p.PersistentPreRunE(cmd, args)
108+
}
103109
}
104110

105111
return nil

cmd/root/flags_test.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,37 @@ func TestGatewayLogic(t *testing.T) {
9898
}
9999
}
100100

101+
func TestGatewayFlags_CallsAncestorPersistentPreRunE(t *testing.T) {
102+
// Regression test: addGatewayFlags overrides PersistentPreRunE on the command
103+
// it is applied to. When that command is nested under an intermediate parent
104+
// (e.g. root → serve → api), the old code only checked cmd.Parent(), so a
105+
// grandparent's PersistentPreRunE was silently skipped.
106+
called := false
107+
108+
root := &cobra.Command{Use: "root"}
109+
root.PersistentPreRunE = func(*cobra.Command, []string) error {
110+
called = true
111+
return nil
112+
}
113+
114+
middle := &cobra.Command{Use: "middle"}
115+
116+
leaf := &cobra.Command{
117+
Use: "leaf",
118+
Args: cobra.NoArgs,
119+
RunE: func(*cobra.Command, []string) error { return nil },
120+
}
121+
runConfig := config.RuntimeConfig{}
122+
addGatewayFlags(leaf, &runConfig)
123+
124+
middle.AddCommand(leaf)
125+
root.AddCommand(middle)
126+
127+
root.SetArgs([]string{"middle", "leaf"})
128+
require.NoError(t, root.Execute())
129+
assert.True(t, called, "root PersistentPreRunE should have been called through the intermediate parent")
130+
}
131+
101132
func TestCanonize(t *testing.T) {
102133
t.Parallel()
103134

0 commit comments

Comments
 (0)