From 9c237df1958f488aa9c3dea8ecebf4d702404cd8 Mon Sep 17 00:00:00 2001 From: Nick Schonning Date: Wed, 29 Jul 2026 16:10:21 -0400 Subject: [PATCH 1/2] ci: always run "./update.sh" even if musl is missing - swap calls to `fuction.sh` for normal Node.js file operations - Assume the Alpine build might lag - Read the `security` flag from nodejs.org instead of unofficial-builds --- .github/workflows/automatic-updates.yml | 2 +- build-automation.mjs | 62 ++++++++++++------------- 2 files changed, 30 insertions(+), 34 deletions(-) diff --git a/.github/workflows/automatic-updates.yml b/.github/workflows/automatic-updates.yml index 279f09004..4757e9502 100644 --- a/.github/workflows/automatic-updates.yml +++ b/.github/workflows/automatic-updates.yml @@ -22,7 +22,7 @@ jobs: result-encoding: string script: | const { default: script } = await import(`${process.env.GITHUB_WORKSPACE}/build-automation.mjs`); - return script(github); + return script(); - name: Create update PR id: cpr diff --git a/build-automation.mjs b/build-automation.mjs index f5760ec8f..461334240 100644 --- a/build-automation.mjs +++ b/build-automation.mjs @@ -1,4 +1,6 @@ import { promisify } from 'util'; +import { readdirSync, readFileSync } from 'node:fs'; +import { join } from 'node:path'; import child_process from 'child_process'; @@ -7,40 +9,38 @@ const exec = promisify(child_process.exec); // a function that queries the Node.js release website for new versions, // compare the available ones with the ones we use in this repo // and returns whether we should update or not -const checkIfThereAreNewVersions = async (github) => { +const checkIfThereAreNewVersions = async () => { try { - const { stdout: versionsOutput } = await exec( - '. ./functions.sh && get_versions', - { shell: 'bash' }, - ); - - const supportedVersions = versionsOutput.trim().split(' '); + let files = readdirSync('./'); + // get the folders with a digit, assuming they're the Node.js major versions + const supportedVersions = files.filter((file) => { + return file.match(/\d/); + }); let latestSupportedVersions = {}; for (let supportedVersion of supportedVersions) { - const { stdout } = await exec(`ls ${supportedVersion}`); - - const { stdout: fullVersionOutput } = await exec( - `. ./functions.sh && get_full_version ./${supportedVersion}/${stdout.trim().split('\n')[0]}`, - { shell: 'bash' }, + // Grab the Alpine folder, to assume it is more likely to be behind after a Security release + const alpinefolder = readdirSync(join('.', supportedVersion)).find( + (folder) => folder.startsWith('alpine'), ); - console.log(fullVersionOutput); + const fullVersionOutput = readFileSync( + join('.', supportedVersion, alpinefolder, 'Dockerfile'), + 'utf-8', + ); latestSupportedVersions[supportedVersion] = { - fullVersion: fullVersionOutput.trim(), + fullVersion: fullVersionOutput.match( + /NODE_VERSION=(?\d*\.\d*\.\d)/, + ).groups['version'], }; } - const { data: availableVersionsJson } = await github.request( + const availableVersions = await fetch( 'https://nodejs.org/download/release/index.json', ); - - // filter only more recent versions of availableVersionsJson for each major version in latestSupportedVersions' keys - // e.g. if latestSupportedVersions = { "12": "12.22.10", "14": "14.19.0", "16": "16.14.0", "17": "17.5.0" } - // and availableVersions = ["Node.js 12.22.10", "Node.js 12.24.0", "Node.js 14.19.0", "Node.js 14.22.0", "Node.js 16.14.0", "Node.js 16.16.0", "Node.js 17.5.0", "Node.js 17.8.0"] - // return { "12": "12.24.0", "14": "14.22.0", "16": "16.16.0", "17": "17.8.0" } + const availableVersionsJson = await availableVersions.json(); let filteredNewerVersions = {}; @@ -60,6 +60,7 @@ const checkIfThereAreNewVersions = async (github) => { ) { filteredNewerVersions[availableMajor] = { fullVersion: `${availableMajor}.${availableMinor}.${availablePatch}`, + isSecurityRelease: availableVersion.security, }; } } @@ -79,11 +80,12 @@ const checkIfThereAreNewVersions = async (github) => { // a function that queries the Node.js unofficial release website for new musl versions and security releases, // and returns relevant information -const checkForMuslVersionsAndSecurityReleases = async (github, versions) => { +const checkForMuslVersionsAndSecurityReleases = async (versions) => { try { - const { data: unofficialBuildsIndexText } = await github.request( + const unofficialBuildsIndex = await fetch( 'https://unofficial-builds.nodejs.org/download/release/index.json', ); + const unofficialBuildsIndexText = await unofficialBuildsIndex.json(); for (let version of Object.keys(versions)) { const buildVersion = unofficialBuildsIndexText.find( @@ -93,7 +95,6 @@ const checkForMuslVersionsAndSecurityReleases = async (github, versions) => { versions[version].muslBuildExists = buildVersion?.files.includes('linux-x64-musl') ?? false; - versions[version].isSecurityRelease = buildVersion?.security ?? false; } return versions; } catch (error) { @@ -102,24 +103,22 @@ const checkForMuslVersionsAndSecurityReleases = async (github, versions) => { } }; -export default async function (github) { +export default async function () { // if there are no new versions, exit gracefully // if there are new versions, // check for musl builds // then run update.sh - const { shouldUpdate, versions } = await checkIfThereAreNewVersions(github); + const { shouldUpdate, versions } = await checkIfThereAreNewVersions(); if (!shouldUpdate) { console.log('No new versions found. No update required.'); process.exit(0); } else { - const newVersions = await checkForMuslVersionsAndSecurityReleases( - github, - versions, - ); + const newVersions = await checkForMuslVersionsAndSecurityReleases(versions); let updatedVersions = []; for (const [version, newVersion] of Object.entries(newVersions)) { - if (newVersion.muslBuildExists) { + if (newVersion.muslBuildExists || newVersion.isSecurityRelease) { + console.log(`Updating ${newVersion.fullVersion}.`); const { stdout } = await exec( `./update.sh ${newVersion.isSecurityRelease ? '-s ' : ''}${version}`, ); @@ -132,9 +131,6 @@ export default async function (github) { process.exit(0); } } - const { stdout } = await exec(`git diff`); - console.log(stdout); - return updatedVersions.join(', '); } } From 3395aabc89713608c75e92a8ff13500a6051862d Mon Sep 17 00:00:00 2001 From: Nick Schonning Date: Wed, 5 Aug 2026 16:11:05 -0400 Subject: [PATCH 2/2] wip: refactor build-automation --- build-automation.mjs | 198 +++++++++------------- package-lock.json | 383 ++++++++++++++++++++++++++++++++++++++++++- package.json | 4 +- 3 files changed, 467 insertions(+), 118 deletions(-) diff --git a/build-automation.mjs b/build-automation.mjs index 461334240..ea534c3bc 100644 --- a/build-automation.mjs +++ b/build-automation.mjs @@ -1,136 +1,102 @@ -import { promisify } from 'util'; import { readdirSync, readFileSync } from 'node:fs'; import { join } from 'node:path'; -import child_process from 'child_process'; +import shell from 'shelljs'; -const exec = promisify(child_process.exec); +// Track the built versions to output for the GitHub PR +const updatedVersions = []; -// a function that queries the Node.js release website for new versions, -// compare the available ones with the ones we use in this repo -// and returns whether we should update or not -const checkIfThereAreNewVersions = async () => { - try { - let files = readdirSync('./'); - // get the folders with a digit, assuming they're the Node.js major versions - const supportedVersions = files.filter((file) => { - return file.match(/\d/); - }); - - let latestSupportedVersions = {}; - - for (let supportedVersion of supportedVersions) { - // Grab the Alpine folder, to assume it is more likely to be behind after a Security release - const alpinefolder = readdirSync(join('.', supportedVersion)).find( - (folder) => folder.startsWith('alpine'), - ); +// TODO: since we have the full version, and could pass the CHECKSUM value (till +// it goes Tier 2), the update.sh script shouldn't have to look it all up again +async function runUpdate(fullVersion, isSecurityRelease, hasMusl) { + let majorVersion = fullVersion.split('v')[1].split('.')[0]; + if (hasMusl || isSecurityRelease) { + let updateStatement = `bash update.sh ${isSecurityRelease ? '-s ' : ''}${majorVersion}`; + console.log(`Updating ${fullVersion} with '${updateStatement}'.`); + shell.exec(updateStatement); + updatedVersions.push(fullVersion); + } else { + console.error(`There's no musl build for version ${fullVersion} yet.`); + } +} - const fullVersionOutput = readFileSync( - join('.', supportedVersion, alpinefolder, 'Dockerfile'), - 'utf-8', - ); +try { + // get the folders with a digit, assuming they're the Node.js major versions + const supportedVersions = readdirSync('./').filter((file) => { + return file.match(/\d/); + }); - latestSupportedVersions[supportedVersion] = { - fullVersion: fullVersionOutput.match( - /NODE_VERSION=(?\d*\.\d*\.\d)/, - ).groups['version'], - }; - } + console.log(`Found major versions in repo: ${supportedVersions}`); - const availableVersions = await fetch( - 'https://nodejs.org/download/release/index.json', - ); - const availableVersionsJson = await availableVersions.json(); + console.log('Grabbing Index.json files'); + const availableVersions = await fetch( + 'https://nodejs.org/download/release/index.json', + ); + const officialIndexJson = await availableVersions.json(); - let filteredNewerVersions = {}; + const unofficialVersions = await fetch( + 'https://unofficial-builds.nodejs.org/download/release/index.json', + ); + const unofficialBuildsIndexJson = await unofficialVersions.json(); - for (let availableVersion of availableVersionsJson) { - const [availableMajor, availableMinor, availablePatch] = - availableVersion.version.split('v')[1].split('.'); - if (latestSupportedVersions[availableMajor] == null) { - continue; - } - const [_latestMajor, latestMinor, latestPatch] = - latestSupportedVersions[availableMajor].fullVersion.split('.'); - if ( - latestSupportedVersions[availableMajor] && - (Number(availableMinor) > Number(latestMinor) || - (availableMinor === latestMinor && - Number(availablePatch) > Number(latestPatch))) - ) { - filteredNewerVersions[availableMajor] = { - fullVersion: `${availableMajor}.${availableMinor}.${availablePatch}`, - isSecurityRelease: availableVersion.security, - }; - } - } + for (let supportedVersion of supportedVersions) { + console.log(`Checking for updates for ${supportedVersion}`); + const folders = readdirSync(join('.', supportedVersion)); - return { - shouldUpdate: - Object.keys(filteredNewerVersions).length > 0 && - JSON.stringify(filteredNewerVersions) !== - JSON.stringify(latestSupportedVersions), - versions: filteredNewerVersions, - }; - } catch (error) { - console.error(error); - process.exit(1); - } -}; + const alpineFolder = folders[0]; -// a function that queries the Node.js unofficial release website for new musl versions and security releases, -// and returns relevant information -const checkForMuslVersionsAndSecurityReleases = async (versions) => { - try { - const unofficialBuildsIndex = await fetch( - 'https://unofficial-builds.nodejs.org/download/release/index.json', + const alpineDockerFile = readFileSync( + join('.', supportedVersion, alpineFolder, 'Dockerfile'), + 'utf-8', + ); + const alpineVersion = + 'v' + + alpineDockerFile.match(/NODE_VERSION=(?\d*\.\d*\.\d)/).groups[ + 'version' + ]; + console.log(`Read Alpine version ${alpineVersion} from ${alpineFolder}`); + + const debianFolder = folders.at(-1); + const debianDockerFile = readFileSync( + join('.', supportedVersion, debianFolder, 'Dockerfile'), + 'utf-8', ); - const unofficialBuildsIndexText = await unofficialBuildsIndex.json(); - for (let version of Object.keys(versions)) { - const buildVersion = unofficialBuildsIndexText.find( - (indexVersion) => - indexVersion.version === `v${versions[version].fullVersion}`, - ); + const debianVersion = + 'v' + + debianDockerFile.match(/NODE_VERSION=(?\d*\.\d*\.\d)/).groups[ + 'version' + ]; + console.log(`Read Debian version ${alpineVersion} from ${debianFolder}`); - versions[version].muslBuildExists = - buildVersion?.files.includes('linux-x64-musl') ?? false; - } - return versions; - } catch (error) { - console.error(error); - process.exit(1); - } -}; + let latestDebian = officialIndexJson.find((indexVersion) => + indexVersion.version.startsWith(`v${supportedVersion}`), + ); -export default async function () { - // if there are no new versions, exit gracefully - // if there are new versions, - // check for musl builds - // then run update.sh - const { shouldUpdate, versions } = await checkIfThereAreNewVersions(); + let hasMusl = + unofficialBuildsIndexJson.find( + (indexVersion) => indexVersion.version === latestDebian, + ) !== null; - if (!shouldUpdate) { - console.log('No new versions found. No update required.'); - process.exit(0); - } else { - const newVersions = await checkForMuslVersionsAndSecurityReleases(versions); - let updatedVersions = []; - for (const [version, newVersion] of Object.entries(newVersions)) { - if (newVersion.muslBuildExists || newVersion.isSecurityRelease) { - console.log(`Updating ${newVersion.fullVersion}.`); - const { stdout } = await exec( - `./update.sh ${newVersion.isSecurityRelease ? '-s ' : ''}${version}`, - ); - console.log(stdout); - updatedVersions.push(newVersion.fullVersion); - } else { - console.log( - `There's no musl build for version ${newVersion.fullVersion} yet.`, - ); - process.exit(0); - } + if (latestDebian.version !== debianVersion) { + console.warn( + `Found new version ${latestDebian.version}, released on ${latestDebian.date}!`, + ); + await runUpdate(latestDebian.version, latestDebian.security, hasMusl); + console.warn(`Alpine and Debian versions do not match!`); + } else if (debianVersion !== alpineVersion) { + console.warn(`Alpine ${alpineVersion} ${latestDebian.version}!`); + await runUpdate(latestDebian.version, latestDebian.security, hasMusl); + } else { + console.log(`Everything up to date for ${latestDebian.version}! +Released: ${latestDebian.date} +Security release: ${latestDebian.security} +Has musl: ${hasMusl}`); } - return updatedVersions.join(', '); } + console.log('Finish the run.'); + updatedVersions.join(', '); +} catch (error) { + console.error(error); + process.exit(1); } diff --git a/package-lock.json b/package-lock.json index db0402bf3..c10d6546c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,46 @@ "doctoc": "2.5.0", "markdown-link-check": "3.14.2", "npm-run-all2": "9.0.2", - "prettier": "3.9.6" + "prettier": "3.9.6", + "shelljs": "0.10.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" } }, "node_modules/@oozcitak/dom": { @@ -210,6 +249,19 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/ccount": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz", @@ -831,6 +883,30 @@ "node": ">=0.10.0" } }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", @@ -838,6 +914,33 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fault": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/fault/-/fault-1.0.4.tgz", @@ -852,6 +955,19 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/format": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/format/-/format-0.2.2.tgz", @@ -861,6 +977,19 @@ "node": ">=0.4.x" } }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/get-uri": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", @@ -876,6 +1005,19 @@ "node": ">= 14" } }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/html-link-extractor": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/html-link-extractor/-/html-link-extractor-1.0.5.tgz", @@ -934,6 +1076,16 @@ "node": ">= 14" } }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -1031,6 +1183,29 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-hexadecimal": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", @@ -1042,6 +1217,16 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -1068,6 +1253,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isexe": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", @@ -1398,6 +1596,23 @@ "node": ">= 0.10.0" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/micromark": { "version": "2.11.4", "resolved": "https://registry.npmjs.org/micromark/-/micromark-2.11.4.tgz", @@ -1533,6 +1748,43 @@ "url": "https://opencollective.com/unified" } }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -1638,6 +1890,19 @@ "npm": ">= 10" } }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -1651,6 +1916,22 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pac-proxy-agent": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", @@ -1862,6 +2143,27 @@ "dev": true, "license": "MIT" }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/read-package-json-fast": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-6.0.0.tgz", @@ -1952,6 +2254,41 @@ "node": ">=0.10" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -2005,6 +2342,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/shelljs": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.10.0.tgz", + "integrity": "sha512-Jex+xw5Mg2qMZL3qnzXIfaxEtBaC4n7xifqaqtrZDdlheR70OGkydrPJWT0V1cA1k3nanC86x9FwAmQl6w3Klw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "execa": "^5.1.1", + "fast-glob": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -2057,6 +2415,16 @@ "node": ">=0.10.0" } }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/structured-source": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz", @@ -2067,6 +2435,19 @@ "boundary": "^2.0.0" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/trough": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/trough/-/trough-1.0.5.tgz", diff --git a/package.json b/package.json index 90fa14566..a70455017 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "private": true, "type": "commonjs", "scripts": { + "build": "node build-automation.mjs", "format:toc": "doctoc --update-only .", "format:toc:check": "doctoc --update-only --dryrun .", "format:prettier": "prettier --write .", @@ -16,6 +17,7 @@ "doctoc": "2.5.0", "markdown-link-check": "3.14.2", "npm-run-all2": "9.0.2", - "prettier": "3.9.6" + "prettier": "3.9.6", + "shelljs": "0.10.0" } }