bootstrap

package
v0.1.23 Latest Latest
Warning

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

Go to latest
Published: Jun 30, 2026 License: Apache-2.0 Imports: 8 Imported by: 0

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

func (a *App) PostReady(fn func()) *App

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

func (a *App) PreReady(fn func() error) *App

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>

Jump to

Keyboard shortcuts

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