From c7e46f9f3dc9e11e596652701e3ba092f091f2e4 Mon Sep 17 00:00:00 2001 From: emozilla Date: Thu, 28 May 2026 03:05:26 -0400 Subject: [PATCH] fix(install.ps1): pre-warm electron-builder winCodeSign cache + fix Stage-Desktop $HasNode false-skip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two bugs caught in the second VM end-to-end run: 1. electron-builder's winCodeSign extraction fails on grandma-class Windows boxes because the .7z archive contains macOS symlinks (darwin/10.12/lib/libcrypto.dylib and libssl.dylib pointing at versioned siblings). Creating symlinks on Windows requires SeCreateSymbolicLinkPrivilege, a per-user right that non-admin accounts don't have on stock Windows. Result: every fresh install on a non-admin user fails Stage-Desktop with a 7-Zip 'cannot create symbolic link' error, retried four times, then bails. Fix: Initialize-ElectronBuilderCache pre-extracts winCodeSign-2.6.0.7z ourselves with -snl (don't preserve symlinks, store as resolved file content) AND -x!darwin (skip the entire macOS subtree — irrelevant on Windows). Writes to electron-builder's expected cache dir before electron-builder gets a chance to try its own broken extraction. Idempotent — fast-paths via signtool.exe sentinel check. 2. Install-Desktop's first guard was 'if (-not $HasNode) skip'. $HasNode is set by Stage-Node into $script:HasNode, but in cross-process driver mode (each -Stage NAME is a fresh powershell.exe spawned by Hermes-Setup.exe), that script-scope variable from the PREVIOUS process is invisible — so the guard always fired and Install-Desktop returned in 900ms with a misleading 'Node.js not available' reason. The real npm probe below it never got to run. Fix: re-probe npm directly via Get-Command when $HasNode is empty/false, since by that point Stage-Node has already verified Node is installed and the only question is whether *this* process can see it on PATH (it can — installer-wide PATH update from Stage-Node). --- scripts/install.ps1 | 119 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 116 insertions(+), 3 deletions(-) diff --git a/scripts/install.ps1 b/scripts/install.ps1 index bdee3ac5e1b..a596137a77e 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -1837,6 +1837,106 @@ function Install-NodeDeps { } } +function Initialize-ElectronBuilderCache { + # Pre-warm electron-builder's winCodeSign cache so it never tries to + # extract the .7z archive itself. + # + # The bug we're working around: winCodeSign-2.6.0.7z contains macOS + # symbolic links under darwin/10.12/lib/ (libcrypto.dylib, libssl.dylib + # pointing at versioned siblings). On Windows, 7-Zip's extraction of + # those symlinks requires SeCreateSymbolicLinkPrivilege — a per-user + # right that non-admin accounts on stock Windows don't have. So + # electron-builder fails the extraction every time it pulls a fresh + # cache, on every grandma-class box. + # + # The fix: do the extraction ourselves with `-x!darwin` to skip the + # entire macOS subtree. electron-builder is doing a WINDOWS build — + # it never reads anything under darwin/ on this code path. As long + # as the cache directory exists with the Windows-relevant files, + # electron-builder's "is the cache present?" check passes and it + # skips its own extraction entirely. + # + # Tooling: we use 7za.exe from the 7zip-bin npm package that + # electron-builder itself depends on (so we know it's present after + # the workspace `npm install` finishes). Falls back to no-op if + # neither is found — electron-builder will then attempt its own + # broken extraction and fail with a recognizable error. + + $cacheRoot = "$env:LOCALAPPDATA\electron-builder\Cache\winCodeSign" + $extractedDir = "$cacheRoot\winCodeSign-2.6.0" + $sentinel = "$extractedDir\windows-10\x64\signtool.exe" + + # Fast-path: already populated from a prior run. + if (Test-Path $sentinel) { + Write-Info "electron-builder winCodeSign cache already populated" + return + } + + # Locate 7za.exe. electron-builder hoists 7zip-bin to the workspace + # root node_modules so apps/desktop's pack step can find it. + $sevenZip = "$InstallDir\node_modules\7zip-bin\win\x64\7za.exe" + if (-not (Test-Path $sevenZip)) { + # Some npm versions don't hoist; check apps/desktop's local copy. + $sevenZip = "$InstallDir\apps\desktop\node_modules\7zip-bin\win\x64\7za.exe" + } + if (-not (Test-Path $sevenZip)) { + Write-Warn "7za.exe not found in node_modules; electron-builder may fail to extract winCodeSign" + Write-Warn " Looked at: $InstallDir\node_modules\7zip-bin\win\x64\7za.exe" + Write-Warn " and: $InstallDir\apps\desktop\node_modules\7zip-bin\win\x64\7za.exe" + return + } + + New-Item -ItemType Directory -Force -Path $cacheRoot | Out-Null + + # Download to a TEMP path; electron-builder doesn't care about the + # archive itself once the extracted dir exists. + $tmpArchive = "$env:TEMP\hermes-wincodesign-$(Get-Random).7z" + $url = "https://github.com/electron-userland/electron-builder-binaries/releases/download/winCodeSign-2.6.0/winCodeSign-2.6.0.7z" + + Write-Info "Pre-extracting winCodeSign to skip electron-builder's broken extraction..." + try { + Invoke-WebRequest -Uri $url -OutFile $tmpArchive -UseBasicParsing -ErrorAction Stop + } catch { + Write-Warn "Failed to download winCodeSign: $_" + Write-Warn " electron-builder will fall back to its own extraction (which fails on non-admin Windows)" + return + } + + # 7-Zip flags: + # x extract with full paths + # -y assume yes on all prompts + # -bd no progress bar (silent) + # -snl do NOT extract symbolic links as links — store as the + # resolved file content instead. This is what stops the + # privilege-not-held crash on the darwin/*.dylib symlinks. + # -x!darwin exclude the entire darwin/ subtree (macOS-specific + # code-signing tools that a Windows build doesn't need) + # -o output directory + # + # Belt-and-suspenders: we BOTH skip the darwin subtree AND tell 7-Zip + # to dereference any remaining symlinks. Either alone would fix it; + # together they're defensive against winCodeSign adding more symlinks + # under linux/ or appxAssets/ in some future release. + $prevEAP = $ErrorActionPreference + $ErrorActionPreference = "Continue" + & $sevenZip x -y -bd -snl "-x!darwin" "-o$cacheRoot" $tmpArchive 2>&1 | Out-Null + $code = $LASTEXITCODE + $ErrorActionPreference = $prevEAP + + Remove-Item -Force $tmpArchive -ErrorAction SilentlyContinue + + if ($code -ne 0) { + Write-Warn "Pre-extraction of winCodeSign failed (7-Zip exit $code)" + return + } + + if (Test-Path $sentinel) { + Write-Success "winCodeSign cache pre-populated at $extractedDir" + } else { + Write-Warn "winCodeSign extraction completed but expected file is missing: $sentinel" + } +} + function Install-Desktop { # Build apps/desktop into a launchable Hermes.exe. Only called from # Stage-Desktop, which is itself only included in the manifest when @@ -1856,9 +1956,15 @@ function Install-Desktop { # don't need to produce an NSIS/MSI artifact here. if (-not $HasNode) { - Write-Warn "Skipping desktop build (Node.js not installed)" - $script:_StageSkippedReason = "Node.js not available" - return + # Cross-process driver mode: each `-Stage NAME` invocation runs in a + # fresh PowerShell process, so $script:HasNode set by Stage-Node + # in the previous process isn't visible. Re-detect rather than + # trusting the global. + if (-not (Get-Command npm -ErrorAction SilentlyContinue)) { + Write-Warn "Skipping desktop build (Node.js / npm not on PATH)" + $script:_StageSkippedReason = "Node.js not available" + return + } } $desktopDir = "$InstallDir\apps\desktop" @@ -1913,6 +2019,13 @@ function Install-Desktop { } Pop-Location + # Pre-warm electron-builder's winCodeSign cache so its own broken + # 7-Zip extraction doesn't fire later. Has to happen AFTER the + # workspace npm install (we need 7za.exe from 7zip-bin) but BEFORE + # `npm run pack` (which triggers electron-builder's first cache miss). + # See Initialize-ElectronBuilderCache for the full rationale. + Initialize-ElectronBuilderCache + # 2. Build apps/desktop. `npm run pack` runs: # assert-root-install + write-build-stamp + stage-native-deps + # tsc -b + vite build + electron-builder --dir