About the lifecycle
A command struct participates in a four-stage lifecycle by implementing marker interfaces. Every stage is optional; unimplemented stages are skipped.
Hook order
Section titled “Hook order”Validate(ctx, session) — side-effect-free constraint checks (Validator interface)PreRun(ctx, session) — initialize resources: open handles, connect, warm caches (PreRunner)Run(ctx, session) — the command body (Runner — required for a runnable command)PostRun(ctx, session) — follow-up logic; runs ONLY after a successful Run (PostRunner)Finally(ctx, session, err) — guaranteed teardown; runs on success AND failure (Finalizer)type Import struct { Source conga.RequiredArg[string] `help:"Data source path"` Target conga.RequiredArg[string] `help:"Target path"` dsn string pool *sql.DB}
func (i *Import) Validate(ctx context.Context, session *conga.Session) error { if i.Source.Get() == i.Target.Get() { return errors.New("source and target must differ") }
return nil}
func (i *Import) PreRun(ctx context.Context, session *conga.Session) error { pool, err := sql.Open("postgres", i.dsn) if err != nil { return err }
i.pool = pool
return nil}
func (i *Import) Run(ctx context.Context, session *conga.Session) error { // ... main work, using i.pool return nil}
func (i *Import) PostRun(ctx context.Context, session *conga.Session) error { return i.pool.Ping() // sanity check after success}
func (i *Import) Finally(ctx context.Context, session *conga.Session, err error) error { if i.pool != nil { return i.pool.Close() }
return nil}Semantics
Section titled “Semantics”- Error short-circuiting: a stage returning an error stops the chain.
PostRunruns only after a successfulRun. - Guaranteed teardown:
Finally(ctx, session, err)receives the outcome error (nil on success) and always runs — the safe place for cleanup. - Exit codes: an error returned from
Runexits with status 1 (runtime failure); usage errors from earlier stages exit with 2. See Errors & Exit Codes. - Validation split: implement
SelfValidator(Validate(ctx) error) on custom value types for self-validation right after coercion, andValidator(Validate(ctx, session) error) on command structs for cross-field semantic checks. Both run before any hook with side effects. - Signals:
app.Run()installs asignal.NotifyContextforSIGINT/SIGTERM; cancellation surfaces asctx.Err()in your hooks.RunContext(ctx)passes your context through untouched.
Schema errors are programmer errors
Section titled “Schema errors are programmer errors”Struct tag linting, duplicate flags, invalid argument order, and similar problems are reported as errors from App.Run/schema building (wrapping ErrSchema); each renders as a fully self-describing message, e.g.:
struct Serve field Port: schema error: invalid struct tag: unknown struct tag "ragne" (did you mean "range"?). ...Panics are reserved for builder-API misuse (e.g. registering a predictor with an empty name).