mirror of
https://github.com/SukkaW/Surge.git
synced 2025-12-13 01:30:37 +08:00
Enable more feature w/ undici
This commit is contained in:
parent
80ab4e69c1
commit
fa70896b8f
@ -6,7 +6,8 @@ import { task } from './trace';
|
|||||||
import { extract as tarExtract } from 'tar-fs';
|
import { extract as tarExtract } from 'tar-fs';
|
||||||
import type { Headers as TarEntryHeaders } from 'tar-fs';
|
import type { Headers as TarEntryHeaders } from 'tar-fs';
|
||||||
import zlib from 'node:zlib';
|
import zlib from 'node:zlib';
|
||||||
import { $fetch } from './lib/make-fetch-happen';
|
import { fetchWithRetry } from './lib/fetch-retry';
|
||||||
|
import { Readable } from 'node:stream';
|
||||||
|
|
||||||
const GITHUB_CODELOAD_URL = 'https://codeload.github.com/sukkalab/ruleset.skk.moe/tar.gz/master';
|
const GITHUB_CODELOAD_URL = 'https://codeload.github.com/sukkalab/ruleset.skk.moe/tar.gz/master';
|
||||||
const GITLAB_CODELOAD_URL = 'https://gitlab.com/SukkaW/ruleset.skk.moe/-/archive/master/ruleset.skk.moe-master.tar.gz';
|
const GITLAB_CODELOAD_URL = 'https://gitlab.com/SukkaW/ruleset.skk.moe/-/archive/master/ruleset.skk.moe-master.tar.gz';
|
||||||
@ -20,7 +21,7 @@ export const downloadPreviousBuild = task(require.main === module, __filename)(a
|
|||||||
}
|
}
|
||||||
|
|
||||||
const tarGzUrl = await span.traceChildAsync('get tar.gz url', async () => {
|
const tarGzUrl = await span.traceChildAsync('get tar.gz url', async () => {
|
||||||
const resp = await $fetch(GITHUB_CODELOAD_URL, { method: 'HEAD' });
|
const resp = await fetchWithRetry(GITHUB_CODELOAD_URL, { method: 'HEAD' });
|
||||||
if (resp.status !== 200) {
|
if (resp.status !== 200) {
|
||||||
console.warn('Download previous build from GitHub failed! Status:', resp.status);
|
console.warn('Download previous build from GitHub failed! Status:', resp.status);
|
||||||
console.warn('Switch to GitLab');
|
console.warn('Switch to GitLab');
|
||||||
@ -30,7 +31,7 @@ export const downloadPreviousBuild = task(require.main === module, __filename)(a
|
|||||||
});
|
});
|
||||||
|
|
||||||
return span.traceChildAsync('download & extract previoud build', async () => {
|
return span.traceChildAsync('download & extract previoud build', async () => {
|
||||||
const resp = await $fetch(tarGzUrl, {
|
const resp = await fetchWithRetry(tarGzUrl, {
|
||||||
headers: {
|
headers: {
|
||||||
'User-Agent': 'curl/8.9.1',
|
'User-Agent': 'curl/8.9.1',
|
||||||
// https://github.com/unjs/giget/issues/97
|
// https://github.com/unjs/giget/issues/97
|
||||||
@ -66,7 +67,7 @@ export const downloadPreviousBuild = task(require.main === module, __filename)(a
|
|||||||
);
|
);
|
||||||
|
|
||||||
return pipeline(
|
return pipeline(
|
||||||
resp.body,
|
Readable.fromWeb(resp.body),
|
||||||
gunzip,
|
gunzip,
|
||||||
extract
|
extract
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,14 +1,46 @@
|
|||||||
import retry from 'async-retry';
|
import retry from 'async-retry';
|
||||||
import picocolors from 'picocolors';
|
import picocolors from 'picocolors';
|
||||||
import { setTimeout } from 'node:timers/promises';
|
import { setTimeout } from 'node:timers/promises';
|
||||||
import { fetch as _fetch } from 'undici';
|
import {
|
||||||
|
fetch as _fetch,
|
||||||
|
interceptors,
|
||||||
|
EnvHttpProxyAgent,
|
||||||
|
setGlobalDispatcher
|
||||||
|
} from 'undici';
|
||||||
|
|
||||||
|
import type { Request, Response, RequestInit } from 'undici';
|
||||||
|
|
||||||
|
import CacheableLookup from 'cacheable-lookup';
|
||||||
|
import type { LookupOptions as CacheableLookupOptions } from 'cacheable-lookup';
|
||||||
|
|
||||||
|
const cacheableLookup = new CacheableLookup();
|
||||||
|
|
||||||
|
const agent = new EnvHttpProxyAgent({
|
||||||
|
allowH2: true,
|
||||||
|
connect: {
|
||||||
|
lookup(hostname, opt, cb) {
|
||||||
|
return cacheableLookup.lookup(hostname, opt as CacheableLookupOptions, cb);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setGlobalDispatcher(agent.compose(
|
||||||
|
interceptors.retry({
|
||||||
|
maxRetries: 5,
|
||||||
|
minTimeout: 10000,
|
||||||
|
errorCodes: ['UND_ERR_HEADERS_TIMEOUT', 'ECONNRESET', 'ECONNREFUSED', 'ENOTFOUND', 'ENETDOWN', 'ENETUNREACH', 'EHOSTDOWN', 'EHOSTUNREACH', 'EPIPE']
|
||||||
|
}),
|
||||||
|
interceptors.redirect({
|
||||||
|
maxRedirections: 5
|
||||||
|
})
|
||||||
|
));
|
||||||
|
|
||||||
function isClientError(err: unknown): err is NodeJS.ErrnoException {
|
function isClientError(err: unknown): err is NodeJS.ErrnoException {
|
||||||
if (!err || typeof err !== 'object') return false;
|
if (!err || typeof err !== 'object') return false;
|
||||||
|
|
||||||
if ('code' in err) return err.code === 'ERR_UNESCAPED_CHARACTERS';
|
if ('code' in err) return err.code === 'ERR_UNESCAPED_CHARACTERS';
|
||||||
if ('message' in err) return err.message === 'Request path contains unescaped characters';
|
if ('message' in err) return err.message === 'Request path contains unescaped characters';
|
||||||
if ('name' in err) return err.name === 'DOMException' || err.name === 'AbortError';
|
if ('name' in err) return err.name === 'AbortError';
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -41,7 +73,6 @@ interface FetchRetryOpt {
|
|||||||
factor?: number,
|
factor?: number,
|
||||||
maxRetryAfter?: number,
|
maxRetryAfter?: number,
|
||||||
// onRetry?: (err: Error) => void,
|
// onRetry?: (err: Error) => void,
|
||||||
retryOnAborted?: boolean,
|
|
||||||
retryOnNon2xx?: boolean,
|
retryOnNon2xx?: boolean,
|
||||||
retryOn404?: boolean
|
retryOn404?: boolean
|
||||||
}
|
}
|
||||||
@ -57,12 +88,11 @@ const DEFAULT_OPT: Required<FetchRetryOpt> = {
|
|||||||
retries: 5,
|
retries: 5,
|
||||||
factor: 6,
|
factor: 6,
|
||||||
maxRetryAfter: 20,
|
maxRetryAfter: 20,
|
||||||
retryOnAborted: false,
|
|
||||||
retryOnNon2xx: true,
|
retryOnNon2xx: true,
|
||||||
retryOn404: false
|
retryOn404: false
|
||||||
};
|
};
|
||||||
|
|
||||||
function createFetchRetry($fetch: typeof fetch): FetchWithRetry {
|
function createFetchRetry(fetch: typeof _fetch): FetchWithRetry {
|
||||||
const fetchRetry: FetchWithRetry = async (url, opts = {}) => {
|
const fetchRetry: FetchWithRetry = async (url, opts = {}) => {
|
||||||
const retryOpts = Object.assign(
|
const retryOpts = Object.assign(
|
||||||
DEFAULT_OPT,
|
DEFAULT_OPT,
|
||||||
@ -70,10 +100,10 @@ function createFetchRetry($fetch: typeof fetch): FetchWithRetry {
|
|||||||
);
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return await retry<Response>(async (bail) => {
|
return await retry(async (bail) => {
|
||||||
try {
|
try {
|
||||||
// this will be retried
|
// this will be retried
|
||||||
const res = (await $fetch(url, opts));
|
const res = (await fetch(url, opts));
|
||||||
|
|
||||||
if ((res.status >= 500 && res.status < 600) || res.status === 429) {
|
if ((res.status >= 500 && res.status < 600) || res.status === 429) {
|
||||||
// NOTE: doesn't support http-date format
|
// NOTE: doesn't support http-date format
|
||||||
@ -126,7 +156,7 @@ function createFetchRetry($fetch: typeof fetch): FetchWithRetry {
|
|||||||
if ((
|
if ((
|
||||||
err.name === 'AbortError'
|
err.name === 'AbortError'
|
||||||
|| ('digest' in err && err.digest === 'AbortError')
|
|| ('digest' in err && err.digest === 'AbortError')
|
||||||
) && !retryOpts.retryOnAborted) {
|
)) {
|
||||||
console.log(picocolors.gray('[fetch abort]'), url);
|
console.log(picocolors.gray('[fetch abort]'), url);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -148,9 +178,9 @@ function createFetchRetry($fetch: typeof fetch): FetchWithRetry {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const k of Object.keys($fetch)) {
|
for (const k of Object.keys(_fetch)) {
|
||||||
const key = k as keyof typeof $fetch;
|
const key = k as keyof typeof _fetch;
|
||||||
fetchRetry[key] = $fetch[key];
|
fetchRetry[key] = _fetch[key];
|
||||||
}
|
}
|
||||||
|
|
||||||
return fetchRetry;
|
return fetchRetry;
|
||||||
|
|||||||
@ -1,8 +1,11 @@
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import fs from 'node:fs';
|
import fs from 'node:fs';
|
||||||
import makeFetchHappen from 'make-fetch-happen';
|
import makeFetchHappen from 'make-fetch-happen';
|
||||||
|
import picocolors from 'picocolors';
|
||||||
// eslint-disable-next-line @typescript-eslint/no-restricted-imports -- type only
|
// eslint-disable-next-line @typescript-eslint/no-restricted-imports -- type only
|
||||||
export type { Response as NodeFetchResponse } from 'node-fetch';
|
import type { Response as NodeFetchResponse } from 'node-fetch';
|
||||||
|
|
||||||
|
export type { NodeFetchResponse };
|
||||||
|
|
||||||
const cachePath = path.resolve(__dirname, '../../.cache/__make_fetch_happen__');
|
const cachePath = path.resolve(__dirname, '../../.cache/__make_fetch_happen__');
|
||||||
fs.mkdirSync(cachePath, { recursive: true });
|
fs.mkdirSync(cachePath, { recursive: true });
|
||||||
@ -21,3 +24,10 @@ export const $fetch = makeFetchHappen.defaults({
|
|||||||
randomize: true
|
randomize: true
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export function printResponseStatus(resp: NodeFetchResponse) {
|
||||||
|
const status = resp.headers.get('X-Local-Cache-Status');
|
||||||
|
if (status) {
|
||||||
|
console.log('[$fetch cache]', { status }, picocolors.gray(resp.url));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -25,6 +25,7 @@
|
|||||||
"async-retry": "^1.3.3",
|
"async-retry": "^1.3.3",
|
||||||
"async-sema": "^3.1.1",
|
"async-sema": "^3.1.1",
|
||||||
"better-sqlite3": "^11.3.0",
|
"better-sqlite3": "^11.3.0",
|
||||||
|
"cacheable-lookup": "^6.1.0",
|
||||||
"ci-info": "^4.0.0",
|
"ci-info": "^4.0.0",
|
||||||
"cli-table3": "^0.6.5",
|
"cli-table3": "^0.6.5",
|
||||||
"csv-parse": "^5.5.6",
|
"csv-parse": "^5.5.6",
|
||||||
|
|||||||
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@ -26,6 +26,9 @@ importers:
|
|||||||
better-sqlite3:
|
better-sqlite3:
|
||||||
specifier: ^11.3.0
|
specifier: ^11.3.0
|
||||||
version: 11.3.0
|
version: 11.3.0
|
||||||
|
cacheable-lookup:
|
||||||
|
specifier: ^6.1.0
|
||||||
|
version: 6.1.0
|
||||||
ci-info:
|
ci-info:
|
||||||
specifier: ^4.0.0
|
specifier: ^4.0.0
|
||||||
version: 4.0.0
|
version: 4.0.0
|
||||||
@ -730,6 +733,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==}
|
resolution: {integrity: sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ==}
|
||||||
engines: {node: ^18.17.0 || >=20.5.0}
|
engines: {node: ^18.17.0 || >=20.5.0}
|
||||||
|
|
||||||
|
cacheable-lookup@6.1.0:
|
||||||
|
resolution: {integrity: sha512-KJ/Dmo1lDDhmW2XDPMo+9oiy/CeqosPguPCrgcVzKyZrL6pM1gU2GmPY/xo6OQPTUaA/c0kwHuywB4E6nmT9ww==}
|
||||||
|
engines: {node: '>=10.6.0'}
|
||||||
|
|
||||||
call-me-maybe@1.0.2:
|
call-me-maybe@1.0.2:
|
||||||
resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==}
|
resolution: {integrity: sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==}
|
||||||
|
|
||||||
@ -2502,6 +2509,8 @@ snapshots:
|
|||||||
tar: 7.4.3
|
tar: 7.4.3
|
||||||
unique-filename: 4.0.0
|
unique-filename: 4.0.0
|
||||||
|
|
||||||
|
cacheable-lookup@6.1.0: {}
|
||||||
|
|
||||||
call-me-maybe@1.0.2: {}
|
call-me-maybe@1.0.2: {}
|
||||||
|
|
||||||
callsites@3.1.0: {}
|
callsites@3.1.0: {}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user