githubapp

package
v0.24.1 Latest Latest
Warning

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

Go to latest
Published: Mar 21, 2024 License: Apache-2.0 Imports: 23 Imported by: 55

Documentation

Overview

Package githubapp implements an http.Handler for GitHub events and provides utilities for building GitHub applications. Most users will create implementations of githubapp.EventHandler to handle different webhook event types and register them with the event dispatcher.

Many functions are instrumented with optional logging and metrics collection. The package also defines functions to create authenticated GitHub clients and manage application installations.

Index

Constants

View Source
const (
	LogKeyEventType       string = "github_event_type"
	LogKeyDeliveryID      string = "github_delivery_id"
	LogKeyRepositoryName  string = "github_repository_name"
	LogKeyRepositoryOwner string = "github_repository_owner"
	LogKeyPRNum           string = "github_pr_num"
	LogKeyInstallationID  string = "github_installation_id"
)
View Source
const (
	MetricsKeyRequests    = "github.requests"
	MetricsKeyRequests2xx = "github.requests.2xx"
	MetricsKeyRequests3xx = "github.requests.3xx"
	MetricsKeyRequests4xx = "github.requests.4xx"
	MetricsKeyRequests5xx = "github.requests.5xx"

	MetricsKeyRequestsCached = "github.requests.cached"

	MetricsKeyRateLimit          = "github.rate.limit"
	MetricsKeyRateLimitRemaining = "github.rate.remaining"
)
View Source
const (
	MetricsKeyQueueLength   = "github.event.queued"
	MetricsKeyActiveWorkers = "github.event.workers"
	MetricsKeyEventAge      = "github.event.age"
	MetricsKeyDroppedEvents = "github.event.dropped"
)
View Source
const (
	DefaultCachingClientCapacity = 64
)
View Source
const (
	DefaultWebhookRoute string = "/api/github/hook"
)
View Source
const (
	MetricsKeyHandlerError = "github.handler.error"
)

Variables

View Source
var (
	ErrCapacityExceeded = errors.New("scheduler: capacity exceeded")
)
View Source
var (
	// HandlerRecoverStackDepth is the max depth of stack trace to recover on a
	// handler panic.
	HandlerRecoverStackDepth = 32
)

Functions

func DefaultAsyncErrorCallback added in v0.3.0

func DefaultAsyncErrorCallback(ctx context.Context, d Dispatch, err error)

DefaultAsyncErrorCallback logs errors.

func DefaultContextDeriver added in v0.3.0

func DefaultContextDeriver(ctx context.Context) context.Context

DefaultContextDeriver copies the logger from the request's context to a new context.

func DefaultErrorCallback

func DefaultErrorCallback(w http.ResponseWriter, r *http.Request, err error)

DefaultErrorCallback logs errors and responds with an appropriate status code.

func DefaultResponseCallback

func DefaultResponseCallback(w http.ResponseWriter, r *http.Request, event string, handled bool)

DefaultResponseCallback responds with a 200 OK for handled events and a 202 Accepted status for all other events. By default, responses are empty. Event handlers may send custom responses by calling the SetResponder function before returning.

func GetInstallationIDFromEvent

func GetInstallationIDFromEvent(event InstallationSource) int64

GetInstallationIDFromEvent returns the installation ID from a GitHub webhook event payload.

func GetResponder

func GetResponder(ctx context.Context) func(http.ResponseWriter, *http.Request)

GetResponder returns the response function that was set by an event handler. If no response function exists, it returns nil. There is usually no reason to call this outside of a response callback implementation.

func InitializeResponder

func InitializeResponder(ctx context.Context) context.Context

InitializeResponder prepares the context to work with SetResponder and GetResponder. It is used to test handlers that call SetResponder or to implement custom event dispatchers that support responders.

func NewDefaultEventDispatcher

func NewDefaultEventDispatcher(c Config, handlers ...EventHandler) http.Handler

NewDefaultEventDispatcher is a convenience method to create an event dispatcher from configuration using the default error and response callbacks.

func NewEventDispatcher

func NewEventDispatcher(handlers []EventHandler, secret string, opts ...DispatcherOption) http.Handler

