b8a09bc5ea
* fix(github): pick allowed merge method so squash-only repos stop 405ing The merge button sent an empty merge_method to GitHub, which defaults to "merge" regardless of repo settings — squash-only / rebase-only repos returned 405 "Merge commits are not allowed on this repository". The backend now resolves an empty method to the first allowed one via a new GetRepoMergeMethods endpoint (5-min cache), and the frontend renders a GitHub-style split button so users can pick among the allowed methods. * fix(github): address PR #999 review feedback - pr-merge-button: extract repo-methods fetch to useRepoMergeMethods hook (Claude bot); hide button while methods are loading so the primary label doesn't flicker (Claude bot); drop e.preventDefault on dropdown items so Radix closes the menu on selection (Greptile P1). - gh_client / pat_client: treat missing allow_* fields as false instead of true so a permission-gated response can't pick a disallowed merge method (cubic P2). - service.MergePR: only overwrite mergeMethod when pickDefaultMergeMethod returns non-empty, so a degenerate all-false config falls through to GitHub's default with the real error (Claude bot). * fix(github): don't hide merge button on lookup failure Round-2 PR feedback (Claude, Greptile, CodeRabbit, cubic): the `if (!methods) return null` guard added in the previous round to avoid label flicker turned a transient repo-methods lookup failure into a permanent UI lockout — the button would stay hidden until unmount/remount. - pr-merge-button: revert the null-guard; treat null as "use backend resolver", render the plain primary button without a dropdown. - use-repo-merge-methods: add a 5-min frontend TTL to match the backend cache so admin merge-strategy flips are visible without a page reload. Catch block now triggers a rerender, and co-waiting consumers attach both fulfilled/rejected handlers to avoid silent rejection swallowing. * test(github): cover useRepoMergeMethods + silence rejection warning Round-3 PR feedback (Claude): - Swallow rejections in the originating fetch chain instead of rethrowing, so the first consumer's promise never escapes the hook as an unhandled rejection. Coalesced consumers already re-render to null, which is the same state a fresh "not loaded" call would show. - Add use-repo-merge-methods.test.ts covering: null-on-missing-params, loading then resolution, cache hit on remount, concurrent-fetch coalescing, retry-on-failure (no cache poisoning, no unhandled rejection), and TTL-expiry refetch via fake timers. * test(github): cover httpGetRepoMergeMethods error branches Round-4 PR feedback (Claude): the new endpoint had only happy-path coverage. Add tests matching httpMergePR's existing shape: - nil client → 503 (ErrNoClient) - GitHubAPIError{404} → 404 - generic upstream error → 500 --------- Co-authored-by: Kandev Agent <agent@kandev.dev>
94 lines
4.7 KiB
Go
94 lines
4.7 KiB
Go
package github
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
)
|
|
|
|
// Client defines the interface for interacting with the GitHub API.
|
|
type Client interface {
|
|
// IsAuthenticated checks if the client is authenticated with GitHub.
|
|
IsAuthenticated(ctx context.Context) (bool, error)
|
|
|
|
// GetAuthenticatedUser returns the username of the authenticated user.
|
|
GetAuthenticatedUser(ctx context.Context) (string, error)
|
|
|
|
// GetPR retrieves a single pull request by number.
|
|
GetPR(ctx context.Context, owner, repo string, number int) (*PR, error)
|
|
|
|
// FindPRByBranch finds an open PR for the given head branch.
|
|
FindPRByBranch(ctx context.Context, owner, repo, branch string) (*PR, error)
|
|
|
|
// ListAuthoredPRs lists open PRs authored by the authenticated user for a repo.
|
|
ListAuthoredPRs(ctx context.Context, owner, repo string) ([]*PR, error)
|
|
|
|
// ListReviewRequestedPRs lists open PRs where the user's review is requested.
|
|
// scope controls the search qualifier: ReviewScopeUser uses user-review-requested:@me,
|
|
// ReviewScopeUserAndTeams (or empty) uses review-requested:@me.
|
|
// filter is an optional additional search qualifier (e.g. "repo:owner/name" or "org:myorg").
|
|
// customQuery, when non-empty, replaces the entire generated query.
|
|
ListReviewRequestedPRs(ctx context.Context, scope, filter, customQuery string) ([]*PR, error)
|
|
|
|
// ListUserOrgs returns the GitHub organizations the authenticated user belongs to.
|
|
ListUserOrgs(ctx context.Context) ([]GitHubOrg, error)
|
|
|
|
// SearchOrgRepos searches repositories in an organization, optionally filtered by a query string.
|
|
SearchOrgRepos(ctx context.Context, org, query string, limit int) ([]GitHubRepo, error)
|
|
|
|
// ListPRReviews lists reviews on a pull request.
|
|
ListPRReviews(ctx context.Context, owner, repo string, number int) ([]PRReview, error)
|
|
|
|
// ListPRComments lists review comments on a pull request.
|
|
// If since is non-nil, only comments updated after that time are returned.
|
|
ListPRComments(ctx context.Context, owner, repo string, number int, since *time.Time) ([]PRComment, error)
|
|
|
|
// ListCheckRuns lists CI check runs for a given git ref (branch or SHA).
|
|
ListCheckRuns(ctx context.Context, owner, repo, ref string) ([]CheckRun, error)
|
|
|
|
// GetPRFeedback fetches aggregated feedback (reviews, comments, checks) for a PR.
|
|
GetPRFeedback(ctx context.Context, owner, repo string, number int) (*PRFeedback, error)
|
|
|
|
// GetPRStatus fetches lightweight PR status (state, review summary, checks summary).
|
|
// Unlike GetPRFeedback, it skips comments for efficiency.
|
|
GetPRStatus(ctx context.Context, owner, repo string, number int) (*PRStatus, error)
|
|
|
|
// ListPRFiles lists files changed in a pull request.
|
|
ListPRFiles(ctx context.Context, owner, repo string, number int) ([]PRFile, error)
|
|
|
|
// ListPRCommits lists commits in a pull request.
|
|
ListPRCommits(ctx context.Context, owner, repo string, number int) ([]PRCommitInfo, error)
|
|
|
|
// SubmitReview submits a review on a pull request.
|
|
// event is one of "APPROVE", "COMMENT", "REQUEST_CHANGES".
|
|
SubmitReview(ctx context.Context, owner, repo string, number int, event, body string) error
|
|
|
|
// MergePR merges a pull request. mergeMethod is one of "merge", "squash", "rebase".
|
|
MergePR(ctx context.Context, owner, repo string, number int, mergeMethod string) error
|
|
|
|
// ListRepoBranches lists branches for a repository.
|
|
ListRepoBranches(ctx context.Context, owner, repo string) ([]RepoBranch, error)
|
|
|
|
// GetRepoMergeMethods reports which merge methods a repository allows.
|
|
GetRepoMergeMethods(ctx context.Context, owner, repo string) (RepoMergeMethods, error)
|
|
|
|
// ListIssues searches for open issues (not PRs) matching the given query.
|
|
// filter is an optional additional search qualifier (e.g. "repo:owner/name" or "label:bug").
|
|
// customQuery, when non-empty, replaces the entire generated query.
|
|
ListIssues(ctx context.Context, filter, customQuery string) ([]*Issue, error)
|
|
|
|
// SearchPRs searches for PRs matching the given query.
|
|
// filter is an optional additional search qualifier (e.g. "author:@me" or "repo:owner/name").
|
|
// customQuery, when non-empty, replaces the entire generated query.
|
|
SearchPRs(ctx context.Context, filter, customQuery string) ([]*PR, error)
|
|
|
|
// SearchPRsPaged is the paginated variant of SearchPRs. page is 1-indexed;
|
|
// perPage is clamped to GitHub's 1..100 range by the implementation.
|
|
SearchPRsPaged(ctx context.Context, filter, customQuery string, page, perPage int) (*PRSearchPage, error)
|
|
|
|
// ListIssuesPaged is the paginated variant of ListIssues.
|
|
ListIssuesPaged(ctx context.Context, filter, customQuery string, page, perPage int) (*IssueSearchPage, error)
|
|
|
|
// GetIssueState returns the state of a single issue ("open" or "closed").
|
|
GetIssueState(ctx context.Context, owner, repo string, number int) (string, error)
|
|
}
|