Documentation
¶
Overview ¶
Package github provides GitHub CLI integration for PR and issue operations.
It wraps the gh CLI binary (https://cli.github.com/) via os/exec to provide pull request creation, review, merge, and CI/CD status checking. All GitHub interactions are abstracted behind testable interfaces.
Index ¶
- Constants
- Variables
- func NewGHClient(root string) *ghClient
- func NewPRMerger(gh GHClient, reviewer PRReviewer, logger *slog.Logger) (*prMerger, error)
- func NewPRReviewer(gh GHClient, qualityGate quality.Gate, logger *slog.Logger) (*prReviewer, error)
- type Author
- type Check
- type CheckConclusion
- type CheckStatus
- type CloseResult
- type Comment
- type DefaultIssueCloser
- type ExecFunc
- type GHClient
- type Issue
- type IssueCloser
- type IssueCloserOption
- type IssueParser
- type Label
- type MergeMethod
- type MergeOptions
- type MergeResult
- type PRCreateOptions
- type PRDetails
- type PRMerger
- type PRReviewer
- type PrerequisiteCheck
- type Registry
- type RetryError
- type ReviewDecision
- type ReviewInput
- type ReviewReport
- type SpecLinker
- type SpecMapping
Constants ¶
const RegistryFileName = "github-spec-registry.json"
RegistryFileName is the JSON file that maps GitHub issues to SPEC IDs.
const RegistryVersion = "1.0.0"
RegistryVersion is the current schema version of the registry file.
Variables ¶
var ( // ErrGHNotFound indicates the gh CLI binary is not in PATH. ErrGHNotFound = errors.New("github: gh CLI not found") // ErrGHNotAuthenticated indicates gh is not authenticated. ErrGHNotAuthenticated = errors.New("github: gh not authenticated") // ErrPRNotFound indicates the specified pull request does not exist. ErrPRNotFound = errors.New("github: pull request not found") // ErrPRAlreadyExists indicates a PR already exists for this branch. ErrPRAlreadyExists = errors.New("github: pull request already exists for branch") // ErrMergeBlocked indicates one or more merge prerequisites are not met. ErrMergeBlocked = errors.New("github: merge blocked by unmet prerequisites") // ErrMergeConflict indicates the PR has merge conflicts. ErrMergeConflict = errors.New("github: merge conflicts detected") // ErrCIFailed indicates CI/CD checks failed. ErrCIFailed = errors.New("github: CI/CD checks failed") // ErrReviewRequired indicates PR review has not been approved. ErrReviewRequired = errors.New("github: review approval required") // ErrAutoMergeNotRequested indicates the --auto-merge flag was not specified. ErrAutoMergeNotRequested = errors.New("github: auto-merge not requested") // ErrIssueNotFound indicates the specified issue does not exist. ErrIssueNotFound = errors.New("github: issue not found") // ErrCommentFailed indicates a failure posting a comment to an issue. ErrCommentFailed = errors.New("github: failed to post comment") // ErrCloseFailed indicates a failure closing an issue. ErrCloseFailed = errors.New("github: failed to close issue") // ErrLabelFailed indicates a failure adding a label to an issue. ErrLabelFailed = errors.New("github: failed to add label") // ErrMaxRetriesExceeded indicates all retry attempts have been exhausted. ErrMaxRetriesExceeded = errors.New("github: maximum retries exceeded") // ErrMappingExists indicates the issue is already linked to a SPEC. ErrMappingExists = errors.New("github: issue already linked to a SPEC") // ErrMappingNotFound indicates no SPEC is linked to the issue. ErrMappingNotFound = errors.New("github: no SPEC linked to issue") // ErrNilGHClient indicates a nil GHClient was provided to a constructor. ErrNilGHClient = errors.New("github: GHClient must not be nil") // ErrNilQualityGate indicates a nil quality.Gate was provided to a constructor. ErrNilQualityGate = errors.New("github: quality gate must not be nil") // ErrNilReviewer indicates a nil PRReviewer was provided to a constructor. ErrNilReviewer = errors.New("github: PRReviewer must not be nil") )
Sentinel errors for the github package.
Functions ¶
func NewGHClient ¶
func NewGHClient(root string) *ghClient
NewGHClient creates a new GitHub CLI client rooted at the given directory.
func NewPRMerger ¶
func NewPRMerger(gh GHClient, reviewer PRReviewer, logger *slog.Logger) (*prMerger, error)
NewPRMerger creates a merger that checks prerequisites before merging. Returns an error if gh or reviewer is nil.
Types ¶
type Check ¶
type Check struct {
Name string `json:"name"`
Status string `json:"status"`
Conclusion string `json:"conclusion"`
}
Check represents a single CI/CD status check.
type CheckConclusion ¶
type CheckConclusion string
CheckConclusion represents the overall CI/CD check result.
const ( // CheckPass indicates all CI/CD checks passed. CheckPass CheckConclusion = "pass" // CheckFail indicates one or more CI/CD checks failed. CheckFail CheckConclusion = "fail" // CheckPending indicates CI/CD checks are still running. CheckPending CheckConclusion = "pending" )
type CheckStatus ¶
type CheckStatus struct {
Overall CheckConclusion
Checks []Check
}
CheckStatus holds the aggregated CI/CD check results for a PR.
type CloseResult ¶
type CloseResult struct {
// IssueNumber is the GitHub issue that was processed.
IssueNumber int
// CommentPosted indicates whether the success comment was posted.
CommentPosted bool
// LabelAdded indicates whether the resolved label was added.
LabelAdded bool
// IssueClosed indicates whether the issue was closed.
IssueClosed bool
}
CloseResult holds the outcome of an issue closure operation.
type Comment ¶
type Comment struct {
Body string `json:"body"`
Author Author `json:"author"`
CreatedAt time.Time `json:"createdAt"`
}
Comment represents a GitHub issue comment.
type DefaultIssueCloser ¶
type DefaultIssueCloser struct {
// contains filtered or unexported fields
}
DefaultIssueCloser implements IssueCloser using the gh CLI.
func NewIssueCloser ¶
func NewIssueCloser(root string, opts ...IssueCloserOption) *DefaultIssueCloser
NewIssueCloser creates a new DefaultIssueCloser rooted at the given directory.
func (*DefaultIssueCloser) Close ¶
func (c *DefaultIssueCloser) Close(ctx context.Context, issueNumber int, comment string) (*CloseResult, error)
Close posts a comment, adds the "resolved" label, and closes the issue. It retries each step up to maxRetries times with exponential backoff.
The operation is sequential: comment -> label -> close. If comment posting fails, label and close are skipped. If label fails, close is still attempted (label is non-critical). The result tracks which steps succeeded for partial failure recovery.
type ExecFunc ¶
ExecFunc is the function signature for executing gh CLI commands. It matches the signature of execGH for seamless integration.
type GHClient ¶
type GHClient interface {
// PRCreate creates a pull request and returns the PR number.
PRCreate(ctx context.Context, opts PRCreateOptions) (int, error)
// PRView retrieves PR details by number.
PRView(ctx context.Context, number int) (*PRDetails, error)
// PRMerge merges a PR by number using the specified method.
// If deleteBranch is true, the head branch is deleted after merge.
PRMerge(ctx context.Context, number int, method MergeMethod, deleteBranch bool) error
// PRChecks returns the CI/CD check status for a PR.
PRChecks(ctx context.Context, number int) (*CheckStatus, error)
// Push pushes the current branch to the remote.
Push(ctx context.Context, dir string) error
// IsAuthenticated checks whether gh is authenticated.
IsAuthenticated(ctx context.Context) error
}
GHClient abstracts GitHub CLI (gh) operations for testability.
type Issue ¶
type Issue struct {
Number int `json:"number"`
Title string `json:"title"`
Body string `json:"body"`
Labels []Label `json:"labels"`
Author Author `json:"author"`
Comments []Comment `json:"comments"`
}
Issue represents a parsed GitHub issue.
func ParseIssueFromJSON ¶
ParseIssueFromJSON parses an Issue from raw JSON bytes. Useful for testing and offline processing.
func (*Issue) LabelNames ¶
LabelNames returns a string slice of label names for this issue.
type IssueCloser ¶
type IssueCloser interface {
// Close posts a comment, adds the resolved label, and closes the issue.
// Returns a partial CloseResult even on failure to indicate which steps succeeded.
Close(ctx context.Context, issueNumber int, comment string) (*CloseResult, error)
}
IssueCloser posts a success comment, adds a resolved label, and closes a GitHub issue.
type IssueCloserOption ¶
type IssueCloserOption func(*DefaultIssueCloser)
IssueCloserOption configures a DefaultIssueCloser.
func WithCloserLogger ¶
func WithCloserLogger(l *slog.Logger) IssueCloserOption
WithCloserLogger sets the logger for the issue closer.
func WithExecFunc ¶
func WithExecFunc(fn ExecFunc) IssueCloserOption
WithExecFunc sets a custom execution function (used for testing).
func WithMaxRetries ¶
func WithMaxRetries(n int) IssueCloserOption
WithMaxRetries sets the maximum number of retry attempts per operation.
func WithRetryDelay ¶
func WithRetryDelay(d time.Duration) IssueCloserOption
WithRetryDelay sets the base delay between retry attempts. Actual delay doubles with each attempt (exponential backoff). Zero disables backoff; negative values are ignored.
type IssueParser ¶
IssueParser parses GitHub issues.
func NewIssueParser ¶
func NewIssueParser(root string) IssueParser
NewIssueParser returns an IssueParser that uses the gh CLI. The root parameter sets the working directory for gh commands, which must be inside the target GitHub repository.
type MergeMethod ¶
type MergeMethod string
MergeMethod represents the Git merge strategy for a PR.
const ( // MergeMethodMerge creates a merge commit. MergeMethodMerge MergeMethod = "merge" // MergeMethodSquash squashes all commits into one. MergeMethodSquash MergeMethod = "squash" // MergeMethodRebase rebases commits onto the base branch. MergeMethodRebase MergeMethod = "rebase" )
type MergeOptions ¶
type MergeOptions struct {
// AutoMerge indicates whether --auto-merge was specified.
AutoMerge bool
// Method is the merge strategy (merge, squash, rebase).
Method MergeMethod
// DeleteBranch indicates whether to delete the branch after merge.
DeleteBranch bool
// RequireReview indicates whether review approval is required.
RequireReview bool
// RequireChecks indicates whether CI checks must pass.
RequireChecks bool
// SpecID is the SPEC identifier for quality validation context.
SpecID string
}
MergeOptions configures the merge operation.
type MergeResult ¶
type MergeResult struct {
// Merged indicates whether the PR was actually merged.
Merged bool
// PRNumber is the pull request number.
PRNumber int
// Method is the merge strategy used.
Method MergeMethod
// BranchDeleted indicates whether the feature branch was deleted.
BranchDeleted bool
// MergedAt is the timestamp of the merge.
MergedAt time.Time
}
MergeResult summarizes the merge outcome.
type PRCreateOptions ¶
type PRCreateOptions struct {
Title string
Body string
BaseBranch string
HeadBranch string
Labels []string
IssueNumber int
}
PRCreateOptions holds parameters for creating a pull request.
type PRDetails ¶
type PRDetails struct {
Number int `json:"number"`
Title string `json:"title"`
State string `json:"state"`
Mergeable string `json:"mergeable"`
HeadBranch string `json:"headRefName"`
BaseBranch string `json:"baseRefName"`
URL string `json:"url"`
CreatedAt time.Time `json:"createdAt"`
}
PRDetails holds information about an existing pull request.
type PRMerger ¶
type PRMerger interface {
// Merge attempts to merge a PR if all conditions are met.
Merge(ctx context.Context, prNumber int, opts MergeOptions) (*MergeResult, error)
// CheckPrerequisites verifies all merge conditions without merging.
CheckPrerequisites(ctx context.Context, prNumber int, opts MergeOptions) (*PrerequisiteCheck, error)
}
PRMerger handles conditional PR merge operations.
type PRReviewer ¶
type PRReviewer interface {
// Review runs quality validation and CI checks, then generates a review decision.
// Pass nil for input to have Review fetch all data itself.
Review(ctx context.Context, prNumber int, specID string, input *ReviewInput) (*ReviewReport, error)
}
PRReviewer generates quality-based PR review reports.
type PrerequisiteCheck ¶
type PrerequisiteCheck struct {
// AllMet indicates all prerequisites are satisfied.
AllMet bool
// AutoMergeFlag indicates --auto-merge was specified.
AutoMergeFlag bool
// ReviewApproved indicates the PR review approved the changes.
ReviewApproved bool
// ChecksPassed indicates CI/CD checks passed.
ChecksPassed bool
// QualityPassed indicates TRUST 5 quality gates passed.
QualityPassed bool
// Mergeable indicates no merge conflicts exist.
Mergeable bool
// FailureReasons lists the unmet prerequisites.
FailureReasons []string
}
PrerequisiteCheck lists which merge conditions are met.
type Registry ¶
type Registry struct {
Version string `json:"version"`
Mappings []SpecMapping `json:"mappings"`
}
Registry holds the bidirectional mappings between GitHub issues and SPECs.
type RetryError ¶
type RetryError struct {
// Operation describes the operation that was retried.
Operation string
// Attempts is the total number of attempts made.
Attempts int
// LastError is the error from the final attempt.
LastError error
}
RetryError wraps the final error after all retry attempts are exhausted.
func (*RetryError) Error ¶
func (e *RetryError) Error() string
Error returns a human-readable description of the retry failure.
func (*RetryError) Unwrap ¶
func (e *RetryError) Unwrap() error
Unwrap returns the underlying error for errors.Is/errors.As chain.
type ReviewDecision ¶
type ReviewDecision string
ReviewDecision represents the outcome of a PR review.
const ( // ReviewApprove indicates the PR passes all quality checks. ReviewApprove ReviewDecision = "APPROVE" // ReviewRequestChanges indicates the PR requires changes. ReviewRequestChanges ReviewDecision = "REQUEST_CHANGES" // ReviewComment indicates the review has observations but no blocking issues. ReviewComment ReviewDecision = "COMMENT" )
type ReviewInput ¶
type ReviewInput struct {
// PRDetails is optional pre-fetched PR details. If nil, Review fetches it.
PRDetails *PRDetails
// CheckStatus is optional pre-fetched CI check status. If nil, Review fetches it.
CheckStatus *CheckStatus
}
ReviewInput holds optional pre-fetched data to avoid redundant API calls. When fields are non-nil, Review uses them instead of calling the GH API.
type ReviewReport ¶
type ReviewReport struct {
// PRNumber is the pull request number.
PRNumber int
// Decision is the review outcome (APPROVE, REQUEST_CHANGES, COMMENT).
Decision ReviewDecision
// QualityReport is the TRUST 5 quality validation result.
QualityReport *quality.Report
// CheckStatus is the CI/CD pipeline check result.
CheckStatus *CheckStatus
// Summary is a human-readable review summary.
Summary string
// Issues lists specific problems found during review.
Issues []string
}
ReviewReport holds the results of an automated PR review.
type SpecLinker ¶
type SpecLinker interface {
// LinkIssueToSpec creates a new mapping between an issue and a SPEC.
LinkIssueToSpec(issueNum int, specID string) error
// GetLinkedSpec returns the SPEC ID linked to the given issue number.
GetLinkedSpec(issueNum int) (string, error)
// GetLinkedIssue returns the issue number linked to the given SPEC ID.
GetLinkedIssue(specID string) (int, error)
// ListMappings returns all current mappings.
ListMappings() []SpecMapping
}
SpecLinker manages bidirectional links between GitHub issues and SPEC documents.
func NewSpecLinker ¶
func NewSpecLinker(projectRoot string) (SpecLinker, error)
NewSpecLinker creates a SpecLinker that stores mappings in the given project root. Registry file is stored at {projectRoot}/.ae/github-spec-registry.json.