@@ -11,6 +11,58 @@ const { Octokit } = require("@octokit/rest");
1111const { got } = require ( "got" ) ;
1212const sumchecker = require ( 'sumchecker' ) ;
1313
14+ const TRANSIENT_NETWORK_ERROR_CODES = new Set ( [
15+ "ETIMEDOUT" ,
16+ "ECONNRESET" ,
17+ "ECONNREFUSED" ,
18+ "EPIPE" ,
19+ "ENOTFOUND" ,
20+ "ENETUNREACH" ,
21+ "EAI_AGAIN" ,
22+ "EHOSTUNREACH" ,
23+ ] ) ;
24+
25+ function isTransientNetworkError ( err ) {
26+ if ( ! err ) {
27+ return false ;
28+ }
29+ if ( err . code && TRANSIENT_NETWORK_ERROR_CODES . has ( err . code ) ) {
30+ return true ;
31+ }
32+ // got wraps the underlying cause; check it as well.
33+ if ( err . cause && err . cause . code && TRANSIENT_NETWORK_ERROR_CODES . has ( err . cause . code ) ) {
34+ return true ;
35+ }
36+ return false ;
37+ }
38+
39+ async function withRetry ( name , fn , { retries = 5 , baseDelayMs = 2000 } = { } ) {
40+ let attempt = 0 ;
41+ for ( ; ; ) {
42+ try {
43+ return await fn ( ) ;
44+ } catch ( err ) {
45+ attempt ++ ;
46+ if ( attempt > retries || ! isTransientNetworkError ( err ) ) {
47+ throw err ;
48+ }
49+ const delay = Math . min ( baseDelayMs * Math . pow ( 2 , attempt - 1 ) , 30000 ) ;
50+ const code = ( err && err . code ) || ( err . cause && err . cause . code ) || "unknown" ;
51+ console . warn (
52+ `[gulp-electron] ${ name } failed with ${ code } (attempt ${ attempt } /${ retries } ); retrying in ${ delay } ms...`
53+ ) ;
54+ await new Promise ( ( resolve ) => setTimeout ( resolve , delay ) ) ;
55+ }
56+ }
57+ }
58+
59+ const GOT_RETRY_OPTIONS = {
60+ limit : 5 ,
61+ methods : [ "GET" , "HEAD" ] ,
62+ errorCodes : Array . from ( TRANSIENT_NETWORK_ERROR_CODES ) ,
63+ backoffLimit : 30000 ,
64+ } ;
65+
1466async function getDownloadUrl (
1567 ownerRepo , customTag ,
1668 { version, platform, arch, token, artifactName, artifactSuffix }
@@ -61,11 +113,14 @@ async function getDownloadUrl(
61113 const { url, headers } = requestOptions ;
62114 headers . authorization = `token ${ token } ` ;
63115
64- const response = await got ( url , {
65- followRedirect : false ,
66- method : "HEAD" ,
67- headers,
68- } ) ;
116+ const response = await withRetry ( "HEAD release asset" , ( ) =>
117+ got ( url , {
118+ followRedirect : false ,
119+ method : "HEAD" ,
120+ headers,
121+ retry : GOT_RETRY_OPTIONS ,
122+ } )
123+ ) ;
69124
70125 return response . headers . location ;
71126}
@@ -119,7 +174,9 @@ async function download(opts) {
119174 ) ;
120175
121176 if ( opts . repo ) {
122- const url = await getDownloadUrl ( opts . repo , opts . tag , downloadOpts ) ;
177+ const url = await withRetry ( "resolve release asset URL" , ( ) =>
178+ getDownloadUrl ( opts . repo , opts . tag , downloadOpts )
179+ ) ;
123180
124181 downloadOpts = {
125182 ...downloadOpts ,
@@ -134,7 +191,9 @@ async function download(opts) {
134191 const start = new Date ( ) ;
135192 bar . start = start ;
136193
137- return await downloadArtifact ( downloadOpts ) ;
194+ return await withRetry ( `download ${ opts . artifactName || "electron" } ` , ( ) =>
195+ downloadArtifact ( downloadOpts )
196+ ) ;
138197}
139198
140199function downloadStream ( opts ) {
0 commit comments