# Pre-baked CI base image for Woodpecker pipelines. # # Purpose: eliminate the cold `pnpm install` that dominates every pipeline # (~731s median). This image ships the native toolchain (no per-run `apk add`) # AND a warm, content-addressable pnpm store with the dependency-tree tarballs # already fetched at build time. `pnpm fetch` only populates the store from the # lockfile — it does NOT run the native node-gyp builds (better-sqlite3, # node-pty, sqlite3, canvas, sharp); those still compile at `pnpm install`, # which is exactly why the musl toolchain stays baked into this image. A # pipeline `pnpm install --frozen-lockfile --prefer-offline` then resolves # tarballs from local hard-links (no network) and compiles natives against the # already-present toolchain, in tens of seconds instead of ~731s. # # Rebuilt only when `pnpm-lock.yaml` or this Dockerfile change # (see .woodpecker/ci-image.yml). # # Node version is pinned to 24 (Active LTS). This is the follow-up bump from # node:22 — sequenced AFTER the CI cache work landed so the runtime change # carries zero cache variables. node:26 stays held until it reaches LTS # (Oct 2026); the Current line risks native-module (node-gyp) breakage on a # runner that compiles better-sqlite3 / canvas / sharp / node-pty from source. FROM node:24-alpine # Native toolchain required to compile node-gyp deps on musl, plus the # postgresql-client used by the test step's pg_isready readiness probe. `bash` # is baked here too — the sanitization step in ci.yml otherwise does a per-run # `apk add bash`. RUN apk add --no-cache python3 make g++ postgresql-client bash # Pin pnpm to the repo's packageManager version via corepack. RUN corepack enable && corepack prepare pnpm@10.6.2 --activate WORKDIR /app # Pin the store location so the pipeline can point `store-dir` at the same path. ENV PNPM_HOME=/root/.local/share/pnpm RUN pnpm config set store-dir /root/.local/share/pnpm/store # Warm the store. `pnpm fetch` populates the content-addressable store with the # dependency tarballs directly from the lockfile (no package.json / workspace # needed), so a baked store stays valid until the lockfile changes. Note: # `fetch` does NOT compile native modules — that happens later at `pnpm install` # in the pipeline, against the toolchain baked above. COPY pnpm-lock.yaml ./ RUN pnpm fetch --frozen-lockfile