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>
111 lines
3.2 KiB
Go
111 lines
3.2 KiB
Go
package github
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
)
|
|
|
|
// ErrNoClient is returned by NoopClient methods that cannot provide meaningful data.
|
|
var ErrNoClient = errors.New("github client not configured")
|
|
|
|
// NoopClient is a GitHub client that returns empty results for all operations.
|
|
// Used when GitHub integration is not configured or not needed.
|
|
type NoopClient struct{}
|
|
|
|
func (c *NoopClient) IsAuthenticated(context.Context) (bool, error) {
|
|
return false, nil
|
|
}
|
|
|
|
func (c *NoopClient) GetAuthenticatedUser(context.Context) (string, error) {
|
|
return "", nil
|
|
}
|
|
|
|
func (c *NoopClient) GetPR(context.Context, string, string, int) (*PR, error) {
|
|
return nil, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) FindPRByBranch(context.Context, string, string, string) (*PR, error) {
|
|
return nil, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) ListAuthoredPRs(context.Context, string, string) ([]*PR, error) {
|
|
return nil, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) ListReviewRequestedPRs(context.Context, string, string, string) ([]*PR, error) {
|
|
return nil, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) ListUserOrgs(context.Context) ([]GitHubOrg, error) {
|
|
return nil, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) SearchOrgRepos(context.Context, string, string, int) ([]GitHubRepo, error) {
|
|
return nil, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) ListPRReviews(context.Context, string, string, int) ([]PRReview, error) {
|
|
return nil, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) ListPRComments(context.Context, string, string, int, *time.Time) ([]PRComment, error) {
|
|
return nil, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) ListCheckRuns(context.Context, string, string, string) ([]CheckRun, error) {
|
|
return nil, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) GetPRFeedback(context.Context, string, string, int) (*PRFeedback, error) {
|
|
return nil, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) GetPRStatus(context.Context, string, string, int) (*PRStatus, error) {
|
|
return nil, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) ListPRFiles(context.Context, string, string, int) ([]PRFile, error) {
|
|
return nil, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) ListPRCommits(context.Context, string, string, int) ([]PRCommitInfo, error) {
|
|
return nil, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) ListRepoBranches(context.Context, string, string) ([]RepoBranch, error) {
|
|
return nil, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) GetRepoMergeMethods(context.Context, string, string) (RepoMergeMethods, error) {
|
|
return RepoMergeMethods{}, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) SubmitReview(context.Context, string, string, int, string, string) error {
|
|
return ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) MergePR(context.Context, string, string, int, string) error {
|
|
return ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) ListIssues(context.Context, string, string) ([]*Issue, error) {
|
|
return nil, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) ListIssuesPaged(context.Context, string, string, int, int) (*IssueSearchPage, error) {
|
|
return nil, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) SearchPRs(context.Context, string, string) ([]*PR, error) {
|
|
return nil, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) SearchPRsPaged(context.Context, string, string, int, int) (*PRSearchPage, error) {
|
|
return nil, ErrNoClient
|
|
}
|
|
|
|
func (c *NoopClient) GetIssueState(context.Context, string, string, int) (string, error) {
|
|
return "", ErrNoClient
|
|
}
|