From 102b3efed65e5c5f6d4e71909400ca3ff2c461d5 Mon Sep 17 00:00:00 2001 From: Nagi-Inaba <221035391+Nagi-Inaba@users.noreply.github.com> Date: Fri, 9 Oct 2026 03:18:29 +0900 Subject: [PATCH] chore: require Node.js 22 and test Node.js 22 and 24 on Ubuntu, Windows and macOS Node.js 20 reached end of life on 2026-04-30, yet the README, engines, doctor and CI still treated it as the baseline, and macOS was advertised for the installers without any CI coverage. - Raise the minimum to Node.js 22: package engines, doctor (one constant), the Web capability contract, README, the installing guide and the version-support guide. tests/runtime-baseline.test.mjs fails when any of these, the root manifest or the CI matrix disagree, so the baseline cannot drift again. - Verify on Node.js 22 and 24 across ubuntu-latest, windows-latest and macos-latest, with a 30 minute job limit. - Move every workflow action to a release that runs on Node.js 24 (checkout 7.0.1, setup-node 7.0.0, upload-artifact 7.0.1), pinned by commit as the browser workflows already were. The v4 actions run on Node.js 20 and are forced onto Node.js 24 by the runner. - Pin the two Chromium workflows to ubuntu-24.04: ubuntu-latest moves to Ubuntu 26.04 on 2026-10-19 and playwright install --with-deps installs system packages. - Add a private root package.json and .npmrc so contributors run npm install and npm run verify. Without a root manifest npm uses the nearest parent directory that has a package.json or node_modules as the project, so a stray one above the clone captures the install. Verified locally on Node.js 22.19.0 (Windows): verify-all passes with 92 test files. The macOS jobs and the matrix itself run only on GitHub Actions. Co-Authored-By: Claude Sonnet 5.5 --- .../workflows/report-accessibility-e2e.yml | 8 +-- .github/workflows/report-nvda-smoke.yml | 6 +-- .github/workflows/source-monitor.yml | 6 +-- .github/workflows/verify.yml | 11 ++-- .github/workflows/web-evidence-e2e.yml | 6 +-- .npmrc | 3 ++ CHANGELOG.md | 2 + CONTRIBUTING.md | 8 +-- README.en.md | 4 +- README.md | 4 +- docs/installing.md | 4 +- docs/version-support.md | 2 +- package.json | 16 ++++++ release-files.json | 1 + shared/skill/package.json | 2 +- shared/skill/references/web-capabilities.json | 2 +- shared/skill/scripts/doctor.mjs | 7 +-- tests/runtime-baseline.test.mjs | 51 +++++++++++++++++++ 18 files changed, 109 insertions(+), 34 deletions(-) create mode 100644 .npmrc create mode 100644 package.json create mode 100644 tests/runtime-baseline.test.mjs diff --git a/.github/workflows/report-accessibility-e2e.yml b/.github/workflows/report-accessibility-e2e.yml index 4551f5f3..4db61954 100644 --- a/.github/workflows/report-accessibility-e2e.yml +++ b/.github/workflows/report-accessibility-e2e.yml @@ -14,11 +14,11 @@ permissions: jobs: chromium-report: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 timeout-minutes: 15 steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 22 - name: Generate Codex and Claude skills @@ -37,7 +37,7 @@ jobs: node scripts/verify-package.mjs - name: Upload browser verification evidence if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: accessible-report-browser-evidence path: audit-runs/report-accessibility/browser-e2e.json diff --git a/.github/workflows/report-nvda-smoke.yml b/.github/workflows/report-nvda-smoke.yml index f8253744..e68c3127 100644 --- a/.github/workflows/report-nvda-smoke.yml +++ b/.github/workflows/report-nvda-smoke.yml @@ -23,8 +23,8 @@ jobs: runs-on: windows-latest timeout-minutes: 20 steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 22 - name: Generate Codex and Claude skills @@ -59,7 +59,7 @@ jobs: -OutputDirectory audit-runs/nvda-report/evidence - name: Upload NVDA smoke evidence if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: accessible-report-nvda-evidence path: | diff --git a/.github/workflows/source-monitor.yml b/.github/workflows/source-monitor.yml index 19ffc3a0..49f827e6 100644 --- a/.github/workflows/source-monitor.yml +++ b/.github/workflows/source-monitor.yml @@ -13,8 +13,8 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 10 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 22 - name: Inspect official pages @@ -32,7 +32,7 @@ jobs: if test -f "$RUNNER_TEMP/source-monitor/review.md"; then cat "$RUNNER_TEMP/source-monitor/review.md" >> "$GITHUB_STEP_SUMMARY"; fi if test -f "$RUNNER_TEMP/source-monitor/catalog-diff.json"; then echo 'Catalog diff is in the workflow artifact.' >> "$GITHUB_STEP_SUMMARY"; fi if ! test -f "$RUNNER_TEMP/source-monitor/catalog-candidate.json"; then echo 'Catalog candidate could not be built; inspect catalog-refresh.log.' >> "$GITHUB_STEP_SUMMARY"; fi - - uses: actions/upload-artifact@v4 + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: always() with: name: official-source-review diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index 7233aa27..5b0d4b2d 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -13,16 +13,17 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest] - node: [20, 22] + os: [ubuntu-latest, windows-latest, macos-latest] + node: [22, 24] runs-on: ${{ matrix.os }} + timeout-minutes: 30 steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ matrix.node }} - name: Install worksheet test dependency - run: npm install --no-save --ignore-scripts --package-lock=false exceljs@4.4.0 + run: npm install - name: Verify package, catalogs, and tests if: runner.os != 'Windows' run: TEMP="$RUNNER_TEMP" TMP="$RUNNER_TEMP" TMPDIR="$RUNNER_TEMP" node scripts/verify-all.mjs diff --git a/.github/workflows/web-evidence-e2e.yml b/.github/workflows/web-evidence-e2e.yml index f9cff6f6..7ce42a9d 100644 --- a/.github/workflows/web-evidence-e2e.yml +++ b/.github/workflows/web-evidence-e2e.yml @@ -33,11 +33,11 @@ permissions: jobs: chromium-evidence: - runs-on: ubuntu-latest + runs-on: ubuntu-24.04 timeout-minutes: 15 steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: 22 - name: Generate Codex and Claude skills diff --git a/.npmrc b/.npmrc new file mode 100644 index 00000000..685659dc --- /dev/null +++ b/.npmrc @@ -0,0 +1,3 @@ +# Test dependencies are installed without a lockfile and without install scripts. +package-lock=false +ignore-scripts=true diff --git a/CHANGELOG.md b/CHANGELOG.md index 4be5b9bb..71807714 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ This project uses a Keep a Changelog-style structure. Until a tagged release is ### Changed +- Node.js 22 or later is now required (package `engines`, `doctor`, the Web capability contract and the documentation). Node.js 20 reached end of life on 2026-04-30 and is no longer supported or tested. CI runs Node.js 22 and 24 on Ubuntu, Windows and macOS; workflow actions move to versions that run on Node.js 24 and are pinned by commit, and the browser E2E workflows pin Ubuntu 24.04. Contributors install test dependencies with `npm install` through a root `package.json`, which also stops an unrelated parent-directory `package.json` from capturing the install. + - Removed internal plans, implementation reviews, status notes, and research source copies from the public source tree; retained official source links and attribution. - Reviewed the Digital Agency accessibility policy against the monitored source on 2026-09-25. The 18 additional criterion mappings and routing are unchanged; the catalog source hash, review date and attribution binding now match the reviewed page. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e3da56f7..a6f3915b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -28,11 +28,11 @@ The accepted PR records the submitted version and the maintainer's corrections; Run the unified verifier before requesting review: ```sh -npm install --no-save --ignore-scripts --package-lock=false exceljs@4.4.0 -node scripts/verify-all.mjs +npm install +npm run verify ``` -Install test dependencies at the repository root so that local `node_modules` is not part of a skill distribution. XLSX tests use the same pinned ExcelJS version as the CLI package; its dependencies retain their own licenses and are not vendored in release archives. +`npm install` reads the root `package.json` (test dependencies only; the installable CLI package is `shared/skill/package.json`) and `.npmrc` (no lockfile, no install scripts). The root manifest pins the project root, so a stray `package.json` or `node_modules` in a parent directory cannot capture the install. Install at the repository root so that local `node_modules` is not part of a skill distribution. XLSX tests use the same pinned ExcelJS version as the CLI package; its dependencies retain their own licenses and are not vendored in release archives. Node.js 22 or later is required; CI runs Node.js 22 and 24. For focused work, the underlying checks are: @@ -45,7 +45,7 @@ node --test tests/*.test.mjs `docs/cli-reference.md` is generated from the CLI command registry. After adding or changing a command, its options, or its localized help, run `node scripts/build-cli-reference.mjs --write`; `tests/cli-reference.test.mjs` fails when the committed page is out of date. -Platform-specific behavior must remain covered by the Ubuntu/Windows CI matrix. +Platform-specific behavior must remain covered by the Ubuntu/Windows/macOS CI matrix. ## Source and distribution parity diff --git a/README.en.md b/README.en.md index 4137a945..40fe0a34 100644 --- a/README.en.md +++ b/README.en.md @@ -2,7 +2,7 @@ # Information Accessibility Audit Skill and Agent -Installation requires Node.js 20 or later and a pinned source revision. See the [Windows, macOS and Linux install, upgrade, restore and removal guide](docs/installing.md) first. +Installation requires Node.js 22 or later and a pinned source revision. See the [Windows, macOS and Linux install, upgrade, restore and removal guide](docs/installing.md) first. ## In 30 seconds @@ -62,7 +62,7 @@ See the [profile selection and migration guide](docs/profile-selection-and-migra ## Requirements and installation -- Node.js 20 or later +- Node.js 22 or later - A local copy of this repository - The pinned Playwright and axe-core versions, plus host-approved Chromium or installed system Chrome, only when using the browser scan diff --git a/README.md b/README.md index 392c378e..67f01d67 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # 情報アクセシビリティ監査スキル/エージェント -導入にはNode.js 20以上と版を固定したソースが必要です。[3 OS共通の導入・更新・復元・削除手順](docs/installing.md)を先に確認してください。 +導入にはNode.js 22以上と版を固定したソースが必要です。[3 OS共通の導入・更新・復元・削除手順](docs/installing.md)を先に確認してください。 ## 30秒で分かる概要 @@ -62,7 +62,7 @@ AIと自動検査が作るものは、原則として問題候補やE0/E1のsc ## 前提条件と導入 -- Node.js 20以上 +- Node.js 22以上 - リポジトリのローカルコピー - browser scanを使う場合のみ、指定版のPlaywright、axe-core、ホストで利用を認められたChromiumまたはシステムChrome diff --git a/docs/installing.md b/docs/installing.md index 7182a8c0..ca6b0df8 100644 --- a/docs/installing.md +++ b/docs/installing.md @@ -1,8 +1,8 @@ # Installation, upgrade and removal / 導入・更新・削除 -Use a pinned source revision for every install. The [v0.1.0 prerelease](https://github.com/Nagi-Inaba/information-accessibility-skill/releases/tag/v0.1.0) is published, but the package version alone does not identify earlier development revisions. Verify its `SHA256SUMS` and `source-manifest.json`; record the full source commit with the audit. Node.js 20 or later is required. Browser scans additionally need a permitted browser adapter; ledger and report commands do not. +Use a pinned source revision for every install. The [v0.1.0 prerelease](https://github.com/Nagi-Inaba/information-accessibility-skill/releases/tag/v0.1.0) is published, but the package version alone does not identify earlier development revisions. Verify its `SHA256SUMS` and `source-manifest.json`; record the full source commit with the audit. Node.js 22 or later is required. Browser scans additionally need a permitted browser adapter; ledger and report commands do not. -導入ごとにソース版を固定します。[プレリリース v0.1.0](https://github.com/Nagi-Inaba/information-accessibility-skill/releases/tag/v0.1.0)は公開済みですが、版番号だけでは過去の開発版を特定できません。添付の`SHA256SUMS`と`source-manifest.json`を確認し、完全なsource commitを監査記録とともに保管します。Node.js 20以上が必要です。ブラウザー検査には別途利用を認められたadapterが必要ですが、台帳・レポートのコマンドには不要です。 +導入ごとにソース版を固定します。[プレリリース v0.1.0](https://github.com/Nagi-Inaba/information-accessibility-skill/releases/tag/v0.1.0)は公開済みですが、版番号だけでは過去の開発版を特定できません。添付の`SHA256SUMS`と`source-manifest.json`を確認し、完全なsource commitを監査記録とともに保管します。Node.js 22以上が必要です。ブラウザー検査には別途利用を認められたadapterが必要ですが、台帳・レポートのコマンドには不要です。 | Host | Windows | macOS / Linux | Installed components | | --- | --- | --- | --- | diff --git a/docs/version-support.md b/docs/version-support.md index d9e4c57d..3d417d09 100644 --- a/docs/version-support.md +++ b/docs/version-support.md @@ -4,7 +4,7 @@ 現行の監査専用導入と任意のauthorized fixerは、ともにorchestration registry `17.0.0`を使用します。installerは同梱されたfeature manifestとregistry版の一致を確認し、異なる組合せを拒否します。監査専用導入には`fix-authorization`・`fix-handoff`・`change-record`のpayload schemaと修正実行CLIを含めません。通常の監査runはこの構成で作成・検証できます。これらのartifactを含む既存runの読取りには、対応する修正機能と元のpackage/commitを保持してください。修正機能の追加・除外によって既存の監査runや元の証拠を書き換えません。 -セキュリティ修正の対象は現在のmainと、公開済みreleaseがある場合はその最新版です。古い開発commitの個別保守や、旧形式で新しい監査を継続できることは保証しません。実行要件はNode.js 20以上です。Web検査の依存関係は同梱package.jsonを正本とし、現在はaxe-core 4.13.0、任意のPlaywright 1.62.1です。 +セキュリティ修正の対象は現在のmainと、公開済みreleaseがある場合はその最新版です。古い開発commitの個別保守や、旧形式で新しい監査を継続できることは保証しません。実行要件はNode.js 22以上です。Node.js 20は上流のサポートが2026年4月30日に終了したため対象外で、CIはNode.js 22と24をUbuntu・Windows・macOSで確認します。Web検査の依存関係は同梱package.jsonを正本とし、現在はaxe-core 4.13.0、任意のPlaywright 1.62.1です。 ## 現行の主要形式 diff --git a/package.json b/package.json new file mode 100644 index 00000000..baaf4f78 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "information-accessibility-skill-workspace", + "private": true, + "description": "Development workspace for verification. The installable CLI package is shared/skill/package.json.", + "scripts": { + "sync": "node scripts/sync-distributions.mjs --write", + "verify": "node scripts/verify-all.mjs" + }, + "devDependencies": { + "exceljs": "4.4.0" + }, + "engines": { + "node": ">=22" + }, + "license": "MIT" +} diff --git a/release-files.json b/release-files.json index 4581ed50..0ebe903f 100644 --- a/release-files.json +++ b/release-files.json @@ -425,6 +425,7 @@ "tests/retest-delta.test.mjs", "tests/run-evidence.test.mjs", "tests/run-targets.test.mjs", + "tests/runtime-baseline.test.mjs", "tests/runtime-locale.test.mjs", "tests/runtime-procedure-locale.test.mjs", "tests/scan-web-cli.test.mjs", diff --git a/shared/skill/package.json b/shared/skill/package.json index e73d8d3d..b50b7c9f 100644 --- a/shared/skill/package.json +++ b/shared/skill/package.json @@ -24,7 +24,7 @@ } }, "engines": { - "node": ">=20" + "node": ">=22" }, "license": "MIT" } diff --git a/shared/skill/references/web-capabilities.json b/shared/skill/references/web-capabilities.json index 9cc5e67a..5b85e0eb 100644 --- a/shared/skill/references/web-capabilities.json +++ b/shared/skill/references/web-capabilities.json @@ -2,7 +2,7 @@ "schema_version": "1.0.0", "adapter": "playwright-chromium", "preflight_command": "accessibility-audit preflight-web --browser-channel chrome --format json", - "dependencies": { "node": ">=20", "playwright": "1.62.1", "axe-core_for_scan": "4.13.0" }, + "dependencies": { "node": ">=22", "playwright": "1.62.1", "axe-core_for_scan": "4.13.0" }, "browser_channels": ["playwright-chromium", "chrome"], "default_required": ["browser_dom", "accessibility_tree", "keyboard_input", "responsive_viewport", "network_fetch"], "capabilities": [ diff --git a/shared/skill/scripts/doctor.mjs b/shared/skill/scripts/doctor.mjs index 72b15265..b0bfbe96 100644 --- a/shared/skill/scripts/doctor.mjs +++ b/shared/skill/scripts/doctor.mjs @@ -12,6 +12,7 @@ import { normalizeRuntimeLocale, runtimeLocaleFromEnvironment } from "./lib/runt const scriptRoot = path.dirname(fileURLToPath(import.meta.url)); const skillRoot = path.dirname(scriptRoot); const require = createRequire(import.meta.url); +const minimumNodeMajor = 22; function readJson(file) { return JSON.parse(fs.readFileSync(file, "utf8").replace(/^\uFEFF/u, "")); @@ -69,8 +70,8 @@ export function diagnose(root = skillRoot) { } const nodeMajor = Number.parseInt(process.versions.node.split(".", 1)[0], 10); - const nodeSupported = Number.isInteger(nodeMajor) && nodeMajor >= 20; - if (!nodeSupported) errors.push(`Node.js 20 or newer is required; found ${process.versions.node}.`); + const nodeSupported = Number.isInteger(nodeMajor) && nodeMajor >= minimumNodeMajor; + if (!nodeSupported) errors.push(`Node.js ${minimumNodeMajor} or newer is required; found ${process.versions.node}.`); let manifest = { name: "unknown", version: "unknown", bin: {} }; try { @@ -100,7 +101,7 @@ export function diagnose(root = skillRoot) { status, node: { version: process.versions.node, - required: ">=20", + required: `>=${minimumNodeMajor}`, supported: nodeSupported, platform: process.platform, architecture: process.arch diff --git a/tests/runtime-baseline.test.mjs b/tests/runtime-baseline.test.mjs new file mode 100644 index 00000000..1139dbde --- /dev/null +++ b/tests/runtime-baseline.test.mjs @@ -0,0 +1,51 @@ +import assert from "node:assert/strict"; +import fs from "node:fs"; +import path from "node:path"; +import { spawnSync } from "node:child_process"; +import test from "node:test"; +import { fileURLToPath } from "node:url"; + +const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); +const skillRoot = path.join(root, "codex/skills/information-accessibility-practice"); +const readText = (...parts) => fs.readFileSync(path.join(root, ...parts), "utf8").replace(/\r\n/gu, "\n"); +const readJson = (...parts) => JSON.parse(readText(...parts)); + +const skillPackage = readJson("shared/skill/package.json"); +const minimumMajor = Number(/^>=(\d+)$/u.exec(skillPackage.engines.node)?.[1]); + +test("the Node.js baseline is declared once per surface and agrees everywhere", () => { + // Node.js 20 reached end of life on 2026-04-30. + assert.ok(Number.isInteger(minimumMajor) && minimumMajor >= 22, `engines.node must be >=N with N of at least 22: ${skillPackage.engines.node}`); + const workspace = readJson("package.json"); + assert.equal(workspace.private, true); + assert.equal(workspace.engines.node, skillPackage.engines.node); + assert.equal(workspace.devDependencies.exceljs, skillPackage.dependencies.exceljs); + assert.equal(readJson("shared/skill/references/web-capabilities.json").dependencies.node, skillPackage.engines.node); + const doctor = spawnSync(process.execPath, [path.join(skillRoot, "scripts/accessibility-audit.mjs"), "doctor", "--format", "json"], { cwd: root, encoding: "utf8" }); + assert.equal(doctor.status, 0, doctor.stderr || doctor.stdout); + const reported = JSON.parse(doctor.stdout).node; + assert.equal(reported.required, skillPackage.engines.node); + assert.equal(reported.supported, Number.parseInt(process.versions.node, 10) >= minimumMajor); +}); + +test("documentation states the same minimum Node.js major", () => { + for (const file of ["README.md", "README.en.md", "docs/installing.md", "docs/version-support.md"]) { + const stated = [...readText(file).matchAll(/Node\.js (\d+)(?:以上| or later)/gu)].map((match) => Number(match[1])); + assert.ok(stated.length > 0, `${file} states no Node.js minimum`); + assert.ok(stated.every((major) => major === minimumMajor), `${file} states Node.js ${stated.join(", ")}`); + } +}); + +test("CI tests every supported Node.js major on Ubuntu, Windows and macOS and runs nothing older", () => { + const verify = readText(".github/workflows/verify.yml"); + const nodes = JSON.parse(/node:\s*(\[[^\]]*\])/u.exec(verify)?.[1] ?? "[]"); + const systems = (/os:\s*\[([^\]]*)\]/u.exec(verify)?.[1] ?? "").split(",").map((item) => item.trim()); + assert.ok(nodes.includes(minimumMajor), `the verify matrix omits Node.js ${minimumMajor}`); + assert.ok(nodes.every((major) => major >= minimumMajor), `the verify matrix lists ${nodes.join(", ")}`); + assert.deepEqual(systems, ["ubuntu-latest", "windows-latest", "macos-latest"]); + for (const file of fs.readdirSync(path.join(root, ".github/workflows")).filter((name) => name.endsWith(".yml"))) { + for (const match of readText(".github/workflows", file).matchAll(/node-version:\s*(\d+)\s*$/gmu)) { + assert.ok(Number(match[1]) >= minimumMajor, `${file} runs Node.js ${match[1]}`); + } + } +});