| # ============================================================================= | |
| # 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:24-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd | |
| 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"] | |