feat(runner): install stage with progress events
Install stage: package-manager detection (lockfile beats the packageManager field, npm default — vite-kitchen's shape), install run inside the clone with staged status heartbeats, and every failure — including install-before-clone — mapped to the named install-failed cause with a bounded output tail. The spawned-runner suite now runs the full clone → install pipeline on the daemon-served vite-kitchen (real npm registry install) and greps the child's entire stdout+stderr plus the clone's .git/config for the PAT and workspace token. Shared test doubles extracted (exec.mock.ts, tests/runner-session.ts) to keep the suites duplication-free. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016j8z4VHjedXDTjEDNg7qHK
This commit is contained in:
53
apps/runner/tests/runner-session.ts
Normal file
53
apps/runner/tests/runner-session.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { mkdtemp, rm } from "node:fs/promises";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { createLogger } from "@/log";
|
||||
import { startRunnerServer, type RunnerServer } from "@/server";
|
||||
import {
|
||||
connectProtocolClient,
|
||||
handshake,
|
||||
type ProtocolClient,
|
||||
} from "./protocol-client";
|
||||
|
||||
/**
|
||||
* An in-process runner server plus one already-handshaken client, with
|
||||
* log capture (for the credential-leak assertions) and one-call
|
||||
* teardown. In-process so v8 coverage sees the server; the spawn suite
|
||||
* covers the real-child-process shape.
|
||||
*/
|
||||
export interface RunnerSession {
|
||||
client: ProtocolClient;
|
||||
workspaceDir: string;
|
||||
/** Every log line the runner emitted — greppable for leaks. */
|
||||
logLines: string[];
|
||||
close: () => Promise<void>;
|
||||
}
|
||||
|
||||
export async function startRunnerSession(options: {
|
||||
token: string;
|
||||
tmpPrefix: string;
|
||||
heartbeatMs?: number;
|
||||
}): Promise<RunnerSession> {
|
||||
const workspaceDir = await mkdtemp(path.join(os.tmpdir(), options.tmpPrefix));
|
||||
const logLines: string[] = [];
|
||||
const server: RunnerServer = await startRunnerServer({
|
||||
host: "127.0.0.1",
|
||||
port: 0,
|
||||
token: options.token,
|
||||
workspaceDir,
|
||||
heartbeatMs: options.heartbeatMs ?? 100,
|
||||
log: createLogger((line) => logLines.push(line)),
|
||||
});
|
||||
const client = await connectProtocolClient(server.port, options.token);
|
||||
await handshake(client);
|
||||
return {
|
||||
client,
|
||||
workspaceDir,
|
||||
logLines,
|
||||
close: async () => {
|
||||
client.close();
|
||||
await server.close();
|
||||
await rm(workspaceDir, { recursive: true, force: true });
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user