From 6ed1b03e24dbf562662c3f3fc6b325e8e2cac65e Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Mon, 24 Aug 2026 19:10:36 +0200 Subject: [PATCH] chore: align Node 26.7.0 toolchain --- .gitea/workflows/ci.yaml | 6 ++++- .nvmrc | 2 +- README.md | 2 +- package.json | 5 ++-- scripts/check-node-toolchain.mjs | 39 ++++++++++++++++++++++++++++ src/lib/services/soundtracks.test.ts | 5 +++- vitest.config.ts | 5 +++- 7 files changed, 57 insertions(+), 7 deletions(-) create mode 100644 scripts/check-node-toolchain.mjs diff --git a/.gitea/workflows/ci.yaml b/.gitea/workflows/ci.yaml index adaf0220..840ebfc5 100644 --- a/.gitea/workflows/ci.yaml +++ b/.gitea/workflows/ci.yaml @@ -36,6 +36,8 @@ jobs: git fetch --depth 50 origin "${REF}" git checkout -f "${REF}" + node scripts/check-node-toolchain.mjs + export SKIP_ENV_VALIDATION=1 export NODE_ENV=test export DATABASE_URL="mysql://test:test@localhost:3306/test?charset=utf8mb4" @@ -130,6 +132,7 @@ jobs: fi cd "${STAGE}" + node scripts/check-node-toolchain.mjs rm -f tsconfig.tsbuildinfo .tsbuildinfo find . -maxdepth 3 -name '*.tsbuildinfo' -delete 2>/dev/null || true rm -rf .output dist .next .next/types .next/dev .next/cache @@ -291,6 +294,7 @@ jobs: "${WORK}" cd "${WORK}" git checkout "${VERSION}" + node scripts/check-node-toolchain.mjs export NODE_ENV=production export SKIP_ENV_VALIDATION=1 pnpm install --frozen-lockfile @@ -369,7 +373,7 @@ jobs: echo "" echo "| Component | Version | Notes |" echo "| --------- | ------- | ----- |" - echo "| Node.js | >= 22 | Required by Next.js 16 |" + echo "| Node.js | 26.7.0 | Current release pinned in .nvmrc |" echo "| pnpm | >= 10.33.4 | Package manager (npm/yarn not supported) |" echo "| MySQL / MariaDB | 8.0+ / 10.6+ | Shared with the emulator |" echo "| Redis | 7.x+ | Optional — caching, rate limiting, SSE |" diff --git a/.nvmrc b/.nvmrc index 2bd5a0a9..91d2624e 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -22 +26.7.0 diff --git a/README.md b/README.md index 218d83a2..45462106 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Features a premium animated homepage (typewriter hero, floating orbs, scroll cou | Component | Version | Notes | | --------------- | -------------- | ---------------------------------------- | -| Node.js | >= 22 | Required by Next.js 16 | +| Node.js | 26.7.0 | Current release pinned in `.nvmrc` | | pnpm | >= 10.33.4 | Package manager (npm/yarn not supported) | | MySQL / MariaDB | 8.0+ / 10.6+ | Shared with the emulator | | DragonflyDB | 1.x+ | Optional — caching, rate limiting, SSE (Redis-protocol compatible) | diff --git a/package.json b/package.json index 8a854bbf..bdc4c8fa 100644 --- a/package.json +++ b/package.json @@ -3,19 +3,20 @@ "private": true, "type": "module", "engines": { - "node": ">=26" + "node": ">=26.7.0 <27" }, "packageManager": "pnpm@11.22.0", "scripts": { "dev": "next dev", "build": "next build", "start": "next start", + "toolchain:check": "node scripts/check-node-toolchain.mjs", "lint": "biome check .", "biome:lint": "biome check .", "format": "biome format --write .", "knip": "knip --include files,dependencies,devDependencies,unlisted,binaries", "jobs:worker": "tsx scripts/jobs-worker.ts", - "test": "DATABASE_URL=mysql://root:root@localhost:3306/test vitest run", + "test": "vitest run", "typecheck": "tsc --noEmit", "db:generate": "drizzle-kit generate", "db:migrate": "tsx scripts/apply-migrations.ts", diff --git a/scripts/check-node-toolchain.mjs b/scripts/check-node-toolchain.mjs new file mode 100644 index 00000000..dc5a5144 --- /dev/null +++ b/scripts/check-node-toolchain.mjs @@ -0,0 +1,39 @@ +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const projectRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const readProjectFile = (path) => + readFileSync(resolve(projectRoot, path), "utf8"); + +const pinnedVersion = readProjectFile(".nvmrc").trim(); +assert.match( + pinnedVersion, + /^\d+\.\d+\.\d+$/, + ".nvmrc must pin an exact Node.js version", +); + +const major = Number.parseInt(pinnedVersion.split(".")[0], 10); +const packageJson = JSON.parse(readProjectFile("package.json")); + +assert.equal( + packageJson.engines?.node, + `>=${pinnedVersion} <${major + 1}`, + "package.json engines.node must match the .nvmrc release line", +); +assert.equal( + Number.parseInt(packageJson.devDependencies?.["@types/node"], 10), + major, + "@types/node must match the pinned Node.js major", +); + +if (!process.argv.includes("--static")) { + assert.equal( + process.versions.node, + pinnedVersion, + "the active Node.js runtime must match .nvmrc", + ); +} + +console.log(`Node.js toolchain is aligned on ${pinnedVersion}`); diff --git a/src/lib/services/soundtracks.test.ts b/src/lib/services/soundtracks.test.ts index da947df3..57ab0234 100644 --- a/src/lib/services/soundtracks.test.ts +++ b/src/lib/services/soundtracks.test.ts @@ -1,3 +1,4 @@ +import { join } from "node:path"; import { beforeEach, describe, expect, it, vi } from "vitest"; const fsMock = vi.hoisted(() => ({ @@ -29,7 +30,9 @@ describe("getSoundtrackPath", () => { ); }); it("joins the safe code into the soundtrack dir", () => { - expect(getSoundtrackPath("song").endsWith("mp3/song.mp3")).toBe(true); + expect(getSoundtrackPath("song").endsWith(join("mp3", "song.mp3"))).toBe( + true, + ); }); }); diff --git a/vitest.config.ts b/vitest.config.ts index 2f484d0d..7d8e89b9 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -17,7 +17,10 @@ export default defineConfig({ environment: "node", // Modules under test transitively import @/env; skip its strict parse so // unit tests run without a populated .env. - env: { SKIP_ENV_VALIDATION: "1" }, + env: { + SKIP_ENV_VALIDATION: "1", + DATABASE_URL: "mysql://root:root@localhost:3306/test", + }, testTimeout: 10000, exclude: ["e2e/**", "node_modules/**", ".next/**", ".next.prev/**"], coverage: {