UNPKG

@lenne.tech/cli

Version:

lenne.Tech CLI: lt

137 lines (136 loc) 6.87 kB
"use strict"; 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); }); }); }