Skip to content

Commit

Permalink
replace node-fetch with native fetch (#198408)
Browse files Browse the repository at this point in the history
* replace node-fetch with native fetch

* fix dep version

* handle terminated error from fetch

* more error handling
  • Loading branch information
joaomoreno committed Nov 16, 2023
1 parent c520c85 commit 87e7f8b
Show file tree
Hide file tree
Showing 15 changed files with 88 additions and 70 deletions.
46 changes: 27 additions & 19 deletions build/azure-pipelines/common/publish.js

Large diffs are not rendered by default.

44 changes: 26 additions & 18 deletions build/azure-pipelines/common/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

import * as fs from 'fs';
import * as path from 'path';
import fetch, { RequestInit } from 'node-fetch';
import { Readable } from 'stream';
import type { ReadableStream } from 'stream/web';
import { pipeline } from 'node:stream/promises';
import * as yauzl from 'yauzl';
import * as crypto from 'crypto';
Expand Down Expand Up @@ -413,19 +413,23 @@ class State {
}
}

const azdoFetchOptions = { headers: { Authorization: `Bearer ${e('SYSTEM_ACCESSTOKEN')}` }, timeout: 60_000 };
const azdoFetchOptions = { headers: { Authorization: `Bearer ${e('SYSTEM_ACCESSTOKEN')}` } };

async function requestAZDOAPI<T>(path: string): Promise<T> {
const res = await fetch(`${e('BUILDS_API_URL')}${path}?api-version=6.0`, azdoFetchOptions);
const abortController = new AbortController();
const timeout = setTimeout(() => abortController.abort(), 2 * 60 * 1000);

if (!res.ok) {
throw new Error(`Unexpected status code: ${res.status}`);
}
try {
const res = await fetch(`${e('BUILDS_API_URL')}${path}?api-version=6.0`, { ...azdoFetchOptions, signal: abortController.signal });

return await Promise.race([
res.json(),
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 60_000))
]);
if (!res.ok) {
throw new Error(`Unexpected status code: ${res.status}`);
}

return await res.json();
} finally {
clearTimeout(timeout);
}
}

interface Artifact {
Expand Down Expand Up @@ -456,16 +460,20 @@ async function getPipelineTimeline(): Promise<Timeline> {
}

async function downloadArtifact(artifact: Artifact, downloadPath: string): Promise<void> {
const res = await fetch(artifact.resource.downloadUrl, azdoFetchOptions);
const abortController = new AbortController();
const timeout = setTimeout(() => abortController.abort(), 6 * 60 * 1000);

if (!res.ok) {
throw new Error(`Unexpected status code: ${res.status}`);
}
try {
const res = await fetch(artifact.resource.downloadUrl, { ...azdoFetchOptions, signal: abortController.signal });

await Promise.race([
pipeline(res.body, fs.createWriteStream(downloadPath)),
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 5 * 60 * 1000))
]);
if (!res.ok) {
throw new Error(`Unexpected status code: ${res.status}`);
}

await pipeline(Readable.fromWeb(res.body as ReadableStream), fs.createWriteStream(downloadPath));
} finally {
clearTimeout(timeout);
}
}

async function unzip(packagePath: string, outputPath: string): Promise<string> {
Expand Down
4 changes: 2 additions & 2 deletions build/azure-pipelines/common/retry.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion build/azure-pipelines/common/retry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export async function retry<T>(fn: (attempt: number) => Promise<T>): Promise<T>
try {
return await fn(run);
} catch (err) {
if (!/fetch failed|timeout|TimeoutError|Timeout Error|RestError|Client network socket disconnected|socket hang up|ECONNRESET|CredentialUnavailableError|endpoints_resolution_error|Audience validation failed|end of central directory record signature not found/i.test(err.message)) {
if (!/fetch failed|terminated|aborted|timeout|TimeoutError|Timeout Error|RestError|Client network socket disconnected|socket hang up|ECONNRESET|CredentialUnavailableError|endpoints_resolution_error|Audience validation failed|end of central directory record signature not found/i.test(err.message)) {
throw err;
}

Expand Down
7 changes: 3 additions & 4 deletions build/lib/builtInExtensionsCG.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions build/lib/builtInExtensionsCG.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import fetch from 'node-fetch';
import * as fs from 'fs';
import * as path from 'path';
import * as url from 'url';
Expand All @@ -30,7 +29,7 @@ async function downloadExtensionDetails(extension: IExtensionDefinition): Promis
try {
const response = await fetch(`${repositoryContentBaseUrl}/${fileName}`);
if (response.ok) {
return { fileName, body: await response.buffer() };
return { fileName, body: Buffer.from(await response.arrayBuffer()) };
} else if (response.status === 404) {
return { fileName, body: undefined };
} else {
Expand Down
Loading

0 comments on commit 87e7f8b

Please sign in to comment.