Files
feuerwerk/tools/lib/projects.mjs
T
rootandClaude Opus 5 c256859854 Make a dying E2E run fail loudly instead of exiting 0
A run whose chromium died stopped mid-step and exited with code 0, silently
reporting success for checks it never ran: CDP.send() left its promise pending
when the socket closed, and an unsettled promise does not keep node alive.
Waiting on the server had the mirror problem – a bare fetch keeps its socket
open, so a stalled response hung the run forever.

Every wait can now time out, the socket rejects what it can no longer answer,
and reaching process exit without a summary reports RUN DIED with exit 1.
Chromium's stderr and exit signal are kept, so "killed by SIGKILL" is visible
rather than guessed at.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 22:41:56 +02:00

62 lines
2.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Which project each tool writes to, and the guard that keeps every tool away from the
* playground.
*
* playground the user's own project. No tool writes it – it is maintained in the editor.
* fixture rewritten by every e2e run (tools/seed-e2e.mjs + tools/e2e.mjs).
* jumpRun the example game (tools/seed-jumprun.mjs, played by tools/smoke-jumprun.mjs).
*
* All ids exist on the server (created via POST ?r=projects); PUT does not create projects.
*/
export const PROJECTS = {
playground: '649188c15414',
fixture: '3caa9b6a0c92',
jumpRun: '3d271f3bb58b',
};
export const DEFAULT_BASE_URL = 'https://nockal.com/game-maker/';
/**
* Reads `[projectId] [baseUrl]` from argv, falling back to `defaultId`. Targeting the playground
* aborts: no tool may overwrite or mutate it.
*/
export function cliArgs(script, defaultId) {
const projectId = process.argv[2] ?? defaultId;
const baseUrl = process.argv[3] ?? DEFAULT_BASE_URL;
if (projectId === PROJECTS.playground) {
console.error(`refusing to run ${script} against the playground project (${PROJECTS.playground}).`);
process.exit(2);
}
return { projectId, baseUrl };
}
/** Overwrites a project, keeping the name it already has on the server. */
export async function writeProject(projectId, baseUrl, project) {
const url = `${baseUrl}api/index.php?r=projects/${projectId}`;
// A request without a timeout keeps its socket open, so a stalled server hangs the seed
// instead of failing it – and the run that follows never starts.
const timeout = () => AbortSignal.timeout(20_000);
const current = await fetch(url, { signal: timeout() });
if (!current.ok) {
console.error(`GET failed: ${current.status} – create the project first (POST ?r=projects)`);
process.exit(1);
}
const doc = await current.json();
const body = {
revision: doc.revision,
project: { ...project, id: projectId, name: doc.name ?? project.name },
};
const res = await fetch(url, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body),
signal: timeout(),
});
const result = await res.json();
if (!res.ok) {
console.error('PUT failed', res.status, result);
process.exit(1);
}
console.log(`seeded ${projectId} (${body.project.name}) → revision ${result.revision}`);
}