Skip to content

Commit

Permalink
Run Before actions after setting up subcommand
Browse files Browse the repository at this point in the history
This fixes an issue where the Before hook of a parent command would not see
flag values set in the subcommand context.
  • Loading branch information
fjl committed Dec 6, 2024
1 parent 35945b7 commit 646e048
Showing 1 changed file with 36 additions and 16 deletions.
52 changes: 36 additions & 16 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"os"
"path/filepath"
"reflect"
"slices"
"sort"
"strings"
"unicode"
Expand Down Expand Up @@ -561,23 +562,7 @@ func (cmd *Command) Run(ctx context.Context, osArgs []string) (deferErr error) {
}
}

if cmd.Before != nil && !cmd.Root().shellCompletion {
if bctx, err := cmd.Before(ctx, cmd); err != nil {
deferErr = cmd.handleExitCoder(ctx, err)
return deferErr
} else if bctx != nil {
ctx = bctx
}
}

tracef("running flag actions (cmd=%[1]q)", cmd.Name)

if err := cmd.runFlagActions(ctx); err != nil {
return err
}

var subCmd *Command

if args.Present() {
tracef("checking positional args %[1]q (cmd=%[2]q)", args, cmd.Name)

Expand Down Expand Up @@ -613,11 +598,46 @@ func (cmd *Command) Run(ctx context.Context, osArgs []string) (deferErr error) {
}
}

// If a subcommand has been resolved, let it handle the remaining execution.
if subCmd != nil {
tracef("running sub-command %[1]q with arguments %[2]q (cmd=%[3]q)", subCmd.Name, cmd.Args(), cmd.Name)
return subCmd.Run(ctx, cmd.Args().Slice())
}

// This code path is the innermost command execution. Here we actually
// perform the command action.
//
// First, resolve the chain of nested commands up to the parent.
var cmdChain []*Command
for p := cmd; p != nil; p = p.parent {
cmdChain = append(cmdChain, p)
}
slices.Reverse(cmdChain)

// Run Before actions in order.
for _, cmd := range cmdChain {
if cmd.Before == nil {
continue
}
if bctx, err := cmd.Before(ctx, cmd); err != nil {
deferErr = cmd.handleExitCoder(ctx, err)
return deferErr
} else if bctx != nil {
ctx = bctx
}
}

// Run flag actions in order.
// These take a context, so this has to happen after Before actions.
for _, cmd := range cmdChain {
tracef("running flag actions (cmd=%[1]q)", cmd.Name)
if err := cmd.runFlagActions(ctx); err != nil {
deferErr = cmd.handleExitCoder(ctx, err)
return deferErr
}
}

// Run the command action.
if cmd.Action == nil {
cmd.Action = helpCommandAction
} else {
Expand Down

0 comments on commit 646e048

Please sign in to comment.