NewEventDispatcher creates an http.Handler that dispatches GitHub webhook requests to the appropriate event handlers. It validates payload integrity using the given secret value.

Responses are controlled by optional error and response callbacks. If these options are not provided, default callbacks are used.

func PreparePRContext

func PreparePRContext(ctx context.Context, installationID int64, repo *github.Repository, number int) (context.Context, zerolog.Logger)

PreparePRContext adds information about a pull request to the logger in a context and returns the modified context and logger.

func PrepareRepoContext

func PrepareRepoContext(ctx context.Context, installationID int64, repo *github.Repository) (context.Context, zerolog.Logger)

PrepareRepoContext adds information about a repository to the logger in a context and returns the modified context and logger.

func SetResponder

func SetResponder(ctx context.Context, responder func(http.ResponseWriter, *http.Request))

SetResponder sets a function that sends a response to GitHub after event processing completes. The context must be initialized by InitializeResponder. The event dispatcher does this automatically before calling a handler.

Customizing individual handler responses should be rare. Applications that want to modify the standard responses should consider registering a response callback before using this function.

Types

type AsyncErrorCallback added in v0.3.0

type AsyncErrorCallback func(ctx context.Context, d Dispatch, err error)

AsyncErrorCallback is called by an asynchronous scheduler when an event handler returns an error or panics. The error from the handler is passed directly as the final argument.

If the handler panics, err will be a HandlerPanicError.

func MetricsAsyncErrorCallback added in v0.5.0

func MetricsAsyncErrorCallback(reg metrics.Registry) AsyncErrorCallback

MetricsAsyncErrorCallback logs errors and increments an error counter.

type ClientCreator

type ClientCreator interface {
	// NewAppClient returns a new github.Client that performs app authentication for
	// the GitHub app with a specific integration ID and private key. The returned
	// client makes all calls using the application's authorization token. The
	// client gets that token by creating and signing a JWT for the application and
	// requesting a token using it. The token is cached by the client and is
	// refreshed as needed if it expires.
	//
	// Used for performing app-level operations that are not associated with a
	// specific installation.
	//
	// See the following for more information:
	//  * https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#authenticating-as-a-github-app
	//
	// Authenticating as a GitHub App lets you do a couple of things:
	//  * You can retrieve high-level management information about your GitHub App.
	//  * You can request access tokens for an installation of the app.
	//
	// Tips for determining the arguments for this function:
	//  * the integration ID is listed as "ID" in the "About" section of the app's page
	//  * the key bytes must be a PEM-encoded PKCS1 or PKCS8 private key for the application
	NewAppClient() (*github.Client, error)

	// NewAppV4Client returns an app-authenticated v4 API client, similar to NewAppClient.
	NewAppV4Client() (*githubv4.Client, error)

	// NewInstallationClient returns a new github.Client that performs app
	// authentication for the GitHub app with the a specific integration ID, private
	// key, and the given installation ID. The returned client makes all calls using
	// the application's authorization token. The client gets that token by creating
	// and signing a JWT for the application and requesting a token using it. The
	// token is cached by the client and is refreshed as needed if it expires.
	//
	// See the following for more information:
	//  * https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#authenticating-as-an-installation
	//
	// Authenticating as an installation of a Github App lets you perform the following:
	//  * https://developer.github.com/v3/apps/available-endpoints/
	//
	// Tips for determining the arguments for this function:
	//  * the integration ID is listed as "ID" in the "About" section of the app's page
	//  * the installation ID is the ID that is shown in the URL of https://{githubURL}/settings/installations/{#}
	//      (navigate to the "installations" page without the # and go to the app's page to see the number)
	//  * the key bytes must be a PEM-encoded PKCS1 or PKCS8 private key for the application
	NewInstallationClient(installationID int64) (*github.Client, error)

	// NewInstallationV4Client returns an installation-authenticated v4 API client, similar to NewInstallationClient.
	NewInstallationV4Client(installationID int64) (*githubv4.Client, error)

	// NewTokenSourceClient returns a *github.Client that uses the passed in OAuth token source for authentication.
	NewTokenSourceClient(ts oauth2.TokenSource) (*github.Client, error)

	// NewTokenSourceClient returns a *githubv4.Client that uses the passed in OAuth token source for authentication.
	NewTokenSourceV4Client(ts oauth2.TokenSource) (*githubv4.Client, error)

	// NewTokenClient returns a *github.Client that uses the passed in OAuth token for authentication.
	NewTokenClient(token string) (*github.Client, error)

	// NewTokenV4Client returns a *githubv4.Client that uses the passed in OAuth token for authentication.
	NewTokenV4Client(token string) (*githubv4.Client, error)
}

