--- quick_id: 260618-tg2 slug: persistent-ci-dependency-caches-pnpm-sto phase: "20" plan: tg2 status: complete completed: 2026-06-18 tags: [ci, caching, pnpm, playwright] key-files: modified: - .gitea/workflows/ci.yml - docs/DEVELOPMENT.md - apps/api/Dockerfile - .gitea/workflows/publish.yml decisions: - All four CI pnpm installs now target /pnpm-store via --store-dir --prefer-offline flags - PLAYWRIGHT_BROWSERS_PATH added at harness job level (not per-step) so both install and run steps share the same path - D-PROBE-04 comments updated to reflect the new store strategy rather than "no cache" --- # Quick Task 260618-tg2: Persistent CI dependency caches (pnpm store + Playwright) Summary **One-liner:** Point all four CI pnpm installs at `/pnpm-store` and harness Playwright at `/ms-playwright` via host-mounted directories on the act_runner. ## What Was Done ### Task 1 — pnpm store (ci.yml, 4 install lines) Changed all four `pnpm install --frozen-lockfile` lines to `pnpm install --frozen-lockfile --store-dir /pnpm-store --prefer-offline`: - Line ~54: `fast-checks` job - Line ~110: `api` job - Line ~197: `harness` job - Line ~485: `security` job (conditional) Both flags confirmed valid against `pnpm 11.5.1 install --help` before use. Updated the stale D-PROBE-04 comment in each job from "no cache backend / ~30s acceptable" to reflect that installs now target the host-mounted store. ### Task 2 — Playwright browsers (harness job env) Added `PLAYWRIGHT_BROWSERS_PATH: /ms-playwright` to the existing job-level `env:` block on the `harness:` job (alongside the DB_* creds). This means both the `Install Playwright browsers` step and the `Run harness` step inherit the same path, so cached binaries are found at install time and used at test time. Added a comment on the Playwright install step noting the future optimization: baking a runner image with browsers preinstalled would also eliminate the `--with-deps` apt step. ### Task 3 — Host-mount documentation (docs/DEVELOPMENT.md) Added a "CI dependency caches" subsection under the CI Pipeline Overview. Documents: - The two container paths (`/pnpm-store`, `/ms-playwright`) with a reference table - That the act\_runner `config.yaml` `container.options` bind-mount is a **host-side** change - That CI still works without the mounts (ephemeral fallback — just no caching) ## Verification ``` grep -c -- '--store-dir /pnpm-store --prefer-offline' .gitea/workflows/ci.yml → 4 grep -n 'PLAYWRIGHT_BROWSERS_PATH' .gitea/workflows/ci.yml → 187: PLAYWRIGHT_BROWSERS_PATH: /ms-playwright (job-level env) → 278: # PLAYWRIGHT_BROWSERS_PATH=/ms-playwright... (comment) python3 -c 'import yaml,sys; yaml.safe_load(open(".gitea/workflows/ci.yml")); print("YAML valid")' → YAML valid pnpm format:check → All matched files use Prettier code style! pnpm md:lint → Summary: 0 error(s) ``` ### Task 4 — Dockerfile BuildKit pnpm-store cache (added mid-task by user request) Added by the orchestrator after the initial 3 tasks, when the user asked to include the Dockerfile: - `apps/api/Dockerfile`: added `# syntax=docker/dockerfile:1` (line 1) and a BuildKit cache mount (`RUN --mount=type=cache,target=/pnpm-store,id=pnpm-store,sharing=locked ... --store-dir /pnpm-store`) to all three pnpm install stages (builder, pwa-builder, production). `sharing=locked` because builder + pwa-builder run in parallel and would otherwise race the shared store. - `.gitea/workflows/publish.yml`: set `DOCKER_BUILDKIT: '1'` on the "Build production image" step — the publish path uses plain `docker build` (not buildx), and the legacy builder would fail on the `--mount` syntax. BuildKit is default on Docker 23+; set explicitly for safety. - Verified: `format:check` clean (Dockerfile is outside prettier's scope), publish.yml valid YAML. ## Commits | Hash | Message | | --- | --- | | `80b2038` | chore(20): persistent CI caches — pnpm store + Playwright browsers | | `f83d423` | docs(20): document CI persistent cache host-mount dependency | | `6e93e24` | chore(260618-tg2): BuildKit pnpm-store cache mount in Dockerfile build | ## Deviations from Plan Dockerfile cache (Task 4) was added mid-task at the user's request after the initial 3-task plan (it had been explicitly deferred/out-of-scope). The publish workflow's `DOCKER_BUILDKIT=1` was a required companion change so the `--mount` syntax doesn't break the legacy builder. - `--store-dir` flag form matches plan exactly (plan said verify against pnpm; verified: valid) - harness job already had a job-level `env:` map; `PLAYWRIGHT_BROWSERS_PATH` was added to it as instructed - stale comment text updated as instructed ## Known Stubs None. ## Threat Flags None — YAML-only and doc-only changes; no new network endpoints, auth paths, or trust boundaries introduced. ## Self-Check: PASSED - `.gitea/workflows/ci.yml` — modified and committed at 80b2038 - `docs/DEVELOPMENT.md` — modified and committed at f83d423 - YAML validity confirmed by python3 yaml.safe_load - 4 install lines confirmed by grep -c - PLAYWRIGHT_BROWSERS_PATH confirmed at job-level env line 187