FJ-36: add fj actions logs to fetch action run logs #44

Merged
nrupard merged 2 commits from feat/actions-logs-FJ-36 into main 2026-06-08 03:08:37 +02:00
Member

What

Adds fj actions logs <run-number> so action run logs are readable from the terminal (FJ-36). <run-number> is the value shown by fj actions tasks.

  • fj actions logs <run-number> prints each job's full log, sequentially, with a === <job> (<status>) header between jobs.
  • --job <name> restricts output to a single job.
  • --json emits {"run_number": N, "jobs": [{"name", "status", "log"}]}.
  • A nonexistent run number errors clearly (no panic). A job that never started (skipped/blocked) yields an empty log instead of failing the whole command.

Why this shape

Forgejo serves action logs only through its web routes, not /api/v1: there is no log or artifact endpoint in the v14 swagger surface, forgejo-api wraps none, and its request internals are sealed (private RawRequest fields, sealed Endpoint trait) so a custom path cannot be routed through its client. forgejo-api is third-party (codeberg.org/Cyborus/forgejo-api), so forking it to add the call was rejected. The web-route calls instead live in fj-client (the connection/credential layer, reusing the same host URL + token) and are re-exported through fj_core::actions, so the binary keeps delegating through fj_core::<area> and the MCP-reuse contract (no clap/crossterm/fluent in the libs) still holds.

How it talks to Forgejo

Per run: GET .../jobs/{job} 307-redirects to .../attempt/{N}, so the latest attempt N is read from the redirect Location (the log routes are 1-based on attempt with no "latest" alias, and a base POST with attempt 0 500s server-side); POST .../jobs/0/attempt/{N} with empty cursors enumerates the run's jobs (the only place the per-run job list is exposed); GET .../jobs/{job}/attempt/{N}/logs streams each job's complete log. Token-auth POSTs pass Forgejo v14's origin-based cross-origin protection because a CLI sends no Origin/Sec-Fetch-Site header.

Verification

Built, clippy (-D warnings), fmt, and tests pass in the builder image; the MCP-reuse contract grep returns nothing. Exercised against pandoras-box/forgejo-cli on dev.a8n.run: default output, --json (87,880-char log on run #206), --job filter, a cancelled run (#194), and the error paths (bad run number, bad --job name).

## What Adds `fj actions logs <run-number>` so action run logs are readable from the terminal (FJ-36). `<run-number>` is the value shown by `fj actions tasks`. - `fj actions logs <run-number>` prints each job's full log, sequentially, with a `=== <job> (<status>)` header between jobs. - `--job <name>` restricts output to a single job. - `--json` emits `{"run_number": N, "jobs": [{"name", "status", "log"}]}`. - A nonexistent run number errors clearly (no panic). A job that never started (skipped/blocked) yields an empty log instead of failing the whole command. ## Why this shape Forgejo serves action logs only through its **web** routes, not `/api/v1`: there is no log or artifact endpoint in the v14 swagger surface, `forgejo-api` wraps none, and its request internals are sealed (private `RawRequest` fields, sealed `Endpoint` trait) so a custom path cannot be routed through its client. `forgejo-api` is third-party (`codeberg.org/Cyborus/forgejo-api`), so forking it to add the call was rejected. The web-route calls instead live in `fj-client` (the connection/credential layer, reusing the same host URL + token) and are re-exported through `fj_core::actions`, so the binary keeps delegating through `fj_core::<area>` and the MCP-reuse contract (no clap/crossterm/fluent in the libs) still holds. ## How it talks to Forgejo Per run: `GET .../jobs/{job}` 307-redirects to `.../attempt/{N}`, so the latest attempt `N` is read from the redirect `Location` (the log routes are 1-based on attempt with no "latest" alias, and a base POST with attempt 0 500s server-side); `POST .../jobs/0/attempt/{N}` with empty cursors enumerates the run's jobs (the only place the per-run job list is exposed); `GET .../jobs/{job}/attempt/{N}/logs` streams each job's complete log. Token-auth POSTs pass Forgejo v14's origin-based cross-origin protection because a CLI sends no `Origin`/`Sec-Fetch-Site` header. ## Verification Built, clippy (`-D warnings`), fmt, and tests pass in the builder image; the MCP-reuse contract grep returns nothing. Exercised against `pandoras-box/forgejo-cli` on dev.a8n.run: default output, `--json` (87,880-char log on run #206), `--job` filter, a cancelled run (#194), and the error paths (bad run number, bad `--job` name).
feat(actions): add fj actions logs to fetch run logs
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 41s
b4ed515c84
`fj actions` had no way to read an action run's logs, so a failed CI run could only be diagnosed from the web UI. This adds `fj actions logs <run-number>` (the run number shown by `fj actions tasks`), with `--job <name>` to restrict to one job and `--json` emitting `{"run_number", "jobs":[{"name","status","log"}]}`. A nonexistent run produces a clear error instead of a panic, and a job that never started (skipped/blocked) yields an empty log rather than failing the command.

Forgejo serves action logs only through its web routes, not `/api/v1`: there is no log or artifact endpoint in the swagger surface, `forgejo-api` wraps none, and its request internals are sealed (private `RawRequest` fields, sealed `Endpoint` trait) so a custom path cannot be routed through its client. `forgejo-api` is third-party (codeberg.org/Cyborus/forgejo-api), so forking it was rejected. Instead the web-route calls live in `fj-client` (the connection/credential layer), reusing the same host URL and token, and are re-exported through `fj_core::actions` so the binary keeps delegating through `fj_core::<area>`.

Retrieval per run: GET `.../jobs/{job}` 307-redirects to `.../attempt/{N}` (the log routes are 1-based on attempt with no "latest" alias, and a base POST with attempt 0 500s server-side), so N is read from the redirect Location; POST `.../jobs/0/attempt/{N}` with empty cursors enumerates the run's jobs (the only place the per-run job list is exposed); GET `.../jobs/{job}/attempt/{N}/logs` streams each job's complete log. Token-auth POSTs pass Forgejo v14's origin-based cross-origin protection because a CLI sends no Origin/Sec-Fetch-Site header.

#FJ-36

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
fix(actions): clear auth error when logs redirect to login
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 38s
Create release / Create release from merged PR (pull_request) Has been skipped
a3df720ad4
`latest_attempt` accepted any 3xx as the attempt redirect. A signed-out request (no/invalid token, or an instance with REQUIRE_SIGNIN_VIEW) is redirected to the login page, not to `.../attempt/{N}`, so the user saw a confusing `could not read attempt number from job URL "/user/login?..."` instead of an auth error. Now any redirect whose Location is not an attempt URL surfaces a clear "not authorized" message pointing at `fj auth add-key` and token scope.

#FJ-36

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
nrupard deleted branch feat/actions-logs-FJ-36 2026-06-08 03:08:37 +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!44
No description provided.