feat(hive-forge): add pr-merge verb (#1670)

Adds `hive-forge pr-merge <n> [--method merge|rebase] [--keep-branch]
[--force]` wrapping POST /repos/{owner}/{repo}/pulls/{n}/merge, so agents on
the peer-review-and-merge workflow have a CLI path instead of the raw API.

- Methods: merge (default) | rebase. Squash is intentionally not offered.
- Deletes the head branch after merge unless --keep-branch.
- Safe by default: refuses unless the PR is mergeable, CI is not red, and no
  review's current verdict requests changes (latest-per-reviewer wins, so a
  later approval clears an earlier request-changes). --force overrides and
  also sets Forgejo's force_merge.
- New client helper post_no_content for the 200-empty-body merge response.
This commit is contained in:
atlas 2026-06-15 09:54:33 +02:00 committed by mara
commit 9d8367bb24
5 changed files with 231 additions and 0 deletions

View file

@ -229,6 +229,29 @@ impl Client {
Ok(())
}
/// POST a JSON body to `<api>/<path>` for an endpoint that returns a
/// 2xx with an empty body (so there is nothing to decode). Used by
/// `pr-merge` — Forgejo's merge endpoint answers `200 OK` with no body
/// on success and a non-2xx (e.g. `405`) when the PR is not mergeable.
///
/// # Errors
///
/// Returns an error if the request fails to send (transport/network
/// error) or the server responds with a non-2xx status (the response
/// body is included in the error).
pub fn post_no_content<B: Serialize>(&self, path: &str, body: &B) -> Result<()> {
let url = format!("{}{}", self.api(), path);
let resp = self
.http
.post(&url)
.header(CONTENT_TYPE, "application/json")
.json(body)
.send()
.context("POST")?;
check_status(resp, &format!("POST {url}"))?;
Ok(())
}
/// DELETE `<api>/<path>`. Optional JSON body for endpoints that
/// need it (Forgejo's subscription unwatch uses bodyless DELETE).
pub fn delete(&self, path: &str, body: Option<&Value>) -> Result<()> {