moving
This commit is contained in:
@@ -1,130 +1,387 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Downloads the correct Node.js binary for the current platform and places it
|
||||
* in app/src-tauri/resources/ for Tauri bundling.
|
||||
* Download and verify the official Node.js distribution used by the desktop
|
||||
* sidecar. The full archive is required because it is the authoritative source
|
||||
* for the matching Node binary, Node license, and bundled npm runtime.
|
||||
*
|
||||
* Defaults to the Node version running this script so copied native modules
|
||||
* from node_modules match the bundled runtime ABI. Set
|
||||
* WAGGLE_BUNDLED_NODE_VERSION to pin a different version intentionally.
|
||||
*
|
||||
* Uses Node.js official distribution (https://nodejs.org/dist/).
|
||||
* Caches in scripts/.cache/ to avoid re-downloading.
|
||||
* Desktop packaging pins one exact supported Node.js release. Release and PR
|
||||
* workflows install dependencies with the same version, and the extracted
|
||||
* runtime proves it can load the installed native SQLite binding before any
|
||||
* packaged runtime resource is replaced.
|
||||
*/
|
||||
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const root = path.resolve(__dirname, '..');
|
||||
const resourcesDir = path.join(root, 'app', 'src-tauri', 'resources');
|
||||
const cacheDir = path.join(__dirname, '.cache');
|
||||
const stagedRuntimeDir = path.join(
|
||||
resourcesDir,
|
||||
'node_modules',
|
||||
'waggle-node-runtime',
|
||||
);
|
||||
const SAFE_NPM_BRACE_EXPANSION_VERSION = '2.1.4';
|
||||
const safeNpmBraceExpansionSource = path.join(
|
||||
root,
|
||||
'node_modules',
|
||||
'archiver-utils',
|
||||
'node_modules',
|
||||
'brace-expansion',
|
||||
);
|
||||
const SAFE_NPM_IP_ADDRESS_VERSION = '10.4.0';
|
||||
const safeNpmIpAddressSource = path.join(root, 'node_modules', 'ip-address');
|
||||
|
||||
const NODE_VERSION = process.env.WAGGLE_BUNDLED_NODE_VERSION ?? process.versions.node;
|
||||
const DESKTOP_NODE_VERSION = '22.23.2';
|
||||
const NODE_VERSION = DESKTOP_NODE_VERSION;
|
||||
if (!/^\d+\.\d+\.\d+$/.test(NODE_VERSION)) {
|
||||
console.error(`[bundle-node] FATAL — invalid Node.js version: ${NODE_VERSION}`);
|
||||
console.error(`[bundle-node] FATAL - invalid Node.js version: ${NODE_VERSION}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const platform = process.platform;
|
||||
const arch = process.env.TARGET_ARCH || process.arch;
|
||||
|
||||
// macOS "universal" is not a valid download target: nodejs.org ships per-arch
|
||||
// binaries (node-vX-darwin-arm64 / -x64), and a universal run would fall
|
||||
// through to the x64 tarball and ship an x64-only node in an arm64 bundle.
|
||||
// Build each arch separately and lipo the app bundle instead.
|
||||
if (arch === 'universal') {
|
||||
console.error(
|
||||
'[bundle-node] FATAL — TARGET_ARCH=universal is not supported.\n'
|
||||
+ ' Node.js ships per-arch binaries. Build each arch separately:\n'
|
||||
+ ' TARGET_ARCH=arm64 (aarch64-apple-darwin) and TARGET_ARCH=x64\n'
|
||||
+ ' (x86_64-apple-darwin) — see release.yml\'s macOS matrix and the app\n'
|
||||
+ ' tauri:build:mac:arm64 / :x64 scripts.',
|
||||
'[bundle-node] FATAL - TARGET_ARCH=universal is not supported.\n'
|
||||
+ ' Node.js ships per-arch binaries. Build arm64 and x64 separately.',
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
if (!['x64', 'arm64'].includes(arch)) {
|
||||
console.error(`[bundle-node] FATAL - unsupported target architecture: ${arch}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const archivePlatform = platform === 'win32'
|
||||
? 'win'
|
||||
: platform === 'darwin'
|
||||
? 'darwin'
|
||||
: platform === 'linux'
|
||||
? 'linux'
|
||||
: null;
|
||||
if (!archivePlatform) {
|
||||
console.error(`[bundle-node] FATAL - unsupported platform: ${platform}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const archiveExtension = platform === 'win32' ? 'zip' : 'tar.gz';
|
||||
const distributionName = `node-v${NODE_VERSION}-${archivePlatform}-${arch}`;
|
||||
const archiveName = `${distributionName}.${archiveExtension}`;
|
||||
const distributionUrl = `https://nodejs.org/dist/v${NODE_VERSION}`;
|
||||
const archivePath = path.join(cacheDir, archiveName);
|
||||
const shasumsPath = path.join(cacheDir, `node-v${NODE_VERSION}-SHASUMS256.txt`);
|
||||
const extractDir = path.join(cacheDir, `${distributionName}-verified`);
|
||||
const extractedRoot = path.join(extractDir, distributionName);
|
||||
const nodeSource = platform === 'win32'
|
||||
? path.join(extractedRoot, 'node.exe')
|
||||
: path.join(extractedRoot, 'bin', 'node');
|
||||
const npmSource = path.join(
|
||||
extractedRoot,
|
||||
...(platform === 'win32'
|
||||
? ['node_modules', 'npm']
|
||||
: ['lib', 'node_modules', 'npm']),
|
||||
);
|
||||
const nodeLicenseSource = path.join(extractedRoot, 'LICENSE');
|
||||
const destBinary = path.join(resourcesDir, platform === 'win32' ? 'node.exe' : 'node');
|
||||
|
||||
function fail(message) {
|
||||
console.error(`[bundle-node] FATAL - ${message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function assertNativeRuntimeCompatible() {
|
||||
const betterSqlitePath = path.join(root, 'node_modules', 'better-sqlite3');
|
||||
const probe = `
|
||||
const path = require('node:path');
|
||||
const Database = require(path.join(process.argv[1], 'node_modules', 'better-sqlite3'));
|
||||
const database = new Database(':memory:');
|
||||
const row = database.prepare('SELECT 1 AS ok').get();
|
||||
database.close();
|
||||
if (row?.ok !== 1) throw new Error('SQLite query probe returned an invalid result');
|
||||
process.stdout.write(JSON.stringify({
|
||||
version: process.versions.node,
|
||||
abi: process.versions.modules,
|
||||
arch: process.arch,
|
||||
}));
|
||||
`;
|
||||
|
||||
try {
|
||||
const output = execFileSync(nodeSource, ['-e', probe, root], {
|
||||
cwd: root,
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
}).trim();
|
||||
const runtime = JSON.parse(output);
|
||||
if (runtime.version !== NODE_VERSION || runtime.arch !== arch) {
|
||||
fail(
|
||||
`native ABI compatibility probe used Node.js v${runtime.version ?? 'unknown'} `
|
||||
+ `(${runtime.arch ?? 'unknown'}, ABI ${runtime.abi ?? 'unknown'}); expected `
|
||||
+ `v${NODE_VERSION} (${arch})`,
|
||||
);
|
||||
}
|
||||
console.log(
|
||||
`[bundle-node] Native ABI probe passed with Node.js v${runtime.version} `
|
||||
+ `(${runtime.arch}, ABI ${runtime.abi})`,
|
||||
);
|
||||
} catch (error) {
|
||||
const detail = String(error?.stderr ?? error?.message ?? error).trim().slice(0, 4_000);
|
||||
fail(
|
||||
`native ABI compatibility probe failed for Node.js v${NODE_VERSION} (${arch}) `
|
||||
+ `against ${betterSqlitePath}. Reinstall dependencies with Node.js `
|
||||
+ `v${NODE_VERSION} (npm ci) before packaging. ${detail}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async function download(url, destination) {
|
||||
console.log(`[bundle-node] Downloading ${url}`);
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) fail(`download failed: HTTP ${response.status} (${url})`);
|
||||
const temporary = `${destination}.${process.pid}.tmp`;
|
||||
fs.writeFileSync(temporary, Buffer.from(await response.arrayBuffer()));
|
||||
fs.renameSync(temporary, destination);
|
||||
}
|
||||
|
||||
function sha256(file) {
|
||||
return createHash('sha256').update(fs.readFileSync(file)).digest('hex');
|
||||
}
|
||||
|
||||
function expectedArchiveHash(shasums) {
|
||||
for (const line of shasums.split(/\r?\n/)) {
|
||||
const match = /^([a-f0-9]{64})\s+\*?(.+)$/.exec(line.trim());
|
||||
if (match?.[2] === archiveName) return match[1];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function extractedRuntimeComplete() {
|
||||
return [
|
||||
nodeSource,
|
||||
nodeLicenseSource,
|
||||
path.join(npmSource, 'LICENSE'),
|
||||
path.join(npmSource, 'bin', 'npm-cli.js'),
|
||||
path.join(npmSource, 'bin', 'npx-cli.js'),
|
||||
].every((file) => fs.existsSync(file) && fs.lstatSync(file).isFile());
|
||||
}
|
||||
|
||||
function directorySizeBytes(dir) {
|
||||
let bytes = 0;
|
||||
const stack = [dir];
|
||||
while (stack.length > 0) {
|
||||
const current = stack.pop();
|
||||
for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
|
||||
const full = path.join(current, entry.name);
|
||||
if (entry.isDirectory()) stack.push(full);
|
||||
else if (entry.isFile()) bytes += fs.statSync(full).size;
|
||||
}
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
function writeWrappers() {
|
||||
const binDir = path.join(stagedRuntimeDir, 'bin');
|
||||
fs.mkdirSync(binDir, { recursive: true });
|
||||
if (platform === 'win32') {
|
||||
const wrapper = (cli) => [
|
||||
'@ECHO OFF',
|
||||
'SETLOCAL',
|
||||
'SET "NODE_EXE=%~dp0\\..\\..\\..\\node.exe"',
|
||||
`SET "NPM_CLI_JS=%~dp0\\..\\node_modules\\npm\\bin\\${cli}-cli.js"`,
|
||||
'"%NODE_EXE%" "%NPM_CLI_JS%" %*',
|
||||
'EXIT /B %ERRORLEVEL%',
|
||||
'',
|
||||
].join('\r\n');
|
||||
fs.writeFileSync(path.join(binDir, 'npm.cmd'), wrapper('npm'), 'utf8');
|
||||
fs.writeFileSync(path.join(binDir, 'npx.cmd'), wrapper('npx'), 'utf8');
|
||||
return;
|
||||
}
|
||||
|
||||
const wrapper = (cli) => [
|
||||
'#!/bin/sh',
|
||||
'SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)',
|
||||
`exec "$SCRIPT_DIR/../../../node" "$SCRIPT_DIR/../node_modules/npm/bin/${cli}-cli.js" "$@"`,
|
||||
'',
|
||||
].join('\n');
|
||||
for (const cli of ['npm', 'npx']) {
|
||||
const wrapperPath = path.join(binDir, cli);
|
||||
fs.writeFileSync(wrapperPath, wrapper(cli), 'utf8');
|
||||
fs.chmodSync(wrapperPath, 0o755);
|
||||
}
|
||||
}
|
||||
|
||||
fs.mkdirSync(cacheDir, { recursive: true });
|
||||
fs.mkdirSync(resourcesDir, { recursive: true });
|
||||
|
||||
const destBinary = platform === 'win32'
|
||||
? path.join(resourcesDir, 'node.exe')
|
||||
: path.join(resourcesDir, 'node');
|
||||
|
||||
// Check cache
|
||||
const cacheKey = `node-v${NODE_VERSION}-${platform}-${arch}`;
|
||||
const cachedBinary = path.join(cacheDir, platform === 'win32' ? `${cacheKey}.exe` : cacheKey);
|
||||
|
||||
if (fs.existsSync(cachedBinary)) {
|
||||
console.log(`[bundle-node] Using cached Node.js v${NODE_VERSION} (${platform}-${arch})`);
|
||||
fs.copyFileSync(cachedBinary, destBinary);
|
||||
if (platform !== 'win32') {
|
||||
fs.chmodSync(destBinary, 0o755);
|
||||
async function loadExpectedHash(refresh = false) {
|
||||
if (refresh) fs.rmSync(shasumsPath, { force: true });
|
||||
if (!fs.existsSync(shasumsPath)) {
|
||||
await download(`${distributionUrl}/SHASUMS256.txt`, shasumsPath);
|
||||
}
|
||||
const size = (fs.statSync(destBinary).size / 1024 / 1024).toFixed(1);
|
||||
console.log(`[bundle-node] → ${destBinary} (${size} MB)`);
|
||||
process.exit(0);
|
||||
const expected = expectedArchiveHash(fs.readFileSync(shasumsPath, 'utf8'));
|
||||
if (expected) return expected;
|
||||
if (!refresh) return loadExpectedHash(true);
|
||||
fail(`official SHASUMS256.txt has no entry for ${archiveName}`);
|
||||
}
|
||||
|
||||
// Download
|
||||
if (platform === 'win32') {
|
||||
// Windows: direct .exe download
|
||||
const url = `https://nodejs.org/dist/v${NODE_VERSION}/win-${arch}/node.exe`;
|
||||
console.log(`[bundle-node] Downloading Node.js v${NODE_VERSION} (${platform}-${arch})...`);
|
||||
console.log(` ${url}`);
|
||||
let expectedHash = await loadExpectedHash();
|
||||
let refreshedShasums = false;
|
||||
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
console.error(`[bundle-node] Download failed: HTTP ${response.status}`);
|
||||
process.exit(1);
|
||||
if (fs.existsSync(archivePath) && sha256(archivePath) !== expectedHash) {
|
||||
expectedHash = await loadExpectedHash(true);
|
||||
refreshedShasums = true;
|
||||
if (sha256(archivePath) !== expectedHash) {
|
||||
console.warn(`[bundle-node] Discarding checksum-mismatched cache: ${archiveName}`);
|
||||
fs.rmSync(archivePath, { force: true });
|
||||
fs.rmSync(extractDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
const buffer = Buffer.from(await response.arrayBuffer());
|
||||
fs.writeFileSync(cachedBinary, buffer);
|
||||
fs.copyFileSync(cachedBinary, destBinary);
|
||||
|
||||
} else {
|
||||
// macOS/Linux: download .tar.gz and extract bin/node
|
||||
const nodeArch = arch === 'arm64' ? 'arm64' : 'x64';
|
||||
const osPart = platform === 'darwin' ? 'darwin' : 'linux';
|
||||
const archiveName = `node-v${NODE_VERSION}-${osPart}-${nodeArch}.tar.gz`;
|
||||
const url = `https://nodejs.org/dist/v${NODE_VERSION}/${archiveName}`;
|
||||
|
||||
console.log(`[bundle-node] Downloading Node.js v${NODE_VERSION} (${osPart}-${nodeArch})...`);
|
||||
console.log(` ${url}`);
|
||||
|
||||
const archivePath = path.join(cacheDir, archiveName);
|
||||
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
console.error(`[bundle-node] Download failed: HTTP ${response.status}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const buffer = Buffer.from(await response.arrayBuffer());
|
||||
fs.writeFileSync(archivePath, buffer);
|
||||
|
||||
// Extract bin/node from the tarball. The archive root is the versioned dir
|
||||
// (node-vX-os-arch/), so the member must include that prefix; --strip-
|
||||
// components=1 then drops it so the file lands at <extractDir>/bin/node.
|
||||
// BSD tar (macOS) matches members against the FULL archived path, so a bare
|
||||
// "bin/node" matches nothing → "tar: bin/node: Not found in archive".
|
||||
// (safe — no user input in args)
|
||||
const extractDir = path.join(cacheDir, 'extract');
|
||||
fs.mkdirSync(extractDir, { recursive: true });
|
||||
const member = `node-v${NODE_VERSION}-${osPart}-${nodeArch}/bin/node`;
|
||||
execFileSync('tar', ['xzf', archivePath, '-C', extractDir, '--strip-components=1', member], {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
|
||||
const extractedNode = path.join(extractDir, 'bin', 'node');
|
||||
fs.copyFileSync(extractedNode, cachedBinary);
|
||||
fs.copyFileSync(cachedBinary, destBinary);
|
||||
fs.chmodSync(destBinary, 0o755);
|
||||
|
||||
// Cleanup extracted files
|
||||
}
|
||||
if (!fs.existsSync(archivePath)) {
|
||||
await download(`${distributionUrl}/${archiveName}`, archivePath);
|
||||
}
|
||||
let actualHash = sha256(archivePath);
|
||||
if (actualHash !== expectedHash && !refreshedShasums) {
|
||||
expectedHash = await loadExpectedHash(true);
|
||||
refreshedShasums = true;
|
||||
actualHash = sha256(archivePath);
|
||||
}
|
||||
if (actualHash !== expectedHash) {
|
||||
fs.rmSync(archivePath, { force: true });
|
||||
fs.rmSync(extractDir, { recursive: true, force: true });
|
||||
fail(`SHA-256 mismatch for ${archiveName}: expected ${expectedHash}, got ${actualHash}`);
|
||||
}
|
||||
console.log(`[bundle-node] Verified ${archiveName} against official SHASUMS256.txt`);
|
||||
|
||||
fs.rmSync(extractDir, { recursive: true, force: true });
|
||||
fs.mkdirSync(extractDir, { recursive: true });
|
||||
execFileSync('tar', ['-xf', archivePath, '-C', extractDir], { stdio: 'inherit' });
|
||||
if (!extractedRuntimeComplete()) {
|
||||
fail(`verified archive is missing Node, npm, or required license files: ${archiveName}`);
|
||||
}
|
||||
|
||||
const size = (fs.statSync(destBinary).size / 1024 / 1024).toFixed(1);
|
||||
console.log(`[bundle-node] Node.js v${NODE_VERSION} (${platform}-${arch}) → ${destBinary} (${size} MB)`);
|
||||
assertNativeRuntimeCompatible();
|
||||
|
||||
fs.copyFileSync(nodeSource, destBinary);
|
||||
if (platform !== 'win32') fs.chmodSync(destBinary, 0o755);
|
||||
|
||||
fs.rmSync(stagedRuntimeDir, { recursive: true, force: true });
|
||||
fs.mkdirSync(path.join(stagedRuntimeDir, 'node_modules'), { recursive: true });
|
||||
fs.copyFileSync(nodeLicenseSource, path.join(stagedRuntimeDir, 'NODE-LICENSE'));
|
||||
fs.cpSync(npmSource, path.join(stagedRuntimeDir, 'node_modules', 'npm'), {
|
||||
recursive: true,
|
||||
dereference: true,
|
||||
});
|
||||
const safeNpmBraceExpansionManifest = path.join(
|
||||
safeNpmBraceExpansionSource,
|
||||
'package.json',
|
||||
);
|
||||
if (!fs.existsSync(safeNpmBraceExpansionManifest)) {
|
||||
fail('lock-installed brace-expansion hardening source is missing');
|
||||
}
|
||||
const safeNpmBraceExpansion = JSON.parse(
|
||||
fs.readFileSync(safeNpmBraceExpansionManifest, 'utf8'),
|
||||
);
|
||||
if (safeNpmBraceExpansion.version !== SAFE_NPM_BRACE_EXPANSION_VERSION) {
|
||||
fail(
|
||||
`lock-installed brace-expansion is ${safeNpmBraceExpansion.version}; `
|
||||
+ `expected ${SAFE_NPM_BRACE_EXPANSION_VERSION}`,
|
||||
);
|
||||
}
|
||||
const stagedNpmBraceExpansion = path.join(
|
||||
stagedRuntimeDir,
|
||||
'node_modules',
|
||||
'npm',
|
||||
'node_modules',
|
||||
'brace-expansion',
|
||||
);
|
||||
fs.rmSync(stagedNpmBraceExpansion, { recursive: true, force: true });
|
||||
fs.cpSync(safeNpmBraceExpansionSource, stagedNpmBraceExpansion, {
|
||||
recursive: true,
|
||||
dereference: true,
|
||||
});
|
||||
const stagedNpmBraceExpansionVersion = JSON.parse(
|
||||
fs.readFileSync(path.join(stagedNpmBraceExpansion, 'package.json'), 'utf8'),
|
||||
).version;
|
||||
if (stagedNpmBraceExpansionVersion !== SAFE_NPM_BRACE_EXPANSION_VERSION) {
|
||||
fail(
|
||||
`staged npm brace-expansion is ${stagedNpmBraceExpansionVersion}; `
|
||||
+ `expected ${SAFE_NPM_BRACE_EXPANSION_VERSION}`,
|
||||
);
|
||||
}
|
||||
console.log(
|
||||
`[bundle-node] Hardened bundled npm with brace-expansion `
|
||||
+ `${SAFE_NPM_BRACE_EXPANSION_VERSION}`,
|
||||
);
|
||||
const safeNpmIpAddressManifest = path.join(safeNpmIpAddressSource, 'package.json');
|
||||
if (!fs.existsSync(safeNpmIpAddressManifest)) {
|
||||
fail('lock-installed ip-address hardening source is missing');
|
||||
}
|
||||
const safeNpmIpAddress = JSON.parse(
|
||||
fs.readFileSync(safeNpmIpAddressManifest, 'utf8'),
|
||||
);
|
||||
if (safeNpmIpAddress.version !== SAFE_NPM_IP_ADDRESS_VERSION) {
|
||||
fail(
|
||||
`lock-installed ip-address is ${safeNpmIpAddress.version}; `
|
||||
+ `expected ${SAFE_NPM_IP_ADDRESS_VERSION}`,
|
||||
);
|
||||
}
|
||||
const stagedNpmIpAddress = path.join(
|
||||
stagedRuntimeDir,
|
||||
'node_modules',
|
||||
'npm',
|
||||
'node_modules',
|
||||
'ip-address',
|
||||
);
|
||||
fs.rmSync(stagedNpmIpAddress, { recursive: true, force: true });
|
||||
fs.cpSync(safeNpmIpAddressSource, stagedNpmIpAddress, {
|
||||
recursive: true,
|
||||
dereference: true,
|
||||
});
|
||||
const stagedNpmIpAddressVersion = JSON.parse(
|
||||
fs.readFileSync(path.join(stagedNpmIpAddress, 'package.json'), 'utf8'),
|
||||
).version;
|
||||
if (stagedNpmIpAddressVersion !== SAFE_NPM_IP_ADDRESS_VERSION) {
|
||||
fail(
|
||||
`staged npm ip-address is ${stagedNpmIpAddressVersion}; `
|
||||
+ `expected ${SAFE_NPM_IP_ADDRESS_VERSION}`,
|
||||
);
|
||||
}
|
||||
console.log(
|
||||
`[bundle-node] Hardened bundled npm with ip-address ${SAFE_NPM_IP_ADDRESS_VERSION}`,
|
||||
);
|
||||
fs.writeFileSync(
|
||||
path.join(stagedRuntimeDir, 'package.json'),
|
||||
`${JSON.stringify({
|
||||
name: 'waggle-node-runtime',
|
||||
private: true,
|
||||
version: NODE_VERSION,
|
||||
description: 'Verified Node.js npm runtime staged for the Waggle desktop sidecar',
|
||||
}, null, 2)}\n`,
|
||||
'utf8',
|
||||
);
|
||||
writeWrappers();
|
||||
|
||||
const npmManifest = JSON.parse(
|
||||
fs.readFileSync(path.join(stagedRuntimeDir, 'node_modules', 'npm', 'package.json'), 'utf8'),
|
||||
);
|
||||
for (const cli of ['npm', 'npx']) {
|
||||
const cliPath = path.join(stagedRuntimeDir, 'node_modules', 'npm', 'bin', `${cli}-cli.js`);
|
||||
const version = execFileSync(destBinary, [cliPath, '--version'], {
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
}).trim();
|
||||
if (version !== npmManifest.version) {
|
||||
fail(`${cli} preflight returned ${version}; expected npm ${npmManifest.version}`);
|
||||
}
|
||||
}
|
||||
|
||||
const nodeSize = (fs.statSync(destBinary).size / 1024 / 1024).toFixed(1);
|
||||
const npmSize = (directorySizeBytes(stagedRuntimeDir) / 1024 / 1024).toFixed(1);
|
||||
console.log(
|
||||
`[bundle-node] Node.js v${NODE_VERSION} + npm v${npmManifest.version} `
|
||||
+ `(${platform}-${arch}) -> resources (${nodeSize} MB node, ${npmSize} MB npm runtime)`,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user