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 ¶
- type ApproveHandler
- type ChangedFiles
- type CloseHandler
- type CommentedHandler
- type Dispatcher
- type DraftHandler
- type Event
- type EventHandler
- type Messenger
- type OpenHandler
- type PR
- type PathMappings
- type RepoBehavior
- type RequestChangeHandler
- type Review
- type ReviewSessions
- type Router
- type Sender
- type Store
- type TargetResolver
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( store Store, behavior RepoBehavior, messenger Messenger, composer *slack.Composer, logger *slog.Logger, detector *aireview.Detector, reviews ReviewSessions, ) *ApproveHandler
NewApproveHandler builds an ApproveHandler.
func (*ApproveHandler) Applicable ¶
type ChangedFiles ¶ added in v0.20.0
type ChangedFiles interface {
ListPullRequestFiles(ctx context.Context, owner, repo string, number int) ([]string, error)
}
ChangedFiles fetches the repo-relative paths a PR touches.
type CloseHandler ¶
type CloseHandler struct {
// contains filtered or unexported fields
}
CloseHandler reacts to a PR being closed (merged or not). It updates every stored Slack message with a [Merged]/[Closed] decoration and, if enabled, adds the corresponding reaction emoji to each one.
func NewCloseHandler ¶
func NewCloseHandler( store Store, behavior RepoBehavior, messenger Messenger, composer *slack.Composer, logger *slog.Logger, reviews ReviewSessions, ) *CloseHandler
NewCloseHandler builds a CloseHandler.
func (*CloseHandler) Applicable ¶
func (h *CloseHandler) Applicable(e Event) bool
Applicable returns true when the action is "closed".
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( store Store, behavior RepoBehavior, messenger Messenger, composer *slack.Composer, logger *slog.Logger, detector *aireview.Detector, reviews ReviewSessions, ) *CommentedHandler
NewCommentedHandler builds a CommentedHandler.
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 every stored Slack message and deletes the PR row — the PR will be re-announced when it's marked ready_for_review again.
func NewDraftHandler ¶
func NewDraftHandler(store Store, messenger Messenger, 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 Messenger ¶ added in v0.20.0
type Messenger interface {
PostMessage(ctx context.Context, channel string, msg slack.Message) (messageID string, err error)
UpdateMessage(ctx context.Context, channel, messageID string, msg slack.Message) error
DeleteMessage(ctx context.Context, channel, messageID string) error
AddReaction(ctx context.Context, channel, messageID, name string) error
}
Messenger is the subset of a chat messenger the handlers use. Slack is the only implementation today (slack.Client satisfies it).
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 fans out one Slack message per resolved target channel and records each message for later updates.
func NewOpenHandler ¶
func NewOpenHandler( store Store, resolver TargetResolver, messenger Messenger, composer *slack.Composer, logger *slog.Logger, ) *OpenHandler
NewOpenHandler builds an OpenHandler.
func (*OpenHandler) Applicable ¶
func (h *OpenHandler) Applicable(e Event) bool
Applicable returns true for "ready_for_review", or "opened" on a non-draft PR.
func (*OpenHandler) Handle ¶
func (h *OpenHandler) Handle(ctx context.Context, e Event) error
Handle posts one message per resolved target channel and records each. It is idempotent per channel: an existing message for a channel is skipped, so a redelivery or a partial-failure retry only posts the missing channels.
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 PathMappings ¶ added in v0.20.0
type PathMappings interface {
Get(ctx context.Context, repository string) (store.RepoMapping, error)
RepoHasPathRules(repository string) bool
TargetsForFiles(repository string, files []string) []store.Target
}
PathMappings is the slice of the mappings provider the Router consumes.
type RepoBehavior ¶ added in v0.20.0
type RepoBehavior interface {
Get(ctx context.Context, repository string) (store.RepoMapping, error)
}
RepoBehavior resolves a repository's per-repo behavioral config (reactions, review flags). Close/draft/review need it but not the per-channel targets.
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( store Store, behavior RepoBehavior, messenger Messenger, composer *slack.Composer, logger *slog.Logger, detector *aireview.Detector, reviews ReviewSessions, ) *RequestChangeHandler
NewRequestChangeHandler builds a RequestChangeHandler.
func (*RequestChangeHandler) Applicable ¶
type Review ¶
type Review struct {
State string
}
Review carries the review state (approved | commented | changes_requested).
type ReviewSessions ¶ added in v0.21.0
type ReviewSessions interface {
GetActive(ctx context.Context, repository string, prNumber int) (store.CodeReview, error)
Finish(ctx context.Context, repository string, prNumber int) error
Reviewers(ctx context.Context, repository string, prNumber int) ([]store.CodeReview, error)
}
ReviewSessions is the review-session view the review and close handlers use. store.CodeReviews satisfies it.
type Router ¶ added in v0.20.0
type Router struct {
// contains filtered or unexported fields
}
Router resolves routing, layering per-path rules over the base repo/org tier when the repository configures `paths:` and a changed-files fetcher is available. With no fetcher (no GitHub token) or no path rules for the repo it is exactly the provider's Get. A fetch error is treated softly: it logs and falls back to the repo tier, so a GitHub hiccup never drops a notification.
func NewRouter ¶ added in v0.20.0
func NewRouter(mappings PathMappings, files ChangedFiles, logger *slog.Logger) *Router
NewRouter builds a Router. files may be nil (no token) — path routing is then inert and every PR resolves to its repo/org tier.
func (*Router) ResolveTargets ¶ added in v0.20.0
func (r *Router) ResolveTargets(ctx context.Context, repository string, prNumber int) (store.RepoMapping, []store.Target, error)
ResolveTargets returns the per-repo behavior plus the fan-out targets for a PR. With no fetcher (no token) or no path rules it returns a single base target. A files-API error is soft: it logs and returns the base target.
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 Store ¶ added in v0.20.0
type Store interface {
AddMessage(ctx context.Context, repository string, prNumber int, channel, messageID string) error
Messages(ctx context.Context, repository string, prNumber int) ([]store.Message, error)
Touch(ctx context.Context, repository string, prNumber int) error
MarkClosed(ctx context.Context, repository string, prNumber int) error
Delete(ctx context.Context, repository string, prNumber int) error
}
Store persists tracked PRs and their per-channel messages.