fix: always create the central DB during onboarding, never ask

The step was a skippable prompt, but declining left an install Fusion cannot
run on — reported by a line that described the breakage without fixing it.
There is no useful negative answer.

It also blocked non-interactive startup: `pnpm dev --tunnel` sat on "Run
central db now? (Y/n)", never reached listening, and so served nothing at all
while the tunnel had no dev server to point at.

The database is now created unconditionally when absent; the already-exists
path is untouched. Scripted onboarding tests lose their leading central-DB
answer, and the skip-every-step case now asserts the database is still
created.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
gsxdsm
2026-08-18 20:40:34 -07:00
parent f12b9f8463
commit ce69558c7a
3 changed files with 36 additions and 19 deletions

View File

@@ -0,0 +1,7 @@
---
"@runfusion/fusion": patch
---
summary: Onboarding no longer asks whether to create the central database — it always creates it.
category: fix
dev: `runOnboard` gated central-DB creation behind `runSkippableStep(prompts, "Central DB", ...)`. Declining produced an install Fusion cannot run on, acknowledged only by a "database was not created or initialized" line, so the negative answer had no useful outcome. It also blocked non-interactive startups: a `pnpm dev --tunnel` stopped on `Run central db now? (Y/n)` never reached listening, so nothing was served. The step now runs unconditionally when the database is absent; the "already exists" path is unchanged. Scripted prompt sequences in `onboard.test.ts` lost their leading central-DB answer accordingly, and the skip-everything case now asserts the database is still created.

View File

@@ -163,7 +163,7 @@ describe("onboard", () => {
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
await runOnboard({
input: inputFrom(["y", "y", "4", "y", "y", "n", "y"]),
input: inputFrom(["y", "4", "y", "y", "n", "y"]),
});
expect(logSpy).toHaveBeenCalledWith(expect.stringContaining("Creating central DB"));
expect(centralInitMock).toHaveBeenCalled();
@@ -187,7 +187,7 @@ describe("onboard", () => {
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
await runOnboard({
input: inputFrom(["y", "y", "3", "local", "Local server", "http://localhost:8080/v1/", "", "qwen, , qwen", "y", "y", "n", "n", "n"]),
input: inputFrom(["y", "3", "local", "Local server", "http://localhost:8080/v1/", "", "qwen, , qwen", "y", "y", "n", "n", "n"]),
});
const registry = JSON.parse(readFileSync(path, "utf8"));
@@ -209,7 +209,7 @@ describe("onboard", () => {
if (message.includes("Stored API key")) expect(persisted).toBe(true);
});
await runOnboard({ input: inputFrom(["y", "y", "1", "test-key", "y", "y", "n", "y"]) });
await runOnboard({ input: inputFrom(["y", "1", "test-key", "y", "y", "n", "y"]) });
expect(providerAuth.setApiKey).toHaveBeenCalledWith("openrouter", "test-key");
expect(logSpy).toHaveBeenCalledWith("✓ Stored API key for openrouter");
});
@@ -219,7 +219,7 @@ describe("onboard", () => {
mockProviderAuthFactory.mockReturnValue(providerAuth);
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
await runOnboard({ input: inputFrom(["y", "y", "1", "test-key", "y", "y", "y", "y"]) });
await runOnboard({ input: inputFrom(["y", "1", "test-key", "y", "y", "y", "y"]) });
expect(providerAuth.setApiKey).toHaveBeenCalledWith("openrouter", "test-key");
expect(mockRunInit).toHaveBeenCalledTimes(1);
@@ -241,7 +241,7 @@ describe("onboard", () => {
expect(providerAuth.setApiKey).not.toHaveBeenCalled();
expect(mockRunInit).not.toHaveBeenCalled();
await runOnboard({ force: true, input: inputFrom(["y", "n", "n", "n", "n"]) });
await runOnboard({ force: true, input: inputFrom(["n", "n", "n", "n"]) });
expect(globalSettingsState.cliOnboardingCompletedAt).not.toBe("2026-06-01T00:00:00.000Z");
});
@@ -283,9 +283,15 @@ describe("onboard", () => {
const providerAuth = makeProviderAuth();
mockProviderAuthFactory.mockReturnValue(providerAuth);
await runOnboard({ input: inputFrom(["n", "n", "n", "n", "n"]) });
await runOnboard({ input: inputFrom(["n", "n", "n", "n"]) });
expect(centralInitMock).not.toHaveBeenCalled();
/*
FNXC:Onboarding 2026-08-19-03:38:
The central DB is no longer skippable: declining left an install Fusion cannot run on, and the
prompt blocked non-interactive startups outright (a `pnpm dev --tunnel` sat on it and never
listened). Skipping every OPTIONAL step must still leave a created database.
*/
expect(centralInitMock).toHaveBeenCalled();
expect(mockRunInit).not.toHaveBeenCalled();
expect(globalSettingsState.testMode).toBeUndefined();
expect(typeof globalSettingsState.cliOnboardingCompletedAt).toBe("string");
@@ -296,7 +302,7 @@ describe("onboard", () => {
const providerAuth = makeProviderAuth();
mockProviderAuthFactory.mockReturnValue(providerAuth);
await runOnboard({ input: inputFrom(["y", "n", "y", "y", "n", "y"]) });
await runOnboard({ input: inputFrom(["n", "y", "y", "n", "y"]) });
expect(providerAuth.setApiKey).not.toHaveBeenCalled();
expect(mockRunInit).toHaveBeenCalledTimes(1);
expect(globalSettingsState.testMode).toBe(false);
@@ -307,7 +313,7 @@ describe("onboard", () => {
const providerAuth = makeProviderAuth();
mockProviderAuthFactory.mockReturnValue(providerAuth);
await runOnboard({ input: inputFrom(["y", "y", "4", "n", "y", "n", "y"]) });
await runOnboard({ input: inputFrom(["y", "4", "n", "y", "n", "y"]) });
expect(mockRunInit).not.toHaveBeenCalled();
expect(globalSettingsState.testMode).toBe(false);
expect(typeof globalSettingsState.cliOnboardingCompletedAt).toBe("string");

View File

@@ -281,16 +281,20 @@ export async function runOnboard(options: OnboardOptions = {}): Promise<void> {
if (existsSync(centralDbPath)) {
console.log(`✓ Central DB already exists: ${centralDbPath}`);
} else {
const ranCentralDb = await runSkippableStep(prompts, "Central DB", async () => {
console.log(`Creating central DB: ${centralDbPath}`);
const central = new CentralCore();
await central.init();
await central.close();
console.log("✓ Central DB initialized");
});
if (!ranCentralDb) {
console.log("Central DB setup skipped; database was not created or initialized.");
}
/*
FNXC:Onboarding 2026-08-19-03:38:
The central DB is created unconditionally — no prompt. Fusion cannot run without it, so
declining produced an install that was broken in a way the message ("database was not created
or initialized") described but did not fix. It also blocked non-interactive startups: a
`pnpm dev --tunnel` sat on "Run central db now? (Y/n)" and never listened, so nothing was
served and the tunnel had no dev server to point at.
*/
console.log("\nCentral DB:");
console.log(`Creating central DB: ${centralDbPath}`);
const central = new CentralCore();
await central.init();
await central.close();
console.log("✓ Central DB initialized");
}
const authStorage = createFusionAuthStorage();