@lenne.tech/cli
Version:
lenne.Tech CLI: lt
137 lines (136 loc) • 6.87 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.runInstall = runInstall;
const caddy_1 = require("./caddy");
const dev_service_1 = require("./dev-service");
/**
* Run the one-time per-machine `lt dev` setup. Idempotent — safe to
* re-run. When `opts.auto` is set the heading reflects that it was
* triggered by another command (e.g. `lt dev init`).
*/
function runInstall(toolbox_1) {
return __awaiter(this, arguments, void 0, function* (toolbox, opts = {}) {
const { print: { colors, error, info, success, warning }, } = toolbox;
info('');
info(colors.bold(opts.auto ? 'Preparing this machine for lt dev (lt dev install)' : 'lt dev install — one-time per-machine setup'));
info(colors.dim('─'.repeat(60)));
const plat = (0, dev_service_1.platformSupported)();
if (plat === 'unsupported') {
error(`Service management is not supported on ${process.platform}. Only macOS and Linux are covered.`);
info(` Workaround: run \`${colors.cyan(`caddy run --config ${caddy_1.paths.caddyfile}`)}\` manually.`);
return { blocked: true, caddyMissing: false, ok: false, unsupported: true };
}
let blocked = false;
// 1. caddy on PATH
const hasCaddy = yield (0, caddy_1.caddyAvailable)();
if (hasCaddy) {
success('caddy is on PATH');
}
else {
warning('caddy is not installed.');
info(` → macOS: ${colors.cyan('brew install caddy')}`);
info(` → Linux: ${colors.cyan('https://caddyserver.com/docs/install')}`);
info(' (Do NOT start it via `brew services` — `lt dev install` runs its own service.)');
blocked = true;
}
// 2. Caddyfile stub
(0, caddy_1.writeCaddyfile)('# lt dev — managed Caddyfile\n# Add per-project blocks via `lt dev up`.\n');
success(`Caddyfile present at ${caddy_1.paths.caddyfile}`);
if (!hasCaddy) {
info('');
error('Cannot continue setup until Caddy is installed. Re-run `lt dev install` afterwards.');
return { blocked: true, caddyMissing: true, ok: false, unsupported: false };
}
// 3. brew services conflict warning
const brewConflict = yield detectBrewCaddyConflict();
if (brewConflict) {
warning('A `brew services caddy` instance is registered.');
info(` Stop it (it crash-loops against our Caddyfile): ${colors.cyan('brew services stop caddy')}`);
info(' `lt dev install` runs its own service — the brew one is no longer needed.');
}
// 4. Install our LaunchAgent / systemd unit
const paths = (0, dev_service_1.getServicePaths)();
info('');
info(`Installing ${plat === 'darwin' ? 'LaunchAgent' : 'systemd-user unit'} at:`);
info(colors.dim(` ${paths.unitFile}`));
const installResult = yield (0, dev_service_1.installService)();
if (!installResult.ok) {
error(installResult.message);
blocked = true;
}
else if (installResult.created) {
success(installResult.message);
}
else {
info(colors.dim(installResult.message));
}
// 5. Wait for admin endpoint
if (installResult.ok) {
info(colors.dim('Waiting for Caddy admin endpoint (:2019) ...'));
const ready = yield (0, dev_service_1.waitForServiceReady)(8000);
const status = yield (0, dev_service_1.getServiceStatus)();
if (ready && status.daemonReachable) {
success(`Caddy daemon ready${status.pid ? ` (pid ${status.pid})` : ''}.`);
}
else if (status.loaded && !status.daemonReachable) {
warning('Service is loaded but admin endpoint did not respond within 8s.');
info(colors.dim(` Logs: ${paths.logFile} / ${paths.errFile}`));
blocked = true;
}
else {
warning('Caddy daemon did not start. See logs:');
info(colors.dim(` ${paths.logFile}`));
info(colors.dim(` ${paths.errFile}`));
blocked = true;
}
}
// 6. Validate Caddyfile
if (installResult.ok) {
const validation = yield (0, caddy_1.validateCaddyfile)();
if (validation.ok)
success('Caddyfile validates');
else
warning(`Caddyfile validation: ${validation.stderr.split('\n').slice(0, 2).join(' / ')}`);
}
// 7. CA trust
info('');
info(colors.bold('Local CA trust'));
info(' Caddy creates its local CA on first run. To trust it system-wide,');
info(' run this once (HOME must be preserved so sudo keeps the user-scoped');
info(' CA, otherwise caddy looks in /var/root and fails):');
info(` ${colors.cyan('sudo -E HOME="$HOME" caddy trust')}`);
info(` Browsers will then accept ${colors.cyan('https://*.localhost')} without warnings.`);
return { blocked, caddyMissing: false, ok: installResult.ok, unsupported: false };
});
}
/**
* Quick `brew services list` scan for a registered caddy service.
* Returns true on macOS if any entry contains "caddy" — error/started
* alike, both are conflicts. Always returns false on non-darwin or
* when `brew` is unavailable (no false positives).
*/
function detectBrewCaddyConflict() {
if (process.platform !== 'darwin')
return Promise.resolve(false);
return new Promise((resolve) => {
var _a;
const { spawn } = require('child_process');
const child = spawn('brew', ['services', 'list'], { stdio: ['ignore', 'pipe', 'ignore'] });
let out = '';
(_a = child.stdout) === null || _a === void 0 ? void 0 : _a.on('data', (b) => (out += String(b)));
child.on('error', () => resolve(false));
child.on('close', () => {
const conflict = /\bcaddy\b/.test(out) && !/^caddy\s+none\b/m.test(out);
resolve(conflict);
});
});
}