From 069221a0fccbb3b48a64c25aa425ba97efb2ba5e Mon Sep 17 00:00:00 2001 From: Matsu Date: Thu, 20 Aug 2026 18:31:43 +0300 Subject: [PATCH] build: Install pnpm from npm in the runners images (no-changelog) (#36702) Co-authored-by: Claude Opus 5 (1M context) --- .github/workflows/docker-build-smoke.yml | 10 +++++-- docker/images/runners/Dockerfile | 32 +++++++++++---------- docker/images/runners/Dockerfile.distroless | 22 +++++++------- scripts/dockerize-n8n.mjs | 4 ++- 4 files changed, 38 insertions(+), 30 deletions(-) diff --git a/.github/workflows/docker-build-smoke.yml b/.github/workflows/docker-build-smoke.yml index 7154045dbec..0a0c20b965e 100644 --- a/.github/workflows/docker-build-smoke.yml +++ b/.github/workflows/docker-build-smoke.yml @@ -8,6 +8,12 @@ name: 'Docker Build Smoke Test' # build base image (no Docker cache) → # build n8n + runners images (no Docker cache) +env: + # The images copy native bindings from the host build, so both have to use the same + # Node. dockerize-n8n.mjs reads this env for the image builds. Keep in sync with + # NODE_VERSION in docker-build-push.yml, which is what the published images use. + NODE_VERSION: '26.5.1' + on: schedule: - cron: '0 3 * * *' # 3:00 AM UTC, after the nightly Docker build at midnight @@ -44,9 +50,7 @@ jobs: - name: Build full chain (no cache) uses: ./.github/actions/setup-nodejs with: - # Keep in sync with NODE_VERSION in docker/images/n8n/Dockerfile: the image - # copies native bindings from the host build. - node-version: '24.18.1' + node-version: ${{ env.NODE_VERSION }} build-command: 'pnpm build:docker:clean' enable-docker-cache: true diff --git a/docker/images/runners/Dockerfile b/docker/images/runners/Dockerfile index ac913fa04c4..32b6ca4016c 100644 --- a/docker/images/runners/Dockerfile +++ b/docker/images/runners/Dockerfile @@ -1,4 +1,5 @@ -ARG NODE_VERSION=24.18.1 +ARG NODE_VERSION=26.5.1 +ARG PNPM_VERSION=10.32.1 ARG PYTHON_VERSION=3.13 # ============================================================================== @@ -10,16 +11,15 @@ COPY ./dist/task-runner-javascript /app/task-runner-javascript WORKDIR /app/task-runner-javascript # Pin pnpm to the repo's packageManager version. `pnpm deploy` drops the -# packageManager field from the deployed package.json, so a bare -# `corepack enable pnpm` resolves to corepack's bundled latest (pnpm 11.x). -# pnpm 11 defaults minimumReleaseAge to 1440 (24h), so the `pnpm add` below -# re-resolves the deployed graph and rejects any dependency published less -# than a day ago — e.g. a first-party @n8n_io/ai-assistant-sdk bump landed -# within hours of its npm publish. This container step only re-resolves an -# already-vetted, already-pinned dependency set; the real supply-chain age -# policy is enforced by the host install (pnpm-workspace.yaml + SafeChain). -ARG PNPM_VERSION=10.32.1 -RUN corepack enable pnpm && corepack prepare "pnpm@${PNPM_VERSION}" --activate +# packageManager field from the deployed package.json, so nothing else here pins it, +# and pnpm 11.x defaults minimumReleaseAge to 1440 (24h), which would make the +# `pnpm add` below reject any dependency published less than a day ago — e.g. a +# first-party @n8n_io/ai-assistant-sdk bump landed within hours of its npm publish. +# This container step only re-resolves an already-vetted, already-pinned dependency +# set; the real supply-chain age policy is enforced by the host install +# (pnpm-workspace.yaml + SafeChain). +ARG PNPM_VERSION +RUN npm i -g "pnpm@${PNPM_VERSION}" # Remove `catalog` and `workspace` references from package.json to allow `pnpm add` in extended images RUN node -e "const pkg = require('./package.json'); \ @@ -113,6 +113,8 @@ RUN set -e; \ # STAGE 4: Node alpine base for JS task runner # ============================================================================== FROM node:${NODE_VERSION}-alpine3.24 AS node-alpine +ARG PNPM_VERSION +RUN npm i -g "pnpm@${PNPM_VERSION}" # ============================================================================== # STAGE 5: Runtime @@ -138,10 +140,10 @@ RUN apk add --no-cache ca-certificates tini libstdc++ libc6-compat && \ pip install --no-cache-dir "pip>=26.0" && \ apk del apk-tools -# Bring corepack and pnpm over, to make the image easier to extend -COPY --from=node-alpine /usr/local/lib/node_modules/corepack /usr/local/lib/node_modules/corepack -RUN ln -s ../lib/node_modules/corepack/dist/corepack.js /usr/local/bin/corepack && \ - ln -s ../lib/node_modules/corepack/dist/pnpm.js /usr/local/bin/pnpm +# Bring pnpm over, to make the image easier to extend. Node no longer bundles +# corepack, and this runtime has no npm, so pnpm is copied in as a package. +COPY --from=node-alpine /usr/local/lib/node_modules/pnpm /usr/local/lib/node_modules/pnpm +RUN ln -s ../lib/node_modules/pnpm/bin/pnpm.cjs /usr/local/bin/pnpm RUN addgroup -g 1000 -S runner \ && adduser -u 1000 -S -G runner -h /home/runner -D runner diff --git a/docker/images/runners/Dockerfile.distroless b/docker/images/runners/Dockerfile.distroless index 036673a5059..5a7c77d6bf8 100644 --- a/docker/images/runners/Dockerfile.distroless +++ b/docker/images/runners/Dockerfile.distroless @@ -12,7 +12,8 @@ # - Uses distroless nonroot user (UID 65532) # ============================================================================== -ARG NODE_VERSION=24.18.1 +ARG NODE_VERSION=26.5.1 +ARG PNPM_VERSION=10.32.1 ARG PYTHON_VERSION=3.13 @@ -25,16 +26,15 @@ COPY ./dist/task-runner-javascript /app/task-runner-javascript WORKDIR /app/task-runner-javascript # Pin pnpm to the repo's packageManager version. `pnpm deploy` drops the -# packageManager field from the deployed package.json, so a bare -# `corepack enable pnpm` resolves to corepack's bundled latest (pnpm 11.x). -# pnpm 11 defaults minimumReleaseAge to 1440 (24h), so the `pnpm add` below -# re-resolves the deployed graph and rejects any dependency published less -# than a day ago — e.g. a first-party @n8n_io/ai-assistant-sdk bump landed -# within hours of its npm publish. This container step only re-resolves an -# already-vetted, already-pinned dependency set; the real supply-chain age -# policy is enforced by the host install (pnpm-workspace.yaml + SafeChain). -ARG PNPM_VERSION=10.32.1 -RUN corepack enable pnpm && corepack prepare "pnpm@${PNPM_VERSION}" --activate +# packageManager field from the deployed package.json, so nothing else here pins it, +# and pnpm 11.x defaults minimumReleaseAge to 1440 (24h), which would make the +# `pnpm add` below reject any dependency published less than a day ago — e.g. a +# first-party @n8n_io/ai-assistant-sdk bump landed within hours of its npm publish. +# This container step only re-resolves an already-vetted, already-pinned dependency +# set; the real supply-chain age policy is enforced by the host install +# (pnpm-workspace.yaml + SafeChain). +ARG PNPM_VERSION +RUN npm i -g "pnpm@${PNPM_VERSION}" # Remove `catalog` and `workspace` references from package.json to allow `pnpm add` RUN node -e "const pkg = require('./package.json'); \ diff --git a/scripts/dockerize-n8n.mjs b/scripts/dockerize-n8n.mjs index 03a5f952684..46f735525ef 100755 --- a/scripts/dockerize-n8n.mjs +++ b/scripts/dockerize-n8n.mjs @@ -142,7 +142,9 @@ const rootDir = isInScriptsDir ? path.join(__dirname, '..') : __dirname; const noCache = process.env.DOCKER_BUILD_NO_CACHE === 'true'; const withBaseImage = process.env.DOCKER_BUILD_BASE_IMAGE === 'true'; -const nodeVersion = process.env.NODE_VERSION || '24.16.0'; +// Keep in sync with NODE_VERSION in .github/workflows/docker-build-push.yml, +// which is what the published images are actually built with. +const nodeVersion = process.env.NODE_VERSION || '26.5.1'; const config = { base: {