From 5d82a877a864fbe4ca60936de2d1a30480d8ce54 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Thu, 16 Jul 2026 15:32:53 +0000 Subject: [PATCH 01/10] Update Node.js to v24 --- .nvmrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.nvmrc b/.nvmrc index f9e7451e..ca5c3500 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22.23.1 +24.18.0 From 658bc113e6d2cbd2f37e08ebb9f3b9b68c932597 Mon Sep 17 00:00:00 2001 From: Sag Date: Thu, 16 Jul 2026 17:56:04 +0200 Subject: [PATCH 02/10] Moved the runtime and typings onto Node 24 alongside .nvmrc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Renovate update only bumped .nvmrc, which nothing in CI or production reads: the Cloud Run image and every test container come from the Dockerfile FROM line, and our docker:disableMajor preset means Renovate can never bump that base image major itself. Merged alone, the repo would declare Node 24 while everything that actually runs stayed on 22 — and a green build here would prove nothing, since the tests execute inside the Node 22 image. - Dockerfile base image → node:24.18.0-alpine, so tests and production genuinely move to 24 - @types/node → 24.13.3 and the renovate allowedVersions pin '<23' → '<25', per the lockstep rule documented in renovate.json5 --- .github/renovate.json5 | 8 +- Dockerfile | 2 +- package.json | 2 +- pnpm-lock.yaml | 319 +++++++++++++++++++++-------------------- 4 files changed, 168 insertions(+), 163 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 77e45848..94b283d1 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -36,16 +36,16 @@ ] }, - // Pin @types/node to the deployed Node runtime major (22.x, see Dockerfile + // Pin @types/node to the deployed Node runtime major (24.x, see Dockerfile // and .nvmrc). Bumping the typings ahead of the runtime lets the compiler - // accept Node-24-only APIs that won't exist in production. Bump - // allowedVersions in lockstep when the Node base image is upgraded. + // accept APIs that won't exist in production. Bump allowedVersions in + // lockstep when the Node base image is upgraded. { description: 'Pin @types/node to the deployed Node runtime major', matchPackageNames: [ '@types/node', ], - allowedVersions: '<23', + allowedVersions: '<25', }, ], } diff --git a/Dockerfile b/Dockerfile index 8a5b6e6b..27b0e72d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM node:22.23.1-alpine@sha256:16e22a550f3863206a3f701448c45f7912c6896a62de43add43bb9c86130c3e2 +FROM node:24.18.0-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd RUN apk add python3 g++ make RUN apk add --no-cache ca-certificates diff --git a/package.json b/package.json index 846b0794..9c9df6d4 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "@fedify/cli": "2.3.2", "@types/html-to-text": "9.0.4", "@types/jsonwebtoken": "9.0.10", - "@types/node": "22.20.1", + "@types/node": "24.13.3", "@types/node-jose": "1.1.13", "@types/sanitize-html": "2.16.1", "@types/sinon": "22.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bc7ebb5b..35f3318d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -16,7 +16,7 @@ importers: version: 2.3.2(@fedify/fedify@2.3.2)(hono@4.12.30) '@fedify/redis': specifier: 2.3.2 - version: 2.3.2(@fedify/fedify@2.3.2)(ioredis@5.11.1) + version: 2.3.2(@fedify/fedify@2.3.2)(ioredis@5.11.1(supports-color@10.2.2)) '@fedify/vocab': specifier: 2.3.2 version: 2.3.2 @@ -34,7 +34,7 @@ importers: version: 0.21.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1)) '@google-cloud/pubsub': specifier: 5.3.1 - version: 5.3.1 + version: 5.3.1(supports-color@10.2.2) '@google-cloud/storage': specifier: 7.21.0 version: 7.21.0 @@ -64,7 +64,7 @@ importers: version: 2.9.0(@opentelemetry/api@1.9.1) '@sentry/node': specifier: 10.65.0 - version: 10.65.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1)) + version: 10.65.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(supports-color@10.2.2) awilix: specifier: 13.0.5 version: 13.0.5 @@ -79,13 +79,13 @@ importers: version: 10.0.0 ioredis: specifier: 5.11.1 - version: 5.11.1 + version: 5.11.1(supports-color@10.2.2) jsonwebtoken: specifier: 9.0.3 version: 9.0.3 knex: specifier: 3.3.0 - version: 3.3.0(mysql2@3.22.6(@types/node@22.20.1)) + version: 3.3.0(mysql2@3.22.6(@types/node@24.13.3)) ky: specifier: 2.0.2 version: 2.0.2 @@ -97,7 +97,7 @@ importers: version: 4.3.3 mysql2: specifier: 3.22.6 - version: 3.22.6(@types/node@22.20.1) + version: 3.22.6(@types/node@24.13.3) node-html-parser: specifier: 9.0.0 version: 9.0.0 @@ -112,7 +112,7 @@ importers: version: 2.17.6 sharp: specifier: 0.35.3 - version: 0.35.3(@types/node@22.20.1) + version: 0.35.3(@types/node@24.13.3) uuid: specifier: 14.0.1 version: 14.0.1 @@ -134,7 +134,7 @@ importers: version: 10.5.0 '@fedify/cli': specifier: 2.3.2 - version: 2.3.2(@jimp/custom@0.22.12)(@types/node@22.20.1)(typescript@7.0.2) + version: 2.3.2(@jimp/custom@0.22.12)(@types/node@24.13.3)(supports-color@10.2.2)(typescript@7.0.2) '@types/html-to-text': specifier: 9.0.4 version: 9.0.4 @@ -142,8 +142,8 @@ importers: specifier: 9.0.10 version: 9.0.10 '@types/node': - specifier: 22.20.1 - version: 22.20.1 + specifier: 24.13.3 + version: 24.13.3 '@types/node-jose': specifier: 1.1.13 version: 1.1.13 @@ -173,10 +173,10 @@ importers: version: 7.0.2 vite-tsconfig-paths: specifier: 6.1.1 - version: 6.1.1(typescript@7.0.2)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(yaml@2.9.0)) + version: 6.1.1(supports-color@10.2.2)(typescript@7.0.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0)) vitest: specifier: 4.1.10 - version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@22.20.1)(@vitest/coverage-v8@4.1.10)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(yaml@2.9.0)) + version: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/coverage-v8@4.1.10)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0)) wiremock-captain: specifier: 4.1.3 version: 4.1.3 @@ -1568,8 +1568,8 @@ packages: '@types/node@20.19.43': resolution: {integrity: sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==} - '@types/node@22.20.1': - resolution: {integrity: sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==} + '@types/node@24.13.3': + resolution: {integrity: sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -3662,6 +3662,9 @@ packages: undici-types@6.21.0: resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + undici@6.27.0: resolution: {integrity: sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==} engines: {node: '>=18.17'} @@ -3957,7 +3960,7 @@ snapshots: semifies: 1.0.0 source-map: 0.6.1 - '@apm-js-collab/tracing-hooks@0.10.1': + '@apm-js-collab/tracing-hooks@0.10.1(supports-color@10.2.2)': dependencies: '@apm-js-collab/code-transformer': 0.15.0 debug: 4.4.3(supports-color@10.2.2) @@ -4239,11 +4242,11 @@ snapshots: '@faker-js/faker@10.5.0': {} - '@fedify/cli@2.3.2(@jimp/custom@0.22.12)(@types/node@22.20.1)(typescript@7.0.2)': + '@fedify/cli@2.3.2(@jimp/custom@0.22.12)(@types/node@24.13.3)(supports-color@10.2.2)(typescript@7.0.2)': dependencies: '@cfworker/json-schema': 4.1.1 '@fedify/fedify': 2.3.2 - '@fedify/init': 2.3.2(@types/node@22.20.1) + '@fedify/init': 2.3.2(@types/node@24.13.3) '@fedify/relay': 2.3.2 '@fedify/sqlite': 2.3.2(@fedify/fedify@2.3.2) '@fedify/vocab': 2.3.2 @@ -4252,8 +4255,8 @@ snapshots: '@fedify/webfinger': 2.3.2 '@fxts/core': 1.26.0 '@hongminhee/localtunnel': 0.3.1 - '@inquirer/prompts': 7.10.1(@types/node@22.20.1) - '@jimp/core': 1.6.1 + '@inquirer/prompts': 7.10.1(@types/node@24.13.3) + '@jimp/core': 1.6.1(supports-color@10.2.2) '@jimp/wasm-webp': 1.6.1 '@js-temporal/polyfill': 0.5.1 '@logtape/file': 2.2.4(@logtape/logtape@2.2.4) @@ -4273,7 +4276,7 @@ snapshots: fetch-mock: 12.6.0 hono: 4.12.30 icojs: 0.19.5(@jimp/custom@0.22.12) - inquirer: 12.11.1(@types/node@22.20.1) + inquirer: 12.11.1(@types/node@24.13.3) inquirer-toggle: 1.0.1 jimp: 1.6.1 ora: 8.2.0 @@ -4314,11 +4317,11 @@ snapshots: '@fedify/fedify': 2.3.2 hono: 4.12.30 - '@fedify/init@2.3.2(@types/node@22.20.1)': + '@fedify/init@2.3.2(@types/node@24.13.3)': dependencies: '@david/dax': dax@0.46.1 '@fxts/core': 1.26.0 - '@inquirer/prompts': 7.10.1(@types/node@22.20.1) + '@inquirer/prompts': 7.10.1(@types/node@24.13.3) '@logtape/logtape': 2.2.4 '@optique/core': 1.1.1 '@optique/run': 1.1.1 @@ -4329,12 +4332,12 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@fedify/redis@2.3.2(@fedify/fedify@2.3.2)(ioredis@5.11.1)': + '@fedify/redis@2.3.2(@fedify/fedify@2.3.2)(ioredis@5.11.1(supports-color@10.2.2))': dependencies: '@fedify/fedify': 2.3.2 '@js-temporal/polyfill': 0.5.1 '@logtape/logtape': 2.2.4 - ioredis: 5.11.1 + ioredis: 5.11.1(supports-color@10.2.2) '@fedify/relay@2.3.2': dependencies: @@ -4444,7 +4447,7 @@ snapshots: '@google-cloud/promisify@5.1.0': {} - '@google-cloud/pubsub@5.3.1': + '@google-cloud/pubsub@5.3.1(supports-color@10.2.2)': dependencies: '@google-cloud/paginator': 6.0.2 '@google-cloud/precise-date': 5.1.0 @@ -4456,7 +4459,7 @@ snapshots: arrify: 2.0.1 extend: 3.0.2 google-auth-library: 10.9.0 - google-gax: 5.0.7 + google-gax: 5.0.7(supports-color@10.2.2) google-logging-utils: 1.1.4 heap-js: 2.7.1 is-stream-ended: 0.1.4 @@ -4614,35 +4617,35 @@ snapshots: '@inquirer/ansi@1.0.2': {} - '@inquirer/checkbox@4.3.2(@types/node@22.20.1)': + '@inquirer/checkbox@4.3.2(@types/node@24.13.3)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@22.20.1) + '@inquirer/core': 10.3.2(@types/node@24.13.3) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.20.1) + '@inquirer/type': 3.0.10(@types/node@24.13.3) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 22.20.1 + '@types/node': 24.13.3 - '@inquirer/confirm@5.1.21(@types/node@22.20.1)': + '@inquirer/confirm@5.1.21(@types/node@24.13.3)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.20.1) - '@inquirer/type': 3.0.10(@types/node@22.20.1) + '@inquirer/core': 10.3.2(@types/node@24.13.3) + '@inquirer/type': 3.0.10(@types/node@24.13.3) optionalDependencies: - '@types/node': 22.20.1 + '@types/node': 24.13.3 - '@inquirer/core@10.3.2(@types/node@22.20.1)': + '@inquirer/core@10.3.2(@types/node@24.13.3)': dependencies: '@inquirer/ansi': 1.0.2 '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.20.1) + '@inquirer/type': 3.0.10(@types/node@24.13.3) cli-width: 4.1.0 mute-stream: 2.0.0 signal-exit: 4.1.0 wrap-ansi: 6.2.0 yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 22.20.1 + '@types/node': 24.13.3 '@inquirer/core@8.2.4': dependencies: @@ -4660,102 +4663,102 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 6.2.0 - '@inquirer/editor@4.2.23(@types/node@22.20.1)': + '@inquirer/editor@4.2.23(@types/node@24.13.3)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.20.1) - '@inquirer/external-editor': 1.0.3(@types/node@22.20.1) - '@inquirer/type': 3.0.10(@types/node@22.20.1) + '@inquirer/core': 10.3.2(@types/node@24.13.3) + '@inquirer/external-editor': 1.0.3(@types/node@24.13.3) + '@inquirer/type': 3.0.10(@types/node@24.13.3) optionalDependencies: - '@types/node': 22.20.1 + '@types/node': 24.13.3 - '@inquirer/expand@4.0.23(@types/node@22.20.1)': + '@inquirer/expand@4.0.23(@types/node@24.13.3)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.20.1) - '@inquirer/type': 3.0.10(@types/node@22.20.1) + '@inquirer/core': 10.3.2(@types/node@24.13.3) + '@inquirer/type': 3.0.10(@types/node@24.13.3) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 22.20.1 + '@types/node': 24.13.3 - '@inquirer/external-editor@1.0.3(@types/node@22.20.1)': + '@inquirer/external-editor@1.0.3(@types/node@24.13.3)': dependencies: chardet: 2.2.0 iconv-lite: 0.7.3 optionalDependencies: - '@types/node': 22.20.1 + '@types/node': 24.13.3 '@inquirer/figures@1.0.15': {} - '@inquirer/input@4.3.1(@types/node@22.20.1)': + '@inquirer/input@4.3.1(@types/node@24.13.3)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.20.1) - '@inquirer/type': 3.0.10(@types/node@22.20.1) + '@inquirer/core': 10.3.2(@types/node@24.13.3) + '@inquirer/type': 3.0.10(@types/node@24.13.3) optionalDependencies: - '@types/node': 22.20.1 + '@types/node': 24.13.3 - '@inquirer/number@3.0.23(@types/node@22.20.1)': + '@inquirer/number@3.0.23(@types/node@24.13.3)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.20.1) - '@inquirer/type': 3.0.10(@types/node@22.20.1) + '@inquirer/core': 10.3.2(@types/node@24.13.3) + '@inquirer/type': 3.0.10(@types/node@24.13.3) optionalDependencies: - '@types/node': 22.20.1 + '@types/node': 24.13.3 - '@inquirer/password@4.0.23(@types/node@22.20.1)': + '@inquirer/password@4.0.23(@types/node@24.13.3)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@22.20.1) - '@inquirer/type': 3.0.10(@types/node@22.20.1) + '@inquirer/core': 10.3.2(@types/node@24.13.3) + '@inquirer/type': 3.0.10(@types/node@24.13.3) optionalDependencies: - '@types/node': 22.20.1 - - '@inquirer/prompts@7.10.1(@types/node@22.20.1)': - dependencies: - '@inquirer/checkbox': 4.3.2(@types/node@22.20.1) - '@inquirer/confirm': 5.1.21(@types/node@22.20.1) - '@inquirer/editor': 4.2.23(@types/node@22.20.1) - '@inquirer/expand': 4.0.23(@types/node@22.20.1) - '@inquirer/input': 4.3.1(@types/node@22.20.1) - '@inquirer/number': 3.0.23(@types/node@22.20.1) - '@inquirer/password': 4.0.23(@types/node@22.20.1) - '@inquirer/rawlist': 4.1.11(@types/node@22.20.1) - '@inquirer/search': 3.2.2(@types/node@22.20.1) - '@inquirer/select': 4.4.2(@types/node@22.20.1) + '@types/node': 24.13.3 + + '@inquirer/prompts@7.10.1(@types/node@24.13.3)': + dependencies: + '@inquirer/checkbox': 4.3.2(@types/node@24.13.3) + '@inquirer/confirm': 5.1.21(@types/node@24.13.3) + '@inquirer/editor': 4.2.23(@types/node@24.13.3) + '@inquirer/expand': 4.0.23(@types/node@24.13.3) + '@inquirer/input': 4.3.1(@types/node@24.13.3) + '@inquirer/number': 3.0.23(@types/node@24.13.3) + '@inquirer/password': 4.0.23(@types/node@24.13.3) + '@inquirer/rawlist': 4.1.11(@types/node@24.13.3) + '@inquirer/search': 3.2.2(@types/node@24.13.3) + '@inquirer/select': 4.4.2(@types/node@24.13.3) optionalDependencies: - '@types/node': 22.20.1 + '@types/node': 24.13.3 - '@inquirer/rawlist@4.1.11(@types/node@22.20.1)': + '@inquirer/rawlist@4.1.11(@types/node@24.13.3)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.20.1) - '@inquirer/type': 3.0.10(@types/node@22.20.1) + '@inquirer/core': 10.3.2(@types/node@24.13.3) + '@inquirer/type': 3.0.10(@types/node@24.13.3) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 22.20.1 + '@types/node': 24.13.3 - '@inquirer/search@3.2.2(@types/node@22.20.1)': + '@inquirer/search@3.2.2(@types/node@24.13.3)': dependencies: - '@inquirer/core': 10.3.2(@types/node@22.20.1) + '@inquirer/core': 10.3.2(@types/node@24.13.3) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.20.1) + '@inquirer/type': 3.0.10(@types/node@24.13.3) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 22.20.1 + '@types/node': 24.13.3 - '@inquirer/select@4.4.2(@types/node@22.20.1)': + '@inquirer/select@4.4.2(@types/node@24.13.3)': dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@22.20.1) + '@inquirer/core': 10.3.2(@types/node@24.13.3) '@inquirer/figures': 1.0.15 - '@inquirer/type': 3.0.10(@types/node@22.20.1) + '@inquirer/type': 3.0.10(@types/node@24.13.3) yoctocolors-cjs: 2.1.3 optionalDependencies: - '@types/node': 22.20.1 + '@types/node': 24.13.3 '@inquirer/type@1.5.5': dependencies: mute-stream: 1.0.0 - '@inquirer/type@3.0.10(@types/node@22.20.1)': + '@inquirer/type@3.0.10(@types/node@24.13.3)': optionalDependencies: - '@types/node': 22.20.1 + '@types/node': 24.13.3 '@ioredis/commands@1.10.0': {} @@ -4787,14 +4790,14 @@ snapshots: transitivePeerDependencies: - encoding - '@jimp/core@1.6.1': + '@jimp/core@1.6.1(supports-color@10.2.2)': dependencies: '@jimp/file-ops': 1.6.1 '@jimp/types': 1.6.1 '@jimp/utils': 1.6.1 await-to-js: 3.0.0 exif-parser: 0.1.12 - file-type: 21.3.4 + file-type: 21.3.4(supports-color@10.2.2) mime: 3.0.0 transitivePeerDependencies: - supports-color @@ -4818,7 +4821,7 @@ snapshots: '@jimp/js-bmp@1.6.1': dependencies: - '@jimp/core': 1.6.1 + '@jimp/core': 1.6.1(supports-color@10.2.2) '@jimp/types': 1.6.1 '@jimp/utils': 1.6.1 bmp-ts: 1.0.9 @@ -4827,7 +4830,7 @@ snapshots: '@jimp/js-gif@1.6.1': dependencies: - '@jimp/core': 1.6.1 + '@jimp/core': 1.6.1(supports-color@10.2.2) '@jimp/types': 1.6.1 gifwrap: 0.10.1 omggif: 1.0.10 @@ -4836,7 +4839,7 @@ snapshots: '@jimp/js-jpeg@1.6.1': dependencies: - '@jimp/core': 1.6.1 + '@jimp/core': 1.6.1(supports-color@10.2.2) '@jimp/types': 1.6.1 jpeg-js: 0.4.4 transitivePeerDependencies: @@ -4844,7 +4847,7 @@ snapshots: '@jimp/js-png@1.6.1': dependencies: - '@jimp/core': 1.6.1 + '@jimp/core': 1.6.1(supports-color@10.2.2) '@jimp/types': 1.6.1 pngjs: 7.0.0 transitivePeerDependencies: @@ -4852,7 +4855,7 @@ snapshots: '@jimp/js-tiff@1.6.1': dependencies: - '@jimp/core': 1.6.1 + '@jimp/core': 1.6.1(supports-color@10.2.2) '@jimp/types': 1.6.1 utif2: 4.1.0 transitivePeerDependencies: @@ -4866,7 +4869,7 @@ snapshots: '@jimp/plugin-blur@1.6.1': dependencies: - '@jimp/core': 1.6.1 + '@jimp/core': 1.6.1(supports-color@10.2.2) '@jimp/utils': 1.6.1 transitivePeerDependencies: - supports-color @@ -4878,7 +4881,7 @@ snapshots: '@jimp/plugin-color@1.6.1': dependencies: - '@jimp/core': 1.6.1 + '@jimp/core': 1.6.1(supports-color@10.2.2) '@jimp/types': 1.6.1 '@jimp/utils': 1.6.1 tinycolor2: 1.6.0 @@ -4888,7 +4891,7 @@ snapshots: '@jimp/plugin-contain@1.6.1': dependencies: - '@jimp/core': 1.6.1 + '@jimp/core': 1.6.1(supports-color@10.2.2) '@jimp/plugin-blit': 1.6.1 '@jimp/plugin-resize': 1.6.1 '@jimp/types': 1.6.1 @@ -4899,7 +4902,7 @@ snapshots: '@jimp/plugin-cover@1.6.1': dependencies: - '@jimp/core': 1.6.1 + '@jimp/core': 1.6.1(supports-color@10.2.2) '@jimp/plugin-crop': 1.6.1 '@jimp/plugin-resize': 1.6.1 '@jimp/types': 1.6.1 @@ -4909,7 +4912,7 @@ snapshots: '@jimp/plugin-crop@1.6.1': dependencies: - '@jimp/core': 1.6.1 + '@jimp/core': 1.6.1(supports-color@10.2.2) '@jimp/types': 1.6.1 '@jimp/utils': 1.6.1 zod: 3.25.76 @@ -4939,7 +4942,7 @@ snapshots: '@jimp/plugin-hash@1.6.1': dependencies: - '@jimp/core': 1.6.1 + '@jimp/core': 1.6.1(supports-color@10.2.2) '@jimp/js-bmp': 1.6.1 '@jimp/js-jpeg': 1.6.1 '@jimp/js-png': 1.6.1 @@ -4959,7 +4962,7 @@ snapshots: '@jimp/plugin-print@1.6.1': dependencies: - '@jimp/core': 1.6.1 + '@jimp/core': 1.6.1(supports-color@10.2.2) '@jimp/js-jpeg': 1.6.1 '@jimp/js-png': 1.6.1 '@jimp/plugin-blit': 1.6.1 @@ -4979,7 +4982,7 @@ snapshots: '@jimp/plugin-resize@1.6.1': dependencies: - '@jimp/core': 1.6.1 + '@jimp/core': 1.6.1(supports-color@10.2.2) '@jimp/types': 1.6.1 zod: 3.25.76 transitivePeerDependencies: @@ -4987,7 +4990,7 @@ snapshots: '@jimp/plugin-rotate@1.6.1': dependencies: - '@jimp/core': 1.6.1 + '@jimp/core': 1.6.1(supports-color@10.2.2) '@jimp/plugin-crop': 1.6.1 '@jimp/plugin-resize': 1.6.1 '@jimp/types': 1.6.1 @@ -4998,7 +5001,7 @@ snapshots: '@jimp/plugin-threshold@1.6.1': dependencies: - '@jimp/core': 1.6.1 + '@jimp/core': 1.6.1(supports-color@10.2.2) '@jimp/plugin-color': 1.6.1 '@jimp/plugin-hash': 1.6.1 '@jimp/types': 1.6.1 @@ -5125,12 +5128,12 @@ snapshots: '@opentelemetry/resources': 2.9.0(@opentelemetry/api@1.9.1) '@opentelemetry/sdk-trace': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1)': + '@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/api-logs': 0.220.0 import-in-the-middle: 3.3.1 - require-in-the-middle: 8.0.1 + require-in-the-middle: 8.0.1(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -5311,7 +5314,7 @@ snapshots: dependencies: '@sentry/conventions': 0.15.1 - '@sentry/node-core@10.65.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))': + '@sentry/node-core@10.65.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1))': dependencies: '@sentry/conventions': 0.15.1 '@sentry/core': 10.65.0 @@ -5320,19 +5323,19 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.1 '@opentelemetry/core': 2.9.0(@opentelemetry/api@1.9.1) - '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) - '@sentry/node@10.65.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))': + '@sentry/node@10.65.0(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(supports-color@10.2.2)': dependencies: '@opentelemetry/api': 1.9.1 - '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1) + '@opentelemetry/instrumentation': 0.220.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2) '@opentelemetry/sdk-trace-base': 2.9.0(@opentelemetry/api@1.9.1) '@sentry/conventions': 0.15.1 '@sentry/core': 10.65.0 - '@sentry/node-core': 10.65.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) + '@sentry/node-core': 10.65.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/instrumentation@0.220.0(@opentelemetry/api@1.9.1)(supports-color@10.2.2))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) '@sentry/opentelemetry': 10.65.0(@opentelemetry/api@1.9.1)(@opentelemetry/core@2.9.0(@opentelemetry/api@1.9.1))(@opentelemetry/sdk-trace-base@2.9.0(@opentelemetry/api@1.9.1)) - '@sentry/server-utils': 10.65.0 + '@sentry/server-utils': 10.65.0(supports-color@10.2.2) import-in-the-middle: 3.3.1 transitivePeerDependencies: - '@opentelemetry/core' @@ -5347,11 +5350,11 @@ snapshots: '@sentry/conventions': 0.15.1 '@sentry/core': 10.65.0 - '@sentry/server-utils@10.65.0': + '@sentry/server-utils@10.65.0(supports-color@10.2.2)': dependencies: '@apm-js-collab/code-transformer': 0.15.0 '@apm-js-collab/code-transformer-bundler-plugins': 0.5.0 - '@apm-js-collab/tracing-hooks': 0.10.1 + '@apm-js-collab/tracing-hooks': 0.10.1(supports-color@10.2.2) '@sentry/conventions': 0.15.1 '@sentry/core': 10.65.0 magic-string: 0.30.21 @@ -5397,7 +5400,7 @@ snapshots: '@teppeis/multimaps@3.0.0': {} - '@tokenizer/inflate@0.4.1': + '@tokenizer/inflate@0.4.1(supports-color@10.2.2)': dependencies: debug: 4.4.3(supports-color@10.2.2) token-types: 6.1.2 @@ -5435,7 +5438,7 @@ snapshots: '@types/jsonwebtoken@9.0.10': dependencies: '@types/ms': 2.1.0 - '@types/node': 22.20.1 + '@types/node': 24.13.3 '@types/mdast@4.0.4': dependencies: @@ -5445,11 +5448,11 @@ snapshots: '@types/mute-stream@0.0.4': dependencies: - '@types/node': 22.20.1 + '@types/node': 24.13.3 '@types/node-jose@1.1.13': dependencies: - '@types/node': 22.20.1 + '@types/node': 24.13.3 '@types/node@16.9.1': {} @@ -5457,16 +5460,16 @@ snapshots: dependencies: undici-types: 6.21.0 - '@types/node@22.20.1': + '@types/node@24.13.3': dependencies: - undici-types: 6.21.0 + undici-types: 7.18.2 '@types/normalize-package-data@2.4.4': {} '@types/request@2.48.13': dependencies: '@types/caseless': 0.12.5 - '@types/node': 22.20.1 + '@types/node': 24.13.3 '@types/tough-cookie': 4.0.5 form-data: 2.5.6 @@ -5564,7 +5567,7 @@ snapshots: obug: 2.1.3 std-env: 4.2.0 tinyrainbow: 3.1.0 - vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@22.20.1)(@vitest/coverage-v8@4.1.10)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(yaml@2.9.0)) + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/coverage-v8@4.1.10)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0)) '@vitest/expect@4.1.10': dependencies: @@ -5575,13 +5578,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.10(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(yaml@2.9.0))': + '@vitest/mocker@4.1.10(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.10 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(yaml@2.9.0) + vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0) '@vitest/pretty-format@4.1.10': dependencies: @@ -6105,9 +6108,9 @@ snapshots: token-types: 6.1.2 uint8array-extras: 1.5.0 - file-type@21.3.4: + file-type@21.3.4(supports-color@10.2.2): dependencies: - '@tokenizer/inflate': 0.4.1 + '@tokenizer/inflate': 0.4.1(supports-color@10.2.2) strtok3: 10.3.5 token-types: 6.1.2 uint8array-extras: 1.5.0 @@ -6292,7 +6295,7 @@ snapshots: - encoding - supports-color - google-gax@5.0.7: + google-gax@5.0.7(supports-color@10.2.2): dependencies: '@grpc/grpc-js': 1.14.4 '@grpc/proto-loader': 0.8.1 @@ -6303,7 +6306,7 @@ snapshots: object-hash: 3.0.0 proto3-json-serializer: 3.0.4 protobufjs: 7.6.5 - retry-request: 8.0.3 + retry-request: 8.0.3(supports-color@10.2.2) rimraf: 5.0.10 transitivePeerDependencies: - supports-color @@ -6413,7 +6416,7 @@ snapshots: transitivePeerDependencies: - supports-color - http-proxy-agent@7.0.2: + http-proxy-agent@7.0.2(supports-color@10.2.2): dependencies: agent-base: 7.1.4 debug: 4.4.3(supports-color@10.2.2) @@ -6473,21 +6476,21 @@ snapshots: dependencies: '@inquirer/core': 8.2.4 - inquirer@12.11.1(@types/node@22.20.1): + inquirer@12.11.1(@types/node@24.13.3): dependencies: '@inquirer/ansi': 1.0.2 - '@inquirer/core': 10.3.2(@types/node@22.20.1) - '@inquirer/prompts': 7.10.1(@types/node@22.20.1) - '@inquirer/type': 3.0.10(@types/node@22.20.1) + '@inquirer/core': 10.3.2(@types/node@24.13.3) + '@inquirer/prompts': 7.10.1(@types/node@24.13.3) + '@inquirer/type': 3.0.10(@types/node@24.13.3) mute-stream: 2.0.0 run-async: 4.0.6 rxjs: 7.8.2 optionalDependencies: - '@types/node': 22.20.1 + '@types/node': 24.13.3 interpret@2.2.0: {} - ioredis@5.11.1: + ioredis@5.11.1(supports-color@10.2.2): dependencies: '@ioredis/commands': 1.10.0 cluster-key-slot: 1.1.1 @@ -6570,7 +6573,7 @@ snapshots: jimp@1.6.1: dependencies: - '@jimp/core': 1.6.1 + '@jimp/core': 1.6.1(supports-color@10.2.2) '@jimp/diff': 1.6.1 '@jimp/js-bmp': 1.6.1 '@jimp/js-gif': 1.6.1 @@ -6649,7 +6652,7 @@ snapshots: jwa: 2.0.1 safe-buffer: 5.2.1 - knex@3.3.0(mysql2@3.22.6(@types/node@22.20.1)): + knex@3.3.0(mysql2@3.22.6(@types/node@24.13.3)): dependencies: colorette: 2.0.19 commander: 10.0.1 @@ -6666,7 +6669,7 @@ snapshots: tarn: 3.1.2 tildify: 2.0.0 optionalDependencies: - mysql2: 3.22.6(@types/node@22.20.1) + mysql2: 3.22.6(@types/node@24.13.3) transitivePeerDependencies: - supports-color @@ -6866,9 +6869,9 @@ snapshots: mute-stream@2.0.0: {} - mysql2@3.22.6(@types/node@22.20.1): + mysql2@3.22.6(@types/node@24.13.3): dependencies: - '@types/node': 22.20.1 + '@types/node': 24.13.3 aws-ssl-profiles: 1.1.2 denque: 2.1.0 generate-function: 2.3.1 @@ -7098,7 +7101,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.2 - '@types/node': 22.20.1 + '@types/node': 24.13.3 long: 5.3.2 pvtsutils@1.3.6: @@ -7182,7 +7185,7 @@ snapshots: require-directory@2.1.1: {} - require-in-the-middle@8.0.1: + require-in-the-middle@8.0.1(supports-color@10.2.2): dependencies: debug: 4.4.3(supports-color@10.2.2) module-details-from-path: 1.0.4 @@ -7212,10 +7215,10 @@ snapshots: - encoding - supports-color - retry-request@8.0.3: + retry-request@8.0.3(supports-color@10.2.2): dependencies: extend: 3.0.2 - teeny-request: 10.1.3 + teeny-request: 10.1.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -7288,7 +7291,7 @@ snapshots: setimmediate@1.0.5: {} - sharp@0.35.3(@types/node@22.20.1): + sharp@0.35.3(@types/node@24.13.3): dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 @@ -7319,7 +7322,7 @@ snapshots: '@img/sharp-win32-arm64': 0.35.3 '@img/sharp-win32-ia32': 0.35.3 '@img/sharp-win32-x64': 0.35.3 - '@types/node': 22.20.1 + '@types/node': 24.13.3 shebang-command@2.0.0: dependencies: @@ -7466,9 +7469,9 @@ snapshots: tarn@3.1.2: {} - teeny-request@10.1.3: + teeny-request@10.1.3(supports-color@10.2.2): dependencies: - http-proxy-agent: 7.0.2 + http-proxy-agent: 7.0.2(supports-color@10.2.2) https-proxy-agent: 7.0.6 node-fetch: 3.3.2 stream-events: 1.0.5 @@ -7583,6 +7586,8 @@ snapshots: undici-types@6.21.0: {} + undici-types@7.18.2: {} + undici@6.27.0: {} unicorn-magic@0.4.0: {} @@ -7643,17 +7648,17 @@ snapshots: '@types/unist': 3.0.3 vfile-message: 4.0.3 - vite-tsconfig-paths@6.1.1(typescript@7.0.2)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(yaml@2.9.0)): + vite-tsconfig-paths@6.1.1(supports-color@10.2.2)(typescript@7.0.2)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0)): dependencies: debug: 4.4.3(supports-color@10.2.2) globrex: 0.1.2 tsconfck: 3.1.6(typescript@7.0.2) - vite: 8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(yaml@2.9.0) + vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0) transitivePeerDependencies: - supports-color - typescript - vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(yaml@2.9.0): + vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0): dependencies: lightningcss: 1.32.0 picomatch: 4.0.5 @@ -7661,15 +7666,15 @@ snapshots: rolldown: 1.1.5 tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 22.20.1 + '@types/node': 24.13.3 esbuild: 0.28.1 fsevents: 2.3.3 yaml: 2.9.0 - vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@22.20.1)(@vitest/coverage-v8@4.1.10)(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(yaml@2.9.0)): + vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@24.13.3)(@vitest/coverage-v8@4.1.10)(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.10 - '@vitest/mocker': 4.1.10(vite@8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(yaml@2.9.0)) + '@vitest/mocker': 4.1.10(vite@8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.10 '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 @@ -7686,11 +7691,11 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 8.1.4(@types/node@22.20.1)(esbuild@0.28.1)(yaml@2.9.0) + vite: 8.1.4(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@opentelemetry/api': 1.9.1 - '@types/node': 22.20.1 + '@types/node': 24.13.3 '@vitest/coverage-v8': 4.1.10(vitest@4.1.10) transitivePeerDependencies: - msw From 46b382cb586d0d9ea74100d0c65ef5db6386e560 Mon Sep 17 00:00:00 2001 From: Sag Date: Thu, 16 Jul 2026 17:56:04 +0200 Subject: [PATCH 03/10] Pinned the lockfile check to .nvmrc instead of the floating lts/* alias setup-node's lts/* already resolves to a different Node major than the deployed runtime and would silently jump to Node 26 at the next LTS switch, so the frozen-lockfile install was being validated on a Node version nobody ships. Reading .nvmrc keeps the check on the same version as the Dockerfile. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f2c4cdda..4da4f0b8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -53,7 +53,7 @@ jobs: - name: Set up Node.js uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 with: - node-version: lts/* + node-version-file: .nvmrc cache: pnpm - name: Check pnpm-lock.yaml From b07fa45219a46fa7f58daf16c0915593ad733753 Mon Sep 17 00:00:00 2001 From: Sag Date: Tue, 21 Jul 2026 10:57:40 +0200 Subject: [PATCH 04/10] Pinned the nix dev shell to Node 24 The flake's overlay claimed to be the central place to set the node version but just passed through the nixpkgs default, which the lock file (from June 2025) resolves to Node 22. With the rest of the repo on Node 24, direnv users would silently get a different major than CI and production for host-side tooling. Pinning nodejs_24 makes the shell track the deployed major regardless of what the locked nixpkgs defaults to. --- .nix/flakes/nodejs/flake.nix | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.nix/flakes/nodejs/flake.nix b/.nix/flakes/nodejs/flake.nix index 6ab4de54..6ebfc495 100644 --- a/.nix/flakes/nodejs/flake.nix +++ b/.nix/flakes/nodejs/flake.nix @@ -21,9 +21,10 @@ }); }; - # This gives us a central place to set the node version + # This gives us a central place to set the node version. Keep the major in + # sync with .nvmrc and the Dockerfile base image. node_overlay = final: prev: { - nodejs = prev.nodejs; + nodejs = prev.nodejs_24; }; eachSystem = f: From 1351aa61fb798725444c978e7f4f8daa8da82bc9 Mon Sep 17 00:00:00 2001 From: Sag Date: Tue, 21 Jul 2026 10:57:56 +0200 Subject: [PATCH 05/10] Grouped Node.js major updates so the base image and .nvmrc move together MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This upgrade shipped as a Renovate commit that only bumped .nvmrc plus manual companion commits for everything else, because docker:disableMajor blocks the node base image major while the nvm manager is unconstrained. Left as-is, the next Node major would repeat that split — and worse: CI now installs from .nvmrc, so merging a lone .nvmrc bump would have CI validating a runtime production doesn't run. Grouping the docker node package with the node-version update makes Renovate open one PR that moves both, with automerge off since a runtime major always deserves review. --- .github/renovate.json5 | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 94b283d1..94d55dd5 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -36,6 +36,29 @@ ] }, + // docker:disableMajor blocks Node majors on the base image, but nothing + // stopped Renovate bumping .nvmrc to a new major on its own — and since CI + // installs from .nvmrc, that would leave CI testing a runtime production + // doesn't run. Re-enable majors for the node image and group them with the + // .nvmrc update so both move together in a single, manually reviewed PR. + { + description: 'Group Node.js major updates (Dockerfile base image + .nvmrc)', + groupName: 'Node.js', + groupSlug: 'node-major', + matchDepNames: [ + 'node', + ], + matchDatasources: [ + 'docker', + 'node-version', + ], + matchUpdateTypes: [ + 'major', + ], + enabled: true, + automerge: false, + }, + // Pin @types/node to the deployed Node runtime major (24.x, see Dockerfile // and .nvmrc). Bumping the typings ahead of the runtime lets the compiler // accept APIs that won't exist in production. Bump allowedVersions in From 37dc1ec7ff73a740dadeee90f488032734ebd589 Mon Sep 17 00:00:00 2001 From: Sag Date: Tue, 21 Jul 2026 10:58:09 +0200 Subject: [PATCH 06/10] Removed the unused native build toolchain from the Docker image MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit python3/g++/make were only ever needed for install-time compilation, which this image never does: pnpm install runs with --ignore-scripts, pnpm-workspace.yaml denies all build scripts, and every native dep (sharp, esbuild, mysql2) ships prebuilt binaries or is pure JS. The later apk del couldn't reclaim the bytes either — in a single-stage image the toolchain stays baked into the earlier layer, so the pushed image carried ~200MB of dead weight that Cloud Run pulls on every cold-start scale-out, and every cache-busted build (like this Node bump) reinstalled it for nothing. --- Dockerfile | 3 --- 1 file changed, 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index 27b0e72d..84460547 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,5 @@ FROM node:24.18.0-alpine@sha256:a0b9bf06e4e6193cf7a0f58816cc935ff8c2a908f81e6f1a95432d679c54fbfd -RUN apk add python3 g++ make RUN apk add --no-cache ca-certificates WORKDIR /opt/activitypub @@ -20,8 +19,6 @@ COPY vitest.config.ts vitest.config.ts ENV NODE_ENV=production RUN pnpm build -RUN apk del python3 g++ make - EXPOSE 8080 CMD ["node", "dist/app.js"] From 8482dbf682cd89b479c43da8d2dc3eebc9f6896a Mon Sep 17 00:00:00 2001 From: Sag Date: Tue, 21 Jul 2026 10:58:21 +0200 Subject: [PATCH 07/10] Declared the supported Node major in engines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Node version was pinned in .nvmrc, the Dockerfile, and CI, but nothing surfaced a mismatch for local installs — a contributor still on Node 22 could pnpm install and run host-side tooling without any signal that the project had moved on. With engines set, pnpm warns on the mismatch at install time. Deliberately not enabling engine-strict: a hard failure would also apply to dependencies' engines ranges and risks breaking installs over third-party metadata. --- package.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/package.json b/package.json index 9c9df6d4..016b376a 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,9 @@ "license": "MIT", "main": "src/app.ts", "type": "module", + "engines": { + "node": ">=24 <25" + }, "packageManager": "pnpm@11.11.0", "scripts": { "build": "esbuild src/app.ts --sourcemap --platform=neutral --bundle --packages=external --outfile=dist/app.js", From 51fcb40adf6d08535bf510cb02b2e72bc03b8d13 Mon Sep 17 00:00:00 2001 From: Sag Date: Tue, 21 Jul 2026 10:58:35 +0200 Subject: [PATCH 08/10] Made the @types/node pin comment version-agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The comment hardcoded the runtime major in prose, so every Node upgrade had to edit the explanation as well as the allowedVersions value — and a missed edit would leave it describing the wrong major. It also lost precision in the last rewrite ('APIs that won't exist in production' no longer said whose APIs). Now only allowedVersions needs touching on a Node bump, and the rationale spells out the hazard: typings newer than the runtime let code compile against APIs production doesn't have. --- .github/renovate.json5 | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 94d55dd5..c1ba2897 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -59,10 +59,10 @@ automerge: false, }, - // Pin @types/node to the deployed Node runtime major (24.x, see Dockerfile - // and .nvmrc). Bumping the typings ahead of the runtime lets the compiler - // accept APIs that won't exist in production. Bump allowedVersions in - // lockstep when the Node base image is upgraded. + // Pin @types/node to the deployed Node runtime major (see Dockerfile and + // .nvmrc). Bumping the typings ahead of the runtime lets the compiler + // accept newer-Node APIs that don't exist in the production runtime. Bump + // allowedVersions in lockstep when the Node base image is upgraded. { description: 'Pin @types/node to the deployed Node runtime major', matchPackageNames: [ From 94f8cc455b13db509a6b9cd401e61d5671b20756 Mon Sep 17 00:00:00 2001 From: Sag Date: Wed, 22 Jul 2026 09:58:48 +0200 Subject: [PATCH 09/10] Grouped all Node.js updates in Renovate, not only majors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The blanket docker automerge rule could ship a node base-image minor/patch bump straight to production while the matching .nvmrc bump sat in a separate PR waiting for manual review — leaving CI (which installs Node from .nvmrc) testing an older runtime than production, the exact skew the Node.js group was added to prevent. Dropping the major-only restriction puts every Node update for the base image and .nvmrc into one manually reviewed PR, so the two can't diverge. --- .github/renovate.json5 | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/.github/renovate.json5 b/.github/renovate.json5 index c1ba2897..5fb5dd9d 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -36,15 +36,16 @@ ] }, - // docker:disableMajor blocks Node majors on the base image, but nothing - // stopped Renovate bumping .nvmrc to a new major on its own — and since CI - // installs from .nvmrc, that would leave CI testing a runtime production - // doesn't run. Re-enable majors for the node image and group them with the - // .nvmrc update so both move together in a single, manually reviewed PR. + // CI installs Node from .nvmrc while production runs the Dockerfile base + // image, so any update that moves one without the other leaves CI testing + // a runtime production doesn't run. Group ALL Node updates (base image + + // .nvmrc) into a single, manually reviewed PR: enabled overrides + // docker:disableMajor for majors, and automerge overrides the blanket + // docker automerge rule above so a base-image bump can't ship on its own. { - description: 'Group Node.js major updates (Dockerfile base image + .nvmrc)', + description: 'Group Node.js updates (Dockerfile base image + .nvmrc)', groupName: 'Node.js', - groupSlug: 'node-major', + groupSlug: 'node', matchDepNames: [ 'node', ], @@ -52,9 +53,6 @@ 'docker', 'node-version', ], - matchUpdateTypes: [ - 'major', - ], enabled: true, automerge: false, }, From bd89c41de0d4c92b7121ea2f9421e3a2d5e4b01b Mon Sep 17 00:00:00 2001 From: Sag Date: Wed, 22 Jul 2026 09:59:00 +0200 Subject: [PATCH 10/10] Simplified the engines.node range to "24" "24" desugars to the same semver range as ">=24 <25" but is a single token to bump on the next Node major, and cleaner for Renovate to replace. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5898cc8f..a59b401c 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "main": "src/app.ts", "type": "module", "engines": { - "node": ">=24 <25" + "node": "24" }, "packageManager": "pnpm@11.14.0", "scripts": {