From 67dfff0bf118a5c4c3552424de14012d8b67fa64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=CC=81s=20Pozsgai?= Date: Thu, 7 May 2026 11:34:02 +0200 Subject: [PATCH 1/3] feat: enforce .nvmrc --- .github/workflows/node.yaml | 12 +++-- .github/workflows/publish.yaml | 12 +++-- .nvmrc | 1 + bin/ensureNvmrc.mjs | 93 ++++++++++++++++++++++++++++++++++ package.json | 2 + 5 files changed, 112 insertions(+), 8 deletions(-) create mode 100644 .nvmrc create mode 100644 bin/ensureNvmrc.mjs diff --git a/.github/workflows/node.yaml b/.github/workflows/node.yaml index e46fb76..3c33455 100644 --- a/.github/workflows/node.yaml +++ b/.github/workflows/node.yaml @@ -15,10 +15,12 @@ jobs: - uses: actions/checkout@v6 with: persist-credentials: false - - name: Use Node.js 22.x + - name: Use Node.js from .nvmrc uses: actions/setup-node@v6 with: - node-version: 22.x + node-version-file: .nvmrc + - name: Validate .nvmrc + run: node ./bin/ensureNvmrc.mjs - name: Prepare Environment run: | corepack enable @@ -45,10 +47,12 @@ jobs: - uses: actions/checkout@v6 with: persist-credentials: false - - name: Use Node.js 22.x + - name: Use Node.js from .nvmrc uses: actions/setup-node@v6 with: - node-version: 22.x + node-version-file: .nvmrc + - name: Validate .nvmrc + run: node ./bin/ensureNvmrc.mjs - name: Prepare Environment run: | corepack enable diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 17cbf8e..9768a8f 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -32,12 +32,14 @@ jobs: with: fetch-depth: 0 persist-credentials: false - - name: Use Node.js 18.x + - name: Use Node.js from .nvmrc uses: actions/setup-node@v6 with: - node-version: 18.x + node-version-file: .nvmrc - name: Enable corepack run: corepack enable + - name: Validate .nvmrc + run: node ./bin/ensureNvmrc.mjs - name: Determine publish info id: do-publish run: | @@ -114,10 +116,12 @@ jobs: with: fetch-depth: 0 persist-credentials: false - - name: Use Node.js 24.x + - name: Use Node.js from .nvmrc uses: actions/setup-node@v6 with: - node-version: 24.x + node-version-file: .nvmrc + - name: Validate .nvmrc + run: node ./bin/ensureNvmrc.mjs - name: Download release artifact uses: actions/download-artifact@v8 diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..1d9b783 --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22.12.0 diff --git a/bin/ensureNvmrc.mjs b/bin/ensureNvmrc.mjs new file mode 100644 index 0000000..ebd024b --- /dev/null +++ b/bin/ensureNvmrc.mjs @@ -0,0 +1,93 @@ +#! /usr/bin/env node +import { readFileSync } from 'fs' +import { readFile, writeFile } from 'fs/promises' + +const args = new Set(process.argv.slice(2)) +const shouldFix = args.has('--fix') || args.has('-f') + +function getRequiredNodeVersion() { + const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8')) + const range = pkg?.engines?.node + if (!range || typeof range !== 'string') { + throw new Error('`package.json#engines.node` is missing or not a string') + } + + // Keep this script dependency-free: it runs in CI before `yarn install`. + // Prefer extracting the minimum required version (major.minor.patch) from common range shapes. + const match = + range.match(/>=\s*v?(?\d+)(?:\.(?\d+))?(?:\.(?\d+))?/) ?? + range.match(/\bv?(?\d+)(?:\.(?\d+))?(?:\.(?\d+))?\b/) + + const major = match?.groups?.major ? Number.parseInt(match.groups.major, 10) : NaN + const minor = match?.groups?.minor ? Number.parseInt(match.groups.minor, 10) : null + const patch = match?.groups?.patch ? Number.parseInt(match.groups.patch, 10) : null + + if (!Number.isInteger(major) || major <= 0) { + throw new Error(`Unable to determine required Node version from engines.node: "${range}"`) + } + if (minor !== null && (!Number.isInteger(minor) || minor < 0)) { + throw new Error(`Unable to determine required Node version from engines.node: "${range}"`) + } + if (patch !== null && (!Number.isInteger(patch) || patch < 0)) { + throw new Error(`Unable to determine required Node version from engines.node: "${range}"`) + } + + // If a minor is specified in engines, we enforce it in .nvmrc too. + // Patch defaults to 0 when omitted. + const expected = + minor === null ? String(major) : `${major}.${minor}.${patch === null ? 0 : patch}` + + return { expected, range } +} + +function normalizeNvmrc(value) { + const trimmed = String(value ?? '').trim() + if (trimmed.startsWith('v')) return trimmed.slice(1) + return trimmed +} + +async function main() { + const { expected, range } = getRequiredNodeVersion() + + let actual = null + try { + actual = normalizeNvmrc(await readFile(new URL('../.nvmrc', import.meta.url), 'utf-8')) + } catch (e) { + if (e?.code !== 'ENOENT') throw e + } + + if (actual === expected) return + + if (shouldFix) { + await writeFile(new URL('../.nvmrc', import.meta.url), expected + '\n', 'utf-8') + console.log(`Wrote .nvmrc (${expected}) from package.json engines.node (${range})`) + return + } + + if (actual === null) { + console.error( + [ + 'Missing .nvmrc.', + `Expected .nvmrc to contain: ${expected}`, + `Derived from package.json engines.node: ${range}`, + '', + 'Fix: yarn fix:nvmrc', + ].join('\n'), + ) + } else { + console.error( + [ + '.nvmrc is out of sync with package.json.', + `Found .nvmrc: ${actual}`, + `Expected: ${expected}`, + `Derived from package.json engines.node: ${range}`, + '', + 'Fix: yarn fix:nvmrc', + ].join('\n'), + ) + } + + process.exitCode = 1 +} + +await main() diff --git a/package.json b/package.json index 1d6d1be..266d5cd 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,8 @@ "sofie-version": "./bin/updateVersion.mjs" }, "scripts": { + "check:nvmrc": "node ./bin/ensureNvmrc.mjs", + "fix:nvmrc": "node ./bin/ensureNvmrc.mjs --fix", "changelog": "./bin/updateVersion.mjs", "release": "run reset && run changelog", "reset": "git clean -dfx && git reset --hard && yarn", From ee3b1c3339f4c92821cd80b33f36064b20ae59f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=CC=81s=20Pozsgai?= Date: Thu, 7 May 2026 14:46:41 +0200 Subject: [PATCH 2/3] chore: change script names --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 266d5cd..351bddb 100644 --- a/package.json +++ b/package.json @@ -32,14 +32,14 @@ "sofie-version": "./bin/updateVersion.mjs" }, "scripts": { - "check:nvmrc": "node ./bin/ensureNvmrc.mjs", - "fix:nvmrc": "node ./bin/ensureNvmrc.mjs --fix", "changelog": "./bin/updateVersion.mjs", "release": "run reset && run changelog", "reset": "git clean -dfx && git reset --hard && yarn", "validate:dependencies": "yarn npm audit --environment production && run license-validate", "validate:dev-dependencies": "yarn npm audit --environment development", - "license-validate": "./bin/checkLicenses.mjs" + "license-validate": "./bin/checkLicenses.mjs", + "lint:nvmrc": "node ./bin/ensureNvmrc.mjs", + "lint:nvmrc:fix": "node ./bin/ensureNvmrc.mjs --fix" }, "files": [ "/CHANGELOG.md", From 77facea71d3f9ba671d60148ba7f1ad83231c01e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andra=CC=81s=20Pozsgai?= Date: Tue, 19 May 2026 17:38:04 +0200 Subject: [PATCH 3/3] chore: fix script name in ensureNvmrc.mjs --- bin/ensureNvmrc.mjs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/ensureNvmrc.mjs b/bin/ensureNvmrc.mjs index ebd024b..700fd18 100644 --- a/bin/ensureNvmrc.mjs +++ b/bin/ensureNvmrc.mjs @@ -71,7 +71,7 @@ async function main() { `Expected .nvmrc to contain: ${expected}`, `Derived from package.json engines.node: ${range}`, '', - 'Fix: yarn fix:nvmrc', + 'Fix: yarn lint:nvmrc:fix', ].join('\n'), ) } else { @@ -82,7 +82,7 @@ async function main() { `Expected: ${expected}`, `Derived from package.json engines.node: ${range}`, '', - 'Fix: yarn fix:nvmrc', + 'Fix: yarn lint:nvmrc:fix', ].join('\n'), ) }