-
Notifications
You must be signed in to change notification settings - Fork 6.2k
Add desktop perf trace automation #59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a1177c7
8d90f14
25cc139
41caab9
419ecc0
220497a
ae08eb8
758b3e1
4622dbd
a293a01
0bca5a4
6721804
c48a7b9
1d76eb5
2dc8fcd
47d5dc2
e666957
1a98fc6
1a39d75
ff45694
ca93003
42ef93a
52920ab
5c68c77
a1dfc84
7f6786f
0014818
ae27c20
cbbfd0c
2c0a3bd
ae1626b
1314980
142b728
adba5d8
91fb3a5
a41c0ab
e69fe94
9b99c9b
c2966e6
ae6712b
cdf7b01
eb20d1f
0e26c94
4339050
356a5c4
6365657
2b96cc2
7f5e9a7
51b89ef
def1086
33eee2e
b6036ce
5f7e22a
e0e3e3a
709a5c1
e70bc7b
d95ccbf
142ce0f
2afff24
edd758d
37dfce2
179e24a
c3b5475
0a733fe
7aff63f
a787ec4
2090002
f0c5334
5069734
65b9144
7ed1df1
8c1a85e
c2ddc46
5eb73a8
5d9bade
87aa2d6
cd2846b
ef95679
0542b8e
1a6b684
a122cb7
273a84c
b0eef5c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,24 +1,184 @@ | ||
| import { spawn } from "node:child_process"; | ||
|
|
||
| import waitOn from "wait-on"; | ||
| import fs from "node:fs"; | ||
| import net from "node:net"; | ||
| import path from "node:path"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unused
|
||
|
|
||
| const port = Number(process.env.ELECTRON_RENDERER_PORT ?? 5173); | ||
| const devServerUrl = `http://localhost:${port}`; | ||
|
|
||
| await waitOn({ | ||
| resources: [ | ||
| `tcp:${port}`, | ||
| "file:dist-electron/main.js", | ||
| "file:dist-electron/preload.js", | ||
| "file:../server/dist/index.mjs", | ||
| ], | ||
| }); | ||
| const STARTUP_TIMEOUT_MS = Number(process.env.T3CODE_ELECTRON_STARTUP_TIMEOUT_MS ?? 120_000); | ||
|
|
||
| async function canConnect(host, probePort, timeoutMs = 1_000) { | ||
| return new Promise((resolve) => { | ||
| const socket = new net.Socket(); | ||
| let settled = false; | ||
|
|
||
| const finish = (value) => { | ||
| if (settled) return; | ||
| settled = true; | ||
| socket.destroy(); | ||
| resolve(value); | ||
| }; | ||
|
|
||
| socket.setTimeout(timeoutMs); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟢 Low
🤖 Prompt for AI |
||
| socket.once("connect", () => finish(true)); | ||
| socket.once("timeout", () => finish(false)); | ||
| socket.once("error", () => finish(false)); | ||
| socket.connect(probePort, host); | ||
| }); | ||
| } | ||
|
|
||
|
macroscopeapp[bot] marked this conversation as resolved.
|
||
| const f = (p) => path.join(import.meta.dirname, "..", p); | ||
|
|
||
| function inspectBundleFile(bundleFile) { | ||
| let stat = null; | ||
| try { | ||
| stat = fs.statSync(bundleFile.path); | ||
| } catch { | ||
| return { | ||
| ...bundleFile, | ||
| status: "missing", | ||
| mtimeMs: null, | ||
| reason: "not found", | ||
| }; | ||
| } | ||
|
|
||
| return { | ||
| ...bundleFile, | ||
| status: "ready", | ||
| mtimeMs: stat.mtimeMs, | ||
| reason: "ok", | ||
| }; | ||
| } | ||
|
|
||
| function describeBundleStates(states) { | ||
| return states | ||
| .map((state) => { | ||
| const mtime = | ||
| typeof state.mtimeMs === "number" ? new Date(state.mtimeMs).toISOString() : "n/a"; | ||
| return `${state.label}=${state.status} (mtime=${mtime}; reason=${state.reason})`; | ||
| }) | ||
| .join("; "); | ||
| } | ||
|
|
||
| function waitForDesktopBundles(timeoutMs) { | ||
| const startedAt = Date.now(); | ||
| const bundleFiles = [ | ||
| { | ||
| path: f("dist-electron/main.mjs"), | ||
| label: "desktop/main.mjs", | ||
| }, | ||
| { | ||
| path: f("dist-electron/preload.cjs"), | ||
| label: "desktop/preload.cjs", | ||
| }, | ||
| { | ||
| path: f("../server/dist/index.mjs"), | ||
| label: "server/index.mjs", | ||
| }, | ||
| ]; | ||
| let lastProgressLogAt = 0; | ||
|
|
||
| return new Promise((resolve, reject) => { | ||
| const tick = () => { | ||
| const states = bundleFiles.map(inspectBundleFile); | ||
| const missing = states.filter((state) => state.status === "missing"); | ||
|
|
||
| if (missing.length === 0) { | ||
| resolve(); | ||
| return; | ||
| } | ||
|
|
||
| const elapsedMs = Date.now() - startedAt; | ||
| if (elapsedMs >= timeoutMs) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🟢 Low
🤖 Prompt for AI |
||
| const parts = []; | ||
| if (missing.length > 0) { | ||
| parts.push(`missing: ${missing.map((state) => state.label).join(", ")}`); | ||
| } | ||
| reject( | ||
| new Error( | ||
| `[dev-electron] timed out after ${timeoutMs}ms waiting for bundles (${parts.join("; ")})\n[dev-electron] bundle state: ${describeBundleStates(states)}`, | ||
| ), | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| if (Date.now() - lastProgressLogAt >= 5_000) { | ||
| lastProgressLogAt = Date.now(); | ||
| const waitParts = []; | ||
| if (missing.length > 0) { | ||
| waitParts.push(`missing=${missing.map((state) => state.label).join(",")}`); | ||
| } | ||
| console.log( | ||
| `[dev-electron] still waiting for bundles after ${elapsedMs}ms (${waitParts.join("; ")})`, | ||
| ); | ||
| } | ||
|
|
||
| setTimeout(tick, 250); | ||
| }; | ||
|
|
||
| tick(); | ||
| }); | ||
| } | ||
|
|
||
| function waitForDevServer(probePort, timeoutMs) { | ||
| const startedAt = Date.now(); | ||
| const candidates = [ | ||
| { host: "127.0.0.1", url: `http://127.0.0.1:${probePort}` }, | ||
| { host: "::1", url: `http://[::1]:${probePort}` }, | ||
| { host: "localhost", url: `http://localhost:${probePort}` }, | ||
| ]; | ||
|
|
||
| return new Promise((resolve, reject) => { | ||
| const tick = () => { | ||
| void Promise.all( | ||
| candidates.map(async (candidate) => ({ | ||
| candidate, | ||
| connected: await canConnect(candidate.host, probePort), | ||
| })), | ||
| ) | ||
| .then((results) => { | ||
| const ready = results.find((entry) => entry.connected); | ||
| if (ready) { | ||
| resolve(ready.candidate.url); | ||
| return; | ||
| } | ||
|
|
||
| if (Date.now() - startedAt >= timeoutMs) { | ||
| reject( | ||
| new Error( | ||
| `[dev-electron] timed out after ${timeoutMs}ms waiting for renderer dev server on port ${probePort}`, | ||
| ), | ||
| ); | ||
| return; | ||
| } | ||
|
|
||
| setTimeout(tick, 250); | ||
| }) | ||
| .catch((error) => { | ||
| reject(error); | ||
| }); | ||
| }; | ||
|
|
||
| tick(); | ||
| }); | ||
| } | ||
|
|
||
| console.log("[dev-electron] waiting for desktop/server bundles"); | ||
| await waitForDesktopBundles(STARTUP_TIMEOUT_MS); | ||
|
|
||
| console.log(`[dev-electron] waiting for renderer dev server on port ${port}`); | ||
| const devServerUrl = await waitForDevServer(port, STARTUP_TIMEOUT_MS); | ||
| console.log(`[dev-electron] launching electron with renderer url ${devServerUrl}`); | ||
|
|
||
| const command = process.platform === "win32" ? "electronmon.cmd" : "electronmon"; | ||
| const childEnv = { ...process.env }; | ||
| delete childEnv.ELECTRON_RUN_AS_NODE; | ||
|
|
||
| const child = spawn(command, ["dist-electron/main.js"], { | ||
| if (process.platform === "linux" && !childEnv.ELECTRON_DISABLE_SANDBOX) { | ||
| childEnv.ELECTRON_DISABLE_SANDBOX = "1"; | ||
| console.log("[dev-electron] enabling ELECTRON_DISABLE_SANDBOX=1 on Linux"); | ||
| } | ||
|
|
||
| const child = spawn(command, ["dist-electron/main.mjs"], { | ||
| stdio: "inherit", | ||
| env: { | ||
| ...childEnv, | ||
|
|
@@ -29,3 +189,8 @@ const child = spawn(command, ["dist-electron/main.js"], { | |
| child.on("exit", (code) => { | ||
| process.exit(code ?? 0); | ||
| }); | ||
|
|
||
| child.on("error", (error) => { | ||
| console.error("[dev-electron] failed to launch electronmon:", error.message); | ||
| process.exit(1); | ||
| }); | ||


Uh oh!
There was an error while loading. Please reload this page.