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.

g1t.sh
272 linesCodeBlameRaw
1/**
2 * The whole service, on Node, opening builds for g1t Actions jobs
3 * (`start_from_workflow`): a D1 made of node:sqlite with the service's
4 * migrations, the other services as fakes that answer by method, and
5 * Cloudflare's API as a recorded fetch. See moves.flow.test.ts.
6 */
7
8import assert from "node:assert/strict";
9import { readdirSync, readFileSync } from "node:fs";
10import { registerHooks } from "node:module";
11import { DatabaseSync } from "node:sqlite";
12import { beforeEach, test } from "node:test";
13
14registerHooks({
15 resolve(specifier, context, next) {
16 try {
17 return next(specifier, context);
18 } catch (error) {
19 if (specifier.startsWith(".")) return next(`${specifier}.ts`, context);
20 throw error;
21 }
22 },
23});
24const { default: worker } = await import("./index.ts");
25
26function d1(db: DatabaseSync) {
27 const value = (v: unknown) => (v === undefined ? null : typeof v === "boolean" ? Number(v) : v);
28 const statement = (sql: string, params: unknown[] = []) => ({
29 sql,
30 params,
31 bind: (...args: unknown[]) => statement(sql, args.map(value)),
32 async first<T>() {
33 return (db.prepare(sql).get(...(params as never[])) as T) ?? null;
34 },
35 async all<T>() {
36 return { results: db.prepare(sql).all(...(params as never[])) as T[], success: true };
37 },
38 async run() {
39 const r = db.prepare(sql).run(...(params as never[]));
40 return { success: true, meta: { changes: Number(r.changes) } };
41 },
42 });
43 return {
44 prepare: (sql: string) => statement(sql),
45 async batch(statements: ReturnType<typeof statement>[]) {
46 db.exec("BEGIN");
47 try {
48 const out = statements.map((s) => ({ results: db.prepare(s.sql).all(...(s.params as never[])) }));
49 db.exec("COMMIT");
50 return out;
51 } catch (error) {
52 db.exec("ROLLBACK");
53 throw error;
54 }
55 },
56 };
57}
58
59const REPO = "rep_web";
60const PROJECT = "prj_web";
61const SHA = "4b8d0f2a6c1e3579bd02468ace13579bdf02468a";
62
63function world() {
64 const sqlite = new DatabaseSync(":memory:");
65 const dir = new URL("../migrations/", import.meta.url);
66 for (const file of readdirSync(dir).sort()) sqlite.exec(readFileSync(new URL(file, dir), "utf8"));
67 const state = {
68 sqlite,
69 run: { id: "run_1", event: "push", ref: "refs/heads/main", sha: SHA, pull: null as number | null, status: "in_progress", actor: "ada" },
70 jobStatus: "in_progress",
71 approval: null as null | { state: string },
72 pull: { status: "open", headCommit: SHA, branch: "feature" as string | null, fork: null as null | { namespace: string; name: string } },
73 collaborator: "write",
74 plan: true,
75 /** What other services were asked, as `SERVICE method`. */
76 calls: [] as string[],
77 /** What `resolve_settings` was asked for. */
78 resolved: [] as { environment: string; trusted: boolean }[],
79 };
80 const project = () => ({
81 id: PROJECT,
82 workspace: "acme",
83 slug: "web",
84 name: "web",
85 primary: true,
86 source: { kind: "hosted", repoId: REPO, repo: { namespace: "acme", name: "web" }, defaultBranch: "main", rootDir: "site" },
87 });
88 const ok = (value: unknown) => ({ ok: true, value });
89 const service = (name: string, answer: (method: string, args: any) => unknown) => ({
90 async fetch(input: string | Request, init?: RequestInit) {
91 const url = typeof input === "string" ? input : input.url;
92 const method = new URL(url).pathname.split("/").pop()!;
93 state.calls.push(`${name} ${method}`);
94 const args = init?.body ? JSON.parse(String(init.body)) : {};
95 return Response.json((await answer(method, args)) ?? ok(null));
96 },
97 });
98 const env = {
99 DB: d1(sqlite),
100 CLOUDFLARE_API_TOKEN: "test",
101 CLOUDFLARE_ACCOUNT_ID: "acct",
102 DISPATCH_NAMESPACE: "g1t-deployments",
103 SITE: "https://g1t.sh",
104 REPOS: service("REPOS", (method) => {
105 if (method === "get") return ok({ id: REPO, namespace: "acme", name: "web", isPrivate: true, defaultBranch: "main" });
106 return ok([]);
107 }),
108 PROJECTS: service("PROJECTS", (method) => (method === "by_repo" ? [project()] : ok(project()))),
109 IDENTITY: service("IDENTITY", (method, args) => {
110 if (method === "get_workspace") return { id: `wsp_${args.slug}`, slug: args.slug };
111 if (method === "collaborator_permission") return ok({ role: state.collaborator });
112 return ok(null);
113 }),
114 BILLING: service("BILLING", (method) => {
115 if (method === "has_feature") return state.plan ? ok(true) : { ok: false, error: { code: "payment_required", message: "Deployments need the Team plan." } };
116 if (method === "check_limit") return ok({ state: "ok" });
117 if (method === "prices") return { prices: [] };
118 return ok(null);
119 }),
120 WORK: service("WORK", (method) => {
121 if (method === "get_pull") return ok({ pull: { ...state.pull, number: 9, author: { username: "eve", kind: "user" } } });
122 return ok(null);
123 }),
124 ACTIONS: service("ACTIONS", (method, args) => {
125 if (method === "run") {
126 return ok({ run: state.run, jobs: [{ id: "job_1", status: state.jobStatus }], notes: [], approval: state.approval });
127 }
128 if (method === "resolve_settings") {
129 state.resolved.push({ environment: args.environment, trusted: args.trusted });
130 return { secrets: { API_KEY: "s3cret-value" }, variables: { PUBLIC_URL: "https://example.com" } };
131 }
132 return ok(null);
133 }),
134 RUNNER: service("RUNNER", () => ok(true)),
135 };
136 return { state, env };
137}
138
139let w: ReturnType<typeof world>;
140const realFetch = globalThis.fetch;
141
142beforeEach(() => {
143 w = world();
144 globalThis.fetch = (async (input: string | Request) => {
145 const url = new URL(typeof input === "string" ? input : input.url);
146 w.state.calls.push(`CLOUDFLARE ${url.pathname}`);
147 if (url.pathname.endsWith("/graphql")) return Response.json({ data: { viewer: { accounts: [{}] } } });
148 return Response.json({ success: true, result: { jwt: "done", buckets: [] }, result_info: { total_pages: 1 } });
149 }) as typeof fetch;
150 w.state.sqlite
151 .prepare(
152 `INSERT INTO settings (project_id, repo_id, workspace, slug, enabled, previews, production, idle_days, updated_at)
153 VALUES (?, ?, 'acme', 'web', 1, 1, 1, 7, '2026-10-05T00:00:00Z')`,
154 )
155 .run(PROJECT, REPO);
156});
157
158process.on("exit", () => {
159 globalThis.fetch = realFetch;
160});
161
162const jobActor = (scopes: string[] = ["contents:read", "deployments:write"]) => ({
163 id: "wsp_acme",
164 username: "acme",
165 kind: "workspace",
166 verified: true,
167 workspaces: [{ slug: "acme", role: "member" }],
168 token: { token_id: "tok_job", repo: "acme/web", job: { run_id: "run_1", job_id: "job_1" }, scopes },
169});
170
171async function post(path: string, body: unknown): Promise<any> {
172 const response = await worker.fetch(
173 new Request(`https://deployments${path}`, { method: "POST", body: JSON.stringify(body) }),
174 w.env as never,
175 {} as never,
176 );
177 return response.json();
178}
179
180const open = (body: Record<string, unknown> = {}) =>
181 post("/rpc/start_from_workflow", { actor: jobActor(), repo: { namespace: "acme", name: "web" }, ...body });
182
183const rows = (sql: string, ...params: unknown[]) =>
184 (w.state.sqlite.prepare(sql).all(...(params as never[])) as any[]).map((row) => ({ ...row }));
185
186test("a push to the production branch opens a production build the job runs, with no sandbox and nothing reserved", async () => {
187 const answer = await open();
188 assert.equal(answer.ok, true, JSON.stringify(answer));
189 const build = answer.value;
190 assert.equal(build.kind, "production");
191 assert.equal(build.project, "web");
192 assert.equal(build.commit, SHA);
193 assert.equal(build.url, "https://web-acme.g1t.page");
194 assert.equal(build.trusted, true);
195 assert.equal(build.root_dir, "site");
196 assert.deepEqual(build.build_secrets, { API_KEY: "s3cret-value" });
197 assert.deepEqual(build.build_env, { PUBLIC_URL: "https://example.com" });
198 assert.match(build.token, /^[0-9a-f]{64}$/);
199 assert.deepEqual(rows("SELECT kind, status, run_id, job_id, created_by, trusted FROM deployments"), [
200 { kind: "production", status: "queued", run_id: "run_1", job_id: "job_1", created_by: "ada", trusted: 1 },
201 ]);
202 // The job runs it: no sandbox, and no compute reserved for it.
203 assert.ok(!w.state.calls.includes("RUNNER start_deploy"));
204 assert.ok(!w.state.calls.some((call) => call === "BILLING reserve" || call === "BILLING entitlements"));
205 // The commit's status says it is building.
206 assert.ok(w.state.calls.includes("WORK set_commit_status"));
207});
208
209test("the job reports with the build's token, and its seconds are not charged again", async () => {
210 const { value: build } = await open();
211 assert.equal((await post(`/jobs/${build.deploy_id}/started`, { token: build.token })).ok, true);
212 const finished = await post(`/jobs/${build.deploy_id}/finish`, {
213 token: build.token,
214 worker: { compatibilityDate: "2026-09-26", vars: {}, notFoundHandling: "single-page-application" },
215 completionJwt: "done",
216 buildSeconds: 42,
217 detected: "static",
218 });
219 assert.equal(finished.ok, true, JSON.stringify(finished));
220 assert.equal(rows("SELECT error FROM deployments")[0].error, null);
221 assert.deepEqual(rows("SELECT status, build_seconds, detected FROM deployments"), [{ status: "ready", build_seconds: 42, detected: "static" }]);
222 assert.deepEqual(rows("SELECT script, kind FROM apps"), [{ script: "web-acme", kind: "production" }]);
223 assert.ok(!w.state.calls.includes("BILLING charge_feature"));
224 assert.deepEqual(rows("SELECT build_seconds FROM meters WHERE build_seconds > 0"), []);
225 // The token was for that build alone, until it finished.
226 assert.equal((await post(`/jobs/${build.deploy_id}/started`, { token: build.token })).ok, false);
227});
228
229test("another branch builds its preview; a pull request's run builds the pull request's", async () => {
230 w.state.run = { ...w.state.run, ref: "refs/heads/feature/x" };
231 const branch = (await open()).value;
232 assert.deepEqual([branch.kind, branch.branch, branch.number], ["preview", "feature/x", null]);
233 w.state.run = { ...w.state.run, event: "pull_request", ref: "refs/pull/9/merge", pull: 9 };
234 const pull = (await open()).value;
235 assert.deepEqual([pull.kind, pull.branch, pull.number, pull.trusted], ["preview", "feature", 9, true]);
236 assert.deepEqual(w.state.resolved.map((r) => r.environment), ["preview", "preview"]);
237});
238
239test("a pull request from a fork, or one that had to be approved, gets no build secrets", async () => {
240 w.state.run = { ...w.state.run, event: "pull_request", ref: "refs/pull/9/merge", pull: 9 };
241 w.state.pull = { ...w.state.pull, branch: null, fork: { namespace: "eve", name: "web" } };
242 const fork = (await open()).value;
243 assert.deepEqual([fork.branch, fork.trusted, fork.build_secrets], ["pr-9", false, {}]);
244 w.state.pull = { ...w.state.pull, branch: "feature", fork: null };
245 w.state.approval = { state: "approved" };
246 const approved = (await open()).value;
247 assert.deepEqual([approved.trusted, approved.build_secrets], [false, {}]);
248 assert.deepEqual(w.state.resolved.map((r) => r.trusted), [false, false]);
249});
250
251test("it is refused without a job's write token, after the job, without the plan, or with previews off", async () => {
252 const refused = async (body: Record<string, unknown>) => (await open(body)).error?.code;
253 assert.equal(await refused({ actor: jobActor(["deployments:read"]) }), "forbidden");
254 assert.equal(await refused({ actor: { ...jobActor(), token: { token_id: "tok_person", scopes: null } } }), "forbidden");
255 assert.equal(await refused({ repo: { namespace: "acme", name: "api" } }), "forbidden");
256 assert.equal(await refused({ project: "docs" }), "not_found");
257 w.state.jobStatus = "completed";
258 assert.equal(await refused({}), "conflict");
259 w.state.jobStatus = "in_progress";
260 w.state.run = { ...w.state.run, ref: "refs/tags/v1.0.0" };
261 assert.equal(await refused({}), "invalid");
262 w.state.run = { ...w.state.run, ref: "refs/heads/main" };
263 w.state.plan = false;
264 const unpaid = await open();
265 assert.deepEqual([unpaid.ok, unpaid.error.code], [false, "payment_required"]);
266 assert.deepEqual(rows("SELECT status FROM deployments"), [{ status: "skipped" }]);
267 w.state.plan = true;
268 w.state.sqlite.prepare("UPDATE settings SET previews = 0").run();
269 w.state.run = { ...w.state.run, ref: "refs/heads/feature" };
270 assert.equal(await refused({}), "conflict");
271 assert.ok(!w.state.calls.includes("RUNNER start_deploy"));
272});