ghappsetup

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Dec 16, 2025 License: MIT Imports: 13 Imported by: 0

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

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

type LoadFunc func(ctx context.Context) error

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

func NewRuntime(cfg Config) (*Runtime, error)

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

func (r *Runtime) EnsureLoaded(ctx context.Context) error

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

func (r *Runtime) Handler(inner http.Handler) http.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

func (r *Runtime) InstallerHandler(cfg installer.Config) (http.Handler, error)

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

func (r *Runtime) IsReady() bool

IsReady returns true if configuration has been successfully loaded.

func (*Runtime) ListenForReloads

func (r *Runtime) ListenForReloads(ctx context.Context) <-chan struct{}

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

func (r *Runtime) Reload(ctx context.Context) error

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

func (r *Runtime) Start(ctx context.Context) error

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

func (r *Runtime) StartAsync(ctx context.Context) <-chan error

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.

Jump to

Keyboard shortcuts

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