Documentation
¶
Overview ¶
Package ghappsetup provides a unified runtime for GitHub App lifecycle management. It coordinates configuration loading, readiness gating, and hot reloading for both HTTP servers and AWS Lambda functions.
The Runtime type provides a consistent interface across both environments:
HTTP servers use Start() to block until config loads, Handler() to wrap requests with a ReadyGate, and ListenForReloads() for SIGHUP handling.
Lambda functions use EnsureLoaded() for lazy initialization with retry logic on each invocation.
HTTP Server Usage ¶
For HTTP servers, create a Runtime and use its methods to manage the application lifecycle:
runtime, err := ghappsetup.NewRuntime(ghappsetup.Config{
LoadFunc: loadConfig,
AllowedPaths: []string{"/healthz", "/setup", "/callback", "/"},
})
if err != nil {
log.Fatal(err)
}
mux := http.NewServeMux()
mux.HandleFunc("/healthz", runtime.HealthHandler())
mux.HandleFunc("/webhook", webhookHandler)
// Option A: Manual installer wiring
installerHandler, _ := installer.New(installer.Config{
Store: runtime.Store(),
Manifest: manifest,
OnReloadNeeded: runtime.ReloadCallback(),
})
// Option B: Convenience method (recommended)
installerHandler, _ := runtime.InstallerHandler(installer.Config{
Manifest: manifest,
})
mux.Handle("/setup", installerHandler)
srv := &http.Server{Handler: runtime.Handler(mux)}
go srv.ListenAndServe()
// Block until config loads, then listen for SIGHUP reloads
runtime.Start(ctx)
runtime.ListenForReloads(ctx)
Lambda Usage ¶
For Lambda functions, use EnsureLoaded() at the start of each handler invocation:
var runtime *ghappsetup.Runtime
func init() {
runtime, _ = ghappsetup.NewRuntime(ghappsetup.Config{
LoadFunc: func(ctx context.Context) error {
// Resolve SSM parameters if needed
if err := ssmresolver.ResolveEnvironmentWithDefaults(ctx); err != nil {
return err
}
return initHandler()
},
})
}
func handler(ctx context.Context, req events.APIGatewayV2HTTPRequest) (Response, error) {
if err := runtime.EnsureLoaded(ctx); err != nil {
return serviceUnavailableResponse(), nil
}
return handleRequest(ctx, req)
}
Environment Detection ¶
The Runtime automatically detects whether it's running in an HTTP server or Lambda environment by checking for the AWS_LAMBDA_FUNCTION_NAME environment variable. This affects default retry settings:
- HTTP: 30 retries with 2-second intervals (suitable for startup)
- Lambda: 5 retries with 1-second intervals (suitable for cold starts)
Installer Integration ¶
The Runtime integrates with the installer package for GitHub App creation. When credentials are saved via the installer, the Runtime's reload callback is invoked to trigger configuration reloading. This eliminates the need for global state and enables proper dependency injection.
Index ¶
- type Config
- type Environment
- type LoadFunc
- type Runtime
- func (r *Runtime) EnsureLoaded(ctx context.Context) error
- func (r *Runtime) Environment() Environment
- func (r *Runtime) Handler(inner http.Handler) http.Handler
- func (r *Runtime) HealthHandler() http.HandlerFunc
- func (r *Runtime) InstallerHandler(cfg installer.Config) (http.Handler, error)
- func (r *Runtime) IsReady() bool
- func (r *Runtime) ListenForReloads(ctx context.Context) <-chan struct{}
- func (r *Runtime) Reload(ctx context.Context) error
- func (r *Runtime) ReloadCallback() func()
- func (r *Runtime) ResetLoadState()
- func (r *Runtime) Start(ctx context.Context) error
- func (r *Runtime) StartAsync(ctx context.Context) <-chan error
- func (r *Runtime) Store() configstore.Store
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Config ¶
type Config struct {
// Store is the credential storage backend. If nil, one will be created
// automatically using configstore.NewFromEnv().
Store configstore.Store
// LoadFunc is called to load application configuration. This is required.
// The function should read configuration from environment variables or
// other sources and initialize application state. It will be called
// during startup and on reload triggers.
LoadFunc LoadFunc
// AllowedPaths specifies HTTP paths that should be served even before
// configuration is loaded. This is typically used for health checks and
// installer endpoints. Only applicable in HTTP environments.
AllowedPaths []string
// MaxRetries is the maximum number of times to retry loading configuration.
// If zero, defaults are used based on detected environment:
// HTTP: 30 retries, Lambda: 5 retries.
MaxRetries int
// RetryInterval is the time to wait between retry attempts.
// If zero, defaults are used based on detected environment:
// HTTP: 2 seconds, Lambda: 1 second.
RetryInterval time.Duration
}
Config configures the Runtime behavior.
type Environment ¶
type Environment int
Environment represents the detected runtime environment.
const ( // EnvironmentHTTP indicates a long-running HTTP server environment. EnvironmentHTTP Environment = iota // EnvironmentLambda indicates an AWS Lambda function environment. EnvironmentLambda )
type LoadFunc ¶
LoadFunc is the function called to load application configuration. It should return an error if configuration is not yet available, which will trigger a retry according to the configured retry policy.
type Runtime ¶
type Runtime struct {
// contains filtered or unexported fields
}
Runtime coordinates GitHub App configuration loading, readiness gating, and hot reloading. It provides a unified interface for both HTTP servers and Lambda functions.
func NewRuntime ¶
NewRuntime creates a new Runtime with the given configuration. It auto-detects the runtime environment (HTTP vs Lambda) and applies appropriate defaults for retry behavior.
func (*Runtime) EnsureLoaded ¶
EnsureLoaded ensures that configuration has been loaded, performing lazy initialization if needed. This method is idempotent and safe to call from multiple goroutines.
On first call, it attempts to load configuration with retry logic. Subsequent calls return immediately if already loaded, or return the last error if loading failed.
This method is intended for Lambda environments where configuration loading happens lazily on first request. For HTTP servers, use Start instead.
Example usage in a Lambda handler:
func handler(ctx context.Context, req events.APIGatewayV2HTTPRequest) (Response, error) {
if err := runtime.EnsureLoaded(ctx); err != nil {
return serviceUnavailableResponse(), nil
}
return handleRequest(ctx, req)
}
func (*Runtime) Environment ¶
func (r *Runtime) Environment() Environment
Environment returns the detected runtime environment.
func (*Runtime) Handler ¶
Handler wraps the given http.Handler with a ReadyGate that returns 503 Service Unavailable for requests to non-allowed paths before the runtime is ready. Paths specified in Config.AllowedPaths are always forwarded to the inner handler.
The returned handler should be used as the server's main handler.
func (*Runtime) HealthHandler ¶
func (r *Runtime) HealthHandler() http.HandlerFunc
HealthHandler returns an http.HandlerFunc that reports the runtime's readiness status. It returns 200 OK with body "ok" when ready, or 503 Service Unavailable with body "not ready" when not ready.
func (*Runtime) InstallerHandler ¶
InstallerHandler creates an installer http.Handler with the Runtime's store and reload callback pre-configured. This is a convenience method that simplifies the common case of wiring up the installer with the runtime.
The returned handler should be mounted at /setup, /setup/, /callback, and / paths. For example:
installerHandler, err := runtime.InstallerHandler(installer.Config{
Manifest: manifest,
AppDisplayName: "My App",
})
if err != nil {
log.Fatal(err)
}
mux.Handle("/setup", installerHandler)
mux.Handle("/setup/", installerHandler)
mux.Handle("/callback", installerHandler)
mux.Handle("/", installerHandler)
The Config.Store and Config.OnReloadNeeded fields are automatically set by this method and should not be provided in the input config.
func (*Runtime) ListenForReloads ¶
ListenForReloads starts listening for SIGHUP signals and reload triggers from ReloadCallback. When a reload is triggered, LoadFunc is called. The returned channel is closed when the context is canceled.
This should be called after Start() completes successfully.
func (*Runtime) Reload ¶
Reload triggers a configuration reload by calling LoadFunc. This is safe to call from multiple goroutines; concurrent reload requests are coalesced.
func (*Runtime) ReloadCallback ¶
func (r *Runtime) ReloadCallback() func()
ReloadCallback returns a function suitable for use as installer.Config.OnReloadNeeded. The returned function triggers an asynchronous reload.
func (*Runtime) ResetLoadState ¶
func (r *Runtime) ResetLoadState()
ResetLoadState resets the Lambda loading state, allowing EnsureLoaded to attempt loading again. This is primarily useful for testing.
func (*Runtime) Start ¶
Start blocks until configuration is successfully loaded, then marks the runtime as ready. It uses the configured retry policy to attempt loading. Returns an error if configuration cannot be loaded after all retries.
This method is intended for HTTP server environments. For Lambda, use EnsureLoaded instead.
func (*Runtime) StartAsync ¶
StartAsync begins configuration loading in the background and returns immediately. The returned channel receives the result of the loading operation - nil on success or an error if loading failed after all retries. The channel is closed after sending the result.
This method is intended for HTTP server environments where you want to start serving immediately (for health checks and installer endpoints) while configuration loads in the background.
func (*Runtime) Store ¶
func (r *Runtime) Store() configstore.Store
Store returns the credential storage backend used by this Runtime. This is useful when manually wiring up the installer.