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:
7
.changeset/onboarding-central-db-no-prompt.md
Normal file
7
.changeset/onboarding-central-db-no-prompt.md
Normal 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.
|
||||
@@ -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");
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user