diff --git a/.github/workflows/release-windows.yml b/.github/workflows/release-windows.yml index a1647600..cfaff05b 100644 --- a/.github/workflows/release-windows.yml +++ b/.github/workflows/release-windows.yml @@ -165,16 +165,47 @@ jobs: run: | node scripts/ci/verify-signature.js --require-signed --target electron/dist/win-unpacked/OpenSwarm.exe if ($LASTEXITCODE -ne 0) { throw "inner OpenSwarm.exe is not validly signed" } - node scripts/ci/verify-signature.js --require-signed --target electron/dist/OpenSwarm-Setup-x64.exe + # Squirrel writes Setup.exe into dist\squirrel-windows\, not dist\ root. + node scripts/ci/verify-signature.js --require-signed --target electron/dist/squirrel-windows/OpenSwarm-Setup-x64.exe if ($LASTEXITCODE -ne 0) { throw "OpenSwarm-Setup-x64.exe (installer) is not validly signed" } + # The squirrel target emits RELEASES + nupkg + Setup.exe but NO latest.yml. + # Existing NSIS clients poll latest.yml; without it they never see the + # update and are stranded on the old build. Generate it next to the Setup so + # both client kinds are served by the one release. + - name: Generate latest.yml for the Squirrel installer + shell: pwsh + run: | + $ErrorActionPreference = 'Stop' + $version = (Get-Content electron/package.json | ConvertFrom-Json).version + $setup = 'electron/dist/squirrel-windows/OpenSwarm-Setup-x64.exe' + pwsh -NoProfile -File scripts\gen-squirrel-latest-yml.ps1 -SetupPath $setup -Version $version -OutPath 'electron/dist/squirrel-windows/latest.yml' + Get-Content 'electron/dist/squirrel-windows/latest.yml' + + # electron-builder published Setup + RELEASES + nupkg to the draft release; + # attach the latest.yml it cannot emit so NSIS clients can migrate. + - name: Upload latest.yml to the release (publish runs) + if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.publish == 'true') + shell: pwsh + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + $ErrorActionPreference = 'Stop' + $version = (Get-Content electron/package.json | ConvertFrom-Json).version + $tag = "v$version" + gh release upload $tag electron/dist/squirrel-windows/latest.yml --clobber + if ($LASTEXITCODE -ne 0) { throw "failed to upload latest.yml to $tag" } + Write-Host "Attached latest.yml to release $tag (NSIS clients can now migrate)" + - name: Upload artifact (non-publish runs) if: github.event_name == 'workflow_dispatch' && github.event.inputs.publish != 'true' uses: actions/upload-artifact@v4 with: name: openswarm-windows-x64 path: | - electron/dist/*.exe - electron/dist/latest.yml + electron/dist/squirrel-windows/*.exe + electron/dist/squirrel-windows/RELEASES + electron/dist/squirrel-windows/*.nupkg + electron/dist/squirrel-windows/latest.yml if-no-files-found: error retention-days: 14 diff --git a/scripts/gen-squirrel-latest-yml.ps1 b/scripts/gen-squirrel-latest-yml.ps1 new file mode 100644 index 00000000..2f153d53 --- /dev/null +++ b/scripts/gen-squirrel-latest-yml.ps1 @@ -0,0 +1,47 @@ +# Generates an electron-updater latest.yml for a Squirrel.Windows build. +# +# Squirrel's electron-builder target emits RELEASES + .nupkg + Setup.exe but NO +# latest.yml. Existing NSIS clients poll latest.yml, so without this file every +# already-installed NSIS user is silently stranded on the old build and never +# migrates. This writes the latest.yml that points those clients at the Squirrel +# Setup.exe (electron-updater runs it with `--updated /S --force-run`, which the +# migration proof confirmed installs Squirrel and triggers the firstrun cleanup). +param( + [Parameter(Mandatory = $true)][string]$SetupPath, + [Parameter(Mandatory = $true)][string]$Version, + [Parameter(Mandatory = $true)][string]$OutPath +) +$ErrorActionPreference = 'Stop' + +if (-not (Test-Path $SetupPath)) { throw "Setup not found: $SetupPath" } + +$fileName = Split-Path $SetupPath -Leaf + +# electron-updater wants base64(sha512(file)), not hex. Stream it so a ~560MB +# installer doesn't get slurped whole into memory. +$sha = [System.Security.Cryptography.SHA512]::Create() +$fs = [System.IO.File]::OpenRead($SetupPath) +try { $hashBytes = $sha.ComputeHash($fs) } finally { $fs.Dispose(); $sha.Dispose() } +$sha512 = [System.Convert]::ToBase64String($hashBytes) +$size = (Get-Item $SetupPath).Length +$date = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffZ") + +$yml = @" +version: $Version +files: + - url: $fileName + sha512: $sha512 + size: $size +path: $fileName +sha512: $sha512 +releaseDate: '$date' +"@ + +# electron-updater parses LF yaml; force LF + UTF-8 without BOM so the parser +# doesn't choke on a leading 0xEF 0xBB 0xBF. +$yml = $yml -replace "`r`n", "`n" +[System.IO.File]::WriteAllText($OutPath, $yml, (New-Object System.Text.UTF8Encoding($false))) + +Write-Host "Wrote $OutPath" +Write-Host " version=$Version file=$fileName size=$size" +Write-Host " sha512=$($sha512.Substring(0,24))..."