|
9 | 9 | "os" |
10 | 10 | "path/filepath" |
11 | 11 | "reflect" |
| 12 | + "slices" |
12 | 13 | "sort" |
13 | 14 | "strings" |
14 | 15 | "unicode" |
@@ -561,23 +562,7 @@ func (cmd *Command) Run(ctx context.Context, osArgs []string) (deferErr error) { |
561 | 562 | } |
562 | 563 | } |
563 | 564 |
|
564 | | - if cmd.Before != nil && !cmd.Root().shellCompletion { |
565 | | - if bctx, err := cmd.Before(ctx, cmd); err != nil { |
566 | | - deferErr = cmd.handleExitCoder(ctx, err) |
567 | | - return deferErr |
568 | | - } else if bctx != nil { |
569 | | - ctx = bctx |
570 | | - } |
571 | | - } |
572 | | - |
573 | | - tracef("running flag actions (cmd=%[1]q)", cmd.Name) |
574 | | - |
575 | | - if err := cmd.runFlagActions(ctx); err != nil { |
576 | | - return err |
577 | | - } |
578 | | - |
579 | 565 | var subCmd *Command |
580 | | - |
581 | 566 | if args.Present() { |
582 | 567 | tracef("checking positional args %[1]q (cmd=%[2]q)", args, cmd.Name) |
583 | 568 |
|
@@ -613,11 +598,46 @@ func (cmd *Command) Run(ctx context.Context, osArgs []string) (deferErr error) { |
613 | 598 | } |
614 | 599 | } |
615 | 600 |
|
| 601 | + // If a subcommand has been resolved, let it handle the remaining execution. |
616 | 602 | if subCmd != nil { |
617 | 603 | tracef("running sub-command %[1]q with arguments %[2]q (cmd=%[3]q)", subCmd.Name, cmd.Args(), cmd.Name) |
618 | 604 | return subCmd.Run(ctx, cmd.Args().Slice()) |
619 | 605 | } |
620 | 606 |
|
| 607 | + // This code path is the innermost command execution. Here we actually |
| 608 | + // perform the command action. |
| 609 | + // |
| 610 | + // First, resolve the chain of nested commands up to the parent. |
| 611 | + var cmdChain []*Command |
| 612 | + for p := cmd; p != nil; p = p.parent { |
| 613 | + cmdChain = append(cmdChain, p) |
| 614 | + } |
| 615 | + slices.Reverse(cmdChain) |
| 616 | + |
| 617 | + // Run Before actions in order. |
| 618 | + for _, cmd := range cmdChain { |
| 619 | + if cmd.Before == nil { |
| 620 | + continue |
| 621 | + } |
| 622 | + if bctx, err := cmd.Before(ctx, cmd); err != nil { |
| 623 | + deferErr = cmd.handleExitCoder(ctx, err) |
| 624 | + return deferErr |
| 625 | + } else if bctx != nil { |
| 626 | + ctx = bctx |
| 627 | + } |
| 628 | + } |
| 629 | + |
| 630 | + // Run flag actions in order. |
| 631 | + // These take a context, so this has to happen after Before actions. |
| 632 | + for _, cmd := range cmdChain { |
| 633 | + tracef("running flag actions (cmd=%[1]q)", cmd.Name) |
| 634 | + if err := cmd.runFlagActions(ctx); err != nil { |
| 635 | + deferErr = cmd.handleExitCoder(ctx, err) |
| 636 | + return deferErr |
| 637 | + } |
| 638 | + } |
| 639 | + |
| 640 | + // Run the command action. |
621 | 641 | if cmd.Action == nil { |
622 | 642 | cmd.Action = helpCommandAction |
623 | 643 | } else { |
|
0 commit comments