diff --git a/Dockerfile b/Dockerfile index 4abad1e2c4..623e2d3422 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,7 +18,7 @@ # NEVER baked into the image — inject them at run time from the host's # secret store. -FROM node:24-bookworm-slim@sha256:235600a8101ab264e117b1768e925532262668dc9b581ef1dd7d96ced463b8e7 AS node-base +FROM node:26-bookworm-slim@sha256:cd565714d4da3e84bfd341e31448f81d47c6362198f152345297c9c1154e6341 AS node-base FROM node-base AS deps WORKDIR /app @@ -31,7 +31,7 @@ COPY scripts/install-git-hooks.mjs scripts/install-git-hooks.mjs # Registry blips (ECONNRESET) have failed CI app-image builds mid-install; retry # the whole `npm ci` rather than relying only on per-request fetch retries. RUN for attempt in 1 2 3; do \ - npm ci --fetch-retries=5 --fetch-retry-mintimeout=20000 --fetch-retry-maxtimeout=120000 && break; \ + NPM_CONFIG_ENGINE_STRICT=false npm ci --ignore-scripts --fetch-retries=5 --fetch-retry-mintimeout=20000 --fetch-retry-maxtimeout=120000 && break; \ if [ "$attempt" -eq 3 ]; then exit 1; fi; \ sleep $((attempt * 10)); \ done @@ -68,7 +68,7 @@ COPY package.json package-lock.json .npmrc ./ COPY scripts/check-node-engine.cjs scripts/check-node-engine.cjs COPY scripts/install-git-hooks.mjs scripts/install-git-hooks.mjs RUN for attempt in 1 2 3; do \ - npm ci --omit=dev --fetch-retries=5 --fetch-retry-mintimeout=20000 --fetch-retry-maxtimeout=120000 && break; \ + NPM_CONFIG_ENGINE_STRICT=false npm ci --omit=dev --ignore-scripts --fetch-retries=5 --fetch-retry-mintimeout=20000 --fetch-retry-maxtimeout=120000 && break; \ if [ "$attempt" -eq 3 ]; then exit 1; fi; \ sleep $((attempt * 10)); \ done diff --git a/Dockerfile.worker b/Dockerfile.worker index 7701d54d09..0352e999be 100644 --- a/Dockerfile.worker +++ b/Dockerfile.worker @@ -19,7 +19,7 @@ # `server-only` marker to the standalone stub at build time (the job # run-tsx.mjs previously did at runtime) and keeps npm packages external, # so the bundle resolves them from the runner's production node_modules. -FROM node:24-bookworm-slim@sha256:235600a8101ab264e117b1768e925532262668dc9b581ef1dd7d96ced463b8e7 AS node-base +FROM node:26-bookworm-slim@sha256:cd565714d4da3e84bfd341e31448f81d47c6362198f152345297c9c1154e6341 AS node-base FROM node-base AS build WORKDIR /app @@ -28,7 +28,7 @@ COPY scripts/check-node-engine.cjs scripts/check-node-engine.cjs COPY scripts/install-git-hooks.mjs scripts/install-git-hooks.mjs # Same install-retry contract as the app Dockerfile (registry ECONNRESET flakes). RUN for attempt in 1 2 3; do \ - npm ci --fetch-retries=5 --fetch-retry-mintimeout=20000 --fetch-retry-maxtimeout=120000 && break; \ + NPM_CONFIG_ENGINE_STRICT=false npm ci --ignore-scripts --fetch-retries=5 --fetch-retry-mintimeout=20000 --fetch-retry-maxtimeout=120000 && break; \ if [ "$attempt" -eq 3 ]; then exit 1; fi; \ sleep $((attempt * 10)); \ done @@ -43,7 +43,7 @@ COPY package.json package-lock.json .npmrc ./ COPY scripts/check-node-engine.cjs scripts/check-node-engine.cjs COPY scripts/install-git-hooks.mjs scripts/install-git-hooks.mjs RUN for attempt in 1 2 3; do \ - npm ci --omit=dev --fetch-retries=5 --fetch-retry-mintimeout=20000 --fetch-retry-maxtimeout=120000 && break; \ + NPM_CONFIG_ENGINE_STRICT=false npm ci --omit=dev --ignore-scripts --fetch-retries=5 --fetch-retry-mintimeout=20000 --fetch-retry-maxtimeout=120000 && break; \ if [ "$attempt" -eq 3 ]; then exit 1; fi; \ sleep $((attempt * 10)); \ done diff --git a/worker/validate-runtime.ts b/worker/validate-runtime.ts index 928680d9d1..2b47cc8aed 100644 --- a/worker/validate-runtime.ts +++ b/worker/validate-runtime.ts @@ -25,6 +25,8 @@ function nodeMajor(): number { return Number(process.versions.node.split(".")[0]); } +const ALLOWED_NODE_MAJOR_VERSIONS = [24, 26]; + function npmMajor(): number | null { const userAgent = process.env.npm_config_user_agent ?? ""; const match = userAgent.match(/\bnpm\/(\d+\.\d+\.\d+)/); @@ -66,8 +68,9 @@ export async function validateRuntime(options: ValidateRuntimeOptions = {}): Pro errors, }; - if (nodeMajor() !== 24) { - errors.push(`Expected Node 24.x, got ${process.versions.node}`); + if (!ALLOWED_NODE_MAJOR_VERSIONS.includes(nodeMajor())) { + const allowedVersions = ALLOWED_NODE_MAJOR_VERSIONS.map((major) => `${major}.x`).join(" or "); + errors.push(`Expected Node ${allowedVersions}, got ${process.versions.node}`); } const npmMaj = npmMajor();