kmuc-dev-cli
Version:
Complete development CLI for modern web projects - From init to deploy, Docker, CI/CD, testing, and more
196 lines (164 loc) • 6.39 kB
JavaScript
const chalk = require('chalk').default || require('chalk');
const ora = require('ora').default || require('ora');
const fs = require('fs').promises;
const { exec, spawn } = require('child_process');
const { promisify } = require('util');
const path = require('path');
const execAsync = promisify(exec);
async function logsCommand(options) {
const isDetailed = options.detailed || false;
try {
// Prüfe ob docker-compose.yml existiert
const composeExists = await checkFile('docker-compose.yml');
if (!composeExists) {
console.error(chalk.red('\n❌ Kein Docker-Projekt gefunden!'));
console.log(chalk.yellow('💡 Führe zuerst "kmuc init" aus\n'));
process.exit(1);
}
// Lese docker-compose.yml um Services zu identifizieren
const composeContent = await fs.readFile('docker-compose.yml', 'utf8');
const services = parseServices(composeContent);
if (services.length === 0) {
console.error(chalk.red('\n❌ Keine Services gefunden!\n'));
process.exit(1);
}
console.log(chalk.cyan.bold('\n📋 KMUC CLI Logs\n'));
// Zeige verfügbare Services
console.log(chalk.gray('Verfügbare Services:'));
services.forEach(service => {
console.log(chalk.gray(` • ${service}`));
});
console.log();
// Prüfe ob Container laufen
const spinner = ora('Prüfe Container Status...').start();
try {
const { stdout } = await execAsync('docker-compose ps --services --filter "status=running"');
const runningServices = stdout.trim().split('\n').filter(s => s);
if (runningServices.length === 0) {
spinner.fail('Keine laufenden Container gefunden');
console.log();
console.log(chalk.yellow('💡 Starte dein Projekt mit:'));
console.log(chalk.gray(' kmuc publish\n'));
process.exit(0);
}
spinner.succeed(`${runningServices.length} Container läuft/laufen`);
} catch (error) {
spinner.fail('Konnte Container Status nicht prüfen');
console.log(chalk.yellow('⚠️ Docker Compose nicht verfügbar\n'));
process.exit(1);
}
// Intelligente Log-Anzeige
console.log();
if (isDetailed) {
console.log(chalk.cyan.bold('🔍 Detaillierte Logs (alle Services)\n'));
console.log(chalk.gray('Drücke Ctrl+C zum Beenden\n'));
// Zeige detaillierte Logs mit Timestamps
const logsProcess = spawn('docker-compose', ['logs', '-f', '--timestamps'], {
stdio: 'inherit',
cwd: process.cwd()
});
logsProcess.on('error', (error) => {
console.error(chalk.red('\n❌ Fehler beim Anzeigen der Logs:'), error.message);
process.exit(1);
});
// Graceful shutdown
process.on('SIGINT', () => {
console.log(chalk.cyan('\n\n👋 Logs beendet\n'));
logsProcess.kill();
process.exit(0);
});
} else {
// Intelligente Kurzansicht: Hauptservice und Fehler
console.log(chalk.cyan.bold('📊 Live Logs (Hauptservice + Fehler)\n'));
console.log(chalk.gray('Nutze --detailed für alle Logs\n'));
console.log(chalk.gray('Drücke Ctrl+C zum Beenden\n'));
// Bestimme Hauptservice (app, web, oder erster Service)
let mainService = 'app';
if (!services.includes('app')) {
mainService = services.find(s => s.includes('web') || s.includes('frontend') || s.includes('backend')) || services[0];
}
// Zeige nur Hauptservice, aber mit Fehlerfilterung für alle
const logsArgs = isDetailed
? ['logs', '-f', '--timestamps']
: ['logs', '-f', '--tail=50', mainService];
const logsProcess = spawn('docker-compose', logsArgs, {
stdio: ['inherit', 'pipe', 'pipe'],
cwd: process.cwd()
});
// Verarbeite Output
logsProcess.stdout.on('data', (data) => {
const lines = data.toString().split('\n');
lines.forEach(line => {
if (!line.trim()) return;
// Highlight Errors und Warnings
if (line.toLowerCase().includes('error') || line.toLowerCase().includes('exception')) {
console.log(chalk.red(line));
} else if (line.toLowerCase().includes('warn')) {
console.log(chalk.yellow(line));
} else if (line.toLowerCase().includes('success') || line.toLowerCase().includes('started')) {
console.log(chalk.green(line));
} else {
console.log(chalk.gray(line));
}
});
});
logsProcess.stderr.on('data', (data) => {
const lines = data.toString().split('\n');
lines.forEach(line => {
if (line.trim()) {
console.log(chalk.red(line));
}
});
});
logsProcess.on('error', (error) => {
console.error(chalk.red('\n❌ Fehler beim Anzeigen der Logs:'), error.message);
process.exit(1);
});
// Graceful shutdown
process.on('SIGINT', () => {
console.log(chalk.cyan('\n\n👋 Logs beendet'));
console.log(chalk.gray('\n💡 Nützliche Befehle:'));
console.log(chalk.gray(' kmuc logs --detailed'), chalk.dim('- Alle Logs'));
console.log(chalk.gray(' docker-compose ps'), chalk.dim('- Container Status'));
console.log(chalk.gray(' docker-compose restart'), chalk.dim('- Container neu starten\n'));
logsProcess.kill();
process.exit(0);
});
}
} catch (error) {
console.error(chalk.red('\n❌ Fehler:'), error.message);
process.exit(1);
}
}
async function checkFile(filename) {
try {
await fs.access(path.join(process.cwd(), filename));
return true;
} catch {
return false;
}
}
function parseServices(composeContent) {
const services = [];
const lines = composeContent.split('\n');
let inServices = false;
for (const line of lines) {
if (line.trim() === 'services:') {
inServices = true;
continue;
}
if (inServices) {
// Wenn neue Top-Level Section beginnt (z.B. networks, volumes)
if (line.match(/^[a-z]+:$/) && !line.startsWith(' ')) {
break;
}
// Service Definition (2 Spaces Indent)
const serviceMatch = line.match(/^ ([a-z0-9_-]+):/);
if (serviceMatch) {
services.push(serviceMatch[1]);
}
}
}
return services;
}
module.exports = { logsCommand };