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.

172 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';
17import { WorkerEntrypoint } from 'cloudflare:workers';
18export class ScreenshotStub extends WorkerEntrypoint {
19 async image(input) {
20 if (input.host !== 'public-acme.g1t.page' || input.commit !== 'abcdef1234567') throw new Error('Unexpected screenshot target');
21 return {body:new TextEncoder().encode('production-preview').buffer,contentType:'image/jpeg',commit:input.commit,capturedAt:'2026-10-15T02:00:00Z'};
22 }
23}
24export default { async fetch(request, env, ctx) {
25 if (new URL(request.url).pathname === '/__seed') {
26 await env.DOWNLOADS.put('web-assets/assets/app-old12345.css', ${JSON.stringify(css)}, {httpMetadata:{contentType:'text/css'}});
27 const cache = new URL('/pricing.data', request.url);
28 cache.searchParams.set('_g1t_build', ${JSON.stringify(build)});
29 cache.searchParams.set('_g1t_representation', 'html');
30 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())}}));
31 return new Response('seeded');
32 }
33 return app.fetch(request, env, ctx);
34}};`;
35const files = [
36 join(root, "index.js"),
37 ...readdirSync(join(root, "assets"))
38 .filter((name) => name.endsWith(".js"))
39 .map((name) => join(root, "assets", name)),
40];
41const runtime = new Miniflare(
42 convertV4MiniflareOptions({
43 name: "web-deploy-check",
44 cf: false,
45 modulesRoot: root,
46 modules: [{ type: "ESModule", path: join(root, "deploy-check.mjs"), contents: seed }, ...files.map((path) => ({ type: "ESModule", path }))],
47 compatibilityDate: config.compatibility_date,
48 compatibilityFlags: config.compatibility_flags,
49 r2Buckets: ["DOWNLOADS"],
50 serviceBindings: {
51 IDENTITY: async () => Response.json(null),
52 PROJECTS: async (request) => {
53 const method = new URL(request.url).pathname.split("/").pop();
54 const args = await request.json();
55 if (method !== "get") return Response.json({ ok: true, value: [] });
56 if (args.slug !== "public") return Response.json({ ok: false, error: { code: "not_found", message: "Not found" } });
57 return Response.json({ ok: true, value: { source: { kind: "hosted", repo: { namespace: "acme", name: "public" } } } });
58 },
59 DEPLOYMENTS: async (request) => {
60 const method = new URL(request.url).pathname.split("/").pop();
61 const args = await request.json();
62 assert.equal(method, "list_environments", "Public previews must not read member-only deployment lists");
63 assert.equal(args.viewer, null);
64 assert.equal(args.repo.name, "public");
65 const current = {
66 id: "dpl_live",
67 source: "g1t_page",
68 state: "success",
69 sha: "abcdef1234567",
70 environment_url: "https://public-acme.g1t.page",
71 updated_at: "2026-10-15T01:00:00Z",
72 };
73 return Response.json({
74 ok: true,
75 value: {
76 environments: [
77 {
78 name: "production",
79 production_environment: true,
80 updated_at: current.updated_at,
81 current,
82 latest: { ...current, id: "dpl_next", sha: "bbbbbbb", state: "in_progress" },
83 },
84 ],
85 },
86 });
87 },
88 SCREENSHOTS: { name: "web-deploy-check", entrypoint: "ScreenshotStub" },
89 },
90 }),
91);
92try {
93 const origin = await runtime.ready;
94 const request = (path, init) => fetch(new URL(path, origin), { ...init, signal: AbortSignal.timeout(15_000) });
95 assert.equal((await request("/__seed")).status, 200);
96 const screenshot = await request("/acme/public/production.jpg?v=abcdef1234567-" + Date.parse("2026-10-15T01:00:00Z"));
97 assert.equal(screenshot.status, 200, "Signed-out readers can see public production screenshots");
98 assert.equal(screenshot.headers.get("content-type"), "image/jpeg");
99 assert.equal(screenshot.headers.get("cache-control"), "private, max-age=31536000, immutable");
100 assert.equal(await screenshot.text(), "production-preview");
101 assert.equal((await request("/acme/private/production.jpg")).status, 404, "Unreadable projects never expose screenshots");
102 console.log("Public production screenshots: anonymous access, private-project denial, and serving commit selection verified.");
103 for (const [path, destination] of [
104 ["/developers", "https://docs.g1t.sh/reference/developers/"],
105 ["/openapi.json", "https://api.g1t.sh/openapi.json"],
106 ["/mcp", "https://mcp.g1t.sh"],
107 ]) {
108 const answer = await request(path, { redirect: "manual" });
109 assert.equal(answer.status, 308);
110 assert.equal(answer.headers.get("location"), new URL(destination).href);
111 }
112 // Run Markdown first, then HTML at the same URL: the cache must not mix them.
113 for (const path of ["/", "/__agent-readiness-missing"]) {
114 for (const accept of ["text/markdown", "text/html", "text/markdown"]) {
115 const answer = await request(path, { headers: { accept } });
116 assert.equal(answer.status, path === "/" ? 200 : 404);
117 assert.ok(
118 answer.headers
119 .get("vary")
120 ?.toLowerCase()
121 .split(",")
122 .map((value) => value.trim())
123 .includes("accept"),
124 );
125 assert.ok(answer.headers.get("content-type")?.startsWith(accept));
126 const body = await answer.text();
127 if (accept === "text/markdown") {
128 assert.match(body, /\[.*\]\(.*\/llms.txt\)/);
129 assert.ok(body.length > 20);
130 } else {
131 assert.match(body, /<html/);
132 if (path === "/") assert.match(body, /rel="canonical"/);
133 }
134 }
135 }
136 assert.equal((await request("/", { headers: { accept: "application/json" } })).status, 406);
137 for (const accept of ["text/markdown", "text/html"]) {
138 const answer = await request("/", { method: "HEAD", headers: { accept } });
139 assert.equal(answer.status, 200);
140 assert.ok(answer.headers.get("content-type")?.startsWith(accept));
141 assert.equal(await answer.text(), "");
142 }
143 const features = await request("/features", { headers: { accept: "text/html" } });
144 assert.equal(features.status, 200);
145 const featureHtml = await features.text();
146 assert.match(featureHtml, /<h1[^>]*>From the first conversation to shipped code[.]<\/h1>/);
147 assert.match(featureHtml, /<title>g1t features/);
148 assert.match(featureHtml, /href="https:\/\/g1t.sh\/features"/);
149 assert.match(featureHtml, /href="\/features"[^>]*>Features/);
150 assert.match(featureHtml, /href="\/developers"/);
151 console.log("Features: public HTML, branded title, canonical URL and navigation verified.");
152 const old = await request("/assets/app-old12345.css");
153 assert.equal(old.status, 200);
154 assert.equal(old.headers.get("content-type"), "text/css");
155 assert.equal(await old.text(), css);
156 const missing = await request("/assets/app-missing0.css");
157 assert.equal(missing.status, 404);
158 assert.equal(missing.headers.get("cache-control"), "no-store");
159 assert.doesNotMatch(missing.headers.get("content-type") ?? "", /html/);
160 for (const path of ["/pricing.data", "/flagon-io/-/home.data?tab=one&_routes=root"]) {
161 const answer = await request(path, { headers: { "x-g1t-build": "previous-build" } });
162 assert.equal(answer.status, 202);
163 const body = await answer.text();
164 assert.match(body, /reload/);
165 assert.ok(body.includes(path.split(".data")[0]));
166 assert.doesNotMatch(body, /_routes|wrong-build-cache/);
167 }
168 console.log("Deployment safety: old CSS survives, missing assets stay isolated, old navigations bypass cache and loaders.");
169 console.log("Agent pages: Markdown/HTML negotiation, isolated caches, 404s, HEAD, canonical metadata and discovery redirects verified.");
170} finally {
171 await runtime.dispose();
172}