verify-deploy: retry with backoff and check res.ok

The previous deploy (staff-tags 1.0.5) failed CI at this exact step with a
raw "Failed to parse JSON" error, even though the deploy itself had
succeeded seconds earlier (confirmed live) - Cloudflare's edge needs a
moment to propagate a fresh deploy, and the script had no res.ok check to
turn a transient non-OK response into a clear message, let alone retry it.
This commit is contained in:
Ally Founder 2026-08-08 20:59:13 -04:00
parent 0439a6a67b
commit 74cc0e61a4

View file

@ -4,7 +4,33 @@
const BASE_URL = process.env.VERIFY_BASE_URL ?? "https://next.jarviscli.dev";
const idx = await (await fetch(`${BASE_URL}/index.json`)).json();
// Cloudflare's edge can take a few seconds to propagate a fresh deploy - the very first run of
// this script (2026-08-09, staff-tags 1.0.5) failed with a raw "Failed to parse JSON" error from
// a non-OK response fetched immediately after `wrangler deploy` returned, even though the deploy
// itself had succeeded (confirmed live seconds later). Retrying with a short backoff instead of
// failing on the first transient response avoids that false negative, and checking `res.ok`
// turns any real failure into a clear status-code message instead of an opaque parse error.
async function fetchWithRetry(url, { attempts = 5, delayMs = 3000, binary = false } = {}) {
let lastError;
for (let attempt = 1; attempt <= attempts; attempt++) {
try {
const res = await fetch(url);
if (!res.ok) {
throw new Error(`${url} responded ${res.status} ${res.statusText}`);
}
return binary ? await res.arrayBuffer() : await res.json();
} catch (e) {
lastError = e;
if (attempt < attempts) {
console.log(`Attempt ${attempt}/${attempts} for ${url} failed (${e.message}), retrying in ${delayMs}ms...`);
await new Promise(r => setTimeout(r, delayMs));
}
}
}
throw new Error(`${url} failed after ${attempts} attempts: ${lastError.message}`);
}
const idx = await fetchWithRetry(`${BASE_URL}/index.json`);
const ids = Object.keys(idx.plugins ?? {});
if (!ids.length) throw new Error("index.json has no plugins");
@ -12,7 +38,7 @@ const [id, plugin] = Object.entries(idx.plugins)[0];
const version = plugin.channels.latest;
const v = plugin.versions[version];
const buf = await (await fetch(v.url)).arrayBuffer();
const buf = await fetchWithRetry(v.url, { binary: true });
const hash = Buffer.from(await crypto.subtle.digest("SHA-256", buf)).toString("hex");
if (hash !== v.sha256) {