FJ-36: add fj actions logs to fetch action run logs #44
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/actions-logs-FJ-36"
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?
What
Adds
fj actions logs <run-number>so action run logs are readable from the terminal (FJ-36).<run-number>is the value shown byfj 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.--jsonemits{"run_number": N, "jobs": [{"name", "status", "log"}]}.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-apiwraps none, and its request internals are sealed (privateRawRequestfields, sealedEndpointtrait) so a custom path cannot be routed through its client.forgejo-apiis third-party (codeberg.org/Cyborus/forgejo-api), so forking it to add the call was rejected. The web-route calls instead live infj-client(the connection/credential layer, reusing the same host URL + token) and are re-exported throughfj_core::actions, so the binary keeps delegating throughfj_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 attemptNis read from the redirectLocation(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}/logsstreams each job's complete log. Token-auth POSTs pass Forgejo v14's origin-based cross-origin protection because a CLI sends noOrigin/Sec-Fetch-Siteheader.Verification
Built, clippy (
-D warnings), fmt, and tests pass in the builder image; the MCP-reuse contract grep returns nothing. Exercised againstpandoras-box/forgejo-clion dev.a8n.run: default output,--json(87,880-char log on run #206),--jobfilter, a cancelled run (#194), and the error paths (bad run number, bad--jobname).fj actions logsto fetch run logs`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>`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>