CI: eliminate cold pnpm install via pre-baked image (Phase 1) #634

Closed
opened 2026-06-22 21:41:19 +00:00 by jason.woltje · 0 comments
Owner

Problem

Every Woodpecker pipeline runs a cold pnpm install — full network fetch of the dep tree + musl native rebuilds (better-sqlite3, node-pty, sqlite3, canvas, sharp) + a fresh apk add python3 make g++ — with zero caching across runs. Median install 731s (12.2 min), paid twice per push (ci + publish).

Phase 1 (this issue)

Pre-baked CI image (toolchain + warm pnpm store via pnpm fetch) pushed to the Gitea registry CI already publishes to. No cluster access required — repo commits only.

  • Dockerfile.ci — node:22-alpine base + python3/make/g++/postgresql-client + pnpm@10.6.2 + pnpm fetch --frozen-lockfile (warms store, compiles natives once).
  • .woodpecker/ci-image.yml — kaniko build/push of ci-base:latest + lockfile-hash tag, triggered only when pnpm-lock.yaml or Dockerfile.ci change.
  • .woodpecker/ci.yml + .woodpecker/publish.yml — switch install image to the baked ci-base:latest, drop apk add, use pnpm install --frozen-lockfile --prefer-offline.
  • Framework template packages/mosaic/framework/tools/quality/templates/monorepo/.woodpecker.yml — single cached install instead of re-running npm ci in 6 steps.

Expected: install ~731s → ~30–60s (~11 min/workflow, ~20 min/push).

Node 22→24 bump is a SEPARATE follow-up PR (keep zero version variables while landing the cache change).

Board report: docs/reports/woodpecker-ci-cache-board-2026-06-22.md (jarvis-brain).

Phase 2 (durable RWX Longhorn PVC for the pnpm store) is tracked separately and NOT in scope here.

## Problem Every Woodpecker pipeline runs a *cold* `pnpm install` — full network fetch of the dep tree + musl native rebuilds (better-sqlite3, node-pty, sqlite3, canvas, sharp) + a fresh `apk add python3 make g++` — with zero caching across runs. Median install **731s (12.2 min)**, paid **twice** per push (ci + publish). ## Phase 1 (this issue) Pre-baked CI image (toolchain + warm pnpm store via `pnpm fetch`) pushed to the Gitea registry CI already publishes to. No cluster access required — repo commits only. - `Dockerfile.ci` — node:22-alpine base + python3/make/g++/postgresql-client + pnpm@10.6.2 + `pnpm fetch --frozen-lockfile` (warms store, compiles natives once). - `.woodpecker/ci-image.yml` — kaniko build/push of `ci-base:latest` + lockfile-hash tag, triggered only when `pnpm-lock.yaml` or `Dockerfile.ci` change. - `.woodpecker/ci.yml` + `.woodpecker/publish.yml` — switch install image to the baked `ci-base:latest`, drop `apk add`, use `pnpm install --frozen-lockfile --prefer-offline`. - Framework template `packages/mosaic/framework/tools/quality/templates/monorepo/.woodpecker.yml` — single cached install instead of re-running `npm ci` in 6 steps. **Expected:** install ~731s → ~30–60s (~11 min/workflow, ~20 min/push). Node 22→24 bump is a SEPARATE follow-up PR (keep zero version variables while landing the cache change). Board report: `docs/reports/woodpecker-ci-cache-board-2026-06-22.md` (jarvis-brain). Phase 2 (durable RWX Longhorn PVC for the pnpm store) is tracked separately and NOT in scope here.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mosaicstack/stack#634