History

fix embedded web ui release smoke

Rhys Sullivan committed 7 months agov1.4.0-beta.5

Commit 2365bf51 parent fa61e979 files · +83 −15Browse files

Unified · Split

9 files
  1. .changeset/green-bobcats-shop.md +5 −0
  2. .changeset/pre.json +1 −0
  3. apps/cli/package.json +1 −1
  4. apps/cli/src/build.ts +4 −2
  5. apps/cli/src/embedded-web-ui.gen.d.ts +1 −1
  6. apps/cli/src/embedded-web-ui.gen.ts +3 −0
  7. apps/cli/src/main.ts +1 −4
  8. bun.lock +1 −1
  9. tests/release-bootstrap-smoke.test.ts +66 −6
.changeset/green-bobcats-shop.md+5−0
@@ -0,0 +1,5 @@
1+---
2+"executor": patch
3+---
4+
5+Fix compiled web serving so embedded UI assets are bundled into release binaries, and extend the bootstrap smoke test to verify the root web page and docs routes respond successfully.
.changeset/pre.json+1−0
@@ -26,6 +26,7 @@ · 25 unmodified lines
2626 "@executor/ui": "1.4.0-beta.0"
2727 },
2828 "changesets": [
29+ "green-bobcats-shop",
2930 "kind-cycles-hang",
3031 "ripe-yaks-sniff",
3132 "tiny-melons-jump"
apps/cli/package.json+1−1
@@ -1,6 +1,6 @@
11 {
22 "name": "executor",
3− "version": "1.4.0-beta.4",
3+ "version": "1.4.0-beta.5",
44 "private": true,
55 "type": "module",
66 "bin": {
apps/cli/src/build.ts+4−2
@@ -121,6 +121,8 @@ const createEmbeddedWebUISource = async () => { · 120 unmodified lines
121121 // Build platform binaries
122122 // ---------------------------------------------------------------------------
123123
124+const EMBEDDED_WEB_UI_STUB = `const files: Record<string, string> | null = null;\n\nexport default files;\n`;
125+
124126 const buildBinaries = async (targets: Target[]) => {
125127 const meta = await readMetadata();
126128 const binaries: Record<string, string> = {};
@@ -143,7 +145,7 @@ const buildBinaries = async (targets: Target[]) => { · 16 unmodified lines
143145 console.log(`Building ${name}...`);
144146
145147 await Bun.build({
146− entrypoints: [join(cliRoot, "src/main.ts"), embeddedWebUIPath],
148+ entrypoints: [join(cliRoot, "src/main.ts")],
147149 minify: true,
148150 compile: {
149151 target: bunTarget(target) as any,
@@ -183,7 +185,7 @@ const buildBinaries = async (targets: Target[]) => { · 33 unmodified lines
183185
184186 return binaries;
185187 } finally {
186− await rm(embeddedWebUIPath, { force: true });
188+ await writeFile(embeddedWebUIPath, EMBEDDED_WEB_UI_STUB);
187189 }
188190 };
189191
apps/cli/src/embedded-web-ui.gen.d.ts+1−1
@@ -1,2 +1,2 @@
1−declare const files: Record<string, string>;
1+declare const files: Record<string, string> | null;
22 export default files;
apps/cli/src/embedded-web-ui.gen.ts+3−0
@@ -0,0 +1,3 @@
1+const files: Record<string, string> | null = null;
2+
3+export default files;
apps/cli/src/main.ts+1−4
@@ -40,10 +40,7 @@ const DEFAULT_PORT = 8788; · 39 unmodified lines
4040 const DEFAULT_BASE_URL = `http://localhost:${DEFAULT_PORT}`;
4141
4242 // Embedded web UI — baked into compiled binaries via `with { type: "file" }`
43−const embeddedWebUI: Record<string, string> | null =
44− await import("./embedded-web-ui.gen")
45− .then((m) => m.default as Record<string, string>)
46− .catch(() => null);
43+import embeddedWebUI from "./embedded-web-ui.gen";
4744
4845 // ---------------------------------------------------------------------------
4946 // Helpers
bun.lock+1−1
@@ -18,7 +18,7 @@ · 17 unmodified lines
1818 },
1919 "apps/cli": {
2020 "name": "executor",
21− "version": "1.4.0-beta.4",
21+ "version": "1.4.0-beta.5",
2222 "bin": {
2323 "executor": "./bin/executor.ts",
2424 },
tests/release-bootstrap-smoke.test.ts+66−6
@@ -161,15 +161,75 @@ describe("release bootstrap smoke", () => { · 160 unmodified lines
161161 const installedBinaryStat = await readFile(installedBinaryPath);
162162 expect(installedBinaryStat.byteLength).toBeGreaterThan(0);
163163
164− const secondRun = await runCommand(
164+ const probeServer = createServer((_, response) => {
165+ response.statusCode = 204;
166+ response.end();
167+ });
168+ const webPort = await listen(probeServer);
169+ await closeServer(probeServer);
170+
171+ const webProcess = spawn(
165172 process.execPath,
166− [join(installedPackageDir, "bin", "executor"), "--help"],
167− installedPackageDir,
173+ [join(installedPackageDir, "bin", "executor"), "web", "--port", String(webPort)],
174+ {
175+ cwd: installedPackageDir,
176+ stdio: ["ignore", "pipe", "pipe"],
177+ },
168178 );
169− const secondCombinedOutput = `${secondRun.stdout}\n${secondRun.stderr}`;
170179
171− expect(secondRun.exitCode, secondCombinedOutput).toBe(0);
172− expect(secondCombinedOutput).not.toContain("downloading release asset");
180+ let webStdout = "";
181+ let webStderr = "";
182+ webProcess.stdout.setEncoding("utf8");
183+ webProcess.stdout.on("data", (chunk) => {
184+ webStdout += chunk;
185+ });
186+ webProcess.stderr.setEncoding("utf8");
187+ webProcess.stderr.on("data", (chunk) => {
188+ webStderr += chunk;
189+ });
190+
191+ try {
192+ const deadline = Date.now() + 30_000;
193+ let rootResponse: Response | null = null;
194+ while (Date.now() < deadline) {
195+ await new Promise((resolveDelay) => setTimeout(resolveDelay, 250));
196+ try {
197+ rootResponse = await fetch(`http://127.0.0.1:${webPort}/`);
198+ if (rootResponse.ok) {
199+ break;
200+ }
201+ } catch {
202+ // keep polling until the server is ready
203+ }
204+ }
205+
206+ expect(rootResponse, `${webStdout}\n${webStderr}`).not.toBeNull();
207+ expect(rootResponse!.status, `${webStdout}\n${webStderr}`).toBe(200);
208+ const rootHtml = await rootResponse!.text();
209+ expect(rootHtml.toLowerCase()).toContain("<html");
210+
211+ const docsResponse = await fetch(`http://127.0.0.1:${webPort}/docs`);
212+ expect(docsResponse.status, `${webStdout}\n${webStderr}`).toBe(200);
213+
214+ const secondRun = await runCommand(
215+ process.execPath,
216+ [join(installedPackageDir, "bin", "executor"), "--help"],
217+ installedPackageDir,
218+ );
219+ const secondCombinedOutput = `${secondRun.stdout}\n${secondRun.stderr}`;
220+
221+ expect(secondRun.exitCode, secondCombinedOutput).toBe(0);
222+ expect(secondCombinedOutput).not.toContain("downloading release asset");
223+ } finally {
224+ webProcess.kill("SIGTERM");
225+ await Promise.race([
226+ new Promise((resolveClose) => webProcess.once("close", () => resolveClose(undefined))),
227+ new Promise((resolveClose) => setTimeout(resolveClose, 5_000)),
228+ ]);
229+ if (webProcess.exitCode === null) {
230+ webProcess.kill("SIGKILL");
231+ }
232+ }
173233 } finally {
174234 await closeServer(server);
175235 await rm(tempRoot, { recursive: true, force: true });