feat(bin): add Gitea/Forgejo forge support for PR watch and merge - #3068
feat(bin): add Gitea/Forgejo forge support for PR watch and merge#3068chewbakartik wants to merge 14 commits into
Conversation
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Reviews (10): Last reviewed commit: "no-mistakes: apply CI fixes" | Re-trigger Greptile |
|
Speaking as Kun's firstmate: Full diff reviewed at Not merge-ready:
Overlap check: #3044 currently implements fleet-wide GitHub conflict detection, not Gitea merge/watch, but it also changes The correctness and attestation blockers are waiting on the author. The overlap hold is a captain/maintainer integration decision and is not waiting on the author. |
Extends the PR watch-and-merge pipeline with a third provider, tagged "gitea", at parity with GitHub: URL recognition for the https://<host>/<owner>/<repo>/pulls/<n> shape, merge-poll status via tea, and autonomous merge via tea pulls merge. Forgejo is a protocol-compatible fork of Gitea and shares the tea CLI, so one implementation covers both; tea is used over Forgejo's own fj CLI per the captain's own manual testing. GitLab remains watch-only, unchanged, since no owner/repository pair can address its arbitrary-depth namespace. tea addresses a self-hosted instance through a configured login rather than through the URL, so both the poll and the merge path resolve the login whose own host matches the validated record's host, refusing silently (poll) or loudly (merge) on any ambiguity or absence. tea has no head-commit field in any output format, so a Gitea/Forgejo task records no pr_head=, mirroring GitLab's existing precedent. Renames docs/gitlab-merge-watch.md to docs/forge-merge-watch.md, keeping the GitLab evidence intact and adding a Gitea/Forgejo section built from read-only verification against a real self-hosted instance; no live arm-and-merge cycle against a real pull request was exercised, so the captain should smoke-test that path before relying on it unattended.
…rges Gitea/Forgejo
The autonomous merge path has since been smoke-tested live against a real Gitea/Forgejo pull request and passed on the first attempt with no code changes required; update the disclosure to reflect that.
caller_has_github_merge_method only matched the long-form --squash/--merge/--rebase/--method flags, so a caller-supplied -r (gh's own --rebase shorthand) was not recognized as an explicit merge method: fm-pr-merge.sh still added the default --squash, producing a command with both --squash and -r, which gh rejects as conflicting methods. Recognize -m/-r/-s alongside the long forms, and correct the existing regression test's expected invocation.
Greptile findings 1 and 2 on PR kunchenguid#3068 are two sides of one normalization. The login-vs-record comparison kept every port, so a login configured with its scheme's default port (https://...:443, http://...:80) could never match the validated portless record that names the same endpoint, so merging or watching a PR on such a login silently refused. Endpoint identity now strips the scheme's default port (https :443, http :80) from both sides of the comparison and lowercases the host, while any other port is kept, which is the guarantee finding 1 requires: a validated Gitea/Forgejo PR URL never carries a port and names the default-port endpoint, a default-ported login is that same endpoint, and a non-default-ported login names a different endpoint and never matches. Both copies of the contract change: bin/fm-pr-merge.sh (the merge path, through gitea_endpoint_identity) and bin/fm-pr-poll.sh (the standalone poll's inline matching, which cannot source shared code). The login-selection section of docs/forge-merge-watch.md is updated to describe endpoint identity, and regression tests cover a default-ported login matching in both the merge suite and the poll security suite.
8f799af to
52cada3
Compare
Intent
Fix Greptile review findings 1 and 2 on the Gitea/Forgejo forge-support pull request #3068 in kunchenguid/firstmate (fork chewbakartik/firstmate, head branch fm/forgejo-forge-support), which blocked the Greptile Review check at 4/5 confidence. The change normalizes the tea login-vs-record endpoint comparison in both bin/fm-pr-merge.sh and the standalone byte-pinned bin/fm-pr-poll.sh: a login configured with its scheme's default port (https :443, http :80) now matches the validated portless PR URL that names the same endpoint, while any non-default port stays significant and never matches; the host is lowercased on both sides. The login-selection section of docs/forge-merge-watch.md is updated to describe endpoint identity, and regression tests cover a default-ported login matching in both the merge suite and the poll security suite. Greptile findings 3 through 7 (foreign --remote override rejection and the closed-list pagination walk) are already fixed in the base head 8f799af and were deliberately left untouched here. The pipeline must push only to the fork branch chewbakartik/firstmate:fm/forgejo-forge-support as a clean fast-forward and attach to the existing open PR #3068; it must not open a new pull request.
What Changed
bin/fm-pr-lib.shparseshttps://<host>/<owner>/<repo>/pulls/<n>URLs into a newgiteaprovider identity (host validated with GitLab's arbitrary self-hosted-host rule, owner/repo with GitHub's fixed shape), andbin/fm-pr-check.shrefuses to arm a watch whenteais not on PATH.bin/fm-pr-poll.sh(byte-static watcher) andbin/fm-pr-merge.shread/merge Gitea/Forgejo pull requests throughtea: the poll walkstea pulls list --state closedpages to find the recorded index, and the merge path resolves the onetea loginwhose endpoint matches the parsed host, callingtea pulls mergewith--style squashby default. Both compare login endpoint to the validated host using endpoint identity — a login on its scheme's default port (https :443,http :80) matches the portless URL naming that endpoint, any non-default port stays distinct, and the host is lowercased on both sides.bin/fm-pr-merge.shextends its override rejection to--login/--remote(and, on Gitea/Forgejo only, the-r/-lshort forms tea maps to--repo/--login) so the repository and login always come from the validated URL, not caller-supplied flags.docs/gitlab-merge-watch.mdtodocs/forge-merge-watch.md, expanded to cover GitHub, GitLab, and Gitea/Forgejo (including the login endpoint-identity/default-port normalization), and updatedREADME.md,docs/architecture.md,docs/scripts.md, anddocs/documentation-audiences.jsonto match.tests/fm-pr-check-security.test.shandtests/fm-pr-merge.test.sh, including cases where a default-ported login matches a portless recorded URL.Risk Assessment
✅ Low: The scoped fix (bin/fm-pr-merge.sh, bin/fm-pr-poll.sh, docs, tests) correctly implements endpoint-identity normalization — traced through default-port (443/80), non-default-port, and case-mismatch cases in both the extracted gitea_endpoint_identity helper and the standalone poll script's duplicated inline logic, both strip only an exact trailing ":443"/":80" suffix tied to the matching scheme so a non-default port is never falsely collapsed; the new tests exercise real behavior (poll/merge output) rather than source text, and the rest of the branch diff corresponds to already-decided or explicitly out-of-scope prior-round work (pagination budget removal, --remote rejection) per the recorded decisions and user intent.
Testing
The fix normalizes tea login-vs-record endpoint comparison (stripping default ports https:443/http:80, lowercasing host) in both bin/fm-pr-merge.sh and bin/fm-pr-poll.sh. Its own added regression tests (one in tests/fm-pr-merge.test.sh, one in tests/fm-pr-check-security.test.sh) exercise exactly the CLI-facing behavior end-to-end — a default-ported tea login now resolves and drives a real merge (tea pulls merge invoked, pr= recorded) and a real poll match (poll reports 'merged') for a portless PR URL. Ran the full merge test file (44/44 pass) and, since the full 200KB security suite exceeded a reasonable local runtime, ran a targeted invocation of just its two relevant cases (both pass). To rule out these being trivially-passing tests, I checked out the pre-fix versions of both scripts and reran the same two new tests: both failed with the exact symptom the intent describes (default-ported login rejected/ambiguous), proving the tests are real regression coverage, then restored the fixed scripts leaving the worktree clean. No test or environment issues found; no findings to report.
Pipeline
Updates from git push no-mistakes
✅ **intent** - passed
✅ No issues found.
🔧 **Rebase** - 3 issues found → auto-fixed ✅
bin/fm-pr-merge.sh- merge conflict rebasing onto origin/maindocs/architecture.md- merge conflict rebasing onto origin/maintests/fm-pr-merge.test.sh- merge conflict rebasing onto origin/main🔧 Fix applied.
✅ Re-checked - no issues remain.
✅ **Review** - passed
✅ No issues found.
✅ **Test** - passed
✅ No issues found.
bash tests/fm-pr-merge.test.sh (all 44 cases pass, including new test_gitea_default_port_login_matches)Focused re-run of tests/fm-pr-check-security.test.sh restricted to test_gitea_login_port_mismatch and test_gitea_default_port_login_matches (both pass)Regression proof: swapped bin/fm-pr-merge.sh and bin/fm-pr-poll.sh back to the pre-fix parent commit (12bfaef) content and re-ran the two new tests — both failed as expected ('did not match a login configured with its https default port' / 'could not resolve exactly one tea login for forge.example'), then restored the fixed scripts and confirmed a clean worktree✅ **Document** - passed
✅ No issues found.
🔧 **Lint** - 1 issue found → auto-fixed (2) ✅
🔧 Fix: {"summary": "no code lint fixes needed; install actionlint to unblock lint check"}
1 warning still open:
🔧 Fix: Install actionlint to $HOME/.local/bin to unblock lint check
✅ Re-checked - no issues remain.
✅ **Push** - passed
✅ No issues found.