From dc67590a96124a62613aefead294d4e638f76cdc Mon Sep 17 00:00:00 2001 From: Jason Woltje Date: Sun, 9 Aug 2026 05:01:09 -0500 Subject: [PATCH] fix(installer): propagate wizard gateway failures (#1120) --- .../integration/unified-wizard.test.ts | 17 ++--- packages/mosaic/src/stages/quick-start.ts | 16 ++--- packages/mosaic/src/wizard.ts | 62 ++++++++++--------- tools/install.sh | 5 +- 4 files changed, 54 insertions(+), 46 deletions(-) diff --git a/packages/mosaic/__tests__/integration/unified-wizard.test.ts b/packages/mosaic/__tests__/integration/unified-wizard.test.ts index 764f5b26..6d0afde0 100644 --- a/packages/mosaic/__tests__/integration/unified-wizard.test.ts +++ b/packages/mosaic/__tests__/integration/unified-wizard.test.ts @@ -193,16 +193,19 @@ describe('Unified wizard (runWizard with default skipGateway)', () => { 'Your timezone': 'UTC', }); - await runWizard({ - mosaicHome: tmpDir, - sourceDir: tmpDir, - prompter, - configService: createConfigService(tmpDir, tmpDir), - skipGatewayNpmInstall: true, - }); + await expect( + runWizard({ + mosaicHome: tmpDir, + sourceDir: tmpDir, + prompter, + configService: createConfigService(tmpDir, tmpDir), + skipGatewayNpmInstall: true, + }), + ).rejects.toThrow('Gateway configuration failed'); const logs = prompter.getLogs(); expect(logs.some((line) => line.includes('Gateway did not become healthy'))).toBe(true); + expect(logs.some((line) => line.includes('Gateway configuration failed'))).toBe(true); expect(logs.some((line) => line.includes('Installation Summary'))).toBe(false); expect(logs.some((line) => line.includes('Mosaic is ready.'))).toBe(false); expect(gatewayConfigMock).toHaveBeenCalledTimes(1); diff --git a/packages/mosaic/src/stages/quick-start.ts b/packages/mosaic/src/stages/quick-start.ts index aa2d63ea..605ce49b 100644 --- a/packages/mosaic/src/stages/quick-start.ts +++ b/packages/mosaic/src/stages/quick-start.ts @@ -68,8 +68,6 @@ export async function quickStartPath( // Gateway config + bootstrap if (!options.skipGateway) { - const headlessRun = process.env['MOSAIC_ASSUME_YES'] === '1' || !process.stdin.isTTY; - try { const configResult = await gatewayConfigStage(prompter, state, { host: options.gatewayHost ?? 'localhost', @@ -81,11 +79,9 @@ export async function quickStartPath( }); if (!configResult.ready || !configResult.host || !configResult.port) { - if (headlessRun) { - prompter.warn('Gateway configuration failed in headless mode — aborting wizard.'); - process.exit(1); - } - return; + const message = 'Gateway configuration failed — aborting wizard.'; + prompter.warn(message); + throw new Error(message); } const bootstrapResult = await gatewayBootstrapStage(prompter, state, { @@ -93,9 +89,9 @@ export async function quickStartPath( port: configResult.port, }); if (!bootstrapResult.completed) { - prompter.warn('Admin bootstrap failed — aborting wizard.'); - process.exit(1); - return; + const message = 'Admin bootstrap failed — aborting wizard.'; + prompter.warn(message); + throw new Error(message); } finalizeResult.showSummary(); } catch (err) { diff --git a/packages/mosaic/src/wizard.ts b/packages/mosaic/src/wizard.ts index 571b7248..2bc69f84 100644 --- a/packages/mosaic/src/wizard.ts +++ b/packages/mosaic/src/wizard.ts @@ -348,18 +348,21 @@ async function runFinishPath( providerType: state.providerType, }); - if (configResult.ready && configResult.host && configResult.port) { - const bootstrapResult = await gatewayBootstrapStage(prompter, state, { - host: configResult.host, - port: configResult.port, - }); - if (!bootstrapResult.completed) { - prompter.warn('Admin bootstrap failed — aborting wizard.'); - process.exit(1); - return; - } - finalizeResult.showSummary(); + if (!configResult.ready || !configResult.host || !configResult.port) { + const message = 'Gateway configuration failed — aborting wizard.'; + prompter.warn(message); + throw new Error(message); } + const bootstrapResult = await gatewayBootstrapStage(prompter, state, { + host: configResult.host, + port: configResult.port, + }); + if (!bootstrapResult.completed) { + const message = 'Admin bootstrap failed — aborting wizard.'; + prompter.warn(message); + throw new Error(message); + } + finalizeResult.showSummary(); } catch (err) { prompter.warn(`Gateway setup failed: ${err instanceof Error ? err.message : String(err)}`); throw err; @@ -420,9 +423,9 @@ async function runHeadlessPath( }); if (!configResult.ready || !configResult.host || !configResult.port) { - prompter.warn('Gateway configuration failed in headless mode — aborting wizard.'); - process.exit(1); - return; + const message = 'Gateway configuration failed in headless mode — aborting wizard.'; + prompter.warn(message); + throw new Error(message); } const bootstrapResult = await gatewayBootstrapStage(prompter, state, { @@ -430,9 +433,9 @@ async function runHeadlessPath( port: configResult.port, }); if (!bootstrapResult.completed) { - prompter.warn('Admin bootstrap failed — aborting wizard.'); - process.exit(1); - return; + const message = 'Admin bootstrap failed — aborting wizard.'; + prompter.warn(message); + throw new Error(message); } finalizeResult.showSummary(); } catch (err) { @@ -477,18 +480,21 @@ async function runKeepPath( skipInstall: options.skipGatewayNpmInstall, }); - if (configResult.ready && configResult.host && configResult.port) { - const bootstrapResult = await gatewayBootstrapStage(prompter, state, { - host: configResult.host, - port: configResult.port, - }); - if (!bootstrapResult.completed) { - prompter.warn('Admin bootstrap failed — aborting wizard.'); - process.exit(1); - return; - } - finalizeResult.showSummary(); + if (!configResult.ready || !configResult.host || !configResult.port) { + const message = 'Gateway configuration failed — aborting wizard.'; + prompter.warn(message); + throw new Error(message); } + const bootstrapResult = await gatewayBootstrapStage(prompter, state, { + host: configResult.host, + port: configResult.port, + }); + if (!bootstrapResult.completed) { + const message = 'Admin bootstrap failed — aborting wizard.'; + prompter.warn(message); + throw new Error(message); + } + finalizeResult.showSummary(); } catch (err) { prompter.warn(`Gateway setup failed: ${err instanceof Error ? err.message : String(err)}`); throw err; diff --git a/tools/install.sh b/tools/install.sh index 96c9ad49..b2871762 100755 --- a/tools/install.sh +++ b/tools/install.sh @@ -762,9 +762,12 @@ if [[ "$FLAG_CHECK" == "false" ]]; then if "$MOSAIC_CMD" wizard; then ok "Wizard complete." else - warn "Wizard exited non-zero." + fail "Wizard failed; installation is incomplete." + echo " Completed: framework and CLI installation" + echo " Failed: gateway configuration or admin bootstrap" echo " You can retry with: ${C}mosaic wizard${RESET}" echo " Or run gateway install alone: ${C}mosaic gateway install${RESET}" + exit 1 fi fi else -- 2.54.0