mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-06 16:34:36 +00:00
fix(daemon): name the cause when a background start fails before logging (#5332)
`paseo daemon start` runs the supervisor with its output discarded, and the supervisor opens its log only after config.json loads and the PID lock is held. A failure before that point went to stderr only, so the launcher pointed at a daemon.log that was never written and showed no cause. Startup failures now also go to the log path the launcher reports, so the error message tails the real cause. Closes #5327 Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
This commit is contained in:
@@ -591,6 +591,25 @@ test("malformed configuration cannot turn a readiness timeout into launch cancel
|
||||
}
|
||||
}, 60_000);
|
||||
|
||||
test("a background start that fails before the supervisor logs names the cause in a log that exists", async () => {
|
||||
const f = await fixture();
|
||||
const home = f.homes[0]!;
|
||||
const configPath = path.join(home, "config.json");
|
||||
try {
|
||||
await mkdir(home, { recursive: true });
|
||||
await writeFile(configPath, '{"version":1,');
|
||||
const failed = await f.run(["daemon", "start", "--home", home, "--timeout", "30"]);
|
||||
expect(failed.code).toBe(1);
|
||||
expect(failed.stderr).toContain(`Logs: ${path.join(home, "daemon.log")}`);
|
||||
expect(failed.stderr).toContain(`Invalid JSON in ${configPath}`);
|
||||
expect(await readFile(path.join(home, "daemon.log"), "utf8")).toContain(
|
||||
`Invalid JSON in ${configPath}`,
|
||||
);
|
||||
} finally {
|
||||
await f.close();
|
||||
}
|
||||
}, 60_000);
|
||||
|
||||
test("onboarding preserves an existing MCP choice unless explicitly supplied", async () => {
|
||||
const f = await fixture();
|
||||
const home = f.homes[0]!;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { fileURLToPath } from "url";
|
||||
import { existsSync } from "node:fs";
|
||||
import { appendFileSync, existsSync, mkdirSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
import {
|
||||
acquirePidLock,
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
updatePidLock,
|
||||
} from "../src/server/pid-lock.js";
|
||||
import { resolvePaseoHome } from "../src/server/paseo-home.js";
|
||||
import { daemonLogPath } from "../src/server/daemon-instance.js";
|
||||
import { PRIVATE_FILE_MODE } from "../src/server/private-files.js";
|
||||
import { loadPersistedConfig } from "../src/server/persisted-config.js";
|
||||
import { runSupervisor } from "./supervisor.js";
|
||||
import { resolveSupervisorLogFile } from "./supervisor-log-config.js";
|
||||
@@ -118,9 +120,7 @@ async function main(): Promise<void> {
|
||||
});
|
||||
} catch (error) {
|
||||
if (error instanceof PidLockError) {
|
||||
process.stderr.write(`${error.message}\n`);
|
||||
process.exit(1);
|
||||
return;
|
||||
failStartup(error.message, error.message);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
@@ -181,8 +181,31 @@ async function main(): Promise<void> {
|
||||
requestSupervisorShutdown = supervisor.requestShutdown;
|
||||
}
|
||||
|
||||
void main().catch((error) => {
|
||||
const message = error instanceof Error ? (error.stack ?? error.message) : String(error);
|
||||
process.stderr.write(`${message}\n`);
|
||||
// The supervisor opens its log only after config and the PID lock succeed. A background
|
||||
// launch discards stderr, so earlier failures also go to the log the launcher points at.
|
||||
function failStartup(detail: string, summary: string): never {
|
||||
process.stderr.write(`${detail}\n`);
|
||||
try {
|
||||
const logPath = daemonLogPath(resolvePaseoHome(process.env));
|
||||
mkdirSync(path.dirname(logPath), { recursive: true });
|
||||
appendFileSync(
|
||||
logPath,
|
||||
`${JSON.stringify({
|
||||
level: "fatal",
|
||||
time: new Date().toISOString(),
|
||||
pid: process.pid,
|
||||
name: "DaemonRunner",
|
||||
msg: summary,
|
||||
})}\n`,
|
||||
{ mode: PRIVATE_FILE_MODE },
|
||||
);
|
||||
} catch {
|
||||
// stderr already carries the failure.
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
void main().catch((error) => {
|
||||
if (error instanceof Error) failStartup(error.stack ?? error.message, error.message);
|
||||
failStartup(String(error), String(error));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user