Skip to content

Commit f4b1ad4

Browse files
authored
fix(build): resolve Windows auto-update checksum failure (#1790)
The Squirrel.Windows updater was failing with "Checksummed file size doesn't match" because the RELEASES file on the CDN referenced nupkg files from older versions that no longer existed (HTTP 403). Root cause: `remoteReleases` in maker-squirrel fetched the CDN's existing RELEASES and appended new entries, accumulating historical references. But `aws s3 sync --delete` in the release workflow correctly removed old nupkgs from the `latest/` path, creating a mismatch between RELEASES entries and actual files. Changes: - Remove `remoteReleases` and set `noDelta: true` so each build produces a RELEASES file with only the current version's entry - Add `postMake` hook to strip UTF-8 BOM from RELEASES files (defense-in-depth against a known electron-winstaller issue) Made-with: Cursor
1 parent df6b313 commit f4b1ad4

2 files changed

Lines changed: 46 additions & 2 deletions

File tree

forge.config.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { ensureThv } from './utils/fetch-thv'
1111
import MakerTarGz from './utils/forge-makers/MakerTarGz'
1212
import MakerDMGWithArch from './utils/forge-makers/MakerDMGWithArch'
1313
import { isPrerelease } from './utils/pre-release'
14+
import { stripBomFromReleasesFiles } from './utils/forge-makers/strip-bom-from-releases'
1415
import packageJson from './package.json'
1516

1617
function isValidPlatform(platform: string): platform is NodeJS.Platform {
@@ -112,14 +113,14 @@ const config: ForgeConfig = {
112113
makers: [
113114
{
114115
name: '@electron-forge/maker-squirrel',
115-
config: (arch: string) => ({
116+
config: () => ({
116117
setupIcon: './icons/icon.ico',
117118
setupExe: 'ToolHive Setup.exe',
118119
noMsi: true,
119120
authors: 'Stacklok',
120121
exe: 'ToolHive.exe',
121122
name: 'ToolHive',
122-
remoteReleases: `https://releases.toolhive.dev/${isPrerelease() ? 'pre-release' : 'stable'}/latest/win32/${arch}`,
123+
noDelta: true,
123124
windowsSign:
124125
process.env.SM_HOST && process.env.SM_API_KEY
125126
? { hookModulePath: './utils/digicert-hook.js' }
@@ -217,6 +218,10 @@ const config: ForgeConfig = {
217218
],
218219

219220
hooks: {
221+
postMake: async (_config, makeResults) => {
222+
await stripBomFromReleasesFiles(makeResults)
223+
return makeResults
224+
},
220225
// copy sqlite deps that already compiled
221226
packageAfterCopy: async (_config, buildPath) => {
222227
const fs = await import('node:fs')
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { promises as fs } from 'node:fs'
2+
import type { ForgeMakeResult } from '@electron-forge/shared-types'
3+
4+
const UTF8_BOM = Buffer.from([0xef, 0xbb, 0xbf])
5+
6+
/**
7+
* Strips the UTF-8 BOM from RELEASES files produced by Squirrel.Windows.
8+
*
9+
* electron-winstaller generates RELEASES files with a UTF-8 BOM prefix (EF BB BF).
10+
* Squirrel.Windows fails to parse BOM-prefixed RELEASES when computing checksums,
11+
* causing "Checksummed file size doesn't match" errors during client updates.
12+
*/
13+
export async function stripBomFromReleasesFiles(
14+
makeResults: ForgeMakeResult[]
15+
): Promise<void> {
16+
for (const result of makeResults) {
17+
const releasesFiles = result.artifacts.filter((f) => f.endsWith('RELEASES'))
18+
19+
for (const releasesPath of releasesFiles) {
20+
try {
21+
const content = await fs.readFile(releasesPath)
22+
23+
if (
24+
content.length >= 3 &&
25+
content[0] === UTF8_BOM[0] &&
26+
content[1] === UTF8_BOM[1] &&
27+
content[2] === UTF8_BOM[2]
28+
) {
29+
await fs.writeFile(releasesPath, content.subarray(3))
30+
console.log(`[postMake] Stripped UTF-8 BOM from ${releasesPath}`)
31+
}
32+
} catch (error) {
33+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
34+
console.warn(`[postMake] Could not process RELEASES file: ${error}`)
35+
}
36+
}
37+
}
38+
}
39+
}

0 commit comments

Comments
 (0)