Files
worldmonitor/Dockerfile.seed-bundle-resilience-validation
Elie Habib 061bb2d8e6 fix(resilience): ship full scripts/ tree in validation Docker image (#3054)
Second hidden-dependency crash from the cherry-picked COPY list. First was _proxy-utils.cjs (fixed post-merge on PR #3041). Now backtest-resilience-outcomes.mjs imports _country-resolver.mjs from PR #3052 and the validation cron crashed in prod with ERR_MODULE_NOT_FOUND.

The explicit file list was meant to keep the image small, but every new local import since has required a matching Dockerfile edit that nobody remembers until the cron crashes. Copy the whole scripts/ tree instead — ~2 MB larger, robust to any future import.
2026-04-13 15:43:55 +04:00

53 lines
2.8 KiB
Docker

# =============================================================================
# Seed Bundle: Resilience Validation (weekly cron)
# =============================================================================
# Runs scripts/seed-bundle-resilience-validation.mjs which spawns:
# - benchmark-resilience-external.mjs
# - backtest-resilience-outcomes.mjs
# - validate-resilience-sensitivity.mjs (imports ../server/*.ts via tsx)
#
# The validation scripts only touch node: builtins, local helpers, and .ts
# files under ../server/ via dynamic import. The ONLY external runtime dep
# is tsx (to register the ESM loader for .ts imports).
# =============================================================================
FROM node:22-alpine
WORKDIR /app
# Install only tsx at /app/node_modules/tsx. Single-package install keeps the
# image small and eliminates the previous ambiguity where tsx existed at both
# /app/node_modules and /app/scripts/node_modules and Node 22 resolved the
# wrong one (which had an incomplete dist/). The test is a build-time assert
# that tsx's ESM loader is at the path referenced by NODE_OPTIONS below.
RUN npm install --prefix /app --no-save --no-audit --no-fund --no-package-lock tsx@4.21.0 \
&& test -f /app/node_modules/tsx/dist/loader.mjs
# Ship the full scripts/ tree rather than a hand-picked file list. The cherry-
# picked approach broke twice: once when _seed-utils.mjs started eagerly
# createRequire()ing _proxy-utils.cjs (PR #3041 post-merge fix), and again
# when backtest-resilience-outcomes.mjs imported _country-resolver.mjs
# (PR #3052 post-merge fix — this file). Each time the bundle's scripts add
# a new local import, the cherry-picked Dockerfile list becomes a hidden
# dependency contract that nobody remembers to update until the cron
# crashes with ERR_MODULE_NOT_FOUND in production. Copying the whole scripts/
# dir is ~2 MB larger and robust to the next import.
#
# validate-resilience-sensitivity.mjs dynamic-imports ../server/*.ts so the
# full server/ tree is copied (scorers pull from shared/ and data/ at runtime).
COPY scripts/ ./scripts/
COPY server/ ./server/
COPY shared/ ./shared/
COPY data/ ./data/
COPY tsconfig.json tsconfig.api.json ./
# Absolute path sidesteps bare-specifier resolution entirely — Node 22 can
# resolve "tsx/esm" to the wrong node_modules when multiple exist, and "tsx/esm"
# also triggers ERR_REQUIRE_CYCLE_MODULE under Node 22 when the loaded .ts
# has its own imports. dist/loader.mjs is tsx's default ESM loader and works
# for both the parent process and children spawned via execFile (which inherit
# NODE_OPTIONS).
ENV NODE_OPTIONS="--max-old-space-size=8192 --dns-result-order=ipv4first --import=file:///app/node_modules/tsx/dist/loader.mjs"
CMD ["node", "scripts/seed-bundle-resilience-validation.mjs"]