'use strict'; /* * build-setup.js – eigenes Setup (installer/) mit der App zusammenbauen * --------------------------------------------------------------------- * Läuft nach "electron-builder --win dir" (siehe "build" in package.json): * * 1. dist/win-unpacked (die fertige App) als ZIP packen * 2. installer/ (WPF, .NET Framework 4.8) mit der Version aus package.json bauen * 3. ZIP hinten an die Setup-.exe hängen: * [Setup.exe][ZIP][8 Byte ZIP-Länge][8 Byte "AEROMCPK"] * (das Setup liest den Anhang selbst aus, siehe installer/Payload.cs) * 4. Zwischenstände wegräumen → übrig bleibt dist/AeroMC-Setup.exe * * Benötigt das .NET SDK (dotnet) – das Setup selbst läuft danach auf jedem * Windows 10/11 ohne Zusatzinstallation. */ const fs = require('fs'); const path = require('path'); const { execFileSync } = require('child_process'); const ROOT = path.join(__dirname, '..'); const DIST = path.join(ROOT, 'dist'); const APP_DIR = path.join(DIST, 'win-unpacked'); const WORK = path.join(DIST, '.setup'); const ZIP = path.join(WORK, 'app.zip'); const OUT = path.join(DIST, 'AeroMC-Setup.exe'); const MAGIC = Buffer.from('AEROMCPK', 'ascii'); function schritt(text) { console.log(`\n» ${text}`); } function main() { const version = require(path.join(ROOT, 'package.json')).version; if (!fs.existsSync(path.join(APP_DIR, 'AeroMC.exe'))) { throw new Error(`${APP_DIR}\\AeroMC.exe fehlt – vorher "electron-builder --win dir" ausführen.`); } fs.rmSync(WORK, { recursive: true, force: true }); fs.mkdirSync(WORK, { recursive: true }); schritt('App wird gepackt …'); // .NET-ZIP über PowerShell: in jedem Windows enthalten, keine zusätzliche npm-Abhängigkeit const ps = [ 'Add-Type -AssemblyName System.IO.Compression.FileSystem', `[System.IO.Compression.ZipFile]::CreateFromDirectory('${APP_DIR.replace(/'/g, "''")}', '${ZIP.replace(/'/g, "''")}', [System.IO.Compression.CompressionLevel]::Optimal, $false)`, ].join('; '); execFileSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', ps], { stdio: 'inherit' }); schritt(`Setup ${version} wird gebaut …`); const stubDir = path.join(WORK, 'stub'); execFileSync('dotnet', [ 'build', path.join(ROOT, 'installer', 'AeroMCSetup.csproj'), '-c', 'Release', `-p:Version=${version}`, '-p:DebugType=none', '--nologo', '-v', 'q', '-o', stubDir, ], { stdio: 'inherit' }); const stub = path.join(stubDir, 'AeroMC-Setup.exe'); if (!fs.existsSync(stub)) throw new Error('Build des Setups fehlgeschlagen.'); schritt('App wird ans Setup angehängt …'); const zipSize = fs.statSync(ZIP).size; const footer = Buffer.alloc(16); footer.writeBigInt64LE(BigInt(zipSize), 0); MAGIC.copy(footer, 8); fs.rmSync(OUT, { force: true }); fs.copyFileSync(stub, OUT); // ZIP in Blöcken anhängen statt komplett in den Speicher zu laden const fd = fs.openSync(OUT, 'a'); try { const src = fs.openSync(ZIP, 'r'); try { const buf = Buffer.alloc(8 * 1024 * 1024); let n; while ((n = fs.readSync(src, buf, 0, buf.length, null)) > 0) fs.writeSync(fd, buf, 0, n); } finally { fs.closeSync(src); } fs.writeSync(fd, footer); } finally { fs.closeSync(fd); } schritt('Aufräumen …'); for (const t of [WORK, APP_DIR, path.join(DIST, 'builder-debug.yml'), path.join(DIST, 'builder-effective-config.yaml')]) { fs.rmSync(t, { recursive: true, force: true }); } const mb = (fs.statSync(OUT).size / 1024 / 1024).toFixed(1); console.log(`\nFertig: ${OUT} (${mb} MB)`); } try { main(); } catch (err) { console.error(`\nFehler: ${err.message}`); process.exit(1); }