Three build-time improvements, all measured on this host.
The build context no longer ships the test suites. `next build` type-checks
the whole project, so `e2e/` and `integration/` in the context made the image
build fail on a type error in test code and pulled @playwright/test, vitest,
msw, testcontainers and drizzle-kit into the image. The CI runs those suites
on the host. Type-check time in the build drops from 2.2s to 1.3s, and a
contract test now fails if application code ever imports from those folders.
The CI now exports the build cache to a directory outside the runner
workspace: /var/cache/epicnext-cms-buildcache for the release and a separate
map for branch preflights, so a branch can never feed the release cache. The
workspace is wiped after every job and `docker builder prune` runs after
every release, so the next build started with an empty cache and downloaded
all 493 packages again (measured: reused 0, downloaded 493). With the
durable export the frozen dependency layer is reused (measured: reused 493,
downloaded 0) even after a full prune. A cache directory that is not
writable only produces a warning; the release is never blocked.
Investigated the remaining ~30s of Next.js build tracing: excluding the
dev-only packages with `outputFileTracingExcludes` made it slower (35s vs
30s), because the traced output was already minimal (5 packages). The cost
is Next's own page-config collection and file tracing over 329 routes, not
the dependency set, so no configuration was kept.
The Docker build was slower than it needed to be and could drift away from
the lockfile. Three separate causes, fixed together.
The image installed a pinned [email protected].1 while package.json pins 12.11.2
through `packageManager`. pnpm silently self-managed the difference at
build time: it downloaded the right version into an uncached location and
never verified the sha512 hash. Install corepack instead and let it resolve
version and hash from the pin, so the image can no longer diverge and a
tampered tarball is rejected. The resolved pnpm lives in COREPACK_HOME
inside the image, so the migrations image still runs pnpm offline
(`docker run --entrypoint pnpm ... db:migrate`).
The pnpm store cache-mount pointed at /root/.local/share/pnpm/store, but
pnpm keeps its store in $PNPM_HOME/store/v11. The mount was a no-op: every
build re-downloaded all 493 packages ("reused 0, downloaded 493"). Mounting
the store where pnpm actually keeps it turns that into "reused 493,
downloaded 0".
The build context carried backups/ (232MB of rotating database dumps) and
public/vendor/ (regenerated by `pnpm assets:editor` from node_modules).
Both grew the context and invalidated the `COPY . .` layer on every daily
backup rotation, forcing a full Next.js rebuild. Excluding them takes the
context from 276MB to 23MB.
Compose now also exports the build cache to .docker-cache/cms and imports it
again, so the cache survives `scripts/docker-prune.sh`, which trims the
daemon cache to 4GB after every deploy. Verified by pruning the entire
daemon cache: the next build restored every layer from the local export and
downloaded nothing.
Also: byparr's floating `:latest` tag is now pulled on every start, the
migrations build in scripts/docker-update.sh imports the same local cache,
the README no longer claims the build needs host networking or an
iptables-disabled daemon, the pnpm rationale in renovate.json reflects that
Docker follows the pin automatically, and contract tests pin the new
invariants so the store path cannot drift back.
Drop the leftover Playwright browser install and e2e smoke test from the deployment script, and update the deployment contract tests to cover the verify-deployed-release smoke check instead.
- Create the runtime write targets (/app/storage, /app/public/nitro-assets,
/app/public/swf, /var/www/Gamedata) owned by UID/GID 33 in the runner image
so running without the bound volumes no longer hits ENOENT.
- Bake a HEALTHCHECK into the image so `docker run` (ci-deploy.sh) also reports
Docker-level health; compose can still override it with its own probe.
- Add the dockerfile:1 syntax pragma and ignore non-pnpm lockfiles so a stray
package-lock.json/yarn.lock can never taint the build context.
- update-Nitrov3.sh: build CMS into .next-staging and swap atomically so a
failed build never takes the live site down; auto-merge new variables
from .env.example; validate env for duplicates/broken lines; restart the
emulator/CMS only when rebuilt or unhealthy; fix step renumbering
- next.config.ts: support NEXT_DIST_DIR for staged production builds
- fix all TS errors (unused imports, missing tryDownloadCandidates helper)
so tsc and the production build pass clean
- add Dockerfile/.dockerignore and switch docker-compose to a CMS container
- include prevailing UI/refactor changes (SurfaceCard, ticketing, tsconfig)