Compare commits

...
Author SHA1 Message Date
Jason Woltje dc67590a96 fix(installer): propagate wizard gateway failures (#1120)
ci/woodpecker/pr/ci Pipeline was successful
2026-08-09 05:01:09 -05:00
4 changed files with 54 additions and 46 deletions
@@ -193,16 +193,19 @@ describe('Unified wizard (runWizard with default skipGateway)', () => {
'Your timezone': 'UTC', 'Your timezone': 'UTC',
}); });
await runWizard({ await expect(
mosaicHome: tmpDir, runWizard({
sourceDir: tmpDir, mosaicHome: tmpDir,
prompter, sourceDir: tmpDir,
configService: createConfigService(tmpDir, tmpDir), prompter,
skipGatewayNpmInstall: true, configService: createConfigService(tmpDir, tmpDir),
}); skipGatewayNpmInstall: true,
}),
).rejects.toThrow('Gateway configuration failed');
const logs = prompter.getLogs(); const logs = prompter.getLogs();
expect(logs.some((line) => line.includes('Gateway did not become healthy'))).toBe(true); 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('Installation Summary'))).toBe(false);
expect(logs.some((line) => line.includes('Mosaic is ready.'))).toBe(false); expect(logs.some((line) => line.includes('Mosaic is ready.'))).toBe(false);
expect(gatewayConfigMock).toHaveBeenCalledTimes(1); expect(gatewayConfigMock).toHaveBeenCalledTimes(1);
+6 -10
View File
@@ -68,8 +68,6 @@ export async function quickStartPath(
// Gateway config + bootstrap // Gateway config + bootstrap
if (!options.skipGateway) { if (!options.skipGateway) {
const headlessRun = process.env['MOSAIC_ASSUME_YES'] === '1' || !process.stdin.isTTY;
try { try {
const configResult = await gatewayConfigStage(prompter, state, { const configResult = await gatewayConfigStage(prompter, state, {
host: options.gatewayHost ?? 'localhost', host: options.gatewayHost ?? 'localhost',
@@ -81,11 +79,9 @@ export async function quickStartPath(
}); });
if (!configResult.ready || !configResult.host || !configResult.port) { if (!configResult.ready || !configResult.host || !configResult.port) {
if (headlessRun) { const message = 'Gateway configuration failed — aborting wizard.';
prompter.warn('Gateway configuration failed in headless mode — aborting wizard.'); prompter.warn(message);
process.exit(1); throw new Error(message);
}
return;
} }
const bootstrapResult = await gatewayBootstrapStage(prompter, state, { const bootstrapResult = await gatewayBootstrapStage(prompter, state, {
@@ -93,9 +89,9 @@ export async function quickStartPath(
port: configResult.port, port: configResult.port,
}); });
if (!bootstrapResult.completed) { if (!bootstrapResult.completed) {
prompter.warn('Admin bootstrap failed — aborting wizard.'); const message = 'Admin bootstrap failed — aborting wizard.';
process.exit(1); prompter.warn(message);
return; throw new Error(message);
} }
finalizeResult.showSummary(); finalizeResult.showSummary();
} catch (err) { } catch (err) {
+34 -28
View File
@@ -348,18 +348,21 @@ async function runFinishPath(
providerType: state.providerType, providerType: state.providerType,
}); });
if (configResult.ready && configResult.host && configResult.port) { if (!configResult.ready || !configResult.host || !configResult.port) {
const bootstrapResult = await gatewayBootstrapStage(prompter, state, { const message = 'Gateway configuration failed — aborting wizard.';
host: configResult.host, prompter.warn(message);
port: configResult.port, throw new Error(message);
});
if (!bootstrapResult.completed) {
prompter.warn('Admin bootstrap failed — aborting wizard.');
process.exit(1);
return;
}
finalizeResult.showSummary();
} }
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) { } catch (err) {
prompter.warn(`Gateway setup failed: ${err instanceof Error ? err.message : String(err)}`); prompter.warn(`Gateway setup failed: ${err instanceof Error ? err.message : String(err)}`);
throw err; throw err;
@@ -420,9 +423,9 @@ async function runHeadlessPath(
}); });
if (!configResult.ready || !configResult.host || !configResult.port) { if (!configResult.ready || !configResult.host || !configResult.port) {
prompter.warn('Gateway configuration failed in headless mode — aborting wizard.'); const message = 'Gateway configuration failed in headless mode — aborting wizard.';
process.exit(1); prompter.warn(message);
return; throw new Error(message);
} }
const bootstrapResult = await gatewayBootstrapStage(prompter, state, { const bootstrapResult = await gatewayBootstrapStage(prompter, state, {
@@ -430,9 +433,9 @@ async function runHeadlessPath(
port: configResult.port, port: configResult.port,
}); });
if (!bootstrapResult.completed) { if (!bootstrapResult.completed) {
prompter.warn('Admin bootstrap failed — aborting wizard.'); const message = 'Admin bootstrap failed — aborting wizard.';
process.exit(1); prompter.warn(message);
return; throw new Error(message);
} }
finalizeResult.showSummary(); finalizeResult.showSummary();
} catch (err) { } catch (err) {
@@ -477,18 +480,21 @@ async function runKeepPath(
skipInstall: options.skipGatewayNpmInstall, skipInstall: options.skipGatewayNpmInstall,
}); });
if (configResult.ready && configResult.host && configResult.port) { if (!configResult.ready || !configResult.host || !configResult.port) {
const bootstrapResult = await gatewayBootstrapStage(prompter, state, { const message = 'Gateway configuration failed — aborting wizard.';
host: configResult.host, prompter.warn(message);
port: configResult.port, throw new Error(message);
});
if (!bootstrapResult.completed) {
prompter.warn('Admin bootstrap failed — aborting wizard.');
process.exit(1);
return;
}
finalizeResult.showSummary();
} }
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) { } catch (err) {
prompter.warn(`Gateway setup failed: ${err instanceof Error ? err.message : String(err)}`); prompter.warn(`Gateway setup failed: ${err instanceof Error ? err.message : String(err)}`);
throw err; throw err;
+4 -1
View File
@@ -762,9 +762,12 @@ if [[ "$FLAG_CHECK" == "false" ]]; then
if "$MOSAIC_CMD" wizard; then if "$MOSAIC_CMD" wizard; then
ok "Wizard complete." ok "Wizard complete."
else 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 " You can retry with: ${C}mosaic wizard${RESET}"
echo " Or run gateway install alone: ${C}mosaic gateway install${RESET}" echo " Or run gateway install alone: ${C}mosaic gateway install${RESET}"
exit 1
fi fi
fi fi
else else