Files
NuklearRabbit 4ad698c4eb Release ForgeFlow 0.8.1
Add advanced Git and deployment workflows, secure backups and auditing, live Gitea integration, desktop notifications, connection validation, and the premium responsive UX refresh.
2026-07-26 00:42:17 +02:00

92 lines
3.8 KiB
JavaScript

import { execFile } from 'node:child_process';
import { access, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { promisify } from 'node:util';
import toolInvocation from '../src/shared/tool-invocation.cjs';
const exec = promisify(execFile);
const { npmProbeCandidates } = toolInvocation;
const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url), 'utf8'));
const checks = [];
const jsonMode = process.argv.includes('--json');
function add(id, name, ok, detail, help = '', severity = 'required') {
const status = ok ? 'pass' : severity === 'warning' ? 'warning' : 'fail';
checks.push({ id, name, status, ok: ok || severity === 'warning', detail, help, severity });
}
const major = Number(process.versions.node.split('.')[0]);
add('node', 'Node.js', major >= 22, process.version, 'Install Node.js 22 or newer.');
try {
const failures = [];
let version = '';
let source = '';
for (const candidate of npmProbeCandidates()) {
try {
const { stdout } = await exec(candidate.file, candidate.args, { windowsHide: true });
version = stdout.trim();
source = candidate.source;
if (version) break;
} catch (error) {
failures.push(`${candidate.source}: ${error.message}`);
}
}
if (!version) throw new Error(failures.join(' | ') || 'No npm invocation candidate succeeded.');
add('npm', 'npm', true, `${version} (${source})`);
} catch (error) {
add('npm', 'npm', false, error.message, 'Install npm together with Node.js and ensure npm.cmd is available on PATH.');
}
try {
const { stdout } = await exec('git', ['--version']);
add('git', 'Git', true, stdout.trim());
const [name, email] = await Promise.all([
exec('git', ['config', '--global', '--get', 'user.name']).then((result) => result.stdout.trim()).catch(() => ''),
exec('git', ['config', '--global', '--get', 'user.email']).then((result) => result.stdout.trim()).catch(() => '')
]);
add('git-identity', 'Git identity', Boolean(name && email), name && email ? `${name} <${email}>` : 'user.name or user.email is missing; commits will remain disabled until configured', 'Configure git config --global user.name and user.email.', 'warning');
} catch (error) {
add('git', 'Git', false, error.message, 'Install Git and ensure git is on PATH.');
}
try {
await access(new URL('../node_modules/electron/package.json', import.meta.url));
add('electron', 'Electron dependency', true, 'installed');
} catch {
add('electron', 'Electron dependency', false, 'not installed', 'Run npm install.');
}
let markerDirectory = null;
try {
markerDirectory = await mkdtemp(path.join(os.tmpdir(), 'forgeflow-doctor-'));
await writeFile(path.join(markerDirectory, 'write-test'), 'ok');
add('temp-storage', 'Local diagnostic storage', true, markerDirectory.replace(os.homedir(), '<HOME>'));
} catch (error) {
add('temp-storage', 'Local diagnostic storage', false, error.message, 'Check local disk permissions and free space.');
} finally {
if (markerDirectory) await rm(markerDirectory, { recursive: true, force: true }).catch(() => {});
}
const blockingChecks = checks.filter((check) => check.status === 'fail');
const report = {
product: 'ForgeFlow',
version: packageJson.version,
generatedAt: new Date().toISOString(),
platform: process.platform,
arch: process.arch,
ready: blockingChecks.length === 0,
checks
};
if (jsonMode) console.log(JSON.stringify(report, null, 2));
else {
console.log('ForgeFlow doctor\n');
for (const check of checks) console.log(`${check.status === 'pass' ? 'PASS' : check.status === 'warning' ? 'WARN' : 'FAIL'} ${check.name.padEnd(26)} ${check.detail}`);
console.log(`\n${report.ready ? 'Environment is ready.' : 'Resolve failed checks before starting ForgeFlow.'}`);
}
if (!report.ready) process.exitCode = 1;