From 35e47ada309ef791dc64d6f08a230922ae9cfe84 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 13 Oct 2026 03:00:15 +0000
Subject: [PATCH] revert: restore last successful main state
Failed run: https://github.com/MonaDevAI/FixLab/actions/runs/38339505999
---
dashboard/public/app.js | 66 ------------
dashboard/public/index.html | 12 ---
dashboard/server.js | 155 -----------------------------
docs/getting-started.md | 8 --
test/dashboard-auth-resume.test.js | 15 ---
test/dashboard-server.test.js | 117 ----------------------
6 files changed, 373 deletions(-)
diff --git a/dashboard/public/app.js b/dashboard/public/app.js
index 1f4675e..5b614a0 100644
--- a/dashboard/public/app.js
+++ b/dashboard/public/app.js
@@ -121,10 +121,6 @@ const playwrightStatus = document.querySelector("#playwright-status");
const playwrightGuidance = document.querySelector("#playwright-guidance");
const playwrightCheck = document.querySelector("#playwright-check");
const playwrightConnect = document.querySelector("#playwright-connect");
-const frontendStatusBadge = document.querySelector("#frontend-status");
-const frontendGuidance = document.querySelector("#frontend-guidance");
-const frontendStart = document.querySelector("#frontend-start");
-const frontendStop = document.querySelector("#frontend-stop");
const onboardingStatus = document.querySelector("#onboarding-status");
const onboardingOrganization = document.querySelector(
"#onboarding-organization"
@@ -576,64 +572,6 @@ async function resumeAfterConnect(status) {
}
}
-function renderFrontendStatus(status) {
- frontendStatusBadge.textContent = !status.configured
- ? "Not configured"
- : status.healthy
- ? "Ready"
- : status.running
- ? "Starting…"
- : "Stopped";
- frontendStart.disabled =
- !status.configured || status.running || status.healthy;
- frontendStop.disabled = !status.running;
- frontendGuidance.textContent = status.error
- ? status.error
- : status.healthy
- ? `React is answering at ${status.healthUrl}. Playwright authentication and live tests reuse it.`
- : status.running
- ? "React is compiling. The first start can take several minutes; this updates automatically."
- : status.lastResult && !status.lastResult.ok
- ? status.lastResult.message
- : "Start the profile-defined React frontend ahead of time so Playwright authentication and live tests reuse it instead of waiting for a cold compile.";
-}
-
-async function refreshFrontendStatus() {
- try {
- renderFrontendStatus(await fetchJson("/api/frontend/status"));
- } catch (error) {
- frontendGuidance.textContent = error.message;
- }
-}
-
-async function startFrontend() {
- try {
- renderFrontendStatus(
- await fetchJson("/api/frontend/start", { method: "POST" })
- );
- } catch {
- await refreshFrontendStatus();
- }
-}
-
-frontendStart.addEventListener("click", () => {
- formError.textContent = "";
- frontendStart.disabled = true;
- void startFrontend();
-});
-
-frontendStop.addEventListener("click", async () => {
- frontendStop.disabled = true;
- try {
- renderFrontendStatus(
- await fetchJson("/api/frontend/stop", { method: "POST" })
- );
- } catch (error) {
- formError.textContent = error.message;
- await refreshFrontendStatus();
- }
-});
-
async function refreshPlaywrightStatus() {
try {
renderPlaywrightStatus(await fetchJson("/api/playwright/status"));
@@ -1433,7 +1371,6 @@ connectRetryLiveTestButton.addEventListener("click", async () => {
connectRetryLiveTestButton.disabled = true;
try {
pendingRetryAfterConnectJobId = activeJob?.id ?? null;
- await startFrontend();
renderPlaywrightStatus(
await fetchJson("/api/playwright/connect", { method: "POST" })
);
@@ -1510,7 +1447,6 @@ playwrightConnect.addEventListener("click", async () => {
formError.textContent = "";
playwrightConnect.disabled = true;
try {
- await startFrontend();
renderPlaywrightStatus(
await fetchJson("/api/playwright/connect", { method: "POST" })
);
@@ -1550,11 +1486,9 @@ onboardingSave.addEventListener("click", async () => {
refresh();
refreshMetrics();
refreshPlaywrightStatus();
-refreshFrontendStatus();
refreshPlaywrightEvidence();
refreshOnboarding();
setInterval(refresh, 1000);
setInterval(refreshMetrics, 5000);
setInterval(refreshPlaywrightStatus, 5000);
-setInterval(refreshFrontendStatus, 5000);
setInterval(refreshPlaywrightEvidence, 10000);
diff --git a/dashboard/public/index.html b/dashboard/public/index.html
index a259557..3786a1b 100644
--- a/dashboard/public/index.html
+++ b/dashboard/public/index.html
@@ -29,18 +29,6 @@
Repository onboarding
diff --git a/dashboard/server.js b/dashboard/server.js
index 06954cb..e3942d5 100644
--- a/dashboard/server.js
+++ b/dashboard/server.js
@@ -2186,77 +2186,6 @@ function playwrightAuthenticationConfig(repository) {
};
}
-function frontendStartConfig(repository) {
- const profile = loadRepositoryProfile(repository);
- const frontend = profile.applications?.frontend ?? {};
- const command =
- typeof frontend.command === "string" ? frontend.command.trim() : "";
- if (!command) {
- throw new Error("repository profile must configure applications.frontend.command");
- }
- return {
- command,
- healthUrl: validatedManualLocalhostUrl(frontend.healthUrl),
- workingDirectory: resolveContainedPath(
- repository,
- frontend.workingDirectory ?? ".",
- "applications.frontend.workingDirectory",
- { mustExist: true }
- )
- };
-}
-
-async function frontendIsHealthy(healthUrl) {
- try {
- const response = await fetch(healthUrl, {
- signal: AbortSignal.timeout(2000)
- });
- await response.body?.cancel();
- return response.status < 500;
- } catch {
- return false;
- }
-}
-
-function stopProcessTree(child) {
- if (!child || child.exitCode !== null || child.signalCode !== null) {
- return;
- }
- if (process.platform === "win32" && child.pid) {
- spawnSync("taskkill", ["/pid", String(child.pid), "/t", "/f"], {
- stdio: "ignore",
- windowsHide: true
- });
- return;
- }
- child.kill();
-}
-
-async function frontendStatus(repository, connection) {
- let healthUrl = "";
- try {
- ({ healthUrl } = frontendStartConfig(repository));
- } catch (error) {
- return {
- configured: false,
- running: Boolean(connection.handle),
- healthy: false,
- owned: Boolean(connection.handle),
- error: error.message,
- lastResult: connection.lastResult
- };
- }
- return {
- configured: true,
- running: Boolean(connection.handle),
- healthy: await frontendIsHealthy(healthUrl),
- owned: Boolean(connection.handle),
- healthUrl,
- error: "",
- lastResult: connection.lastResult
- };
-}
-
function playwrightAuthenticationStatus(repository, connection) {
let configuration;
try {
@@ -2434,7 +2363,6 @@ export function createDashboardServer({
const completedJobs = [];
const artifactDirectories = new Set();
const playwrightConnection = { handle: null, lastResult: null };
- const frontendConnection = { handle: null, lastResult: null };
const playwrightArtifacts = new Map();
const cacheContext = createCacheContext(resolvedRepository);
const metricsPath = cacheContext
@@ -3714,87 +3642,6 @@ export function createDashboardServer({
return;
}
- if (
- request.method === "GET" &&
- requestUrl.pathname === "/api/frontend/status"
- ) {
- sendJson(
- response,
- 200,
- await frontendStatus(resolvedRepository, frontendConnection)
- );
- return;
- }
-
- if (
- request.method === "POST" &&
- requestUrl.pathname === "/api/frontend/start"
- ) {
- let configuration;
- try {
- configuration = frontendStartConfig(resolvedRepository);
- } catch (error) {
- sendJson(response, 400, { error: error.message });
- return;
- }
- if (frontendConnection.handle) {
- sendJson(response, 409, { error: "FixLab already started the frontend" });
- return;
- }
- if (await frontendIsHealthy(configuration.healthUrl)) {
- sendJson(response, 200, {
- ...(await frontendStatus(resolvedRepository, frontendConnection)),
- started: false
- });
- return;
- }
- frontendConnection.lastResult = null;
- const child = spawn(configuration.command, {
- cwd: configuration.workingDirectory,
- shell: true,
- stdio: "ignore",
- windowsHide: true
- });
- frontendConnection.handle = child;
- child.once("error", (error) => {
- frontendConnection.lastResult = {
- ok: false,
- message: error.message,
- finishedAt: new Date().toISOString()
- };
- frontendConnection.handle = null;
- });
- child.once("close", (code) => {
- if (frontendConnection.handle === child) {
- frontendConnection.handle = null;
- }
- frontendConnection.lastResult = {
- ok: code === 0,
- message: `Frontend process exited with code ${code ?? "unknown"}.`,
- finishedAt: new Date().toISOString()
- };
- });
- sendJson(response, 202, {
- ...(await frontendStatus(resolvedRepository, frontendConnection)),
- started: true
- });
- return;
- }
-
- if (
- request.method === "POST" &&
- requestUrl.pathname === "/api/frontend/stop"
- ) {
- const child = frontendConnection.handle;
- frontendConnection.handle = null;
- stopProcessTree(child);
- sendJson(response, 200, {
- ...(await frontendStatus(resolvedRepository, frontendConnection)),
- stopped: Boolean(child)
- });
- return;
- }
-
if (request.method === "GET" && staticFiles.has(requestUrl.pathname)) {
const fileName = staticFiles.get(requestUrl.pathname);
const filePath = join(publicDirectory, fileName);
@@ -3846,8 +3693,6 @@ export function createDashboardServer({
) {
playwrightConnection.handle.kill();
}
- stopProcessTree(frontendConnection.handle);
- frontendConnection.handle = null;
for (const directory of artifactDirectories) {
removeArtifactDirectory(directory);
}
diff --git a/docs/getting-started.md b/docs/getting-started.md
index 93e0f9b..3bc760b 100644
--- a/docs/getting-started.md
+++ b/docs/getting-started.md
@@ -226,14 +226,6 @@ runs the same configured command, waits for you to sign in, and retries the
live test automatically only after the command exits successfully. Sign-in
always stays with you; FixLab never handles credentials.
-The **React frontend** panel starts the profile's `applications.frontend.command`
-in `applications.frontend.workingDirectory` ahead of time and watches
-`healthUrl`. Connect Playwright starts it automatically, so authentication and
-live tests reuse a warm server instead of waiting for a cold compile. FixLab
-stops only the process it started, when you click **Stop React** or the
-dashboard exits, and never touches a server that was already running. A
-Playwright config that sets `reuseExistingServer` picks up the running server.
-
The dashboard remains **Not ready** until the profile also includes a frontend
The dashboard remains **Not ready** until the profile also includes a frontend **Not ready** until the profile also includes a frontendstartup command, loopback health URL, Playwright test command, explicit
diff --git a/test/dashboard-auth-resume.test.js b/test/dashboard-auth-resume.test.js
index bd28511..c638438 100644
--- a/test/dashboard-auth-resume.test.js
+++ b/test/dashboard-auth-resume.test.js
@@ -23,21 +23,6 @@ test("connect-then-retry starts the profile authentication through the API", ()
assert.match(handler, /method: "POST"/u);
});
-test("connecting Playwright starts the frontend first", () => {
- for (const marker of [
- 'playwrightConnect.addEventListener("click"',
- 'connectRetryLiveTestButton.addEventListener("click"'
- ]) {
- const handler = appSource.slice(appSource.indexOf(marker));
- assert.ok(
- handler.indexOf("await startFrontend()") <
- handler.indexOf("/api/playwright/connect"),
- marker
- );
- }
- assert.match(appSource, /\/api\/frontend\/start/u);
-});
-
test("the live test is retried only after successful authentication", () => {
const start = appSource.indexOf("async function resumeAfterConnect");
const body = appSource.slice(
diff --git a/test/dashboard-server.test.js b/test/dashboard-server.test.js
index 340fd95..0fa2837 100644
--- a/test/dashboard-server.test.js
+++ b/test/dashboard-server.test.js
@@ -11,7 +11,6 @@ import {
symlinkSync,
writeFileSync
} from "node:fs";
-import { createServer as createHttpServer } from "node:http";
import { createServer } from "node:net";
import { tmpdir } from "node:os";
import { dirname, join, parse } from "node:path";
@@ -1645,122 +1644,6 @@ test("dashboard checks and starts repository-owned Playwright authentication", a
}
});
-test("dashboard starts, reuses and stops the profile-defined frontend", async () => {
- const repository = createRepository();
- const port = await availablePort();
- writeFileSync(
- join(repository, ".github", "fixlab", "repository-profile.json"),
- JSON.stringify(
- validProfile({
- applications: {
- frontend: {
- workingDirectory: ".",
- command: "node frontend.js",
- port,
- healthUrl: `http://127.0.0.1:${port}`
- }
- }
- })
- )
- );
- writeFileSync(
- join(repository, "frontend.js"),
- `require("node:http").createServer((q, s) => s.end("ok")).listen(${port}, "127.0.0.1");`
- );
- const { dashboard, url } = await startDashboard(repository, () => ({
- completion: new Promise(() => {}),
- terminate() {}
- }));
-
- try {
- const initial = await jsonRequest(url, "/api/frontend/status");
- assert.equal(initial.body.configured, true);
- assert.equal(initial.body.healthy, false);
-
- const started = await jsonRequest(url, "/api/frontend/start", {
- method: "POST"
- });
- assert.equal(started.response.status, 202);
- assert.equal(started.body.started, true);
-
- let status;
- for (let attempt = 0; attempt < 80; attempt += 1) {
- status = await jsonRequest(url, "/api/frontend/status");
- if (status.body.healthy) {
- break;
- }
- await new Promise((resolve) => setTimeout(resolve, 100));
- }
- assert.equal(status.body.healthy, true);
- assert.equal(status.body.owned, true);
-
- const again = await jsonRequest(url, "/api/frontend/start", {
- method: "POST"
- });
- assert.equal(again.response.status, 409);
-
- const stopped = await jsonRequest(url, "/api/frontend/stop", {
- method: "POST"
- });
- assert.equal(stopped.body.stopped, true);
- for (let attempt = 0; attempt < 50; attempt += 1) {
- status = await jsonRequest(url, "/api/frontend/status");
- if (!status.body.healthy) {
- break;
- }
- await new Promise((resolve) => setTimeout(resolve, 100));
- }
- assert.equal(status.body.healthy, false);
- } finally {
- await dashboard.close();
- rmSync(repository, { recursive: true, force: true });
- }
-});
-
-test("dashboard does not restart a frontend that is already healthy", async () => {
- const repository = createRepository();
- const port = await availablePort();
- writeFileSync(
- join(repository, ".github", "fixlab", "repository-profile.json"),
- JSON.stringify(
- validProfile({
- applications: {
- frontend: {
- workingDirectory: ".",
- command: "node does-not-exist.js",
- port,
- healthUrl: `http://127.0.0.1:${port}`
- }
- }
- })
- )
- );
- const external = await new Promise((resolve) => {
- const instance = createHttpServer((request, response) =>
- response.end("ok")
- );
- instance.listen(port, "127.0.0.1", () => resolve(instance));
- });
- const { dashboard, url } = await startDashboard(repository, () => ({
- completion: new Promise(() => {}),
- terminate() {}
- }));
-
- try {
- const result = await jsonRequest(url, "/api/frontend/start", {
- method: "POST"
- });
- assert.equal(result.response.status, 200);
- assert.equal(result.body.started, false);
- assert.equal(result.body.healthy, true);
- assert.equal(result.body.owned, false);
- } finally {
- await dashboard.close();
- await new Promise((resolve) => external.close(resolve));
- rmSync(repository, { recursive: true, force: true });
- }
-});
-
test("zero exit with missing terminal markers fails as incomplete", async () => {
const repository = createRepository();
const executor = ({ onOutput }) => {