Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

110 linesCodeBlameRaw
1import { Miniflare, convertV4MiniflareOptions } from "miniflare";
2import { readFileSync, readdirSync } from "node:fs";
3import { resolve } from "node:path";
4
5// Local-only integration fixture: real built API/Work Workers and D1.
6// Authentication, repository lookup and event transport are deterministic
7// fixtures. No production credentials or remote service bindings are used.
8const root = resolve(import.meta.dirname, "..");
9const user = { id: "usr_test", username: "tester", verified: true, workspaces: [] };
10const repo = {
11 id: "rep_test",
12 namespace: "tester",
13 name: "rehearsal",
14 description: null,
15 isPrivate: false,
16 ownerId: user.id,
17 defaultBranch: "main",
18 forkOf: null,
19 createdAt: "2026-10-14T00:00:00Z",
20};
21const stub = `const user=${JSON.stringify(user)}; const repo=${JSON.stringify(repo)};
22export default { async fetch(request) {
23 const method = new URL(request.url).pathname.split('/').pop();
24 const body = await request.json();
25 if (method === 'user_for_access_token') return Response.json(body.token === 'local-rehearsal' ? user : body.token === 'local-other' ? {...user,id:'usr_other',username:'other'} : null);
26 if (method === 'get') return Response.json({ok:true,value:repo});
27 if (method === 'publish') return Response.json(null);
28 if (method === 'audit_record') return Response.json(1);
29 if (method === 'resolve_slug' || method === 'resolve_path') return Response.json(null);
30 if (method === 'display_usernames') return Response.json({});
31 console.error('Unimplemented local fixture RPC: '+method); return new Response('Unimplemented local fixture RPC: '+method,{status:500});
32}};`;
33const serviceNames = [
34 "IDENTITY",
35 "REPOS",
36 "EVENTS",
37 "RUNNER",
38 "BILLING",
39 "INTEGRATIONS",
40 "WEBHOOKS",
41 "ACTIONS",
42 "SEARCH",
43 "SECURITY",
44 "PROJECTS",
45 "DEPLOYMENTS",
46 "TELEMETRY",
47 "PACKAGES",
48 "ARTIFACTS",
49];
50const bindings = Object.fromEntries(serviceNames.map((name) => [name, "fixture"]));
51const worker = (name, path, extra) => ({
52 name,
53 modules: [
54 { type: "ESModule", path: resolve(root, path, "build/index.js") },
55 { type: "CompiledWasm", path: resolve(root, path, "build/index_bg.wasm") },
56 ],
57 compatibilityDate: "2026-09-26",
58 ...extra,
59});
60export async function startLocal({ port = 8799, persist = false } = {}) {
61 const mf = new Miniflare(
62 convertV4MiniflareOptions({
63 host: "127.0.0.1",
64 port,
65 resourcePersistencePath: persist || undefined,
66 workers: [
67 worker("api", "apps/api", {
68 serviceBindings: { ...bindings, WORK: "work" },
69 bindings: { MCP_URL: `http://127.0.0.1:${port}/mcp`, API_URL: `http://127.0.0.1:${port}`, SITE_URL: `http://127.0.0.1:${port}` },
70 }),
71 worker("work", "services/work", { serviceBindings: bindings, d1Databases: { DB: "local-coordination" } }),
72 { name: "fixture", modules: true, script: stub, compatibilityDate: "2026-09-26" },
73 ],
74 }),
75 );
76 const ready = await mf.ready;
77 const db = await mf.getD1Database("DB", "work");
78 const initialized = await db.prepare("SELECT name FROM sqlite_master WHERE name = 'sessions'").first();
79 if (!initialized) {
80 for (const file of readdirSync(resolve(root, "services/work/migrations"))
81 .filter((f) => f.endsWith(".sql"))
82 .sort()) {
83 try {
84 // These migrations contain no triggers. Keep each complete statement on
85 // one line for D1.exec, whose API treats newlines as statement boundaries.
86 const sql = readFileSync(resolve(root, "services/work/migrations", file), "utf8")
87 .replace(/--[^\n]*/g, "")
88 .replace(/\r?\n/g, " ")
89 .split(";")
90 .map((s) => s.trim())
91 .filter(Boolean)
92 .join(";\n");
93 await db.exec(sql);
94 } catch (error) {
95 await mf.dispose();
96 throw new Error(`${file}: ${error.message}`);
97 }
98 }
99 }
100 return { mf, db, url: ready.origin };
101}
102if (process.argv[1] && resolve(process.argv[1]) === resolve(import.meta.filename)) {
103 const { mf, url } = await startLocal({ persist: resolve(root, "target/submission-audit/local-coordination") });
104 console.log(`Local rehearsal ready: ${url}; MCP: ${url}/mcp; token: local-rehearsal (fixture only)`);
105 for (const signal of ["SIGINT", "SIGTERM"])
106 process.on(signal, async () => {
107 await mf.dispose();
108 process.exit(0);
109 });
110}