perf-audit-cli
Version:
CLI tool for continuous performance monitoring and analysis
160 lines • 5.75 kB
JavaScript
import fs from 'fs';
import path from 'path';
import readline from 'readline';
import { CACHE_DIRECTORY, CACHE_RETENTION_DAYS, DEFAULT_RETENTION_DAYS, MILLISECONDS_PER_DAY, REPORT_EXTENSIONS, } from "../constants/index.js";
import { PerformanceDatabaseService } from "../core/database/index.js";
import { loadConfig } from "../utils/config.js";
import { Logger } from "../utils/logger.js";
export const cleanCommand = async (options) => {
Logger.section('Cleaning performance data...');
try {
const config = await loadConfig();
if (options.all) {
await cleanAllData(config, options.force);
}
else {
const retentionDays = options.days ?? DEFAULT_RETENTION_DAYS;
await cleanOldData(config, retentionDays, options.force);
}
}
catch (error) {
handleCleanError(error);
}
};
const cleanAllData = async (config, force = false) => {
if (!force) {
const confirmed = await confirmAction('This will delete ALL performance data including database and reports. Are you sure?');
if (!confirmed) {
Logger.warn('Clean cancelled.');
return;
}
}
await cleanDatabase();
const deletedReportsCount = cleanAllReports(config);
cleanCacheDirectory();
Logger.success(`${deletedReportsCount} report files deleted`);
Logger.complete('All performance data has been cleaned!');
};
const cleanOldData = async (config, days, force = false) => {
if (!force) {
const confirmed = await confirmAction(`This will delete performance data older than ${days} days. Are you sure?`);
if (!confirmed) {
Logger.warn('Clean cancelled.');
return;
}
}
const deletedBuilds = await cleanOldDatabaseRecords(days);
const deletedReportsCount = cleanOldReports(config, days);
cleanOldCacheFiles();
Logger.success(`${deletedBuilds} old builds deleted from database`);
Logger.success(`${deletedReportsCount} old report files deleted`);
Logger.success('Old cache files cleaned');
Logger.complete(`Performance data older than ${days} days has been cleaned!`);
};
const cleanDatabase = async () => {
const service = await PerformanceDatabaseService.instance();
await service.cleanDatabase();
};
const cleanAllReports = (config) => {
const reportsDir = path.resolve(config.reports.outputDir);
if (!fs.existsSync(reportsDir)) {
return 0;
}
const files = fs.readdirSync(reportsDir);
let deletedCount = 0;
for (const file of files) {
if (isReportFile(file)) {
const filePath = path.join(reportsDir, file);
fs.unlinkSync(filePath);
deletedCount++;
}
}
return deletedCount;
};
const cleanCacheDirectory = () => {
const cacheDir = path.resolve(CACHE_DIRECTORY);
if (fs.existsSync(cacheDir)) {
fs.rmSync(cacheDir, { recursive: true, force: true });
fs.mkdirSync(cacheDir, { recursive: true });
Logger.success('Cache directory cleaned');
}
};
const cleanOldDatabaseRecords = async (days) => {
const db = await PerformanceDatabaseService.instance();
const deletedBuilds = await db.cleanup(days);
await db.close();
return deletedBuilds;
};
const cleanOldReports = (config, days) => {
const reportsDir = path.resolve(config.reports.outputDir);
if (!fs.existsSync(reportsDir)) {
return 0;
}
const cutoffTime = calculateCutoffTime(days);
const files = fs.readdirSync(reportsDir);
let deletedCount = 0;
for (const file of files) {
const filePath = path.join(reportsDir, file);
const stats = fs.statSync(filePath);
if (stats.mtimeMs < cutoffTime && isReportFile(file)) {
fs.unlinkSync(filePath);
deletedCount++;
}
}
return deletedCount;
};
const cleanOldCacheFiles = () => {
const cacheDir = path.resolve(CACHE_DIRECTORY);
if (fs.existsSync(cacheDir)) {
const cutoffTime = calculateCutoffTime(CACHE_RETENTION_DAYS);
cleanDirectoryRecursive(cacheDir, cutoffTime);
}
};
const calculateCutoffTime = (days) => {
return Date.now() - (days * MILLISECONDS_PER_DAY);
};
const isReportFile = (fileName) => {
return REPORT_EXTENSIONS.some(ext => fileName.endsWith(ext));
};
const cleanDirectoryRecursive = (dir, cutoffTime) => {
const items = fs.readdirSync(dir);
for (const item of items) {
const itemPath = path.join(dir, item);
const stats = fs.statSync(itemPath);
if (stats.isDirectory()) {
cleanDirectoryRecursive(itemPath, cutoffTime);
removeEmptyDirectory(itemPath);
}
else if (isOldFile(stats, cutoffTime)) {
fs.unlinkSync(itemPath);
}
}
};
const isOldFile = (stats, cutoffTime) => {
return stats.mtimeMs < cutoffTime;
};
const removeEmptyDirectory = (dirPath) => {
const remainingItems = fs.readdirSync(dirPath);
if (remainingItems.length === 0) {
fs.rmdirSync(dirPath);
}
};
const confirmAction = async (message) => {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
return new Promise(resolve => {
rl.question(Logger.prompt(`${message} (y/N): `), answer => {
rl.close();
const normalizedAnswer = answer.toLowerCase();
resolve(normalizedAnswer === 'y' || normalizedAnswer === 'yes');
});
});
};
const handleCleanError = (error) => {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
Logger.error('Clean failed', { error: errorMessage });
process.exit(1);
};
//# sourceMappingURL=clean.js.map