UNPKG

eol-check

Version:

CLI tool to check End-of-Life (EOL) status of Node.js, package managers, operating systems, dependencies, and databases. Supports HTML reports and GitHub Actions.

221 lines (220 loc) 10.2 kB
#!/usr/bin/env node "use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const commander_1 = require("commander"); const chalk_1 = __importDefault(require("chalk")); const open_1 = __importDefault(require("open")); const scannerEngine_1 = require("./scanners/scannerEngine"); const endoflifeApi_1 = require("./core/endoflifeApi"); const evaluator_1 = require("./core/evaluator"); const dependencyScanner_1 = require("./scanners/dependencyScanner"); const productMapper_1 = require("./core/productMapper"); const htmlReporter_1 = require("./reporters/htmlReporter"); const program = new commander_1.Command(); program .name('eol-check') .description('Check End of Life (EOL) status of your development environment and project dependencies') .version('1.4.3') .option('--json', 'Output results as JSON') .option('--html <filename>', 'Generate HTML report to specified file') .option('--no-browser', 'Do not open HTML report in browser') .option('--verbose', 'Show verbose output') .option('--refresh-cache', 'Force refresh cache from API') .action(async (cmdOptions) => { try { await main(cmdOptions); } catch (err) { console.error(chalk_1.default.red('Error running eol-check:'), err); process.exit(1); } }); program .command('query') .description('Query EOL status for a specific product') .argument('<product>', 'Product name (e.g. nodejs, python, ubuntu)') .argument('[version]', 'Specific version to check') .option('--refresh-cache', 'Force refresh cache from API') .action(async (product, version, cmdOptions) => { try { const data = await (0, endoflifeApi_1.fetchEolData)(product, cmdOptions.refreshCache); if (version) { const result = (0, evaluator_1.evaluateVersion)(product, version, data); let color = chalk_1.default.green; if (result.status === evaluator_1.Status.WARN) color = chalk_1.default.yellow; if (result.status === evaluator_1.Status.ERR) color = chalk_1.default.red; console.log(`${color(`[${result.status}]`)} ${chalk_1.default.bold(result.component)} ${result.version} - ${result.message}`); } else { console.log(chalk_1.default.bold(`EOL Data for ${product}:`)); console.table(data.map((release) => ({ Cycle: release.cycle, 'Release Date': release.releaseDate, 'EOL Date': release.eol, 'LTS': release.lts, }))); } } catch (error) { const errorMsg = error instanceof Error ? error.message : String(error); console.error(chalk_1.default.red('\n✗ Failed to fetch EOL data')); console.error(chalk_1.default.yellow(`Product: ${chalk_1.default.bold(product)}`)); if (errorMsg.includes('404') || errorMsg.includes('Not Found')) { console.error(chalk_1.default.gray(`\nThe product "${product}" was not found on endoflife.date.`)); console.error(chalk_1.default.gray('Please check the product name and try again.')); console.error(chalk_1.default.gray(`\nSearch for available products at: ${chalk_1.default.cyan('https://endoflife.date/api/all.json')}`)); } else { console.error(chalk_1.default.gray(`\nError: ${errorMsg}`)); } } process.exit(0); }); program.parse(process.argv); async function main(options) { if (options.verbose) { console.log(chalk_1.default.blue('Scanning environment...')); } const scanResult = (0, scannerEngine_1.scanEnvironment)(); const results = []; // Check Node.js if (scanResult.nodeVersion) { if (options.verbose) console.log(`Checking Node.js ${scanResult.nodeVersion}...`); const nodeData = await (0, endoflifeApi_1.fetchEolData)('nodejs', options.refreshCache); const result = (0, evaluator_1.evaluateVersion)('Node.js', scanResult.nodeVersion, nodeData); result.category = evaluator_1.Category.RUNTIME; results.push(result); } // Check OS (if detected) if (scanResult.os && scanResult.os !== 'Unknown') { // Note: OS matching is tricky with endoflife.date as they have specific slugs like 'ubuntu', 'alpine'. // For now, we'll try to map common ones or skip if unsure. // This is a simplified implementation for the prototype. const osLower = scanResult.os.toLowerCase(); let product = ''; if (osLower.includes('ubuntu')) product = 'ubuntu'; else if (osLower.includes('alpine')) product = 'alpine'; else if (osLower.includes('debian')) product = 'debian'; if (product) { if (options.verbose) console.log(`Checking OS ${scanResult.os} (mapped to ${product})...`); try { const osData = await (0, endoflifeApi_1.fetchEolData)(product, options.refreshCache); // Extract version from string like "Ubuntu 22.04.5 LTS" -> "22.04" const versionMatch = scanResult.os.match(/(\d+(\.\d+)?)/); if (versionMatch) { const result = (0, evaluator_1.evaluateVersion)(scanResult.os, versionMatch[0], osData); result.category = evaluator_1.Category.OS; results.push(result); } } catch (error) { if (options.verbose) { console.warn(chalk_1.default.yellow(`Warning: Could not fetch EOL data for OS ${scanResult.os}: ${error}`)); } } } } // Check System Services if (scanResult.services.length > 0) { if (options.verbose) console.log('Checking system services...'); for (const service of scanResult.services) { if (options.verbose) console.log(`Checking service ${service.name} (${service.version})...`); try { const eolData = await (0, endoflifeApi_1.fetchEolData)(service.product, options.refreshCache); if (eolData && eolData.length > 0) { const result = (0, evaluator_1.evaluateVersion)(service.name, service.version, eolData); result.category = evaluator_1.Category.SERVICE; results.push(result); } } catch (error) { if (options.verbose) { console.warn(chalk_1.default.yellow(`Warning: Could not fetch EOL data for ${service.name} (${service.product}): ${error}`)); } } } } // Check Project Dependencies if (options.verbose) console.log('Scanning project dependencies...'); const dependencies = (0, dependencyScanner_1.scanDependencies)(process.cwd()); for (const dep of dependencies) { const product = (0, productMapper_1.mapPackageToProduct)(dep.name); if (product) { if (options.verbose) console.log(`Checking dependency ${dep.name} (mapped to ${product})...`); try { const eolData = await (0, endoflifeApi_1.fetchEolData)(product, options.refreshCache); if (eolData && eolData.length > 0) { const version = (0, dependencyScanner_1.cleanVersion)(dep.version); const result = (0, evaluator_1.evaluateVersion)(dep.name, version, eolData); result.category = evaluator_1.Category.DEPENDENCY; results.push(result); } } catch (error) { if (options.verbose) { console.warn(chalk_1.default.yellow(`Warning: Could not fetch EOL data for ${dep.name}: ${error}`)); } else { console.warn(chalk_1.default.yellow(`Warning: Could not fetch EOL data for ${dep.name} (mapped to ${product}). Skipping...`)); } } } } // Generate HTML report if requested if (options.html) { try { (0, htmlReporter_1.generateHtmlReport)(results, options.html); console.log(chalk_1.default.green(`\n✓ HTML report generated: ${options.html}`)); if (options.browser !== false) { await (0, open_1.default)(options.html); if (options.verbose) console.log('Opening report in default browser...'); } } catch (error) { console.error(chalk_1.default.red(`Failed to generate HTML report: ${error}`)); } } if (options.json) { console.log(JSON.stringify(results, null, 2)); } else { console.log(chalk_1.default.bold('\nEOL Check Results:')); let hasError = false; // Group results by category const categories = [evaluator_1.Category.RUNTIME, evaluator_1.Category.OS, evaluator_1.Category.SERVICE, evaluator_1.Category.DEPENDENCY]; for (const category of categories) { const categoryResults = results.filter((r) => r.category === category); if (categoryResults.length === 0) continue; console.log(chalk_1.default.cyan(`\n── ${category} ──`)); categoryResults.forEach((res) => { let color = chalk_1.default.green; if (res.status === evaluator_1.Status.WARN) color = chalk_1.default.yellow; if (res.status === evaluator_1.Status.ERR) { color = chalk_1.default.red; hasError = true; } console.log(`${color(`[${res.status}]`)} ${chalk_1.default.bold(res.component)} ${res.version} - ${res.message}`); }); } if (hasError) { process.exit(1); } } }