func NewCachingClientCreator

func NewCachingClientCreator(delegate ClientCreator, capacity int) (ClientCreator, error)

NewCachingClientCreator returns a ClientCreator that creates a GitHub client for installations of the app specified by the provided arguments. It uses an LRU cache of the provided capacity to store clients created for installations and returns cached clients when a cache hit exists.

func NewClientCreator

func NewClientCreator(v3BaseURL, v4BaseURL string, integrationID int64, privKeyBytes []byte, opts ...ClientOption) ClientCreator

NewClientCreator returns a ClientCreator that creates a GitHub client for installations of the app specified by the provided arguments.

func NewDefaultCachingClientCreator

func NewDefaultCachingClientCreator(c Config, opts ...ClientOption) (ClientCreator, error)

NewDefaultCachingClientCreator returns a ClientCreator using values from the configuration or other defaults.

type ClientLoggingOption added in v0.12.0

type ClientLoggingOption func(*clientLoggingOptions)

ClientLoggingOption controls behavior of client request logs.

func LogRequestBody added in v0.12.0

func LogRequestBody(patterns ...string) ClientLoggingOption

LogRequestBody enables request body logging for requests to paths matching any of the regular expressions in patterns. It panics if any of the patterns is not a valid regular expression.

func LogResponseBody added in v0.12.0

func LogResponseBody(patterns ...string) ClientLoggingOption

LogResponseBody enables response body logging for requests to paths matching any of the regular expressions in patterns. It panics if any of the patterns is not a valid regular expression.

type ClientMiddleware

type ClientMiddleware func(http.RoundTripper) http.RoundTripper

ClientMiddleware modifies the transport of a GitHub client to add common functionality, like logging or metrics collection.

func ClientLogging

func ClientLogging(lvl zerolog.Level, opts ...ClientLoggingOption) ClientMiddleware

ClientLogging creates client middleware that logs request and response information at the given level. If the request fails without creating a response, it is logged with a status code of -1. The middleware uses a logger from the request context.

func ClientMetrics

func ClientMetrics(registry metrics.Registry) ClientMiddleware

ClientMetrics creates client middleware that records metrics about all requests. It also defines the metrics in the provided registry.

type ClientOption

type ClientOption func(c *clientCreator)

func WithClientCaching

func WithClientCaching(alwaysValidate bool, cache func() httpcache.Cache) ClientOption

WithClientCaching sets an HTTP cache for all created clients using the provided cache implementation If alwaysValidate is true, the cache validates all saved responses before returning them. Otherwise, it respects the caching headers returned by GitHub. https://developer.github.com/v3/#conditional-requests

func WithClientMiddleware

func WithClientMiddleware(middleware ...ClientMiddleware) ClientOption

WithClientMiddleware adds middleware that is applied to all created clients.

func WithClientTimeout added in v0.4.0

func WithClientTimeout(timeout time.Duration) ClientOption

WithClientTimeout sets a request timeout for requests made by all created clients.

func WithClientUserAgent

func WithClientUserAgent(agent string) ClientOption

WithClientUserAgent sets the base user agent for all created clients.

func WithTransport added in v0.9.2

func WithTransport(transport http.RoundTripper) ClientOption

WithTransport sets the http.RoundTripper used to make requests. Clients can provide an http.Transport instance to modify TLS, proxy, or timeout options. By default, clients use http.DefaultTransport.

type Config

type Config struct {
	WebURL   string `yaml:"web_url" json:"webUrl"`
	V3APIURL string `yaml:"v3_api_url" json:"v3ApiUrl"`
	V4APIURL string `yaml:"v4_api_url" json:"v4ApiUrl"`

	App struct {
		IntegrationID int64  `yaml:"integration_id" json:"integrationId"`
		WebhookSecret string `yaml:"webhook_secret" json:"webhookSecret"`
		PrivateKey    string `yaml:"private_key" json:"privateKey"`
	} `yaml:"app" json:"app"`

	OAuth struct {
		ClientID     string `yaml:"client_id" json:"clientId"`
		ClientSecret string `yaml:"client_secret" json:"clientSecret"`
	} `yaml:"oauth" json:"oauth"`
}

