@@ -16,12 +16,32 @@ const { spawn } = require("child_process");
1616const { pathToFileURL } = require ( "url" ) ;
1717
1818const UPDATE_GITHUB_OWNER = "HyperlinksSpace" ;
19- const UPDATE_GITHUB_REPO = "HyperlinksSpaceBot" ;
19+ /** Must match `build.publish.repo` and the repo where CI uploads releases. */
20+ const UPDATE_GITHUB_REPO = "HyperlinksSpaceProgram" ;
2021const ZIP_LATEST_YML = "zip-latest.yml" ;
2122/** Same pattern as package.json build.win.artifactName for the zip target. */
2223const WIN_PORTABLE_ZIP_PREFIX = "HyperlinksSpaceApp_" ;
2324const LATEST_YML = "latest.yml" ;
2425
26+ function sleep ( ms ) {
27+ return new Promise ( ( resolve ) => setTimeout ( resolve , ms ) ) ;
28+ }
29+
30+ /** GitHub sometimes returns 502 HTML (Unicorn) or other gateway errors; worth retrying. */
31+ function isTransientGithubUpdateError ( err ) {
32+ if ( ! err ) return false ;
33+ const code = err . statusCode ?? err . status ;
34+ if ( code === 502 || code === 503 || code === 504 ) return true ;
35+ const msg = String ( err . message || err ) ;
36+ if (
37+ / \b 5 0 2 \b | \b 5 0 3 \b | \b 5 0 4 \b | B a d G a t e w a y | S e r v i c e U n a v a i l a b l e | G a t e w a y T i m e o u t | t a k i n g t o o l o n g | E C O N N R E S E T | E T I M E D O U T / i. test (
38+ msg ,
39+ )
40+ )
41+ return true ;
42+ return false ;
43+ }
44+
2545/**
2646 * Prefer zip-latest.yml (has sha512 for the zip). If missing (404), use latest.yml + inferred zip name.
2747 * @returns {{ version: string, fileName: string, sha512: string | null, source: string } }
@@ -308,6 +328,25 @@ function setupAutoUpdater() {
308328 const { autoUpdater } = require ( "electron-updater" ) ;
309329 let manualCheckInProgress = false ;
310330 let manualDownloadInProgress = false ;
331+ let updaterCheckRetrying = false ;
332+
333+ const checkForUpdatesWithRetry = async ( attempts = 4 ) => {
334+ let lastErr ;
335+ for ( let i = 0 ; i < attempts ; i ++ ) {
336+ try {
337+ return await autoUpdater . checkForUpdates ( ) ;
338+ } catch ( e ) {
339+ lastErr = e ;
340+ if ( ! isTransientGithubUpdateError ( e ) || i === attempts - 1 ) throw e ;
341+ const delayMs = 1500 * 2 ** i ;
342+ log (
343+ `[updater] transient GitHub/update error (${ i + 1 } /${ attempts } ), retry in ${ delayMs } ms: ${ e ?. message || e } ` ,
344+ ) ;
345+ await sleep ( delayMs ) ;
346+ }
347+ }
348+ throw lastErr ;
349+ } ;
311350 const currentVersion = app . getVersion ( ) ;
312351 const currentVersionHtml = String ( currentVersion )
313352 . replace ( / & / g, "&" )
@@ -887,6 +926,9 @@ function setupAutoUpdater() {
887926
888927 autoUpdater . on ( "error" , ( err ) => {
889928 log ( `[updater] error: ${ err ?. message || String ( err ) } ` ) ;
929+ if ( updaterCheckRetrying && isTransientGithubUpdateError ( err ) ) {
930+ return ;
931+ }
890932 if ( manualCheckInProgress || manualDownloadInProgress ) {
891933 manualCheckInProgress = false ;
892934 manualDownloadInProgress = false ;
@@ -925,7 +967,12 @@ function setupAutoUpdater() {
925967 showActions : false ,
926968 installEnabled : false ,
927969 } ) ;
928- await autoUpdater . checkForUpdates ( ) ;
970+ updaterCheckRetrying = true ;
971+ try {
972+ await checkForUpdatesWithRetry ( ) ;
973+ } finally {
974+ updaterCheckRetrying = false ;
975+ }
929976 } catch ( e ) {
930977 manualCheckInProgress = false ;
931978 manualDownloadInProgress = false ;
@@ -953,7 +1000,16 @@ function setupAutoUpdater() {
9531000 const markAndCheck = ( ) => {
9541001 lastCheckAt = Date . now ( ) ;
9551002 log ( "[updater] scheduled checkForUpdates()" ) ;
956- void autoUpdater . checkForUpdates ( ) ;
1003+ void ( async ( ) => {
1004+ updaterCheckRetrying = true ;
1005+ try {
1006+ await checkForUpdatesWithRetry ( ) ;
1007+ } catch ( e ) {
1008+ log ( `[updater] checkForUpdates failed after retries: ${ e ?. message || e } ` ) ;
1009+ } finally {
1010+ updaterCheckRetrying = false ;
1011+ }
1012+ } ) ( ) ;
9571013 } ;
9581014
9591015 // 1) On startup (each app launch)
0 commit comments