Documentation
¶
Overview ¶
Package bootstrap orchestrates the sequential initialization and LIFO cleanup of application components at startup.
Component registration order ¶
The env component MUST be the first Add() call, and the log component MUST be the second Add() call. This is a hard convention:
- Every component reads configuration via env.Env() inside Init(), so env must be initialized before any other component.
- go's slog package has a usable default instance from program start, so Init() failures at any stage — including before log.Init() — are captured by slog and written to stderr via the default handler.
- env.Close() is a no-op, so LIFO naturally makes log.Close() the last meaningful shutdown operation.
Basic usage:
bootstrap.New().
Add(envComp.Component("config/app.toml", &configFS)). // MUST be first
Add(logComp.Component()). // MUST be second
AddParallel(dbComp.Component()).
PreReady(migrate).
PreReady(initServices).
Add(ginComp.Component(mount)).
Add(httpComp.Component(func() http.Handler { return router })).
PostReady(func() { slog.Info("server ready") }).
Run()
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type App ¶
type App struct {
// contains filtered or unexported fields
}
App is the startup orchestrator. It manages the ordered initialization and LIFO cleanup of application components.
All components participate in the same LIFO close stack. Because env.Close() is a no-op, log.Close() is the last meaningful close.
func New ¶
func New() *App
New creates an empty App.
The first Add() call MUST register the env component, and the second Add() call MUST register the log component. See package-level documentation for the rationale.
Example ¶
ExampleNew shows the complete bootstrap chain.
Registration order convention — the first two Add() calls are reserved:
- 1st Add: env component (MUST be first — all other Init() calls read env)
- 2nd Add: log component (MUST be second — log.Close() is last via LIFO)
All subsequent Add / AddParallel / PreReady calls are order-independent relative to each other (subject to their own logical dependencies).
bootstrap.New().
Add(envComp.Component("config/app.toml", &configFS)). // 1st — env
Add(logComp.Component()). // 2nd — log
AddParallel(dbComp.Component()).
PreReady(migrate).
Add(ginComp.Component(mount)).
Add(httpComp.Component(handler)).
PostReady(func() { slog.Info("server ready") }).
Run()
package main
import (
"fmt"
"github.com/phcp-tech/common-library-golang/bootstrap"
envComp "github.com/phcp-tech/common-library-golang/env/component"
logComp "github.com/phcp-tech/common-library-golang/log/component"
)
func main() {
app := bootstrap.New().
Add(envComp.Component("config/app.toml")). // 1st — env
Add(logComp.Component()) // 2nd — log
fmt.Println(app != nil)
}
Output: true
func (*App) Add ¶
func (a *App) Add(cs ...IComponent) *App
Add appends one or more components to the sequential startup chain. Components are initialized in registration order and closed in LIFO order.
func (*App) AddParallel ¶
func (a *App) AddParallel(cs ...IComponent) *App
AddParallel appends a group of components that initialize concurrently. All components in the group must succeed before the next phase begins. On shutdown, the group's components are closed concurrently as a unit.
func (*App) PostReady ¶ added in v0.1.15
PostReady registers a callback invoked after all steps succeed and before the process blocks waiting for an OS signal. Multiple calls accumulate; callbacks run in registration order.
Example ¶
ExampleApp_PostReady shows how to register multiple post-startup callbacks. Each PostReady call appends a callback; all run in registration order after every step succeeds and before the process blocks waiting for an OS signal.
bootstrap.New().
Add(envComp.Component("config/app.toml", &configFS)). // 1st — env
Add(logComp.Component()). // 2nd — log
Add(ginComp.Component(mount)).
Add(httpComp.Component(handler)).
PostReady(func() { slog.Info("server ready", "addr", ":8080") }).
PostReady(func() { discovery.Register(serviceID) }).
Run()
package main
import (
"fmt"
"github.com/phcp-tech/common-library-golang/bootstrap"
envComp "github.com/phcp-tech/common-library-golang/env/component"
logComp "github.com/phcp-tech/common-library-golang/log/component"
)
func main() {
app := bootstrap.New().
Add(envComp.Component("config/app.toml")). // 1st — env
Add(logComp.Component()). // 2nd — log
PostReady(func() { fmt.Println("first") }).
PostReady(func() { fmt.Println("second") }).
PostReady(func() { fmt.Println("third") })
fmt.Println(app != nil)
}
Output: true
func (*App) PreReady ¶ added in v0.1.15
PreReady registers a setup function that runs inline in the startup sequence, in registration order relative to Add/AddParallel calls. A non-nil error aborts startup identically to a failed Init(). Multiple calls accumulate; functions run in registration order. PreReady steps have no Close and do not participate in LIFO shutdown.
Example ¶
ExampleApp_PreReady shows how to register inline setup functions that run in the startup sequence before the HTTP server starts. Each PreReady call appends a step that runs in registration order relative to Add/AddParallel calls. A non-nil error aborts startup identically to a failed component Init().
bootstrap.New().
Add(envComp.Component("config/app.toml", &configFS)). // 1st — env
Add(logComp.Component()). // 2nd — log
AddParallel(dbComp.Component()).
PreReady(migrate). // runs after DB is ready
PreReady(initServices). // runs after migrate
Add(ginComp.Component(mount)).
Add(httpComp.Component(handler)).
Run()
package main
import (
"fmt"
"github.com/phcp-tech/common-library-golang/bootstrap"
envComp "github.com/phcp-tech/common-library-golang/env/component"
logComp "github.com/phcp-tech/common-library-golang/log/component"
)
func main() {
app := bootstrap.New().
Add(envComp.Component("config/app.toml")). // 1st — env
Add(logComp.Component()). // 2nd — log
PreReady(func() error { return nil }).
PreReady(func() error { return nil })
fmt.Println(app != nil)
}
Output: true
func (*App) Run ¶
func (a *App) Run()
Run initializes all components in registration order, invokes PostReady callbacks, waits for a shutdown signal, then closes all components in LIFO order.
All Init failures are reported via slog. Go's default slog handler writes to stderr, so failures before the log component is initialized are still captured. After log.Init() replaces the default handler, subsequent failures use the configured output.
On Init failure: rolls back already-initialized components in LIFO order and exits with code 1. Panics in the main goroutine are recovered, written to stderr, and exit with code 2.
type IComponent ¶
type IComponent interface {
// Name returns the component name used in log messages.
Name() string
// Init performs initialization. A non-nil error aborts startup,
// triggers rollback of already-started components, and exits the process.
Init() error
// Close releases resources. Called during shutdown in LIFO order.
// Must never panic or block indefinitely.
Close()
}
IComponent is the lifecycle unit managed by App. Init is called during startup in registration order. Close is called during shutdown in LIFO order and must never fail.
func Func ¶
func Func(name string, init func() error, close func()) IComponent
Func creates an IComponent from an init/close function pair. close may be nil when no cleanup is needed.
Example ¶
ExampleFunc shows how to create a simple IComponent from a pair of functions. close may be nil when no cleanup is needed.
package main
import (
"fmt"
"github.com/phcp-tech/common-library-golang/bootstrap"
)
func main() {
c := bootstrap.Func("my-service",
func() error {
// initialise service
return nil
},
func() {
// release resources
},
)
fmt.Println(c.Name())
fmt.Println(c.Init())
}
Output: my-service <nil>