feat(actions): add artifact list + download subcommands (DEV-410) #71
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/dev-410-actions-artifacts"
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?
Implements DEV-410.
What
Adds artifact access to
fj actions, so a failing Playwright e2e trace/report can be pulled and opened locally without the web UI (the recurring blocker on the chromium SPA-login races: PMS-592/595/605, BUNYIP-294).fj actions artifacts <run>- list each artifact's name, human-readable size, and status.fj actions artifact download <run> [--name <artifact>] [--output <dir>]- write the artifact zip(s) to disk (default: all artifacts for the run into the current directory).How
Like
fj actions logs, artifacts are served only by Forgejo web routes, not/api/v1, soforgejo-apiwraps none of them. Newcrates/fj-client/src/actions_artifacts.rstalks to the web routes with our own reqwest client, reusing the host URL and bearer token.Verified against Forgejo v14 (
routers/web/repo/actions/view.go,routers/web/web.go), the version dev.a8n.run runs:GET /{owner}/{repo}/actions/runs/{run}/artifacts->{"artifacts":[{name,size,status}]}.GET .../artifacts/{name}-> streams{name}.zip(application/zip).Details:
Authorizationheader marked sensitive so reqwest drops it on a cross-origin hop./and\in artifact names so a crafted name cannot escape--output.Layers touched
crates/fj-client/src/actions_artifacts.rs(new):list_artifacts,download_artifact,Artifact.crates/fj-client/src/lib.rs,crates/fj-core/src/actions.rs: re-exports.crates/fj/src/actions.rs:Artifacts+Artifact { Download }subcommands,view_artifacts/download_artifacts,human_size,artifact_zip_filename.crates/fj/localization/en-US/messages.ftl: two new messages.Tests
just pre-commitgreen (fmt, clippy-D warnings, build, full test suite). New unit tests: artifacts JSON shape, web-URL construction,human_sizeboundaries, filename sanitization.Verified live against dev.a8n.run:
artifactson an empty run,--json, and a bad run number all behave; download error paths (no artifacts, unknown--name) behave. A successful zip write could not be exercised live because no failed e2e run with a non-expired artifact existed in retention at implementation time (CI is fully green right now); the download path is contract-verified against Forgejo source and shares the live-verified client/auth/URL construction.AC status
fj actions artifacts <run>lists artifact name + size (+ status).fj actions artifact download <run> [--name] [--output]writes the zip(s) (contract-verified; see note above).--host; list honors--json.fjhelp output. Wiki (external codeberg forgejo-cli wiki) is a separate repo - follow-up to add the two commands there.`fj actions` exposed `tasks` and `logs` but no way to reach a run's artifacts, so a failing Playwright e2e `trace.zip` / report could not be pulled and opened locally without the web UI (the recurring blocker on the chromium SPA-login races). Add two subcommands mirroring the `logs` UX: - `fj actions artifacts <run>` lists each artifact's name, human-readable size, and status. - `fj actions artifact download <run> [--name <artifact>] [--output <dir>]` writes the artifact zip(s) to disk (default: all artifacts for the run into the current directory). Like `logs`, artifacts are served only by Forgejo web routes, not `/api/v1`, so the fetch lives in the connection layer (`fj-client/src/actions_artifacts.rs`) using our own reqwest client that reuses the host URL and bearer token. Verified against Forgejo v14 (`routers/web/repo/actions/view.go`, `web.go`): list is `GET /{owner}/{repo}/actions/runs/{run}/artifacts` returning `{"artifacts":[{name,size,status}]}`; download is `GET .../artifacts/{name}` streaming `{name}.zip`. The list call disables redirects so a sign-in redirect surfaces as an auth error instead of an HTML body; the download call follows redirects so instances that offload artifact storage still resolve, with the token marked sensitive so reqwest drops it on a cross-origin hop. On-disk filenames neutralize path separators in artifact names so a crafted name cannot escape the output directory. Both honor `--host` and, for the list, `--json`. Added unit tests for the artifacts JSON shape, the web URL construction, the human-size formatter, and the filename sanitization. Verified live against dev.a8n.run: list (empty run), `--json`, and a bad run number all behave; download error paths (no artifacts, unknown `--name`) behave. A successful zip write could not be exercised live because no failed e2e run with a non-expired artifact existed in retention at implementation time; the download path is contract-verified against Forgejo source and shares the live-verified client/auth/URL construction. #DEV-410 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>