func (*Config) SetValuesFromEnv

func (c *Config) SetValuesFromEnv(prefix string)

SetValuesFromEnv sets values in the configuration from coresponding environment variables, if they exist. The optional prefix is added to the start of the environment variable names.

type ContextDeriver added in v0.3.0

type ContextDeriver func(context.Context) context.Context

ContextDeriver creates a new independent context from a request's context. The new context must be based on context.Background(), not the input.

type Dispatch added in v0.3.0

type Dispatch struct {
	Handler EventHandler

	EventType  string
	DeliveryID string
	Payload    []byte
}

Dispatch is a webhook payload and the handler that handles it.

func (Dispatch) Execute added in v0.3.0

func (d Dispatch) Execute(ctx context.Context) error

Execute calls the Dispatch's handler with the stored arguments.

type DispatcherOption

type DispatcherOption func(*eventDispatcher)

DispatcherOption configures properties of an event dispatcher.

func WithErrorCallback

func WithErrorCallback(onError ErrorCallback) DispatcherOption

WithErrorCallback sets the error callback for a dispatcher.

func WithResponseCallback

func WithResponseCallback(onResponse ResponseCallback) DispatcherOption

WithResponseCallback sets the response callback for an event dispatcher.

func WithScheduler added in v0.3.0

func WithScheduler(s Scheduler) DispatcherOption

WithScheduler sets the scheduler used to process events. Setting a non-default scheduler can enable asynchronous processing. When a scheduler is asynchronous, the dispatcher validatates event payloads, queues valid events for handling, and then responds to GitHub without waiting for the handler to complete. This is useful when handlers may take longer than GitHub's timeout for webhook deliveries.

type ErrorCallback

type ErrorCallback func(w http.ResponseWriter, r *http.Request, err error)

ErrorCallback is called when an event handler returns an error. The error from the handler is passed directly as the final argument.

func MetricsErrorCallback added in v0.5.0

func MetricsErrorCallback(reg metrics.Registry) ErrorCallback

MetricsErrorCallback logs errors, increments an error counter, and responds with an appropriate status code.

type EventHandler

type EventHandler interface {
	// Handles returns a list of GitHub events that this handler handles
	// See https://developer.github.com/v3/activity/events/types/
	Handles() []string

	// Handle processes the GitHub event "eventType" with the given delivery ID
	// and payload. The EventDispatcher guarantees that the Handle method will
	// only be called for the events returned by Handles().
	//
	// If Handle returns an error, processing stops and the error is passed
	// directly to the configured error handler.
	Handle(ctx context.Context, eventType, deliveryID string, payload []byte) error
}

type HandlerPanicError added in v0.12.1

type HandlerPanicError struct {
	// contains filtered or unexported fields
}

HandlerPanicError is an error created from a recovered handler panic.

func (HandlerPanicError) Error added in v0.12.1

func (e HandlerPanicError) Error() string

func (HandlerPanicError) Format added in v0.12.1

func (e HandlerPanicError) Format(s fmt.State, verb rune)

Format formats the error optionally including the stack trace.

%s    the error message
%v    the error message and the source file and line number for each stack frame

Format accepts the following flags:

%+v   the error message and the function, file, and line for each stack frame

func (HandlerPanicError) StackTrace added in v0.12.1

func (e HandlerPanicError) StackTrace() []runtime.Frame

StackTrace returns the stack of the panicking goroutine.

func (HandlerPanicError) Value added in v0.12.1

func (e HandlerPanicError) Value() interface{}

Value returns the exact value with which panic() was called.

type Installation

type Installation struct {
	ID      int64
	Owner   string
	OwnerID int64
}

Installation is a minimal representation of a GitHub app installation.

type InstallationNotFound

type InstallationNotFound string

InstallationNotFound is returned when no installation exists for a specific owner or repository.

func (InstallationNotFound) Error

