// Package app wires the application together using auxilia/steward for // lifecycle management. New constructs the steward Manager; Run drives // it through Inject -> Init -> Start, blocks on context cancellation, // then runs Stop and Destroy. package app import ( "context" "time" "go.bigb.es/auxilia/culpa" "go.bigb.es/auxilia/steward" "sourcecraft.dev/bigbes/huntsman/internal/config" ) // BuildInfo carries linker-injected build metadata for /providers and logs. type BuildInfo struct { Version string Commit string Date string } // stopTimeout caps how long graceful shutdown is allowed to take after // the run context cancels. Slightly longer than the per-server shutdown // budget so steward can finish ordering Stop calls. const stopTimeout = 35 * time.Second // App holds the steward Manager and the config used to build it. type App struct { mgr *steward.Manager } // New loads config and assembles the dependency graph. func New(ctx context.Context, configPath string, info BuildInfo) (*App, error) { cfg, err := config.Load(configPath) if err != nil { return nil, culpa.Wrap(err, "load config") } mgr := steward.NewManager() mgr.AddComponent(ctx, steward.MustConfigurationAsset(*cfg), steward.MustServiceAsset(&loggerWire{}), steward.MustServiceAsset(&searchWire{}), steward.MustServiceAsset(&healthWire{}), steward.MustServiceAsset(&httpServer{Info: info}, steward.Root()), ) if err := mgr.Inject(ctx); err != nil { return nil, culpa.Wrap(err, "inject") } if err := mgr.Init(ctx); err != nil { return nil, culpa.Wrap(err, "init") } return &App{mgr: mgr}, nil } // Run starts every service, blocks until ctx is cancelled, then runs // graceful shutdown via steward.Stop and Destroy. func (a *App) Run(ctx context.Context) error { if err := a.mgr.Start(ctx); err != nil { stopCtx, cancel := context.WithTimeout(context.Background(), stopTimeout) defer cancel() _ = a.mgr.Stop(stopCtx) return culpa.Wrap(err, "start") } <-ctx.Done() stopCtx, cancel := context.WithTimeout(context.Background(), stopTimeout) defer cancel() if err := a.mgr.Stop(stopCtx); err != nil { return culpa.Wrap(err, "stop") } if err := a.mgr.Destroy(stopCtx); err != nil { return culpa.Wrap(err, "destroy") } return nil }