fix(forgejo-api): client-side head-ref filter in find_pr_by_head (#274) #277
No reviewers
Labels
No labels
bump
major
bump
minor
bump
patch
kind/bug
kind/chore
kind/docs
kind/feature
priority/critical
priority/high
priority/low
priority/medium
size/L
size/M
size/S
size/XL
No milestone
No project
No assignees
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit!277
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "i/274-head-ref-client-filter"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Why
Forgejo's
GET /pulls?state=open&head=owner:refquery filter is empirically ignored — the endpoint returns every open PR in the repo regardless of thehead=qualifier. Live verification againsthttps://git.frankenbit.de/api/v1/repos/frankenbit/release-toolkit:Without a client-side filter,
.[0].numberpicked whichever PR the API sorted first (newest-by-index in practice).release-prep.sh's rolling-mode PATCH then landed on a random feature PR instead of the actual rolling PR — the tmux-tell v0.29.0 cycle 2026-07-01 reproduced this: feature PR #711 (head=i/708-...) was mutated to titlechore(release): v0.29.0+ assembled changelog body when its own merge triggered the release workflow, while the actual rolling PR #703 was ALSO updated correctly. Two PRs with identical rolling-title, one feature-file diff.What
Filter the response client-side by exact
head.label == refORhead.ref == ref:head.label— Forgejo's raw branch name for same-repo PRs (release-prep/rolling)head.ref— canonicalized form (refs/pull/N/head), used as fallback for cross-repo edge cases wherehead.labelis prefixed with the fork owner.head // {}— guards against a rare Forgejo null-head PR shape (the filter degrades to false rather than erroring)Server-side
head=filter kept in the URL for forward-compat: if Forgejo/Gitea ever implements it, the query becomes cheaper (server pre-filters, client re-verifies). No behavior change either way.Coverage
Seven new bats tests in
tests/forgejo-api.bats, using the sameshadow forgejo_api_callpattern the existing #240find_pr_by_merge_shatests use:picks the PR whose head.label matches exactlysingle-PR response with matching head returns itempty response yields empty output, not errorno head matches (all feature PRs) yields emptymatches on head.ref when head.label differs (cross-repo edge)dry-run returns empty (POST fall-through)null head object doesn't crash the filterFull 521-test bats suite green; shellcheck clean on the touched file. (Pre-existing SC1010 warning on
jq -nc --arg doinforgejo_merge_pris unrelated to this change.)What this PR does NOT do
forgejo_update_pr. The mutation site itself is correct — it PATCHes the number it's given. The bug was in the lookup that supplied the wrong number.head=filter. Kept in the URL for forward-compat + so a future Forgejo release that honors the filter makes the query cheaper (server pre-filters, client re-verifies). No downside to keeping it..head.labelsemantics assumptions elsewhere. Onlyforgejo_find_pr_by_headgets the client-side filter; other consumers of head data are untouched.Refs
🤖 Generated with Claude Code
APPROVED — reviewed at head
11db7e0Correct fix for the metadata-mutation defect (the tmux-tell #711 incident I flagged 2026-07-01). Verified at source on live state:
Filter logic is correct. Caller passes
$BRANCH(bare branch name) ashead_ref; in this Forgejo, same-repo PRs storehead.labelas the bare branch name (confirmed: #275'shead.label=i/273-..., no owner prefix). Soselect(.label == $ref or .ref == $ref)matches the right PR. The server-side query keeps theowner:refform (correct for the ignored server filter); the client filter uses the bare ref againsthead.label. Consistent.Independently reproduced the three load-bearing scenarios (raw jq, real-shaped response):
nullhead object →.head // {}guard → no crash, returns match ✓Tests: full
forgejo-api.batsgreen (46); 7 new head-filter tests present. Keeping the server-sidehead=for forward-compat is a no-cost, correct call.Merge-ready.
11db7e07e785c8d12775