func (err InstallationNotFound) Error() string

type InstallationSource

type InstallationSource interface {
	GetInstallation() *github.Installation
}

InstallationSource is implemented by GitHub webhook event payload types.

type InstallationsService

type InstallationsService interface {
	// ListAll returns all installations for this app.
	ListAll(ctx context.Context) ([]Installation, error)

	// GetByOwner returns the installation for an owner (user or organization).
	// It returns an InstallationNotFound error if no installation exists for
	// the owner.
	GetByOwner(ctx context.Context, owner string) (Installation, error)

	// GetByRepository returns the installation for a repository.
	// It returns an InstallationNotFound error if no installation exists for
	// the repository.
	GetByRepository(ctx context.Context, owner string, repo string) (Installation, error)
}

InstallationsService retrieves installation information for a given app. Implementations may chose how to retrieve, store, or cache these values.

This service is useful for background processes that do not respond directly to webhooks, since webhooks provide installation IDs in their payloads.

func NewCachingInstallationsService added in v0.18.0

func NewCachingInstallationsService(delegate InstallationsService, expiry, cleanup time.Duration) InstallationsService

NewCachingInstallationsService returns an InstallationsService that always queries GitHub. It should be created with a client that authenticates as the target. It uses a time based cache of the provided expiry/cleanup time to store app installation info for repositories or owners and returns the cached installation info when a cache hit exists.

func NewInstallationsService

func NewInstallationsService(appClient *github.Client) InstallationsService

NewInstallationsService returns an InstallationsService that always queries GitHub. It should be created with a client that authenticates as the target application.

type ResponseCallback

type ResponseCallback func(w http.ResponseWriter, r *http.Request, event string, handled bool)

ResponseCallback is called to send a response to GitHub after an event is handled. It is passed the event type and a flag indicating if an event handler was called for the event.

type Scheduler added in v0.3.0

type Scheduler interface {
	Schedule(ctx context.Context, d Dispatch) error
}

Scheduler is a strategy for executing event handlers.

The Schedule method takes a Dispatch and executes it by calling the handler for the payload. The execution may be asynchronous, but the scheduler must create a new context in this case. The dispatcher waits for Schedule to return before responding to GitHub, so asynchronous schedulers should only return errors that happen during scheduling, not during execution.

Schedule may return ErrCapacityExceeded if it cannot schedule or queue new events at the time of the call.

func AsyncScheduler added in v0.3.0

func AsyncScheduler(opts ...SchedulerOption) Scheduler

AsyncScheduler returns a scheduler that executes handlers in new goroutines. Goroutines are not reused and there is no limit on the number created.

func DefaultScheduler added in v0.3.0

func DefaultScheduler() Scheduler

DefaultScheduler returns a scheduler that executes handlers in the go routine of the caller and returns any error.

func QueueAsyncScheduler added in v0.3.0

func QueueAsyncScheduler(queueSize int, workers int, opts ...SchedulerOption) Scheduler

QueueAsyncScheduler returns a scheduler that executes handlers in a fixed number of worker goroutines. If no workers are available, events queue until the queue is full.

type SchedulerOption added in v0.3.0

type SchedulerOption func(*scheduler)

SchedulerOption configures properties of a scheduler.

func WithAsyncErrorCallback added in v0.3.0

func WithAsyncErrorCallback(onError AsyncErrorCallback) SchedulerOption

WithAsyncErrorCallback sets the error callback for an asynchronous scheduler. If not set, the scheduler uses DefaultAsyncErrorCallback.

func WithContextDeriver added in v0.3.0

func WithContextDeriver(deriver ContextDeriver) SchedulerOption

WithContextDeriver sets the context deriver for an asynchronous scheduler. If not set, the scheduler uses DefaultContextDeriver.

func WithSchedulingMetrics added in v0.3.0

func WithSchedulingMetrics(r metrics.Registry) SchedulerOption

WithSchedulingMetrics enables metrics reporting for schedulers.

type ValidationError

type ValidationError struct {
	EventType  string
	DeliveryID string
	Cause      error
}

ValidationError is passed to error callbacks when the webhook payload fails validation.

func (ValidationError) Error

func (ve ValidationError) Error() string

Jump to

Keyboard shortcuts

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