feat(bin): add Gitea/Forgejo forge support for PR watch and merge - #3068
chewbakartik wants to merge 1 commit into
Conversation
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains. Reviews (13): Last reviewed commit: "feat(pr): add Gitea/Forgejo as a fully-s..." | 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. |
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
|
Want your agent to iterate on Greptile's feedback? Try greploops. |
23c2965 to
d5d51f7
Compare
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.
d5d51f7 to
8c0df17
Compare
|
RE: Closed-list cap hides old merges (65100fc): "This thread motivated the pagination redesign, which landed since: the single bounded listing is gone. The walk now pages (bin/fm-pr-poll.sh L193-195, --page N --limit 1000) until the server returns a page with zero data rows (L202-203), exiting early at the target index (L199-201). The per-request bound is the forge's own page cap - a server may clamp below 1000 and fullness is never inferred from the requested limit (comment L179-189) - and a header-only page always terminates (test_gitea_poll_stops_on_header_only_last_page). Repository history growth no longer makes the watch ineffective: the watched index is examined whenever it is within the closed list, however many newer PRs have closed since, and no cap can strand the watcher armed past a merge. |
|
RE: Server cap stops pagination (f373aa2): "Correct against the revision that inferred page fullness from the requested --limit; that inference is gone. The current walk never treats a short page as end-of-list: termination is judged by data rows (bin/fm-pr-poll.sh L202-203: [ "$rows" -gt 0 ] || break, with tail -n +2 dropping the CSV header), and the design comment (L179-189) states the rule: a server that clamps its own page size below 1000 would look short on every page, so stopping on that would give up after page one. The requested --limit 1000 (L193-195) is only a request; the server's clamp sets the actual page size and the walk continues to the next page until the list is genuinely exhausted (a zero-row page). Pinned by test_gitea_poll_paginates_past_server_clamped_page_size. |
|
RE: Header-only pages never terminate (63feb29): "Fixed in this revision: tea's CSV always carries the header line, so raw output alone no longer signals exhaustion; a page with zero data rows terminates the walk (bin/fm-pr-poll.sh L202-203), pinned by the zero-data-row break (bin/fm-pr-poll.sh L202-203, after tail -n +2 drops the header) and by test_gitea_poll_stops_on_header_only_last_page. |
| done < <(printf '%s\n' "$raw" | tail -n +2) | ||
| [ "$rows" -gt 0 ] || break | ||
| page=$((page + 1)) | ||
| done |
There was a problem hiding this comment.
"This comment describes the 50,000-row cutoff from an earlier revision of the walk; that cutoff no longer exists in this head, so the failure it describes cannot occur here. The current walk pages the closed list until it is genuinely exhausted, so a merge can never fall outside the watch window: (a) each request is bounded by the forge's own page cap - bin/fm-pr-poll.sh L193-195 requests --page N --limit 1000 and Gitea/Forgejo may clamp that server-side; (b) the walk exits early the moment the target index appears (L199-201), so it reads only as many pages as the target's recency rank implies; (c) termination is judged by data rows, never by the requested limit (L202-203: tail -n +2 drops tea's CSV header and a zero-row page breaks the loop), so a header-only page - the server's own end-of-list marker - always terminates, and a server that clamps pages below 1000 can never look short (design comment L179-189); (d) any tea failure exits silently and leaves the watcher armed for the next cycle (L195), never emitting a false merged. The bound on this walk is therefore the forge's per-page cap and the list's finite length - not a client row budget - and the guarantees are pinned by tests: test_gitea_poll_paginates_past_row_budget (51,000 rows, target on page 52 - still found), test_gitea_poll_stops_on_header_only_last_page, test_gitea_poll_paginates_past_server_clamped_page_size, test_gitea_poll_paginates_closed_list.
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.