Documentation
¶
Overview ¶
Package pullrequest holds the domain model for GitHub pull-request events and the handlers that update Slack in response. Adding a new event trigger means: write a new file in this package implementing EventHandler, register it in the composition root, add a unit test. The dispatcher and the rest of the pipeline do not change.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ApproveHandler ¶
type ApproveHandler struct {
// contains filtered or unexported fields
}
ApproveHandler adds a reaction when a review is submitted with state "approved".
func NewApproveHandler ¶
func NewApproveHandler( messages SlackMessages, mappings RepoMappings, slackClient SlackClient, logger *slog.Logger, emoji string, botEmoji string, detector *aireview.Detector, ) *ApproveHandler
NewApproveHandler builds an ApproveHandler. detector must be non-nil; pass aireview.NewDetector(false) for the disabled state.
func (*ApproveHandler) Applicable ¶
type CloseHandler ¶
type CloseHandler struct {
// contains filtered or unexported fields
}
CloseHandler reacts to a PR being closed (merged or not). It updates the original Slack message with a [Merged]/[Closed] decoration and, if enabled, adds the corresponding reaction emoji.
func NewCloseHandler ¶
func NewCloseHandler( messages SlackMessages, mappings RepoMappings, slackClient SlackClient, composer *slack.Composer, logger *slog.Logger, opts CloseOptions, ) *CloseHandler
NewCloseHandler builds a CloseHandler.
func (*CloseHandler) Applicable ¶
func (h *CloseHandler) Applicable(e Event) bool
Applicable returns true when the action is "closed".
type CloseOptions ¶
CloseOptions tunes the CloseHandler. Reactions on close are toggleable because that's how the legacy PHP service exposed them.
type CommentedHandler ¶
type CommentedHandler struct {
// contains filtered or unexported fields
}
CommentedHandler adds a reaction when a review is submitted or edited with state "commented".
func NewCommentedHandler ¶
func NewCommentedHandler( messages SlackMessages, mappings RepoMappings, slackClient SlackClient, logger *slog.Logger, emoji string, botEmoji string, detector *aireview.Detector, ) *CommentedHandler
NewCommentedHandler builds a CommentedHandler. detector must be non-nil; pass aireview.NewDetector(false) for the disabled state.
func (*CommentedHandler) Applicable ¶
type Dispatcher ¶
type Dispatcher struct {
// contains filtered or unexported fields
}
Dispatcher routes an Event to the first registered EventHandler whose Applicable returns true. Handlers are checked in the order they were registered. If no handler matches, Dispatch logs a Debug line and returns nil (no error) so the HTTP layer still responds 200 OK.
func NewDispatcher ¶
func NewDispatcher(logger *slog.Logger, handlers ...EventHandler) *Dispatcher
NewDispatcher builds a Dispatcher with the given handlers. Registration order is preserved. The logger receives one Debug record per event with no applicable handler — operators enable LOG_LEVEL=debug to triage silent 200 OK deliveries.
type DraftHandler ¶
type DraftHandler struct {
// contains filtered or unexported fields
}
DraftHandler reacts to a PR being converted back to draft. It removes the Slack notification and forgets the message TS — the PR will be re-announced when it's marked ready_for_review again.
func NewDraftHandler ¶
func NewDraftHandler( messages SlackMessages, mappings RepoMappings, slackClient SlackClient, logger *slog.Logger, ) *DraftHandler
NewDraftHandler builds a DraftHandler.
func (*DraftHandler) Applicable ¶
func (h *DraftHandler) Applicable(e Event) bool
Applicable returns true when the action is "converted_to_draft".
type Event ¶
type Event struct {
GitHubEvent string
Action string
Repository string
PR PR
// Review is non-nil only for pull_request_review events.
Review *Review
// PRComment is true for issue_comment events fired on a pull request (the
// payload carried an issue.pull_request reference). False for comments on
// plain issues, which CommentedHandler ignores.
PRComment bool
// Sender identifies the actor that fired the webhook. Type is "User"
// for humans and "Bot" for GitHub Apps (Copilot, dependabot, …).
Sender Sender
}
Event is the immutable record of an incoming pull-request-related webhook, detached from any HTTP payload type. Handlers receive Event and decide via Applicable whether they should run.
type EventHandler ¶
EventHandler is implemented by each PR-lifecycle handler.
Applicable inspects an event and returns true if Handle should run. The dispatcher invokes the first handler whose Applicable returns true and skips the rest — handlers are mutually exclusive.
type OpenHandler ¶
type OpenHandler struct {
// contains filtered or unexported fields
}
OpenHandler reacts to a PR being opened (non-draft) or marked ready_for_review. It posts the first Slack message for the PR and records the message TS for later updates.
func NewOpenHandler ¶
func NewOpenHandler( messages SlackMessages, mappings RepoMappings, slackClient SlackClient, composer *slack.Composer, logger *slog.Logger, dependabotFormat bool, ) *OpenHandler
NewOpenHandler builds an OpenHandler. When dependabotFormat is true, PRs opened by dependabot[bot]/renovate[bot] get the compact composer template; when false they fall back to the standard "please review" message.
func (*OpenHandler) Applicable ¶
func (h *OpenHandler) Applicable(e Event) bool
Applicable returns true for "ready_for_review", or "opened" on a non-draft PR.
type PR ¶
type PR struct {
Number int
Title string
URL string
Author string
Merged bool
Draft bool
// Body is the PR description, consulted by OpenHandler to tell a
// Dependabot/Renovate security advisory from a routine bump.
Body string
// CreatedAt is the PR's open time (pull_request.created_at), rendered as a
// localized date token in the Slack context line. Zero when the payload
// omits it.
CreatedAt time.Time
}
PR holds the PR fields needed across handlers and the message composer.
type RepoMappings ¶
type RepoMappings interface {
Get(ctx context.Context, repository string) (store.RepoMapping, error)
}
RepoMappings reads the per-repository routing to a Slack channel.
type RequestChangeHandler ¶
type RequestChangeHandler struct {
// contains filtered or unexported fields
}
RequestChangeHandler adds a reaction when a review is submitted with state "changes_requested".
func NewRequestChangeHandler ¶
func NewRequestChangeHandler( messages SlackMessages, mappings RepoMappings, slackClient SlackClient, logger *slog.Logger, emoji string, botEmoji string, detector *aireview.Detector, ) *RequestChangeHandler
NewRequestChangeHandler builds a RequestChangeHandler. detector must be non-nil; pass aireview.NewDetector(false) for the disabled state.
func (*RequestChangeHandler) Applicable ¶
type Review ¶
type Review struct {
State string
}
Review carries the review state (approved | commented | changes_requested).
type Sender ¶ added in v0.5.0
Sender identifies the actor on the webhook payload (the reviewer for review events, the PR author for `pull_request` opened events, etc.).
type SlackClient ¶
type SlackClient interface {
PostMessage(ctx context.Context, channel string, msg slack.Message) (ts string, err error)
UpdateMessage(ctx context.Context, channel, ts string, msg slack.Message) error
DeleteMessage(ctx context.Context, channel, ts string) error
AddReaction(ctx context.Context, channel, ts, name string) error
}
SlackClient is the subset of the slack package's client that handlers use.
type SlackMessages ¶
type SlackMessages interface {
Save(ctx context.Context, m store.SlackMessage) error
Get(ctx context.Context, repository string, prNumber int) (store.SlackMessage, error)
Delete(ctx context.Context, repository string, prNumber int) error
// Touch records review/comment activity (bumps updated_at) so the stuck-PR
// digest can tell idle PRs from active ones.
Touch(ctx context.Context, repository string, prNumber int) error
// MarkClosed records that the PR is merged/closed so the digest skips it.
MarkClosed(ctx context.Context, repository string, prNumber int) error
}
SlackMessages reads and writes the per-PR Slack message TS record.