Skip to content

Commit 2b84b73

Browse files
authored
Merge pull request #42 from microsoft/dev/dmitriv/build-fix-electron-download-retry
Retry transient network errors when downloading Electron
2 parents 103480d + 71675c8 commit 2b84b73

1 file changed

Lines changed: 66 additions & 7 deletions

File tree

src/download.js

Lines changed: 66 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,58 @@ const { Octokit } = require("@octokit/rest");
1111
const { got } = require("got");
1212
const 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+
1466
async 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

140199
function downloadStream(opts) {

0 commit comments

Comments
 (0)