fix(pr): json output for pr status, fix relative-url crash (FJ-2) #2

Merged
David merged 2 commits from feat/pr-status-json-relative-url-fix into main 2026-05-24 23:54:54 +02:00
Owner

Resolves FJ-2.

What

fj pr status <id> gains a --json flag and no longer crashes on Forgejo responses that contain relative status URLs.

Why

Two problems, both hit while building claude-bot/actions/forgejo-ci-fix.yml:

  1. The combined-status endpoint returns relative target_url/url values for Forgejo Actions run/job links (e.g. /owner/repo/actions/runs/85/jobs/0). forgejo-api types CommitStatus/CombinedStatus target_url/url as url::Url, which rejects relative URLs, so deserialization aborted the entire call: Error: the response from forgejo was not properly structured ... expected relative URL without a base.
  2. There was no machine-readable output, forcing callers to bypass fj and hit Forgejo's REST API directly (a Tooling Gap Discipline violation).

How

The combined-status response is deserialized into local lenient mirror types (CommitStatusLenient keeps target_url as a plain String), fetched via Request::response_type with manual pagination over the x-total-count header, so the strict upstream CommitStatus/CombinedStatus types are never instantiated for this endpoint. --json emits a record with pr, head_sha, state, statuses[], mergeable, and merged. The top-level state follows Forgejo's combined-status semantics: failure if any check fails, success if every check passes, pending otherwise (including zero checks). Human output is unchanged; progress rendering is suppressed under --json so --wait emits a single final JSON record.

Verification

Reproduced the original crash with the released fj against the issue's sandbox PR (longjacksonle/claude-fix-sandbox #10), then confirmed the rebuilt binary returns valid JSON for that same PR. Exercised one-check, many-check, success, and failure PRs in that repo; the zero-check path is deterministic (the pagination loop breaks immediately on an empty page and combined_state returns pending).

{
  "pr": 10,
  "head_sha": "492cd19161716d8ebebae481f458534ab9b4ee1b",
  "state": "success",
  "statuses": [
    { "context": "CI / no-bananas (pull_request)", "state": "success", "description": "Successful in 7s", "target_url": "/longjacksonle/claude-fix-sandbox/actions/runs/85/jobs/0" }
  ],
  "mergeable": true,
  "merged": false
}

Notes

  • Decision on the FJ-2 "open question" (one JSON shape vs. split): kept mergeability and CI status in one record, matching the JSON example in the issue (which already includes mergeable).
  • Upstream: fj lives at https://codeberg.org/forgejo-contrib/forgejo-cli. This change should be mirrored upstream as a follow-up (no codeberg push access from this environment).

🤖 Generated with Claude Code

Resolves FJ-2. ## What `fj pr status <id>` gains a `--json` flag and no longer crashes on Forgejo responses that contain relative status URLs. ## Why Two problems, both hit while building `claude-bot/actions/forgejo-ci-fix.yml`: 1. The combined-status endpoint returns relative `target_url`/`url` values for Forgejo Actions run/job links (e.g. `/owner/repo/actions/runs/85/jobs/0`). `forgejo-api` types `CommitStatus`/`CombinedStatus` `target_url`/`url` as `url::Url`, which rejects relative URLs, so deserialization aborted the entire call: `Error: the response from forgejo was not properly structured ... expected relative URL without a base`. 2. There was no machine-readable output, forcing callers to bypass `fj` and hit Forgejo's REST API directly (a Tooling Gap Discipline violation). ## How The combined-status response is deserialized into local lenient mirror types (`CommitStatusLenient` keeps `target_url` as a plain `String`), fetched via `Request::response_type` with manual pagination over the `x-total-count` header, so the strict upstream `CommitStatus`/`CombinedStatus` types are never instantiated for this endpoint. `--json` emits a record with `pr`, `head_sha`, `state`, `statuses[]`, `mergeable`, and `merged`. The top-level `state` follows Forgejo's combined-status semantics: `failure` if any check fails, `success` if every check passes, `pending` otherwise (including zero checks). Human output is unchanged; progress rendering is suppressed under `--json` so `--wait` emits a single final JSON record. ## Verification Reproduced the original crash with the released `fj` against the issue's sandbox PR (`longjacksonle/claude-fix-sandbox` #10), then confirmed the rebuilt binary returns valid JSON for that same PR. Exercised one-check, many-check, success, and failure PRs in that repo; the zero-check path is deterministic (the pagination loop breaks immediately on an empty page and `combined_state` returns `pending`). ```json { "pr": 10, "head_sha": "492cd19161716d8ebebae481f458534ab9b4ee1b", "state": "success", "statuses": [ { "context": "CI / no-bananas (pull_request)", "state": "success", "description": "Successful in 7s", "target_url": "/longjacksonle/claude-fix-sandbox/actions/runs/85/jobs/0" } ], "mergeable": true, "merged": false } ``` ## Notes - Decision on the FJ-2 "open question" (one JSON shape vs. split): kept mergeability and CI status in one record, matching the JSON example in the issue (which already includes `mergeable`). - Upstream: `fj` lives at https://codeberg.org/forgejo-contrib/forgejo-cli. This change should be mirrored upstream as a follow-up (no codeberg push access from this environment). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The combined-status response embeds relative target_url/url values for Forgejo Actions run/job links (e.g. /owner/repo/actions/runs/85/jobs/0). forgejo-api types CommitStatus/CombinedStatus target_url/url as url::Url, which rejects relative URLs and aborted the entire `pr status` call with "the response from forgejo was not properly structured". Deserialize the combined-status response into local lenient mirror types (CommitStatusLenient keeps target_url as a plain String), fetched via Request::response_type with manual pagination over the x-total-count header so the strict upstream type is never instantiated.

Add --json to `fj pr status`, emitting a record with pr, head_sha, state, statuses[], mergeable, and merged. The top-level state follows Forgejo's combined-status semantics: failure if any check fails, success if every check passes, pending otherwise (including zero checks). Human output is unchanged; progress rendering is suppressed under --json so --wait emits a single final JSON record.

#FJ-2 State Done

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
David merged commit 53b4fae197 into main 2026-05-24 23:54:54 +02:00
David deleted branch feat/pr-status-json-relative-url-fix 2026-05-24 23:54:54 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
pandoras-box/forgejo-cli!2
No description provided.