Desktop Windows Build #45
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Desktop Windows Build | |
| on: | |
| workflow_dispatch: | |
| jobs: | |
| build-windows-exe: | |
| runs-on: windows-latest | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v4 | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 22 | |
| cache: pnpm | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| # FNXC:DesktopEmbeddedPostgres 2026-07-14-09:39: | |
| # The manual Windows installer path must boot the same embedded database | |
| # payload used by Local mode before it can publish an installer artifact. | |
| - name: Smoke embedded Postgres on Windows | |
| run: pnpm --filter @fusion/core test:embedded-postgres | |
| # FNXC:WindowsDesktopPackaging 2026-07-01-19:45: | |
| # Mirror release.yml: build every workspace package's tsc dist (incl. | |
| # @fusion/core and @fusion/engine, which are gitignored) before packaging. | |
| # Without this the embedded Local runtime's `import("@fusion/engine")` | |
| # resolves to an empty dist and the app crashes with ERR_MODULE_NOT_FOUND. | |
| # `@fusion/desktop build` now also self-builds these, so this is belt-and- | |
| # suspenders parity that additionally covers any other workspace runtime dep. | |
| - name: Build workspace | |
| run: pnpm build | |
| - name: Build desktop package | |
| run: pnpm --filter @fusion/desktop build | |
| # Code-signing hardening is intentionally deferred to FN-5592. | |
| - name: Package signed Windows EXE | |
| if: ${{ env.WINDOWS_CERTIFICATE_BASE64 != '' }} | |
| run: pnpm --filter @fusion/desktop exec electron-builder --projectDir deploy --win --x64 --publish never | |
| env: | |
| WINDOWS_CERTIFICATE_BASE64: ${{ secrets.WINDOWS_CERTIFICATE_BASE64 }} | |
| CSC_LINK: ${{ secrets.WINDOWS_CERTIFICATE_BASE64 }} | |
| CSC_KEY_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }} | |
| CSC_IDENTITY_AUTO_DISCOVERY: "false" | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Package unsigned Windows EXE | |
| if: ${{ env.WINDOWS_CERTIFICATE_BASE64 == '' }} | |
| run: pnpm --filter @fusion/desktop exec electron-builder --projectDir deploy --win --x64 --publish never | |
| env: | |
| WINDOWS_CERTIFICATE_BASE64: ${{ secrets.WINDOWS_CERTIFICATE_BASE64 }} | |
| CSC_IDENTITY_AUTO_DISCOVERY: "false" | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Verify signed artifacts | |
| if: ${{ env.WINDOWS_CERTIFICATE_BASE64 != '' }} | |
| shell: pwsh | |
| env: | |
| WINDOWS_CERTIFICATE_BASE64: ${{ secrets.WINDOWS_CERTIFICATE_BASE64 }} | |
| run: | | |
| $exes = Get-ChildItem packages/desktop/dist-electron -Filter *.exe | |
| if ($exes.Count -eq 0) { Write-Error "No EXE artifacts produced"; exit 1 } | |
| foreach ($exe in $exes) { | |
| $sig = Get-AuthenticodeSignature $exe.FullName | |
| Write-Host "$($exe.Name): $($sig.Status)" | |
| if ($sig.Status -ne 'Valid') { Write-Error "Signature invalid: $($exe.Name) ($($sig.Status))"; exit 1 } | |
| } | |
| - name: Verify Windows runtime resources | |
| shell: pwsh | |
| run: | | |
| # FNXC:WindowsDesktopPackaging 2026-07-01-08:08: | |
| # The Windows app must install Electron's root .pak runtime resources; | |
| # missing chrome_100_percent.pak, chrome_200_percent.pak, or resources.pak | |
| # leaves Fusion.exe unable to start even when the NSIS installer succeeds. | |
| $requiredResources = @('chrome_100_percent.pak', 'chrome_200_percent.pak', 'resources.pak') | |
| $unpackedRoots = Get-ChildItem packages/desktop/dist-electron -Directory -Filter 'win*-unpacked' | |
| if ($unpackedRoots.Count -eq 0) { Write-Error "No win-unpacked directory produced"; exit 1 } | |
| foreach ($root in $unpackedRoots) { | |
| foreach ($resource in $requiredResources) { | |
| $resourcePath = Join-Path $root.FullName $resource | |
| if (!(Test-Path $resourcePath)) { Write-Error "Missing Electron runtime resource: $resourcePath"; exit 1 } | |
| } | |
| } | |
| $nsis = Get-ChildItem packages/desktop/dist-electron -Filter 'Fusion-*-win-*.exe' | Where-Object { $_.Name -notmatch '-portable\.exe$' } | |
| $portable = Get-ChildItem packages/desktop/dist-electron -Filter 'Fusion-*-win-*-portable.exe' | |
| if ($nsis.Count -eq 0) { Write-Error "No NSIS installer artifact produced"; exit 1 } | |
| if ($portable.Count -eq 0) { Write-Error "No portable EXE artifact produced"; exit 1 } | |
| - name: Verify packaged app.asar assets | |
| shell: pwsh | |
| run: | | |
| # FNXC:WindowsDesktopPackaging 2026-07-03-15:40: | |
| # Field report Issue 5: the packaged desktop shipped without preload.js and | |
| # dead-ended on "can't reach the Fusion backend" (preload absence is silent — | |
| # the contextBridge never installs window.fusionShell/fusionAPI). scripts/build.ts | |
| # verifies the pre-package staging tree; this asserts the SHIPPED app.asar itself | |
| # contains the Electron main/preload/renderer entrypoints, since only the packed | |
| # asar reflects what a user installs. | |
| $required = @('dist/main.js', 'dist/preload.js', 'dist/client/index.html') | |
| $unpackedRoots = Get-ChildItem packages/desktop/dist-electron -Directory -Filter 'win*-unpacked' | |
| if ($unpackedRoots.Count -eq 0) { Write-Error "No win-unpacked directory produced"; exit 1 } | |
| foreach ($root in $unpackedRoots) { | |
| $asar = Join-Path $root.FullName 'resources/app.asar' | |
| if (!(Test-Path $asar)) { Write-Error "Missing packaged app.asar: $asar"; exit 1 } | |
| $entries = npx --yes @electron/asar list $asar | |
| if ($LASTEXITCODE -ne 0) { Write-Error "Failed to list app.asar: $asar"; exit 1 } | |
| $normalized = $entries | ForEach-Object { $_.TrimStart('/','\').Replace('\','/') } | |
| foreach ($asset in $required) { | |
| if ($normalized -notcontains $asset) { | |
| Write-Error "app.asar is missing required Electron asset '$asset' in $($root.Name); refusing to ship an incomplete package" | |
| exit 1 | |
| } | |
| } | |
| Write-Host "$($root.Name)/resources/app.asar contains all required Electron assets" | |
| } | |
| # Automated publish is intentionally deferred to FN-5593. | |
| # Keep a single artifact; filenames include -x64 / -arm64 so both arches are captured. | |
| - name: Upload Windows artifacts | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: fusion-desktop-windows | |
| path: | | |
| packages/desktop/dist-electron/*.exe | |
| packages/desktop/dist-electron/*.blockmap |