Files
familysync/.planning/quick/260618-tg2-persistent-ci-dependency-caches-pnpm-sto/260618-tg2-PLAN.md
T
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

100 lines
5.1 KiB
Markdown

---
quick_id: 260618-tg2
slug: persistent-ci-dependency-caches-pnpm-sto
description: Persistent CI dependency caches (pnpm store + Playwright browsers)
type: quick
created: 2026-06-19
files_modified:
- .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:
```yaml
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]