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.

88 linesCodeBlameRaw
1// Exercise the built Worker in workerd before it can be deployed. Storage is
2// temporary and local; no service bindings or Cloudflare credentials are used.
3import assert from "node:assert/strict";
4import { readFileSync, readdirSync } from "node:fs";
5import { join, resolve } from "node:path";
6import { Miniflare, convertV4MiniflareOptions } from "miniflare";
7
8const root = resolve("build/server");
9const config = JSON.parse(readFileSync(join(root, "wrangler.json"), "utf8"));
10const source = readFileSync(join(root, "index.js"), "utf8");
11const build = source.match(/searchParams\.set\("_g1t_build", "([^"]+)"\)/)?.[1];
12assert.ok(build, "The page cache must be separated by build");
13const css = "body{color:red}";
14// Seed R2 and the page cache through a test-only entry, keeping all R2 I/O
15// inside workerd. The application entry itself is the unmodified build.
16const seed = `import app from './index.js';
17export default { async fetch(request, env, ctx) {
18 if (new URL(request.url).pathname === '/__seed') {
19 await env.DOWNLOADS.put('web-assets/assets/app-old12345.css', ${JSON.stringify(css)}, {httpMetadata:{contentType:'text/css'}});
20 const cache = new URL('/pricing.data', request.url);
21 cache.searchParams.set('_g1t_build', ${JSON.stringify(build)});
22 cache.searchParams.set('_g1t_representation', 'html');
23 await caches.default.put(new Request(cache), new Response('wrong-build-cache', {headers:{'cache-control':'public, max-age=300','x-g1t-kept-at':String(Date.now())}}));
24 return new Response('seeded');
25 }
26 return app.fetch(request, env, ctx);
27}};`;
28const files = [join(root, "index.js"), ...readdirSync(join(root, "assets")).filter((name) => name.endsWith(".js")).map((name) => join(root, "assets", name))];
29const runtime = new Miniflare(convertV4MiniflareOptions({
30 name: "web-deploy-check", cf: false, modulesRoot: root,
31 modules: [{ type: "ESModule", path: join(root, "deploy-check.mjs"), contents: seed }, ...files.map((path) => ({ type: "ESModule", path }))],
32 compatibilityDate: config.compatibility_date,
33 compatibilityFlags: config.compatibility_flags,
34 r2Buckets: ["DOWNLOADS"],
35 serviceBindings: {
36 IDENTITY: async () => Response.json(null),
37 PROJECTS: async () => Response.json({ ok: true, value: [] }),
38 },
39}));
40try {
41 const origin = await runtime.ready;
42 const request = (path, init) => fetch(new URL(path, origin), { ...init, signal: AbortSignal.timeout(15_000) });
43 assert.equal((await request("/__seed")).status, 200);
44 for (const [path, destination] of [["/developers", "https://docs.g1t.sh/reference/api/"], ["/openapi.json", "https://api.g1t.sh/openapi.json"], ["/mcp", "https://mcp.g1t.sh"]]) {
45 const answer = await request(path, { redirect: "manual" });
46 assert.equal(answer.status, 308);
47 assert.equal(answer.headers.get("location"), new URL(destination).href);
48 }
49 // Run Markdown first, then HTML at the same URL: the cache must not mix them.
50 for (const path of ["/", "/__agent-readiness-missing"]) {
51 for (const accept of ["text/markdown", "text/html", "text/markdown"]) {
52 const answer = await request(path, { headers: { accept } });
53 assert.equal(answer.status, path === "/" ? 200 : 404);
54 assert.ok(answer.headers.get("vary")?.toLowerCase().split(",").map((value) => value.trim()).includes("accept"));
55 assert.ok(answer.headers.get("content-type")?.startsWith(accept));
56 const body = await answer.text();
57 if (accept === "text/markdown") { assert.match(body, /\[.*\]\(.*\/llms.txt\)/); assert.ok(body.length > 20); }
58 else { assert.match(body, /<html/); if (path === "/") assert.match(body, /rel="canonical"/); }
59 }
60 }
61 assert.equal((await request("/", { headers: { accept: "application/json" } })).status, 406);
62 for (const accept of ["text/markdown", "text/html"]) {
63 const answer = await request("/", { method: "HEAD", headers: { accept } });
64 assert.equal(answer.status, 200);
65 assert.ok(answer.headers.get("content-type")?.startsWith(accept));
66 assert.equal(await answer.text(), "");
67 }
68 const old = await request("/assets/app-old12345.css");
69 assert.equal(old.status, 200);
70 assert.equal(old.headers.get("content-type"), "text/css");
71 assert.equal(await old.text(), css);
72 const missing = await request("/assets/app-missing0.css");
73 assert.equal(missing.status, 404);
74 assert.equal(missing.headers.get("cache-control"), "no-store");
75 assert.doesNotMatch(missing.headers.get("content-type") ?? "", /html/);
76 for (const path of ["/pricing.data", "/flagon-io/-/home.data?tab=one&_routes=root"]) {
77 const answer = await request(path, { headers: { "x-g1t-build": "previous-build" } });
78 assert.equal(answer.status, 202);
79 const body = await answer.text();
80 assert.match(body, /reload/);
81 assert.ok(body.includes(path.split(".data")[0]));
82 assert.doesNotMatch(body, /_routes|wrong-build-cache/);
83 }
84 console.log("Deployment safety: old CSS survives, missing assets stay isolated, old navigations bypass cache and loaders.");
85 console.log("Agent pages: Markdown/HTML negotiation, isolated caches, 404s, HEAD, canonical metadata and discovery redirects verified.");
86} finally {
87 await runtime.dispose();
88}