appx

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 12, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package appx builds a cobra-backed application context: build metadata, env-driven configuration, startup hooks, and shutdown.

Typical flow:

  1. NewAppContext(Main, options...) with WithBuildMeta, optional WithMainRoot / WithPreRunner / WithServes / WithCloseFns.
  2. 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.
  3. AppCtx.Execute dispatches cobra subcommands (`run`, `version`, or commands from AppCtx.AddCommand).
  4. 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

Examples

Constants

View Source
const RuntimeEnvKey = "RUNTIME_ENV"

RuntimeEnvKey is the environment variable read by GetRuntime.

Variables

View Source
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]
)
View Source
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

func NewAppContext(main func() error, options ...Option) *AppCtx

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

func (app *AppCtx) AddCommand(name, short string, runE func(args []string) error)

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

func (app *AppCtx) Close(ctx context.Context) error

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

func (app *AppCtx) Conf(ctx context.Context, configurations ...any) context.Context

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

func (app *AppCtx) Execute() error

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

func (app *AppCtx) MainRoot() string

MainRoot returns the path set by WithMainRoot, or the working directory used when constructing the app.

func (*AppCtx) Name

func (app *AppCtx) Name() string

Name returns the application name from build Meta.

func (*AppCtx) Version

func (app *AppCtx) Version() string

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.

func (*AppOption) PreRun

func (o *AppOption) PreRun()

PreRun runs PreRunners then Serves. Invoked by the `run` command.

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) Overwrite

func (m *Meta) Overwrite(meta Meta)

Overwrite copies non-empty fields from meta onto m.

func (*Meta) Print

func (m *Meta) Print()

Print writes a colored multi-line Meta summary to stdout.

func (*Meta) String

func (m *Meta) String() string

String formats Meta as name:feature@version#commit_date(runtime).

func (*Meta) VersionString added in v0.4.9

func (m *Meta) VersionString() string

VersionString joins Name, Version, CommitID, and lowercase Runtime with "-".

type Option

type Option func(*AppCtx)

Option configures an AppCtx at construction time.

func WithBuildMeta

func WithBuildMeta(meta Meta) Option

WithBuildMeta sets the application build Meta, replacing DefaultMeta.

func WithCloseFns added in v0.4.9

func WithCloseFns(closes ...func() error) Option

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

func WithMainRoot(root string) Option

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.

const (
	// RUNTIME_PROD is the production environment.
	RUNTIME_PROD Runtime = "PROD"
	// RUNTIME_STAGING is the staging environment.
	RUNTIME_STAGING Runtime = "STAGING"
	// RUNTIME_DEV is the local or development environment.
	RUNTIME_DEV Runtime = "DEV"
)

func GetRuntime

func GetRuntime() Runtime

GetRuntime returns the Runtime from RuntimeEnvKey, or RUNTIME_PROD when unset or unrecognized.

func (Runtime) String

func (r Runtime) String() string

String returns the runtime label.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL