moving
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled

This commit is contained in:
Oleg Maslov
2026-09-02 10:10:29 +02:00
commit 0c3e2ead3b
3841 changed files with 970576 additions and 0 deletions

130
scripts/bundle-node.mjs Normal file
View File

@@ -0,0 +1,130 @@
#!/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.
*
* 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.
*/
import fs from 'node:fs';
import path from 'node:path';
import { execFileSync } from 'node:child_process';
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 NODE_VERSION = process.env.WAGGLE_BUNDLED_NODE_VERSION ?? process.versions.node;
if (!/^\d+\.\d+\.\d+$/.test(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.',
);
process.exit(1);
}
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);
}
const size = (fs.statSync(destBinary).size / 1024 / 1024).toFixed(1);
console.log(`[bundle-node] → ${destBinary} (${size} MB)`);
process.exit(0);
}
// 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}`);
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(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
fs.rmSync(extractDir, { recursive: true, force: true });
}
const size = (fs.statSync(destBinary).size / 1024 / 1024).toFixed(1);
console.log(`[bundle-node] Node.js v${NODE_VERSION} (${platform}-${arch}) → ${destBinary} (${size} MB)`);