diff --git a/cli/config.go b/cli/config.go index a992708..8677776 100644 --- a/cli/config.go +++ b/cli/config.go @@ -34,15 +34,17 @@ type Config struct { TasksAfterXref []Task `json:"-"` // while command linked and xref'd, it's time to insert user-defined commands dynamically. TasksAfterLoader []Task `json:"-"` // while external loaders loaded. TasksBeforeParse []Task `json:"-"` // globally pre-parse tasks + TasksParsed []Task `json:"-"` // globally post-parse tasks TasksBeforeRun []Task `json:"-"` // globally pre-run tasks, it's also used as TasksAfterParsed TasksAfterRun []Task `json:"-"` // globally post-run tasks + TasksPostCleanup []Task `json:"-"` // globally post-run tasks, specially for cleanup actions Loaders []Loader `json:"-"` // external loaders. use cli.WithLoader() prefer HelpScreenWriter HelpWriter `json:"help_screen_writer,omitempty"` // redirect stdout for help screen printing DebugScreenWriter HelpWriter `json:"debug_screen_writer,omitempty"` // redirect stdout for debugging outputs Args []string `json:"args,omitempty"` // for testing Env map[string]string `json:"env,omitempty"` // inject env var & values AutoEnv bool `json:"auto_env,omitempty"` // enable envvars auto-binding? - AutoEnvPrefix string `json:"auto_env_prefix,omitempty"` // envvars auto-binding prefix + AutoEnvPrefix string `json:"auto_env_prefix,omitempty"` // envvars auto-binding prefix, bind them to corresponding flags } // Opt for cmdr system diff --git a/cli/worker/pre.go b/cli/worker/pre.go index 6f0609a..bdb4ee2 100644 --- a/cli/worker/pre.go +++ b/cli/worker/pre.go @@ -112,7 +112,7 @@ func (w *workerS) preEnvSet(ctx context.Context) { } func (w *workerS) postEnvLoad(ctx context.Context) { - // logz.InfoContext(ctx, "postEnvLoad()", "FORCE_DEFAULT_ACTION", os.Getenv("FORCE_DEFAULT_ACTION")) + logz.VerboseContext(ctx, "postEnvLoad()", "FORCE_DEFAULT_ACTION", os.Getenv("FORCE_DEFAULT_ACTION")) if w.Store().Has("app.force-default-action") { w.ForceDefaultAction = w.Store().MustBool("app.force-default-action", false) logz.VerboseContext(ctx, "postEnvLoad() - reset forceDefaultAction from store value", "ForceDefaultAction", w.ForceDefaultAction) diff --git a/cli/worker/worker.go b/cli/worker/worker.go index 949bc2b..0ca14d6 100644 --- a/cli/worker/worker.go +++ b/cli/worker/worker.go @@ -398,9 +398,11 @@ func (w *workerS) Run(ctx context.Context, opts ...cli.Opt) (err error) { defer func() { w.attachError(w.postProcess(ctx, pc)) }() if w.invokeTasks(ctx, pc, w.errs, w.Config.TasksBeforeParse...) || w.attachError(w.parse(ctx, pc)) || + w.invokeTasks(ctx, pc, w.errs, w.Config.TasksParsed...) || w.invokeTasks(ctx, pc, w.errs, w.Config.TasksBeforeRun...) || w.attachError(w.exec(ctx, pc)) || w.invokeTasks(ctx, pc, w.errs, w.Config.TasksAfterRun...) || + w.invokeTasks(ctx, pc, w.errs, w.Config.TasksPostCleanup...) || dummy() { // any errors occurred return diff --git a/cmdr.go b/cmdr.go index 378ff47..577903d 100644 --- a/cmdr.go +++ b/cmdr.go @@ -206,6 +206,41 @@ func WithStore(conf store.Store) cli.Opt { } } +// WithExternalLoaders sets the loaders of external sources, which will be loaded +// at cmdr's preparing time (xref-building time). +// +// The orders could be referred as: +// +// - constructing cmdr commands system (by your prepareApp) +// - entering cmdr.Run +// - cmdr preparing stage +// - build commands and flags xref +// - load and apply envvars if matched +// - load external sources +// - post preparing time +// +// - cmdr parsing stage +// - cmdr invoking stage +// - cmdr cleanup stage +// +// Using our loaders repo is a good idea: https://github.com/hedzr/cmdr-loaders +// +// Typical app: +// +// app = cmdr.New(opts...). +// Info("tiny-app", "0.3.1"). +// Author("The Example Authors") // .Description(``).Header(``).Footer(``) +// cmdr.WithStore(store.New()), // use an option store explicitly, or a dummy store by default +// +// cmdr.WithExternalLoaders( +// local.NewConfigFileLoader(), // import "github.com/hedzr/cmdr-loaders/local" to get in advanced external loading features +// local.NewEnvVarLoader(), +// ), +// ) +// if err := app.Run(ctx); err != nil { +// logz.ErrorContext(ctx, "Application Error:", "err", err) // stacktrace if in debug mode/build +// os.Exit(app.SuggestRetCode()) +// } func WithExternalLoaders(loaders ...cli.Loader) cli.Opt { return func(s *cli.Config) { s.Loaders = append(s.Loaders, loaders...) @@ -221,6 +256,17 @@ func WithTasksBeforeParse(tasks ...cli.Task) cli.Opt { } } +// WithTasksParsed installs callbacks after parsing stage. +// +// The internal stages are: initial -> preload + xref -> parse -> run/invoke -> post-actions. +// +// Another way is disabling cmdr default executing/run/invoke stage by WithDontExecuteAction(true). +func WithTasksParsed(tasks ...cli.Task) cli.Opt { + return func(s *cli.Config) { + s.TasksParsed = append(s.TasksParsed, tasks...) + } +} + // WithTasksBeforeRun installs callbacks before run/invoke stage. // // The internal stages are: initial -> preload + xref -> parse -> run/invoke -> post-actions. @@ -230,15 +276,38 @@ func WithTasksBeforeParse(tasks ...cli.Task) cli.Opt { // - preload & xref // - // - parse +// - // - ( = tasksAfterParse ) // - exec (run/invoke) // - +// - +// - basics.closers...Close() func WithTasksBeforeRun(tasks ...cli.Task) cli.Opt { return func(s *cli.Config) { s.TasksBeforeRun = append(s.TasksBeforeRun, tasks...) } } +// WithTasksAfterRun installs callbacks after run/invoke stage. +// +// The internal stages are: initial -> preload + xref -> parse -> run/invoke -> post-actions. +func WithTasksAfterRun(tasks ...cli.Task) cli.Opt { + return func(s *cli.Config) { + s.TasksAfterRun = append(s.TasksAfterRun, tasks...) + } +} + +// WithTasksPostCleanup install callbacks at cmdr ending. +// +// See the stagings order introduce at [WithTasksBeforeRun]. +// +// See also WithTasksSetupPeripherals, WithPeripherals. +func WithTasksPostCleanup(tasks ...cli.Task) cli.Opt { + return func(s *cli.Config) { + s.TasksPostCleanup = append(s.TasksPostCleanup, tasks...) + } +} + // WithTasksSetupPeripherals gives a special chance to setup // your server's peripherals (such as database, redis, message // queues, or others). @@ -256,8 +325,8 @@ func WithTasksBeforeRun(tasks ...cli.Task) cli.Opt { // import "github.com/hedzr/is/basics" // type Obj struct{} // func (o *Obj) Init(context.Context) *Obj { return o } // initialize itself -// func (o *Obj) Close(){...} // destory itself -// app := cmdr.New(cmdr.WithTasksSetupPeripherals(func(ctx context.Context, cmd *CmdS, runner Runner, extras ...any) (err error) { +// func (o *Obj) Close(){...} // destroy itself +// app := cmdr.New(cmdr.WithTasksSetupPeripherals(func(ctx context.Context, cmd cli.Cmd, runner cli.Runner, extras ...any) (err error) { // obj := new(Obj) // basics.RegisterPeripheral(obj.Init(ctx)) // initialize obj at first, and register it to basics.Closers for auto-shutting-down // return @@ -286,15 +355,6 @@ func WithPeripherals(peripherals ...basics.Peripheral) cli.Opt { } } -// WithTasksAfterRun installs callbacks after run/invoke stage. -// -// The internal stages are: initial -> preload + xref -> parse -> run/invoke -> post-actions. -func WithTasksAfterRun(tasks ...cli.Task) cli.Opt { - return func(s *cli.Config) { - s.TasksAfterRun = append(s.TasksAfterRun, tasks...) - } -} - func WithSortInHelpScreen(b bool) cli.Opt { return func(s *cli.Config) { s.SortInHelpScreen = b