Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
baf4306f51 |
@@ -48,7 +48,7 @@ mosaic wizard # Full guided setup (gateway install → verify)
|
|||||||
|
|
||||||
### Requirements
|
### Requirements
|
||||||
|
|
||||||
- Node.js ≥ 20
|
- Node.js ≥ 22
|
||||||
- npm (for global @mosaicstack/mosaic install)
|
- npm (for global @mosaicstack/mosaic install)
|
||||||
- One or more runtimes: [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [Codex](https://github.com/openai/codex), [OpenCode](https://opencode.ai), or [Pi](https://github.com/mariozechner/pi-coding-agent)
|
- One or more runtimes: [Claude Code](https://docs.anthropic.com/en/docs/claude-code), [Codex](https://github.com/openai/codex), [OpenCode](https://opencode.ai), or [Pi](https://github.com/mariozechner/pi-coding-agent)
|
||||||
|
|
||||||
@@ -200,7 +200,7 @@ Consent state is persisted in config. Remote upload is a no-op until you run `mo
|
|||||||
|
|
||||||
### Prerequisites
|
### Prerequisites
|
||||||
|
|
||||||
- Node.js ≥ 20
|
- Node.js ≥ 22
|
||||||
- pnpm 10.6+
|
- pnpm 10.6+
|
||||||
- Docker & Docker Compose
|
- Docker & Docker Compose
|
||||||
|
|
||||||
|
|||||||
@@ -193,19 +193,16 @@ describe('Unified wizard (runWizard with default skipGateway)', () => {
|
|||||||
'Your timezone': 'UTC',
|
'Your timezone': 'UTC',
|
||||||
});
|
});
|
||||||
|
|
||||||
await expect(
|
await runWizard({
|
||||||
runWizard({
|
mosaicHome: tmpDir,
|
||||||
mosaicHome: tmpDir,
|
sourceDir: tmpDir,
|
||||||
sourceDir: tmpDir,
|
prompter,
|
||||||
prompter,
|
configService: createConfigService(tmpDir, tmpDir),
|
||||||
configService: createConfigService(tmpDir, tmpDir),
|
skipGatewayNpmInstall: true,
|
||||||
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);
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ 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',
|
||||||
@@ -79,9 +81,11 @@ export async function quickStartPath(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!configResult.ready || !configResult.host || !configResult.port) {
|
if (!configResult.ready || !configResult.host || !configResult.port) {
|
||||||
const message = 'Gateway configuration failed — aborting wizard.';
|
if (headlessRun) {
|
||||||
prompter.warn(message);
|
prompter.warn('Gateway configuration failed in headless mode — aborting wizard.');
|
||||||
throw new Error(message);
|
process.exit(1);
|
||||||
|
}
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bootstrapResult = await gatewayBootstrapStage(prompter, state, {
|
const bootstrapResult = await gatewayBootstrapStage(prompter, state, {
|
||||||
@@ -89,9 +93,9 @@ export async function quickStartPath(
|
|||||||
port: configResult.port,
|
port: configResult.port,
|
||||||
});
|
});
|
||||||
if (!bootstrapResult.completed) {
|
if (!bootstrapResult.completed) {
|
||||||
const message = 'Admin bootstrap failed — aborting wizard.';
|
prompter.warn('Admin bootstrap failed — aborting wizard.');
|
||||||
prompter.warn(message);
|
process.exit(1);
|
||||||
throw new Error(message);
|
return;
|
||||||
}
|
}
|
||||||
finalizeResult.showSummary();
|
finalizeResult.showSummary();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -348,21 +348,18 @@ async function runFinishPath(
|
|||||||
providerType: state.providerType,
|
providerType: state.providerType,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!configResult.ready || !configResult.host || !configResult.port) {
|
if (configResult.ready && configResult.host && configResult.port) {
|
||||||
const message = 'Gateway configuration failed — aborting wizard.';
|
const bootstrapResult = await gatewayBootstrapStage(prompter, state, {
|
||||||
prompter.warn(message);
|
host: configResult.host,
|
||||||
throw new Error(message);
|
port: configResult.port,
|
||||||
|
});
|
||||||
|
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;
|
||||||
@@ -423,9 +420,9 @@ async function runHeadlessPath(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!configResult.ready || !configResult.host || !configResult.port) {
|
if (!configResult.ready || !configResult.host || !configResult.port) {
|
||||||
const message = 'Gateway configuration failed in headless mode — 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, {
|
||||||
@@ -433,9 +430,9 @@ async function runHeadlessPath(
|
|||||||
port: configResult.port,
|
port: configResult.port,
|
||||||
});
|
});
|
||||||
if (!bootstrapResult.completed) {
|
if (!bootstrapResult.completed) {
|
||||||
const message = 'Admin bootstrap failed — aborting wizard.';
|
prompter.warn('Admin bootstrap failed — aborting wizard.');
|
||||||
prompter.warn(message);
|
process.exit(1);
|
||||||
throw new Error(message);
|
return;
|
||||||
}
|
}
|
||||||
finalizeResult.showSummary();
|
finalizeResult.showSummary();
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
@@ -480,21 +477,18 @@ async function runKeepPath(
|
|||||||
skipInstall: options.skipGatewayNpmInstall,
|
skipInstall: options.skipGatewayNpmInstall,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!configResult.ready || !configResult.host || !configResult.port) {
|
if (configResult.ready && configResult.host && configResult.port) {
|
||||||
const message = 'Gateway configuration failed — aborting wizard.';
|
const bootstrapResult = await gatewayBootstrapStage(prompter, state, {
|
||||||
prompter.warn(message);
|
host: configResult.host,
|
||||||
throw new Error(message);
|
port: configResult.port,
|
||||||
|
});
|
||||||
|
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;
|
||||||
|
|||||||
@@ -12,6 +12,23 @@ MOSAIC_HOME="$TMP/mosaic"
|
|||||||
STATE="$TMP/state"
|
STATE="$TMP/state"
|
||||||
LOG="$TMP/npm.log"
|
LOG="$TMP/npm.log"
|
||||||
mkdir -p "$FAKE_BIN" "$HOME_DIR" "$STATE"
|
mkdir -p "$FAKE_BIN" "$HOME_DIR" "$STATE"
|
||||||
|
REAL_NODE="$(command -v node)"
|
||||||
|
export MOSAIC_TEST_REAL_NODE="$REAL_NODE"
|
||||||
|
|
||||||
|
cat > "$FAKE_BIN/node" <<'FAKE_NODE'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
if [[ "$*" == *'process.versions.node.split'* ]]; then
|
||||||
|
printf '%s' "${MOSAIC_TEST_NODE_MAJOR:-22}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if [[ "${1:-}" == "--version" ]]; then
|
||||||
|
printf 'v%s.0.0\n' "${MOSAIC_TEST_NODE_MAJOR:-22}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
exec "${MOSAIC_TEST_REAL_NODE:?}" "$@"
|
||||||
|
FAKE_NODE
|
||||||
|
chmod +x "$FAKE_BIN/node"
|
||||||
|
|
||||||
cat > "$FAKE_BIN/npm" <<'FAKE_NPM'
|
cat > "$FAKE_BIN/npm" <<'FAKE_NPM'
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
@@ -135,6 +152,20 @@ reset_state() {
|
|||||||
rm -f "$STATE"/*
|
rm -f "$STATE"/*
|
||||||
}
|
}
|
||||||
|
|
||||||
|
reset_state
|
||||||
|
echo "[test] --next rejects Node 20 before any install action"
|
||||||
|
if OUTPUT="$(
|
||||||
|
HOME="$HOME_DIR" MOSAIC_HOME="$MOSAIC_HOME" MOSAIC_PREFIX="$PREFIX" MOSAIC_NO_COLOR=1 \
|
||||||
|
MOSAIC_TEST_NPM_LOG="$LOG" MOSAIC_TEST_STATE="$STATE" MOSAIC_TEST_REAL_NODE="$REAL_NODE" \
|
||||||
|
MOSAIC_TEST_NODE_MAJOR=20 PATH="$FAKE_BIN:$PATH" \
|
||||||
|
bash "$ROOT/tools/install.sh" --cli --next --yes --no-auto-launch 2>&1
|
||||||
|
)"; then
|
||||||
|
echo "expected Node 20 next-lane install to fail" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
grep -qF 'Node.js >= 22 required for the --next lane' <<<"$OUTPUT"
|
||||||
|
[[ ! -s "$LOG" ]] || { echo "Node 20 gate ran npm actions" >&2; exit 1; }
|
||||||
|
|
||||||
reset_state
|
reset_state
|
||||||
echo "[test] --next fast path pins resolved package versions"
|
echo "[test] --next fast path pins resolved package versions"
|
||||||
OUTPUT="$(
|
OUTPUT="$(
|
||||||
|
|||||||
+5
-4
@@ -526,6 +526,10 @@ if [[ "$NODE_MAJOR" -lt 20 ]]; then
|
|||||||
fail "Node.js >= 20 required (found v$(node --version))"
|
fail "Node.js >= 20 required (found v$(node --version))"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
if [[ "$FLAG_NEXT" == "true" && "$NODE_MAJOR" -lt 22 ]]; then
|
||||||
|
fail "Node.js >= 22 required for the --next lane (found v$(node --version))"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "${BOLD}Mosaic Stack Installer${RESET}"
|
echo "${BOLD}Mosaic Stack Installer${RESET}"
|
||||||
@@ -762,12 +766,9 @@ if [[ "$FLAG_CHECK" == "false" ]]; then
|
|||||||
if "$MOSAIC_CMD" wizard; then
|
if "$MOSAIC_CMD" wizard; then
|
||||||
ok "Wizard complete."
|
ok "Wizard complete."
|
||||||
else
|
else
|
||||||
fail "Wizard failed; installation is incomplete."
|
warn "Wizard exited non-zero."
|
||||||
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
|
||||||
|
|||||||
Reference in New Issue
Block a user