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.

141 linesCodeBlameRaw
1// One-time demo cutover. Preview by default; --apply detaches test billing.
2// Keep usage history and flagon-io. Stop on data requiring the general service
3// workflow (enterprise, purchased grants, debt, or historical month closes).
4import assert from "node:assert/strict";
5import { readFileSync, mkdirSync, writeFileSync } from "node:fs";
6import { randomUUID } from "node:crypto";
7import { getPlatformProxy } from "wrangler";
8
9const apply = process.argv.includes("--apply");
10const source = (name) => readFileSync(`services/billing/src/${name}.rs`, "utf8");
11const constant = (file, name) => {
12 const match = source(file).match(new RegExp(`const ${name}: &str = "([\\s\\S]*?)";`));
13 assert.ok(match, `${file}::${name}`);
14 return match[1];
15};
16const found = constant("detach", "FOUND").replace("{internal}", constant("sales", "INTERNAL_SQL"));
17const fixed = [
18 ...source("detach")
19 .split("const STATEMENTS: &[&str] = &[")[1]
20 .split("\n];")[0]
21 .matchAll(/"([^"]*)"/g),
22].map((m) => m[1]);
23// 16 since migration 0064: test-mode complimentary enrollments end too.
24assert.equal(fixed.length, 16, "Review changed detach statements before running");
25const at = new Date().toISOString();
26const month = at.slice(0, 7);
27const by = "codex:user-authorized-demo-cutover";
28const dir = "target/billing-downgrade";
29mkdirSync(dir, { recursive: true });
30const configPath = `${dir}/maintenance.json`;
31writeFileSync(
32 configPath,
33 JSON.stringify({
34 name: "g1t-demo-cutover",
35 account_id: "1e6f2cffa3f445920836e8ebe446bb58",
36 compatibility_date: "2026-09-26",
37 services: [{ binding: "BILLING", service: "g1t-billing", remote: true }],
38 d1_databases: [{ binding: "DB", database_name: "g1t-billing", database_id: "695a6979-fd07-4850-bc97-904a6b4b7a04", remote: true }],
39 }),
40);
41const proxy = await getPlatformProxy({ configPath, persist: false, remoteBindings: true, envFiles: [] });
42try {
43 const { DB, BILLING } = proxy.env;
44 const status = await (await BILLING.fetch("https://billing/rpc/status", { method: "POST", body: "{}" })).json();
45 assert.equal(status.live, false, "Refusing to detach live billing");
46 assert.equal(status.enabled, true);
47 const query = async (sql, args = []) =>
48 (
49 await DB.prepare(sql)
50 .bind(...args)
51 .all()
52 ).results;
53 const snapshot = await query("SELECT * FROM billing_accounts WHERE id='ws_flagon-io'");
54 assert.equal(snapshot.length, 1);
55 const tables = new Set((await query("SELECT name FROM sqlite_master WHERE type='table'")).map((r) => r.name));
56 const targets = (await query(found, [null, month])).filter((r) => r.workspace !== "flagon-io");
57 for (const row of targets) {
58 assert.equal(row.comped + row.enterprise + row.ai_grants + row.pending_past, 0, `Manual review required: ${row.workspace}`);
59 assert.equal(row.balance - Math.max(0, row.paid), 0, `Unexpected balance: ${row.workspace}`);
60 }
61 const overrides = await query(
62 "SELECT id FROM billing_accounts WHERE id <> 'ws_flagon-io' AND (team_granted <> 0 OR terms_kind <> 'standard' OR discount_percent <> 0)",
63 );
64 assert.equal(overrides.length, 0, "Additional plan grants need review");
65 writeFileSync(`${dir}/demo-${apply ? "apply" : "preview"}.json`, JSON.stringify({ at, status, targets, preserved: snapshot }, null, 2));
66 console.log(JSON.stringify({ apply, preserved: "flagon-io", targets }));
67 if (apply) {
68 const key = readFileSync(".secrets/stripe-test-key.txt", "utf8").trim();
69 assert.match(key, /^(sk|rk)_test_/);
70 const version = source("stripe").match(/STRIPE_VERSION: &str = "([^"]+)"/)[1];
71 const stripe = async (path, method = "GET") => {
72 const response = await fetch(`https://api.stripe.com/v1${path}`, { method, headers: { Authorization: `Bearer ${key}`, "Stripe-Version": version } });
73 const value = await response.json();
74 assert.ok(response.ok, `Stripe ${method}: ${response.status} ${value.error?.code ?? ""}`);
75 assert.equal(value.livemode, false, "Expected a test object");
76 return value;
77 };
78 for (const row of targets) {
79 const workspace = row.workspace;
80 const subscriptions = await query("SELECT subscription_id FROM subscriptions WHERE workspace=? AND status <> 'canceled'", [workspace]);
81 const customer = (await query("SELECT customer_id FROM accounts WHERE workspace=?", [workspace]))[0]?.customer_id;
82 if (customer) await stripe(`/customers/${customer}`);
83 for (const { subscription_id: id } of subscriptions) {
84 const subscription = await stripe(`/subscriptions/${id}`);
85 assert.equal(subscription.customer, customer, "Subscription/customer mismatch");
86 if (subscription.status !== "canceled") {
87 assert.equal((await stripe(`/subscriptions/${id}`, "DELETE")).status, "canceled");
88 }
89 }
90 // Refresh after Stripe calls. Each workspace's local changes commit
91 // together; interruption is retryable, including canceled subscriptions.
92 const current = (await query(found, [workspace, month]))[0];
93 assert.equal(current.balance - Math.max(0, current.paid), 0);
94 assert.equal(current.ai_grants + current.pending_past + current.enterprise + current.comped, 0);
95 const values = [workspace, at, by, month];
96 const statements = fixed
97 .filter((sql) => tables.has("invoice_attempts") || !sql.includes("invoice_attempts"))
98 .map((sql) => {
99 const count = Math.max(...[...sql.matchAll(/\?(\d+)/g)].map((m) => Number(m[1])));
100 return DB.prepare(sql).bind(...values.slice(0, count));
101 });
102 if (current.paid > 0) {
103 statements.push(
104 DB.prepare(constant("detach", "LEDGER")).bind(
105 `led_${randomUUID()}`,
106 workspace,
107 -current.paid,
108 "Reverse demo payments before live billing",
109 `test_detach/${workspace}/${at}`,
110 by,
111 at,
112 null,
113 ),
114 );
115 statements.push(DB.prepare(constant("detach", "BALANCE")).bind(workspace, -current.paid, at));
116 }
117 statements.push(
118 DB.prepare("INSERT INTO admin_actions (id, account, action, detail, by, created_at) VALUES (?, ?, ?, ?, ?, ?)").bind(
119 `adm_${randomUUID()}`,
120 `ws_${workspace}`,
121 "test_detach",
122 "User requested Free plan before live launch; Stripe test subscriptions canceled; demo payments reversed; history retained.",
123 by,
124 at,
125 ),
126 );
127 await DB.batch(statements);
128 console.log(JSON.stringify({ detached: workspace }));
129 }
130 assert.deepEqual(await query("SELECT * FROM billing_accounts WHERE id='ws_flagon-io'"), snapshot);
131 const remaining = (await query(found, [null, month])).filter((r) => r.workspace !== "flagon-io");
132 for (const row of remaining) {
133 for (const field of ["subscriptions", "customer", "card_cached", "balance", "paid", "plan_unreversed", "plan_credit", "pending_now", "open_disputes"])
134 assert.equal(row[field], 0, `${row.workspace}: ${field}`);
135 }
136 writeFileSync(`${dir}/demo-verified.json`, JSON.stringify({ at: new Date().toISOString(), rows: remaining, flagonPreserved: true }, null, 2));
137 console.log("Verified: non-flagon workspaces detached; flagon-io terms preserved.");
138 }
139} finally {
140 await proxy.dispose();
141}