Lifecycle Hooks¶
BOA provides lifecycle hooks to customize behavior at different stages of command execution.
Hook Execution Order¶
- Init - Parameter mirrors exist, cobra flags not yet created
- PostCreate - Cobra flags are now registered
- PreValidate - After flags are parsed but before validation
- Validation - Built-in parameter validation
- PreExecute - After validation but before command execution
- Run - The actual command execution
Init Hook¶
Runs during initialization, after BOA creates internal parameter mirrors but before cobra flags are registered.
Interface-based¶
func (c *MyConfig) Init() error {
// Initialize defaults, set up validators
return nil
}
// With HookContext access
func (c *MyConfig) InitCtx(ctx *boa.HookContext) error {
ctx.GetParam(&c.Host).SetDefault(boa.Default("localhost"))
return nil
}
Function-based¶
PostCreate Hook¶
Runs after cobra flags are created but before arguments are parsed. Useful for inspecting or modifying cobra flags.
Interface-based¶
func (c *MyConfig) PostCreate() error {
// Flags are now registered
return nil
}
// With HookContext
func (c *MyConfig) PostCreateCtx(ctx *boa.HookContext) error {
return nil
}
Function-based¶
PreValidate Hook¶
Runs after parameters are parsed but before validation. Ideal for loading config files.
Interface-based¶
func (c *MyConfig) PreValidate() error {
// Manipulate parameters before validation
return nil
}
// With HookContext
func (c *MyConfig) PreValidateCtx(ctx *boa.HookContext) error {
return nil
}
Function-based¶
PreExecute Hook¶
Runs after validation but before the Run function. Use for setup like establishing connections.
Interface-based¶
Function-based¶
HookContext¶
The HookContext provides access to parameter mirrors for advanced configuration:
GetParam(fieldPtr any) Param- Get the Param interface for any fieldHasValue(fieldPtr any) bool- Check if a parameter has a valueAllMirrors() []Param- Get all auto-generated parameter mirrors
Typed Parameter Access¶
For type-safe parameter configuration, use boa.GetParamT[T]() to get a typed view:
func (c *ServerConfig) InitCtx(ctx *boa.HookContext) error {
// Type-safe: SetDefaultT takes int, SetCustomValidatorT takes func(int) error
portParam := boa.GetParamT(ctx, &c.Port)
portParam.SetDefaultT(8080)
portParam.SetCustomValidatorT(func(port int) error {
if port < 1 || port > 65535 {
return fmt.Errorf("port must be between 1 and 65535")
}
return nil
})
return nil
}
The ParamT[T] interface provides both typed and pass-through methods:
| Typed Methods | Description |
|---|---|
SetDefaultT(T) |
Set default value with type safety |
SetCustomValidatorT(func(T) error) |
Set typed validation function |
| Pass-through Methods | Description |
|---|---|
SetAlternatives([]string) |
Set allowed values |
SetStrictAlts(bool) |
Enable/disable strict validation |
SetAlternativesFunc(...) |
Set dynamic completion function |
SetEnv(string) |
Set environment variable |
SetShort(string) |
Set short flag |
SetName(string) |
Set flag name |
SetIsEnabledFn(func() bool) |
Dynamic visibility |
SetRequiredFn(func() bool) |
Dynamic required condition |
Param() |
Access underlying untyped Param |
Example: Programmatic Configuration¶
type ServerConfig struct {
Host string
Port int
LogLevel string
}
func (c *ServerConfig) InitCtx(ctx *boa.HookContext) error {
hostParam := ctx.GetParam(&c.Host)
hostParam.SetDefault(boa.Default("localhost"))
hostParam.SetEnv("SERVER_HOST")
portParam := ctx.GetParam(&c.Port)
portParam.SetDefault(boa.Default(8080))
portParam.SetEnv("SERVER_PORT")
logParam := ctx.GetParam(&c.LogLevel)
logParam.SetDefault(boa.Default("info"))
logParam.SetAlternatives([]string{"debug", "info", "warn", "error"})
logParam.SetStrictAlts(true)
return nil
}
Example: Checking Parameter Sources at Runtime¶
type Params struct {
Host string `default:"localhost"`
Port int `optional:"true"`
}
func main() {
boa.CmdT[Params]{
Use: "server",
RunFuncCtx: func(ctx *boa.HookContext, params *Params, cmd *cobra.Command, args []string) {
if ctx.HasValue(¶ms.Port) {
fmt.Printf("Starting on %s:%d\n", params.Host, params.Port)
} else {
fmt.Printf("Starting on %s (no port)\n", params.Host)
}
},
}.Run()
}
type Params struct {
Host string `default:"localhost"`
Port int `optional:"true"`
}
func main() {
boa.NewCmdT[Params]("server").
WithRunFuncCtx(func(ctx *boa.HookContext, params *Params) {
if ctx.HasValue(¶ms.Port) {
fmt.Printf("Starting on %s:%d\n", params.Host, params.Port)
} else {
fmt.Printf("Starting on %s (no port)\n", params.Host)
}
}).
Run()
}
Note
You can only use one run function variant per command: RunFunc, RunFuncCtx, RunFuncE, or RunFuncCtxE.
Error Handling in Hooks¶
All lifecycle hooks return errors. When using Run(), hook errors cause panics. When using RunE(), hook errors are returned for programmatic handling.
boa.NewCmdT[Params]("cmd").
WithInitFuncE(func(p *Params) error {
return fmt.Errorf("init failed")
}).
RunE() // Returns error instead of panicking
For comprehensive coverage of error handling including Run() vs RunE(), error-returning run functions, and testing patterns, see Error Handling.