<# .SYNOPSIS Build the Windows installer: Electron client + bundled Python node. .DESCRIPTION The Windows counterpart of packaging/build/build-client.sh. Produces a single per-user NSIS installer (MeshBay-Setup-.exe) that lays down the Electron app and, beside it under resources/node-runtime/, the frozen meshbay-node daemon. No hub -- a desktop machine installs client + node (+ common, which is inside the node runtime). Steps: 1. Node >= 22 check 2. npm ci (+ approve Electron's install script, download Chromium) 3. bump Electron to the latest release (Chromium CVE policy -- see build-client.sh; skip with -NoElectronBump) 4. npm run sync-ui (copy the interface from the hub package) 5. build-node-runtime.ps1 (PyInstaller freeze of the daemon) 6. electron-builder --win nsis .PARAMETER FfmpegDir Passed through to build-node-runtime.ps1 -- directory with ffmpeg.exe / ffprobe.exe to bundle. Optional; without it the node uses PATH. .PARAMETER NoElectronBump Keep the pinned Electron instead of upgrading to the latest release. .PARAMETER SkipNodeRuntime Reuse an existing packages/meshbay-client/node-runtime/ (faster iteration on the electron-builder side). #> [CmdletBinding()] param( [string]$FfmpegDir = $env:MESHBAY_FFMPEG_DIR, [switch]$NoElectronBump, [switch]$SkipNodeRuntime ) $ErrorActionPreference = "Stop" Set-StrictMode -Version Latest $WinDir = $PSScriptRoot $Repo = (Resolve-Path (Join-Path $WinDir "..\..")).Path $Client = Join-Path $Repo "packages\meshbay-client" function Step($msg) { Write-Host "==> $msg" -ForegroundColor Cyan } # --- 1. Node ------------------------------------------------------------- if (-not (Get-Command node -ErrorAction SilentlyContinue)) { throw "Node.js not found. Install Node 22 or newer from nodejs.org." } $nodeMajor = [int](& node -e "process.stdout.write(String(process.versions.node.split('.')[0]))") if ($nodeMajor -lt 22) { throw "Node $nodeMajor is too old -- need 22 or newer for Electron" } Step "Node $(& node --version)" Push-Location $Client try { # --- 2. deps ------------------------------------------------------ Step "npm ci" & npm ci --ignore-scripts if ($LASTEXITCODE -ne 0) { throw "npm ci failed" } # --- 3. Electron: build against the latest release -------------- # Writes package.json + package-lock.json, so the build leaves the repo # dirty on purpose -- commit the new pin. if (-not $NoElectronBump) { Step "checking for a newer Electron" $bumped = & node (Join-Path $WinDir "bump-electron.mjs") if ($LASTEXITCODE -ne 0) { throw "electron bump failed" } if ($bumped) { Write-Host " Electron -> $bumped (package.json + lock updated, commit them)" -ForegroundColor Yellow } else { Write-Host " Electron is already current" } } Step "downloading Electron's Chromium" & npm approve-scripts electron 2>$null & node node_modules/electron/install.js # --- 4. UI ----------------------------------------------------- Step "npm run sync-ui" & npm run sync-ui if ($LASTEXITCODE -ne 0) { throw "sync-ui failed" } # --- 5. node runtime --------------------------------------- $rtExe = Join-Path $Client "node-runtime\meshbay-node.exe" if ($SkipNodeRuntime -and (Test-Path $rtExe)) { Step "reusing existing node-runtime/" } else { Step "building the bundled node (PyInstaller)" $rtArgs = @{} if ($FfmpegDir) { $rtArgs["FfmpegDir"] = $FfmpegDir } & (Join-Path $WinDir "build-node-runtime.ps1") @rtArgs if ($LASTEXITCODE -ne 0) { throw "build-node-runtime.ps1 failed" } } # --- 6. installer --------------------------------------- Step "electron-builder --win nsis" & npx electron-builder --win nsis if ($LASTEXITCODE -ne 0) { throw "electron-builder failed" } } finally { Pop-Location } $setup = Get-ChildItem (Join-Path $Client "dist") -Filter "*Setup*.exe" -ErrorAction SilentlyContinue | Sort-Object LastWriteTime | Select-Object -Last 1 Write-Host "" if ($setup) { Write-Host "OK installer: $($setup.FullName)" -ForegroundColor Green Write-Host (" ({0:N0} MB)" -f ($setup.Length / 1MB)) } else { Write-Host "!! no *Setup*.exe found in $Client\dist" -ForegroundColor Red exit 1 }