use DepFn to pass dependencies rather than direct module list

Allows dependency modules to be built in the context of the lifecycle
and command.
This commit is contained in:
Elijah Duffy
2025-06-05 15:00:20 -07:00
parent 6b4196dbda
commit 0badbafa00

View File

@@ -8,14 +8,28 @@ import (
"github.com/urfave/cli/v3" "github.com/urfave/cli/v3"
) )
// DepFn is a function type that returns a slice of dependencies.
type DepFn func(*app.Lifecycle, *cli.Command) ([]*app.Module, error)
// DepList is a convenience function that returns a DepFn that uses a
// predefined list of dependecies.
func DepList(deps ...*app.Module) DepFn {
return func(_ *app.Lifecycle, _ *cli.Command) ([]*app.Module, error) {
if len(deps) == 0 {
return nil, errors.New("no dependencies provided")
}
return deps, nil
}
}
// NewCommand creates a new CLI command with the specified configuration and // NewCommand creates a new CLI command with the specified configuration and
// and dependencies. Returns a standard *cli.Command that can be used directly // and dependencies. Returns a standard *cli.Command that can be used directly
// with urfave/cli/v3 types. If any dependencies are provided, the Before method // with urfave/cli/v3 types. If any dependencies are provided, the Before method
// is overriden to ensure that all dependencies are satisfied before the command // is overriden to ensure that all dependencies are satisfied before the command
// is executed. Requires an app.Lifecycle to be present in the context when the // is executed. Requires an app.Lifecycle to be present in the context when the
// command is executed. // command is executed.
func NewCommand(cmdcfg *cli.Command, depends ...*app.Module) *cli.Command { func NewCommand(cmdcfg *cli.Command, depfn DepFn) *cli.Command {
if len(depends) == 0 { if depfn == nil {
return cmdcfg return cmdcfg
} }
@@ -35,7 +49,12 @@ func NewCommand(cmdcfg *cli.Command, depends ...*app.Module) *cli.Command {
return ctx, errors.New("lifecycle not found in context, cannot run command with dependencies") return ctx, errors.New("lifecycle not found in context, cannot run command with dependencies")
} }
if err := lifecycle.Require(depends...); err != nil { deps, err := depfn(lifecycle, cmd)
if err != nil {
return ctx, err
}
if err := lifecycle.Require(deps...); err != nil {
return ctx, err return ctx, err
} }
return ctx, nil return ctx, nil
@@ -49,9 +68,9 @@ type RootCommandOpts struct {
// Command is the base command configuration. // Command is the base command configuration.
Command *cli.Command Command *cli.Command
// Dependencies are the modules that this command depends on. Note: All // DepFn is a function that returns the modules that this command depends
// root dependencies will be inherited by all subcommands. // on. Note: All root dependencies will be inherited by all subcommands.
Dependencies []*app.Module DepFn DepFn
} }
// NewRootCommand creates a new root CLI command with the specified configuration. // NewRootCommand creates a new root CLI command with the specified configuration.
@@ -111,7 +130,7 @@ func NewRootCommand(rootcfg *RootCommandOpts) *cli.Command {
return nil return nil
} }
return NewCommand(cmdcfg, rootcfg.Dependencies...) return NewCommand(cmdcfg, rootcfg.DepFn)
} }
// VerboseFromCommand checks if the verbose flag is set in the command context. // VerboseFromCommand checks if the verbose flag is set in the command context.