feat(github): add merge button when PR is ready to merge (#979)

* feat(github): add merge button when PR is ready to merge

Surfaces a one-click merge action in the PR detail panel header and as a
compact green pill in the PR topbar hover popover, gated by the existing
isPRReadyToMerge predicate (open, CI success, mergeable_state=clean, and
either approved or no review process).

Backend adds MergePR to the Client interface, implements it across
PAT/gh/Noop/Mock clients, exposes PUT /api/v1/github/prs/:owner/:repo/:number/merge,
and maps GitHub 405/409 to HTTP 409 so the UI can show a clear error.

* fix(github): address PR review feedback on merge endpoint

- Map gh CLI 404/403/405/409 stderr to *GitHubAPIError via a new
  ghMergeStatusCode helper, so merge conflicts surface as HTTP 409 for
  gh CLI users (previously only PAT users got the right status).
- Service.MergePR returns ErrNoClient when unconfigured; httpMergePR
  translates that to HTTP 503 with github_not_configured, matching
  httpListRepoBranches.
- httpMergePR distinguishes empty body (io.EOF, fall through to default
  merge method) from malformed JSON (HTTP 400 invalid payload).
- PRMergeButton tracks a local merged flag so the button stays hidden
  across the async refresh window after a successful merge.

* test(github): cover merge helpers and extract PRMergeButton

- TestGhMergeStatusCode locks in the gh CLI stderr → status-code parser
  for nil, unrelated errors, the HTTP / status: / bare-phrase forms of
  404/403/405/409, and a 500 non-match guard.
- Broaden ghMergeStatusCode to also match "405 Method Not Allowed" /
  "409 Conflict" phrase forms so the merge endpoint's 409 mapping does
  not depend on the gh CLI keeping its current "HTTP <code>" prefix.
- TestHttpMergePR_MalformedJSON covers the new non-EOF bind-error branch
  (truncated body → 400). TestHttpMergePR_NoClient_Returns503 covers the
  ErrNoClient → 503 path on Service.MergePR.
- Document the post/put error-type divergence on PATClient.post so a
  future caller does not silently lose per-status mapping.
- Move PRMergeButton out of pr-shared.tsx into its own file, and reset
  the local "merged" flag when taskPR.id changes so an instance that
  re-renders for a different PR does not stay hidden.

---------

Co-authored-by: Kandev Agent <agent@kandev.dev>
This commit is contained in:
João Salavisa
2026-05-21 03:05:28 +01:00
committed by GitHub
parent 0b2c59a2dd
commit 3d860290da
13 changed files with 465 additions and 1 deletions
+11
View File
@@ -392,6 +392,17 @@ func (s *Service) SubmitReview(ctx context.Context, owner, repo string, number i
return s.client.SubmitReview(ctx, owner, repo, number, event, body)
}
// MergePR merges a pull request. mergeMethod is one of "merge", "squash",
// "rebase"; an empty string lets GitHub apply the repo's default. The caller
// is expected to refresh PR feedback after a successful merge — the background
// poller will catch the merged state on its next pass.
func (s *Service) MergePR(ctx context.Context, owner, repo string, number int, mergeMethod string) error {
if s.client == nil {
return ErrNoClient
}
return s.client.MergePR(ctx, owner, repo, number, mergeMethod)
}
// --- PR Watch operations ---
// CreatePRWatch creates a new PR watch for a (session, repository) pair.