feat(desktop): packaging (C2) : prepare-server, fetch-node, electron-builder
scripts/prepare-server.mjs : rebuild forcé shared+server (évite la non-idempotence du prepack), npm pack du daemon (tarball autonome), extraction, strip des devDependencies (dont @arboretum/shared inliné, non publié), npm install --omit=dev des deps runtime avec le bon prebuild node-pty (cross via npm_config_platform/arch). scripts/fetch-node.mjs : Node standalone épinglé (22.21.1) vérifié SHA256 -> build/node. electron-builder.yml : dist/ en ASAR ; build/server et build/node en extraResources (hors ASAR pour les .node) ; cibles Linux (AppImage+deb), Windows (nsis+portable), macOS (dmg/zip non signé) ; publish generic -> release Gitea. Scripts dist:linux/win/mac. Vérifié : fetch-node (Node bundlé + node:sqlite), prepare-server (structure complète), et la chaîne runtime complète (Node bundlé exécute le daemon empaqueté : handshake + session bash via node-pty OK). Le wrapping electron-builder est une étape machine.
This commit is contained in:
49
packages/desktop/scripts/fetch-node.mjs
Normal file
49
packages/desktop/scripts/fetch-node.mjs
Normal file
@@ -0,0 +1,49 @@
|
||||
// Télécharge un runtime Node standalone (>= 22.16, épinglé) dans build/node, avec vérification
|
||||
// SHA256. Le daemon tourne SUR ce Node (pas celui d'Electron) pour garantir node:sqlite sans flag
|
||||
// et l'ABI node-pty attendue (prefixe `node.`). Options : --platform / --arch (défaut : hôte).
|
||||
import { execFileSync } from 'node:child_process';
|
||||
import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { createHash } from 'node:crypto';
|
||||
import { join, dirname } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const NODE_VERSION = '22.21.1';
|
||||
const HERE = dirname(fileURLToPath(import.meta.url));
|
||||
const BUILD = join(HERE, '..', 'build');
|
||||
const NODE_DIR = join(BUILD, 'node');
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const arg = (name, def) => args.find((a) => a.startsWith(`--${name}=`))?.split('=')[1] ?? def;
|
||||
const platform = arg('platform', process.platform);
|
||||
const arch = arg('arch', process.arch);
|
||||
|
||||
const OS = { linux: 'linux', darwin: 'darwin', win32: 'win' }[platform];
|
||||
if (!OS) throw new Error(`plateforme non supportee: ${platform}`);
|
||||
const ext = platform === 'win32' ? 'zip' : 'tar.xz';
|
||||
const name = `node-v${NODE_VERSION}-${OS}-${arch}`;
|
||||
const base = `https://nodejs.org/dist/v${NODE_VERSION}`;
|
||||
|
||||
async function get(url) {
|
||||
const res = await fetch(url);
|
||||
if (!res.ok) throw new Error(`GET ${url} -> ${res.status}`);
|
||||
return res;
|
||||
}
|
||||
|
||||
rmSync(NODE_DIR, { recursive: true, force: true });
|
||||
mkdirSync(NODE_DIR, { recursive: true });
|
||||
|
||||
const tarball = Buffer.from(await (await get(`${base}/${name}.${ext}`)).arrayBuffer());
|
||||
const shasums = await (await get(`${base}/SHASUMS256.txt`)).text();
|
||||
const expected = shasums.split('\n').find((l) => l.endsWith(`${name}.${ext}`))?.split(/\s+/)[0];
|
||||
const actual = createHash('sha256').update(tarball).digest('hex');
|
||||
if (!expected) throw new Error(`SHA introuvable pour ${name}.${ext}`);
|
||||
if (expected !== actual) throw new Error(`SHA256 mismatch pour ${name}.${ext}`);
|
||||
|
||||
const archive = join(BUILD, `${name}.${ext}`);
|
||||
writeFileSync(archive, tarball);
|
||||
if (ext === 'zip') execFileSync('unzip', ['-q', archive, '-d', BUILD], { stdio: 'inherit' });
|
||||
else execFileSync('tar', ['-xJf', archive, '-C', BUILD], { stdio: 'inherit' });
|
||||
// aplatir node-vX-os-arch/ -> build/node/
|
||||
execFileSync('bash', ['-c', `cp -R "${join(BUILD, name)}/." "${NODE_DIR}/" && rm -rf "${join(BUILD, name)}" "${archive}"`], { stdio: 'inherit' });
|
||||
|
||||
console.log(`fetch-node: Node ${NODE_VERSION} (${OS}-${arch}) -> build/node`);
|
||||
Reference in New Issue
Block a user