From 3c4356af92ede0dd47e4f449b8370d5ceaa16232 Mon Sep 17 00:00:00 2001 From: Christian Visintin Date: Sun, 7 Jun 2026 21:34:01 +0200 Subject: [PATCH] fix(site): robust man-fetch invocation guard, add timeout+retry --- site/scripts/fetch-man.mjs | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/site/scripts/fetch-man.mjs b/site/scripts/fetch-man.mjs index 9dd533c..892e8cf 100644 --- a/site/scripts/fetch-man.mjs +++ b/site/scripts/fetch-man.mjs @@ -14,15 +14,31 @@ export function manUrl(locale) { return `https://raw.githubusercontent.com/${REPO}/${MAN_REF}/${path}`; } -async function fetchText(url) { +async function fetchText(url, { retries = 2 } = {}) { const headers = process.env.GITHUB_TOKEN ? { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` } : {}; - const res = await fetch(url, { headers }); - if (!res.ok) { - throw new Error(`fetch ${url} failed: ${res.status} ${res.statusText}`); + for (let attempt = 0; ; attempt += 1) { + try { + // Abort hung requests and surface them as retryable failures. + const res = await fetch(url, { headers, signal: AbortSignal.timeout(15000) }); + if (!res.ok) { + throw new Error(`fetch ${url} failed: ${res.status} ${res.statusText}`); + } + const text = await res.text(); + if (text.trim().length === 0) { + throw new Error(`fetch ${url} returned empty body`); + } + return text; + } catch (err) { + if (attempt >= retries) { + throw err; + } + await new Promise((resolve) => { + setTimeout(resolve, 500 * (attempt + 1)); + }); + } } - return res.text(); } async function main() { @@ -45,7 +61,9 @@ async function main() { } // Run only when invoked directly (not when imported by tests). -if (import.meta.url === `file://${process.argv[1]}`) { +// Compare via fileURLToPath so paths with spaces (percent-encoded in +// import.meta.url, raw in argv[1]) still match. +if (process.argv[1] === fileURLToPath(import.meta.url)) { main().catch((err) => { console.error(err); process.exit(1);