src/actions.ts — downloadRunLogs (ZIP, guarded unzip, cache namespace) #116

Closed
opened 2026-08-29 03:28:39 +00:00 by david · 1 comment
Owner

Summary

downloadRunLogs() in src/actions.ts: cache-first download of the run-logs ZIP (GET /repos/{owner}/{repo}/actions/runs/{run_id}/logs), size-guarded extraction into the per-job cache namespace so later job_log calls hit the cache.

Background

Part of the Forgejo Actions tooling feature (tracking issue #111 — branch feature/issue-111/forgejo-actions-tools, commits issue-111: ...). Depends on the zip-extraction dependency (#112) and the cache module (#113). Powers the forgejo_action_run_logs tool. The endpoint returns a ZIP of the run's job logs; Node has no built-in unzip, so extraction uses the library chosen in #112. Baseline: Forgejo v16.

Documentation Required

A separate process downloads these into docs/reference/forgejo-actions-api/ before this issue is implemented. Check that folder for the actual reference material before starting.

Implementation Details

  • Cache-first on the run ZIP: path via runZipPath() (#113); if it exists and no refresh, reuse it (still extract/return the listing; do not re-download).
  • Download the ZIP, write to run.zip, then extract with size guards (protect against zip-bombs):
    • per-entry uncompressed size limit,
    • total uncompressed limit,
    • entry-count limit.
      Reject (fail with ApiError) if any guard trips; do not extract oversized entries.
  • Extraction target: the per-job cache namespace — <root>/<owner>/<repo>/<run_id>/<job_id>.log — so later forgejo_action_job_log calls hit the cache. Verify the ZIP's internal layout first: if entries don't map cleanly to job ids, fall back to a separate extraction dir (e.g. <run_id>/extracted/) and document which layout was found.
  • Return a per-file listing (paths + sizes) plus {path, cached} for the ZIP itself.

Acceptance Criteria

  • Cache hit on run.zip (exists, no refresh) does not re-download (mocked fetch not invoked for the ZIP).
  • Download → write run.zip → extraction lands files in the per-job cache namespace (or documented fallback dir) with a per-file listing returned.
  • All three size guards (per-entry, total, entry-count) reject an oversized/malicious archive with ApiError, leaving no partial extraction behind.
  • A fixture ZIP with 2+ job logs extracts correctly and the resulting per-job files are readable by readCachedLog() (#113).
  • Unit tests (mock fetch, real fs in os.tmpdir()) cover cache hit/miss/refresh, guard rejection, and layout mapping.
  • npm run check passes.

Test Plan

  • cd extensions/forgejo && npm test — ZIP cases in tests/actions.test.ts using a fixture archive (create one in the test or commit a tiny fixture).
  • Manual (Forgejo v16+ instance): forgejo_action_run_logs run_id=... → confirm run.zip + per-job logs in ~/.cache/forgejo-actions/...; then forgejo_action_job_log for one of those jobs returns cached: true.
### Summary `downloadRunLogs()` in `src/actions.ts`: cache-first download of the run-logs ZIP (`GET /repos/{owner}/{repo}/actions/runs/{run_id}/logs`), size-guarded extraction into the per-job cache namespace so later `job_log` calls hit the cache. ### Background Part of the Forgejo Actions tooling feature (tracking issue **#111** — branch `feature/issue-111/forgejo-actions-tools`, commits `issue-111: ...`). Depends on the zip-extraction dependency (**#112**) and the cache module (**#113**). Powers the `forgejo_action_run_logs` tool. The endpoint returns a **ZIP** of the run's job logs; Node has no built-in unzip, so extraction uses the library chosen in #112. Baseline: Forgejo v16. ### Documentation Required - `docs/reference/forgejo-actions-api/` (same folder as prior steps): - https://forgejo.org/docs/latest/user/api/usage/ — API usage guide (base URL, auth, `read:repository` scope). - https://codeberg.org/forgejo/forgejo/raw/branch/forgejo/templates/swagger/v1_json.tmpl — v16 swagger: `GET /repos/{owner}/{repo}/actions/runs/{run_id}/logs` (returns a ZIP; response type/layout). - https://codeberg.org/forgejo/forgejo/src/branch/forgejo/routers/api/v1/repo/action.go — server-side handler: **verify the ZIP's internal layout** (do entries map to job ids?) — this determines the extraction strategy. - Plus the zip library docs from `docs/reference/adm-zip/` or `docs/reference/yauzl/` per the #112 decision. A separate process downloads these into `docs/reference/forgejo-actions-api/` before this issue is implemented. **Check that folder for the actual reference material before starting.** ### Implementation Details - Cache-first on the run ZIP: path via `runZipPath()` (**#113**); if it exists and no `refresh`, reuse it (still extract/return the listing; do not re-download). - Download the ZIP, write to `run.zip`, then extract with **size guards** (protect against zip-bombs): - per-entry uncompressed size limit, - total uncompressed limit, - entry-count limit. Reject (fail with `ApiError`) if any guard trips; do not extract oversized entries. - **Extraction target:** the per-job cache namespace — `<root>/<owner>/<repo>/<run_id>/<job_id>.log` — so later `forgejo_action_job_log` calls hit the cache. Verify the ZIP's internal layout first: if entries don't map cleanly to job ids, fall back to a separate extraction dir (e.g. `<run_id>/extracted/`) and document which layout was found. - Return a per-file listing (paths + sizes) plus `{path, cached}` for the ZIP itself. ### Acceptance Criteria - [ ] Cache hit on `run.zip` (exists, no `refresh`) does not re-download (mocked fetch not invoked for the ZIP). - [ ] Download → write `run.zip` → extraction lands files in the per-job cache namespace (or documented fallback dir) with a per-file listing returned. - [ ] All three size guards (per-entry, total, entry-count) reject an oversized/malicious archive with `ApiError`, leaving no partial extraction behind. - [ ] A fixture ZIP with 2+ job logs extracts correctly and the resulting per-job files are readable by `readCachedLog()` (**#113**). - [ ] Unit tests (mock fetch, real fs in `os.tmpdir()`) cover cache hit/miss/refresh, guard rejection, and layout mapping. - [ ] `npm run check` passes. ### Test Plan - `cd extensions/forgejo && npm test` — ZIP cases in `tests/actions.test.ts` using a fixture archive (create one in the test or commit a tiny fixture). - Manual (Forgejo v16+ instance): `forgejo_action_run_logs run_id=...` → confirm `run.zip` + per-job logs in `~/.cache/forgejo-actions/...`; then `forgejo_action_job_log` for one of those jobs returns `cached: true`.
david closed this issue 2026-08-29 07:55:11 +00:00
Author
Owner

pi-loop opened and merged a pull request for this issue: #128

pi-loop opened and merged a pull request for this issue: https://git.excelera.net/david/pi-extensions-and-skills/pulls/128
Sign in to join this conversation.
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
david/pi-extensions-and-skills#116
No description provided.