From 0993774a18919bb5d0f7b535f2ba4364e497b26c Mon Sep 17 00:00:00 2001 From: JSONbored <49853598+JSONbored@users.noreply.github.com> Date: Tue, 7 Jul 2026 04:24:45 -0700 Subject: [PATCH] fix(selfhost): build gittensory-engine before bundling the self-host image The self-host Docker build was failing for every open PR: esbuild couldn't resolve "@jsonbored/gittensory-engine" while bundling gittensory-miner's checked-in lib/*.js artifacts (#2281 extracted their reward-risk scoring into this workspace package). Two compounding problems, confirmed by reproducing the failure both locally and in an actual `docker build`: 1. gittensory-engine ships no committed dist/ -- nothing built it before the self-host bundle step ran. 2. The Dockerfile copied only the root package*.json before running `npm ci` (the usual dependency-layer caching trick), so no workspace package.json existed yet for npm to resolve -- it silently skipped every workspace symlink under node_modules, including gittensory-engine's. Building the engine afterward was not enough on its own: esbuild still couldn't find the module at all, only its dist/ would have been missing if the symlink had existed. Fix: copy the full source before `npm ci` (workspace symlinks resolve correctly), then explicitly build gittensory-engine before the self-host bundle step. Verified with an actual `docker build` (both the `build` target alone and the full multi-stage image) against a clean checkout -- fails identically to CI without this change, builds clean with it. --- Dockerfile | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 77c7096640..0e0c1d2140 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,11 +9,17 @@ ARG GITTENSORY_VERSION= # ECR Public Gallery mirrors Docker Official Images with no rate limits and no auth. FROM public.ecr.aws/docker/library/node:24-slim AS build WORKDIR /app -COPY package*.json ./ +# The full source must land BEFORE `npm ci`, not after: this is an npm-workspaces monorepo +# (workspaces: apps/*, packages/*), and `npm ci` only symlinks node_modules/ to a workspace whose +# directory already exists on disk. Copying just the root package*.json first (the usual dependency-layer +# caching trick) left every workspace package.json missing at `npm ci` time, so npm silently skipped every +# internal symlink -- @jsonbored/gittensory-engine (a workspace dependency of gittensory-miner's checked-in +# lib/*.js artifacts, #2281) then couldn't be resolved by esbuild no matter how/when its own dist/ was built. +COPY . . # --ignore-scripts: no native builds are needed (SQLite is the built-in node:sqlite; @hono/node-server is # pure JS; esbuild ships its binary as an optional dependency, not a script). RUN npm ci --ignore-scripts -COPY . . +RUN npm --workspace @jsonbored/gittensory-engine run build # --all: bundle every dependency into one self-contained dist/server.mjs, so the runtime image needs no # node_modules (≈10× smaller). The bundle has zero `cloudflare:*` imports (stubbed at build), so no loader. RUN node scripts/build-selfhost.mjs --all