Files
Lucas Berger c5cdb9c21d
CI / changes (pull_request) Successful in 4s
CI / api (pull_request) Successful in 2m6s
CI / fast-checks (pull_request) Successful in 2m32s
CI / security (pull_request) Successful in 1m2s
CI / harness (pull_request) Successful in 5m36s
CI / gate (pull_request) Successful in 2s
docs(quick-260618-tg2): persistent CI dependency caches (pnpm store + Playwright + Dockerfile)
2026-06-18 21:21:26 -04:00

5.1 KiB

quick_id, slug, description, type, created, files_modified
quick_id slug description type created files_modified
260618-tg2 persistent-ci-dependency-caches-pnpm-sto Persistent CI dependency caches (pnpm store + Playwright browsers) quick 2026-06-19
.gitea/workflows/ci.yml
docs/DEVELOPMENT.md
apps/api/Dockerfile
.gitea/workflows/publish.yml

Quick Task 260618-tg2: Persistent CI dependency caches (pnpm store + Playwright)

Why

The Gitea runner re-downloads all deps every run: 4 jobs each run pnpm install --frozen-lockfile cold (lines 54/110/197/478), and the harness job re-downloads Playwright browser binaries every run (line 275). The runner is long-lived Docker-on-Unraid, so persisting these via host bind-mounts (/pnpm-store, /ms-playwright, wired in the act_runner config.yaml container.options — a separate manual host change) eliminates the repeat downloads. This avoids actions/cache@v4, which the Phase-8 runner probe found times out on this runner (D-PROBE-04).

Scope this task: the two CI caches only. Verdaccio (registry mirror) and the Dockerfile BuildKit cache mount are explicitly OUT of scope for now.

Tasks

Task 1 — pnpm store: point all CI installs at the persistent store

In .gitea/workflows/ci.yml, change each of the four install steps:

run: pnpm install --frozen-lockfile

run: pnpm install --frozen-lockfile --store-dir /pnpm-store --prefer-offline

Lines 54 (fast-checks), 110 (api), 197 (harness), 478 (security). --store-dir and --prefer-offline are valid pnpm 11.5.1 install flags (verify with pnpm install --help). Do NOT add store-dir to a repo .npmrc — local dev has no /pnpm-store. Update the stale comment near line 51 (the "no cache backend / ~30s acceptable" note) to reflect that installs now use the persistent host-mounted store.

  • verify: grep -c -- '--store-dir /pnpm-store --prefer-offline' .gitea/workflows/ci.yml → 4
  • done: all four installs use the persistent store; YAML still valid.

Task 2 — Playwright: persist browser binaries on the harness job

Add a job-level env: to the harness: job so every step (browser install + test run) resolves the same persistent path:

  harness:
    runs-on: ubuntu-latest
    env:
      PLAYWRIGHT_BROWSERS_PATH: /ms-playwright

(If the harness job already has a job-level env: map, add the key to it rather than duplicating.) Leave npx playwright install --with-deps webkit chromium (line 275) as-is — the binary download is now cached by version; the --with-deps apt step can't persist (add a one-line comment noting "baking a runner image with browsers preinstalled would also drop the --with-deps apt step" as a future optimization).

  • verify: PLAYWRIGHT_BROWSERS_PATH: /ms-playwright present under the harness job; the test-run step (PLAYWRIGHT_BASE_URL ~line 340) inherits it.
  • done: Playwright browsers persist across runs.

Task 3 — Document the host-mount dependency

Add a short subsection to docs/DEVELOPMENT.md (CI/runner area) noting:

  • CI now uses persistent caches at container paths /pnpm-store and /ms-playwright.
  • These require the act_runner config.yaml container.options to bind-mount host dirs to those paths (host change, not in this repo).
  • Without the mounts CI still works — it just falls back to uncached (writes to an ephemeral dir).
  • done: the host-side requirement is discoverable from the repo.

Constraints

  • Must pass local gates before each commit: format:check (prettier), eslint, YAML validity (yq or actionlint if available), typecheck (no TS touched, but run if cheap).
  • Job names and the required-check contexts (CI / fast-checks, CI / api, CI / harness, CI / security, CI / gate) MUST stay identical so branch protection still matches. Do not rename jobs or restructure the job graph.
  • Atomic commits (Task 1, Task 2, Task 3 may be one or separate commits — keep changes coherent).

Task 4 — Dockerfile BuildKit pnpm-store cache (added mid-task per user request)

apps/api/Dockerfile: add # syntax=docker/dockerfile:1 (line 1) and a BuildKit cache mount to all three pnpm install stages (builder, pwa-builder, production): RUN --mount=type=cache,target=/pnpm-store,id=pnpm-store,sharing=locked pnpm install ... --store-dir /pnpm-store. sharing=locked because builder + pwa-builder run in parallel and would otherwise race the store. .gitea/workflows/publish.yml: set DOCKER_BUILDKIT: '1' on the "Build production image" step so the legacy builder can't break on the --mount syntax (BuildKit is default on Docker 23+; explicit for safety).

  • done: image build reuses a persistent BuildKit pnpm-store cache across builds.

OUT OF SCOPE (do not touch)

  • Verdaccio / any .npmrc registry change (deferred — user will set up later).

must_haves

  • truth: "All four ci.yml pnpm installs use --store-dir /pnpm-store --prefer-offline"
  • truth: "The harness job sets PLAYWRIGHT_BROWSERS_PATH=/ms-playwright"
  • truth: "ci.yml remains valid YAML with unchanged job names / required-check contexts"
  • truth: "docs note the act_runner config.yaml host-mount requirement"
  • artifacts: [.gitea/workflows/ci.yml, docs/DEVELOPMENT.md]