Documentation
¶
Overview ¶
Package appx builds a cobra-backed application context: build metadata, env-driven configuration, startup hooks, and shutdown.
Typical flow:
- NewAppContext(Main, options...) with WithBuildMeta, optional WithMainRoot / WithPreRunner / WithServes / WithCloseFns.
- AppCtx.Conf loads config from the environment (and optional config/local.yml), writes defaults under config/, then initializes fields that implement confx types Init hooks.
- AppCtx.Execute dispatches cobra subcommands (`run`, `version`, or commands from AppCtx.AddCommand).
- AppCtx.Close on shutdown closes Conf components and CloseFns.
See example/appx for an end-to-end binary.
Meta and context ¶
Meta holds Name / Feature / Version / CommitID / Date / Runtime. NewAppContext starts from DefaultMeta; WithBuildMeta replaces it. AppCtx.Conf injects Meta via WithAppMeta so nested Initializers (e.g. confotel) can read it with AppMetaFrom / MustAppMeta.
app := appx.NewAppContext(
Main,
appx.WithBuildMeta(appx.Meta{Name: "demo", Version: "v1.0.0"}),
appx.WithMainRoot("."),
)
app.Conf(ctx, &cfg)
Configuration ¶
AppCtx.Conf accepts one or more config pointers. Each named type becomes an envx group (e.g. DEMO__OTEL): merge local.yml → decode env → write config/default.yml and config/.env → types.InitByContext on fields. Anonymous structs are allowed only when a single configuration is passed.
Runners and shutdown ¶
On `run`, AppOption.PreRun runs WithPreRunner sequentially, then WithServes in parallel (BatchRun):
- WithPreRunner: ordered startup init (global config, inject context, …)
- WithServes: long-lived services (HTTP server, cron jobs, …)
- WithCloseFns: extra close hooks; Conf components close automatically in AppCtx.Close and do not need registration
CLI ¶
AppCtx.Execute runs the root cobra command from main. NewAppContext registers `run` (via AppCtx.AddCommand) and `version`.
Runtime ¶
GetRuntime reads RuntimeEnvKey (RUNTIME_ENV): PROD / STAGING / DEV (default PROD). Stored on Meta.Runtime.
Example (Init_failed) ¶
_ = os.Setenv("APP__SomeKey", "")
root := "./testdata2"
app := NewAppContext(
_main,
WithBuildMeta(Meta{Name: "app"}),
WithMainRoot(root),
)
defer os.RemoveAll(root)
defer func() {
fmt.Println(recover())
}()
app.Conf(context.Background(), &struct {
SomeKey MustInitFailed
}{})
Output: failed to init [group:APP] [field:SomeKey]: must fail
Index ¶
- Constants
- Variables
- func BatchRun(runners ...func())
- func BatchRunSync(runners ...func())
- type AppCtx
- func (app *AppCtx) AddCommand(name, short string, runE func(args []string) error)
- func (app *AppCtx) Close(ctx context.Context) error
- func (app *AppCtx) Conf(ctx context.Context, configurations ...any) context.Context
- func (app *AppCtx) Execute() error
- func (app *AppCtx) MainRoot() string
- func (app *AppCtx) Name() string
- func (app *AppCtx) Version() string
- type AppOption
- type Meta
- type Option
- type Runtime
Examples ¶
Constants ¶
const RuntimeEnvKey = "RUNTIME_ENV"
RuntimeEnvKey is the environment variable read by GetRuntime.
Variables ¶
var ( // AppMetaFrom returns [Meta] from ctx if present. // [AppCtx.Conf] injects Meta before types.InitByContext so Initializers // (for example confotel) can resolve service name and version. AppMetaFrom = contextx.From[tCtxMeta, Meta] // MustAppMeta returns [Meta] from ctx or panics when missing. MustAppMeta = contextx.Must[tCtxMeta, Meta] // WithAppMeta stores [Meta] in ctx. WithAppMeta = contextx.With[tCtxMeta, Meta] // CarrierAppMeta builds a contextx.Carrier for [Meta]. CarrierAppMeta = contextx.Carry[tCtxMeta, Meta] )
var DefaultMeta = Meta{ Name: "name", Feature: "branch", Version: "version", CommitID: "commit", Date: time.Now().Format("200601021504"), Runtime: GetRuntime(), }
DefaultMeta is the baseline Meta used when WithBuildMeta is omitted. Runtime is taken from GetRuntime at package init.
Functions ¶
func BatchRun ¶
func BatchRun(runners ...func())
BatchRun executes runners concurrently and waits for all to finish.
func BatchRunSync ¶
func BatchRunSync(runners ...func())
BatchRunSync executes runners sequentially in order.
Types ¶
type AppCtx ¶
type AppCtx struct {
// contains filtered or unexported fields
}
AppCtx is the application handle: cobra CLI, build meta, and env config groups.
func NewAppContext ¶
NewAppContext builds an AppCtx with main as the `run` entry, a hidden root cobra command, and a `version` subcommand. Apply options before AppCtx.Conf.
Example ¶
root := "./testdata1"
app := NewAppContext(
_main,
WithMainRoot(root),
WithBuildMeta(Meta{
Name: "app",
Feature: "main",
Version: "v0.0.1",
CommitID: "efbecda",
Date: "200601021504",
Runtime: RUNTIME_DEV,
}),
WithServes(
func() {
time.Sleep(time.Second * 1)
fmt.Println("batch runner 1")
},
func() {
time.Sleep(time.Second * 2)
fmt.Println("batch runner 2")
},
),
WithPreRunner(
func() {
time.Sleep(time.Second * 1)
fmt.Println("pre runner 1")
},
func() {
time.Sleep(time.Second * 2)
fmt.Println("pre runner 2")
},
),
)
must.NoError(os.MkdirAll(filepath.Join(app.MainRoot(), "config"), os.ModePerm))
must.NoError(os.WriteFile(filepath.Join(app.MainRoot(), "config/local.yml"), []byte(`
APP__CONFIG1__Endpoint_Address: "postgres://hostname:5432/base"
APP__CONFIG1__WorkerID: "100"
APP__CONFIG2__ClientEndpoint_Address: http://localhost:80/demo
APP__CONFIG2__ServerPort: "8080"
`), os.ModePerm))
defer os.RemoveAll(root)
config1 := &Config1{}
config2 := &Config2{}
app.Conf(context.Background(), config1, config2)
cmd := app.cmd
buf := bytes.NewBuffer(nil)
cmd.SetOut(buf)
cmd.SetErr(buf)
{
fmt.Println("exec `app version`")
buf.Reset()
cmd.SetArgs([]string{"version"})
must.NoError(cmd.Execute())
fmt.Println(buf.String())
}
{
fmt.Println("exec `app run`")
buf.Reset()
cmd.SetArgs([]string{"run"})
must.NoError(cmd.Execute())
fmt.Println(buf.String())
}
Output: exec `app version` app:main@v0.0.1#efbecda_200601021504(DEV) exec `app run` app:main@v0.0.1#efbecda_200601021504(DEV) name: app feature: main version: v0.0.1 commit: efbecda date: 200601021504 runtime: DEV APP__CONFIG1__Endpoint_Address=postgres://hostname:5432/base APP__CONFIG1__Endpoint_Auth_DecryptKeyEnv=PASSWORD_DEC_KEY APP__CONFIG1__Endpoint_Auth_Password=-------- APP__CONFIG1__Endpoint_Auth_Username= APP__CONFIG1__Endpoint_Cert_CA= APP__CONFIG1__Endpoint_Cert_Crt= APP__CONFIG1__Endpoint_Cert_Key= APP__CONFIG1__WorkerID=100 APP__CONFIG2__ClientEndpoint_Address=http://localhost:80/demo APP__CONFIG2__ClientEndpoint_Auth_DecryptKeyEnv=PASSWORD_DEC_KEY APP__CONFIG2__ClientEndpoint_Auth_Password=-------- APP__CONFIG2__ClientEndpoint_Auth_Username= APP__CONFIG2__ClientEndpoint_Cert_CA= APP__CONFIG2__ClientEndpoint_Cert_Crt= APP__CONFIG2__ClientEndpoint_Cert_Key= APP__CONFIG2__ServerPort=8080 pre runner 1 pre runner 2 batch runner 1 batch runner 2 main entry
func (*AppCtx) AddCommand ¶ added in v0.4.9
AddCommand registers a cobra subcommand under the app root.
name is the command Use string, short is the Short help text, and runE is invoked with the remaining CLI args when the command is selected. NewAppContext uses this to register `run`.
func (*AppCtx) Close ¶ added in v0.4.9
Close shuts down the application.
It first closes components registered through AppCtx.Conf via types.CloseByContext (no WithCloseFns needed for those), then runs any callbacks registered with WithCloseFns. Errors are joined and returned.
func (*AppCtx) Conf ¶
Conf loads one or more config pointers from the environment, writes default config files under MainRoot/config, and initializes fields that support types.InitByContext. Meta is injected into ctx via WithAppMeta before init.
Each named config type becomes an envx group (e.g. APP__OTEL). Anonymous structs are allowed only when a single configuration is passed.
func (*AppCtx) Execute ¶ added in v0.4.9
Execute runs the root cobra command (typically from main).
Dispatches to registered subcommands such as `run` / `version`, or any command added via AppCtx.AddCommand.
func (*AppCtx) MainRoot ¶
MainRoot returns the path set by WithMainRoot, or the working directory used when constructing the app.
func (*AppCtx) Version ¶
Version returns the formatted build identity string from Meta.String.
type AppOption ¶
type AppOption struct {
*Meta
// PreRunners run sequentially before Serves: ordered startup init such as
// global config or context injection.
PreRunners []func()
// Serves run in parallel after PreRunners: long-lived services such as
// HTTP servers or scheduled jobs.
Serves []func()
// CloseFns are extra close callbacks for [AppCtx.Close]. Components from
// Conf are closed automatically and need not be listed here.
CloseFns []func() error
}
AppOption holds build Meta and startup hooks for an AppCtx.
func (*AppOption) AppendPreRunners ¶
func (o *AppOption) AppendPreRunners(runners ...func())
AppendPreRunners appends ordered startup callbacks.
func (*AppOption) AppendServes ¶ added in v0.4.9
func (o *AppOption) AppendServes(runners ...func())
AppendServes appends long-lived service callbacks.
type Meta ¶
type Meta struct {
Name string `json:"name"`
Feature string `json:"feature"`
Version string `json:"version"`
CommitID string `json:"commit"`
Date string `json:"date"`
Runtime Runtime `json:"runtime"`
}
Meta is the build and runtime identity of an application.
func (*Meta) Print ¶
func (m *Meta) Print()
Print writes a colored multi-line Meta summary to stdout.
func (*Meta) VersionString ¶ added in v0.4.9
VersionString joins Name, Version, CommitID, and lowercase Runtime with "-".
type Option ¶
type Option func(*AppCtx)
Option configures an AppCtx at construction time.
func WithBuildMeta ¶
WithBuildMeta sets the application build Meta, replacing DefaultMeta.
func WithCloseFns ¶ added in v0.4.9
WithCloseFns registers extra close callbacks invoked by AppCtx.Close.
Closable components loaded via AppCtx.Conf do not need registration; they are closed automatically when AppCtx.Close runs.
func WithMainRoot ¶
WithMainRoot sets the application root used to resolve config/local.yml, config/default.yml, and config/.env. When empty, NewAppContext keeps the process working directory.
func WithPreRunner ¶
func WithPreRunner(runners ...func()) Option
WithPreRunner appends ordered callbacks run before Serves on `run`.
Use for sequential startup initialization such as loading global config or injecting a global context, before long-lived services start.
func WithServes ¶ added in v0.4.9
func WithServes(serves ...func()) Option
WithServes appends parallel callbacks run after PreRunners on `run`.
Use for long-lived services such as HTTP servers or scheduled jobs.
type Runtime ¶
type Runtime string
Runtime is the deployment environment label stored on Meta.
func GetRuntime ¶
func GetRuntime() Runtime
GetRuntime returns the Runtime from RuntimeEnvKey, or RUNTIME_PROD when unset or unrecognized.