From 9f135dfaeed269d6a73aedf28bd32f787167dc1f Mon Sep 17 00:00:00 2001 From: igneum-labs <337424239+igneum-labs@users.noreply.github.com> Date: Tue, 6 Oct 2026 19:29:28 +0000 Subject: [PATCH] ship-app: the fork commit comparison is a prefix match (short in the state, full in the inputs manifest); the manifest read bypasses the CDN cache Co-Authored-By: Claude Fable 5.1 --- tools/ship-app.mjs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tools/ship-app.mjs b/tools/ship-app.mjs index be2a36635..cf7ffe509 100644 --- a/tools/ship-app.mjs +++ b/tools/ship-app.mjs @@ -158,7 +158,10 @@ async function gh(args, { quiet = true } = {}) { async function ghJson(args) { const r = await gh(args); if (r.code !== 0) throw new Error(`gh ${args.slice(0, 2).join(' ')} failed: ${r.out.trim()}`); return JSON.parse(r.out); } const sleep = ms => new Promise(r => setTimeout(r, ms)); async function head(url) { const r = await fetch(url, { method: 'HEAD' }); return { status: r.status, length: r.headers.get('content-length') ? Number(r.headers.get('content-length')) : null }; } -async function getJson(url) { const r = await fetch(url); if (!r.ok) return null; return r.json().catch(() => null); } +async function getJson(url) { const r = await fetch(`${url}${url.includes('?') ? '&' : '?'}t=${Date.now()}`, { headers: { 'Cache-Control': 'no-cache' } }); if (!r.ok) return null; return r.json().catch(() => null); } +// the state keeps the fork commit as the preflight read it (short with --branch, full with --node-commit); the inputs +// manifest carries the full one (6 October 2026, 0.3.15: `!==` on short against full re-pushed the inputs twice and failed) +const sameCommit = (a, b) => !!a && !!b && (String(a).startsWith(String(b)) || String(b).startsWith(String(a))); // ---- arguments ----------------------------------------------------------------------------------------------------- const argv = process.argv.slice(2); @@ -363,14 +366,14 @@ async function inputs() { if (!WIN) return done('inputs', 'skipped', '--skip-windows'); const files = [...WIN_INPUTS, ...WORKERS.filter(existsSync)]; const live = await getJson(`${BASE}/payload-inputs.json`); - const same = live && live.node_source_commit === state.forkCommit && files.every(p => live.files && live.files[basename(p)] && live.files[basename(p)].sha256 === sha256(p)); + const same = live && sameCommit(live.node_source_commit, state.forkCommit) && files.every(p => live.files && live.files[basename(p)] && live.files[basename(p)].sha256 === sha256(p)); if (same) return done('inputs', 'already', `payload-inputs.zip carries these files from fork ${state.forkCommit} (built ${live.built_at})`); const cmd = `IGNEUM_WIN_RELEASE=${WIN_RELEASE} IGNEUM_NODE_SRC=${NODE_DIR} packaging/windows/push-inputs.sh`; if (DRY) return done('inputs', 'would', `run ${cmd} (deploys the downloads folder)`); const r = await run('bash', [join(ROOT, 'packaging', 'windows', 'push-inputs.sh')], { env: { IGNEUM_WIN_RELEASE: WIN_RELEASE, IGNEUM_NODE_SRC: NODE_DIR } }); if (r.code !== 0) throw new Error(`push-inputs.sh exited ${r.code}; retry by hand: ${cmd}`); const after = await getJson(`${BASE}/payload-inputs.json`); - if (!after || after.node_source_commit !== state.forkCommit) throw new Error(`the live payload-inputs.json does not name fork ${state.forkCommit} after the push`); + if (!after || !sameCommit(after.node_source_commit, state.forkCommit)) throw new Error(`the live payload-inputs.json does not name fork ${state.forkCommit} after the push`); done('inputs', 'ok', `payload-inputs.zip live, fork ${state.forkCommit}`); }