120 lines
4.2 KiB
JavaScript
120 lines
4.2 KiB
JavaScript
|
|
#!/usr/bin/env node
|
||
|
|
import { spawnSync } from 'node:child_process';
|
||
|
|
import { existsSync } from 'node:fs';
|
||
|
|
import { resolveNatsServer, readNatsServerVersion } from './lib/nats-server-tool.mjs';
|
||
|
|
|
||
|
|
const json = process.argv.includes('--json');
|
||
|
|
const mode = process.argv.includes('--standalone')
|
||
|
|
? 'standalone'
|
||
|
|
: process.argv.includes('--hivecast')
|
||
|
|
? 'hivecast'
|
||
|
|
: process.argv.includes('--all')
|
||
|
|
? 'all'
|
||
|
|
: 'base';
|
||
|
|
|
||
|
|
function check(name, ok, details = {}) {
|
||
|
|
return { name, ok, ...details };
|
||
|
|
}
|
||
|
|
|
||
|
|
function run(command, args) {
|
||
|
|
const result = spawnSync(command, args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
||
|
|
if (result.error) return { ok: false, error: result.error.message };
|
||
|
|
if (result.status !== 0) return { ok: false, error: `${command} exited ${result.status}`, stderr: result.stderr.trim() };
|
||
|
|
return { ok: true, stdout: result.stdout.trim(), stderr: result.stderr.trim() };
|
||
|
|
}
|
||
|
|
|
||
|
|
function major(version) {
|
||
|
|
return Number(String(version).split('.')[0] ?? 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
function findBrowser() {
|
||
|
|
if (process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE && existsSync(process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE)) {
|
||
|
|
return { path: process.env.PLAYWRIGHT_CHROMIUM_EXECUTABLE, source: 'PLAYWRIGHT_CHROMIUM_EXECUTABLE' };
|
||
|
|
}
|
||
|
|
for (const path of ['/usr/bin/google-chrome', '/usr/bin/google-chrome-stable', '/usr/bin/chromium', '/usr/bin/chromium-browser']) {
|
||
|
|
if (existsSync(path)) return { path, source: 'system' };
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
const checks = [];
|
||
|
|
|
||
|
|
const nodeVersion = process.versions.node;
|
||
|
|
checks.push(check('node', major(nodeVersion) >= 22, {
|
||
|
|
version: nodeVersion,
|
||
|
|
required: '22+',
|
||
|
|
fix: 'Install Node.js 22 or newer.',
|
||
|
|
}));
|
||
|
|
|
||
|
|
const pnpm = run('pnpm', ['--version']);
|
||
|
|
checks.push(check('pnpm', pnpm.ok && major(pnpm.stdout) >= 10, {
|
||
|
|
version: pnpm.ok ? pnpm.stdout : null,
|
||
|
|
required: '10+',
|
||
|
|
fix: 'Install pnpm 10: corepack enable && corepack prepare pnpm@10.29.3 --activate',
|
||
|
|
error: pnpm.ok ? undefined : pnpm.error,
|
||
|
|
}));
|
||
|
|
|
||
|
|
let nats = null;
|
||
|
|
try {
|
||
|
|
nats = resolveNatsServer();
|
||
|
|
checks.push(check('nats-server', Boolean(nats), {
|
||
|
|
path: nats?.path ?? null,
|
||
|
|
source: nats?.source ?? null,
|
||
|
|
version: nats ? readNatsServerVersion(nats.path) : null,
|
||
|
|
requiredFor: 'pnpm demo:standalone',
|
||
|
|
fix: 'Run pnpm setup:nats, or install NATS Server and put nats-server on PATH.',
|
||
|
|
}));
|
||
|
|
} catch (error) {
|
||
|
|
checks.push(check('nats-server', false, {
|
||
|
|
error: error instanceof Error ? error.message : String(error),
|
||
|
|
requiredFor: 'pnpm demo:standalone',
|
||
|
|
fix: 'Run pnpm setup:nats, or set MATRIX_NATS_SERVER to the nats-server binary.',
|
||
|
|
}));
|
||
|
|
}
|
||
|
|
|
||
|
|
const browser = findBrowser();
|
||
|
|
checks.push(check('chromium', Boolean(browser), {
|
||
|
|
path: browser?.path ?? null,
|
||
|
|
source: browser?.source ?? null,
|
||
|
|
requiredFor: 'live browser proof in pnpm prove:fresh-samples',
|
||
|
|
fix: 'Run pnpm setup:browser, or set PLAYWRIGHT_CHROMIUM_EXECUTABLE to Chrome/Chromium.',
|
||
|
|
}));
|
||
|
|
|
||
|
|
const hivecastEnv = {
|
||
|
|
MATRIX_CLOUD: Boolean(process.env.MATRIX_CLOUD),
|
||
|
|
MATRIX_SPACE: Boolean(process.env.MATRIX_SPACE),
|
||
|
|
MATRIX_API_KEY: Boolean(process.env.MATRIX_API_KEY),
|
||
|
|
};
|
||
|
|
checks.push(check('hivecast-env', Object.values(hivecastEnv).every(Boolean), {
|
||
|
|
present: hivecastEnv,
|
||
|
|
requiredFor: 'live HiveCast server/browser samples in pnpm prove:fresh-samples',
|
||
|
|
fix: 'Create a HiveCast API key, then export MATRIX_CLOUD, MATRIX_SPACE, and MATRIX_API_KEY.',
|
||
|
|
}));
|
||
|
|
|
||
|
|
const requiredByMode = {
|
||
|
|
base: ['node', 'pnpm'],
|
||
|
|
standalone: ['node', 'pnpm', 'nats-server'],
|
||
|
|
hivecast: ['node', 'pnpm', 'chromium', 'hivecast-env'],
|
||
|
|
all: ['node', 'pnpm', 'nats-server', 'chromium', 'hivecast-env'],
|
||
|
|
};
|
||
|
|
const requiredFailures = checks.filter((item) => requiredByMode[mode].includes(item.name) && !item.ok);
|
||
|
|
const proof = {
|
||
|
|
ok: requiredFailures.length === 0,
|
||
|
|
mode,
|
||
|
|
checks,
|
||
|
|
};
|
||
|
|
|
||
|
|
if (json) {
|
||
|
|
console.log(JSON.stringify(proof, null, 2));
|
||
|
|
} else {
|
||
|
|
console.log(`Matrix SDK doctor mode: ${mode}`);
|
||
|
|
for (const item of checks) {
|
||
|
|
const required = requiredByMode[mode].includes(item.name);
|
||
|
|
const mark = item.ok ? 'ok' : required ? 'missing' : 'optional-missing';
|
||
|
|
console.log(`${mark} ${item.name}${item.version ? ` (${item.version})` : ''}${item.path ? ` at ${item.path}` : ''}`);
|
||
|
|
if (!item.ok) console.log(` fix: ${item.fix}`);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
process.exit(proof.ok ? 0 : 1);
|