From 338f0b22346202b6ab4312ec63c64cec92004b6a Mon Sep 17 00:00:00 2001 From: youngstar-eth <251514042+youngstar-eth@users.noreply.github.com> Date: Thu, 4 Jun 2026 17:44:22 +0300 Subject: [PATCH] fix(desktop): recover from corrupt Electron cache in bootstrap install (Windows) Windows counterpart of #39127: scripts/install.ps1 `Install-Desktop` runs `npm run pack` once and throws on the opaque ENOENT a corrupt cached Electron download produces, with no recovery. Add `Clear-ElectronBuildCache` plus a purge-and-retry-once on pack failure, mirroring the install.sh fix: remove the cached electron-*.zip (%LOCALAPPDATA%\electron\Cache + ELECTRON_CACHE / electron_config_cache overrides) and stale *-unpacked output, then retry so @electron/get re-downloads with its own SHASUM verification. Refs #37544. Co-Authored-By: Claude Opus 4.8 --- scripts/install.ps1 | 65 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/scripts/install.ps1 b/scripts/install.ps1 index d295749eb..c6179e1b6 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -1934,6 +1934,55 @@ function Install-NodeDeps { } } +# Clear the cached Electron download + any half-written unpacked output so the +# next `npm run pack` re-downloads and re-stages from scratch. A corrupt zip in +# the per-user Electron download cache - most often a partial download resumed +# into the same file, leaving concatenated junk - makes electron-builder's +# `app-builder unpack-electron` extract a tree MISSING the electron binary, so +# the final `electron` -> `Hermes` rename dies with ENOENT and every re-run +# repeats the broken extraction forever. +# +# We deliberately do not validate the zip ourselves: the common +# prepended/concatenated-junk corruption slips past naive checks, so a +# self-rolled gate would skip the real-world case. We unconditionally drop the +# cached electron-*.zip (loose copy and any @electron/get hash-subdir copy) plus +# the stale unpacked dir, then let the caller retry once - @electron/get +# re-downloads with its own SHASUM verification, the real source of truth. +# +# Returns the removed paths. Best-effort: never throws. +function Clear-ElectronBuildCache { + param([string]$DesktopDir) + $removed = @() + + # Per-user Electron download cache dirs, honoring the overrides @electron/get + # respects, then the Windows default (%LOCALAPPDATA%\electron\Cache). + $cacheDirs = @() + if ($env:electron_config_cache) { $cacheDirs += $env:electron_config_cache } + if ($env:ELECTRON_CACHE) { $cacheDirs += $env:ELECTRON_CACHE } + if ($env:LOCALAPPDATA) { $cacheDirs += (Join-Path $env:LOCALAPPDATA 'electron\Cache') } + $cacheDirs += (Join-Path $HOME 'AppData\Local\electron\Cache') + + foreach ($dir in $cacheDirs) { + if (-not (Test-Path -LiteralPath $dir)) { continue } + # Recurse: the bad copy may be the top-level zip OR a copy inside an + # @electron/get hash subdir. + $removed += @(Get-ChildItem -LiteralPath $dir -Recurse -Filter 'electron-*.zip' -File -ErrorAction SilentlyContinue | ForEach-Object { + try { Remove-Item -LiteralPath $_.FullName -Force -ErrorAction Stop; $_.FullName } catch { } + }) + } + + # A half-written unpacked dir from an interrupted prior pack poisons the + # rename even after the zip is fixed (win-unpacked / win-arm64-unpacked). + $releaseDir = Join-Path $DesktopDir 'release' + if (Test-Path -LiteralPath $releaseDir) { + $removed += @(Get-ChildItem -LiteralPath $releaseDir -Directory -Filter '*-unpacked' -ErrorAction SilentlyContinue | ForEach-Object { + try { Remove-Item -LiteralPath $_.FullName -Recurse -Force -ErrorAction Stop; $_.FullName } catch { } + }) + } + + return $removed +} + 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 @@ -2067,6 +2116,22 @@ function Install-Desktop { $env:WIN_CSC_KEY_PASSWORD = "" & $npmExe run pack 2>&1 | ForEach-Object { "$_" } | Tee-Object -FilePath $buildLog $code = $LASTEXITCODE + if ($code -ne 0) { + # A corrupt cached Electron zip makes `pack` fail with an opaque + # ENOENT on the final `electron` -> `Hermes` rename: app-builder's + # unpack-electron extracted a partial tree (missing the binary) from + # the bad zip, and re-running reuses the poisoned cache forever. + # Purge the cached download + any stale unpacked output and retry + # once; @electron/get re-downloads with its own SHASUM check. Without + # this a corrupt download hard-fails the whole installer. + $purged = @(Clear-ElectronBuildCache -DesktopDir $desktopDir) + if ($purged.Count -gt 0) { + Write-Warn "Desktop build failed - cleared cached Electron download, retrying once:" + foreach ($p in $purged) { Write-Info " - $p" } + & $npmExe run pack 2>&1 | ForEach-Object { "$_" } | Tee-Object -FilePath $buildLog + $code = $LASTEXITCODE + } + } $ErrorActionPreference = $prevEAP if ($code -ne 0) { $errText = Get-Content $buildLog -Raw -ErrorAction SilentlyContinue