@tamyla/ui-components-react
Version:
React-based UI component library with Factory Bridge pattern - integrates seamlessly with @tamyla/ui-components. Enhanced AI agent discoverability with structured component registry, comprehensive Storybook (8 components), and detailed guides.
207 lines (180 loc) • 7.15 kB
JavaScript
/**
* Workflow Dashboard
*
* Provides a quick overview of workflow status, recent runs, and key metrics.
* Useful for monitoring the health of the pre-production validation process.
*
* Usage:
* - node scripts/workflow-dashboard.js
* - node scripts/workflow-dashboard.js --recent=5
* - node scripts/workflow-dashboard.js --status
*/
import fs from 'fs';
import path from 'path';
import { execSync } from 'child_process';
// Parse command line arguments
const recent = parseInt(process.argv.find(arg => arg.startsWith('--recent='))?.split('=')[1]) || 10;
const showStatus = process.argv.includes('--status');
const json = process.argv.includes('--json');
console.log('📊 WORKFLOW DASHBOARD');
console.log('=====================');
// Check if temp directory exists
const tempDir = 'temp';
if (!fs.existsSync(tempDir)) {
console.log('❌ No workflow data found. Run a workflow first.');
process.exit(1);
}
// Get all workflow reports
const reportFiles = fs.readdirSync(tempDir)
.filter(file => file.startsWith('preprod-report-'))
.sort((a, b) => {
const aTime = fs.statSync(path.join(tempDir, a)).mtime;
const bTime = fs.statSync(path.join(tempDir, b)).mtime;
return bTime - aTime;
});
if (reportFiles.length === 0) {
console.log('❌ No workflow reports found.');
process.exit(1);
}
// Load recent reports
const recentReports = reportFiles.slice(0, recent).map(file => {
try {
const content = JSON.parse(fs.readFileSync(path.join(tempDir, file), 'utf8'));
return {
file,
...content,
timestamp: new Date(content.timestamp)
};
} catch (error) {
console.warn(`⚠️ Could not parse ${file}: ${error.message}`);
return null;
}
}).filter(Boolean);
// Calculate metrics
const metrics = calculateMetrics(recentReports);
// Display dashboard
if (json) {
console.log(JSON.stringify({
metrics,
recentRuns: recentReports.slice(0, 5)
}, null, 2));
} else {
displayDashboard(metrics, recentReports);
}
function calculateMetrics(reports) {
const totalRuns = reports.length;
const successfulRuns = reports.filter(r => r.summary.failed === 0).length;
const failedRuns = totalRuns - successfulRuns;
const avgDuration = reports.reduce((sum, r) => sum + r.duration, 0) / totalRuns;
const successRate = totalRuns > 0 ? (successfulRuns / totalRuns) * 100 : 0;
// Step success rates
const stepStats = {};
reports.forEach(report => {
Object.entries(report.steps).forEach(([step, result]) => {
if (!stepStats[step]) {
stepStats[step] = { total: 0, passed: 0, failed: 0, skipped: 0 };
}
stepStats[step].total++;
if (result.executed) {
if (result.success) stepStats[step].passed++;
else stepStats[step].failed++;
} else if (result.skipped) {
stepStats[step].skipped++;
}
});
});
// Release type distribution
const releaseTypes = {};
reports.forEach(report => {
releaseTypes[report.releaseType] = (releaseTypes[report.releaseType] || 0) + 1;
});
return {
totalRuns,
successfulRuns,
failedRuns,
successRate: successRate.toFixed(1),
avgDuration: avgDuration.toFixed(1),
stepStats,
releaseTypes,
lastRun: reports[0]?.timestamp || null
};
}
function displayDashboard(metrics, reports) {
console.log('');
console.log('📈 OVERVIEW METRICS');
console.log('===================');
console.log(`Total Runs: ${metrics.totalRuns}`);
console.log(`Success Rate: ${metrics.successRate}%`);
console.log(`Successful: ${metrics.successfulRuns}`);
console.log(`Failed: ${metrics.failedRuns}`);
console.log(`Avg Duration: ${(metrics.avgDuration / 1000).toFixed(1)}s`);
console.log(`Last Run: ${metrics.lastRun ? metrics.lastRun.toLocaleString() : 'N/A'}`);
console.log('');
console.log('🏷️ RELEASE TYPE DISTRIBUTION');
console.log('============================');
Object.entries(metrics.releaseTypes).forEach(([type, count]) => {
const percentage = ((count / metrics.totalRuns) * 100).toFixed(1);
console.log(`${type.toUpperCase().padEnd(8)} ${count.toString().padStart(3)} runs (${percentage}%)`);
});
console.log('');
console.log('🔄 STEP SUCCESS RATES');
console.log('====================');
Object.entries(metrics.stepStats).forEach(([step, stats]) => {
const successRate = stats.total > 0 ? ((stats.passed / stats.total) * 100).toFixed(1) : '0.0';
const status = parseFloat(successRate) >= 90 ? '🟢' :
parseFloat(successRate) >= 70 ? '🟡' : '🔴';
console.log(`${status} ${step.padEnd(15)} ${successRate}% (${stats.passed}/${stats.total})`);
});
console.log('');
console.log('📋 RECENT RUNS');
console.log('==============');
reports.slice(0, 5).forEach((report, index) => {
const status = report.summary.failed === 0 ? '✅' : '❌';
const duration = (report.duration / 1000).toFixed(1);
const timeAgo = getTimeAgo(report.timestamp);
console.log(`${index + 1}. ${status} ${report.releaseType.toUpperCase().padStart(6)} | ${duration}s | ${timeAgo}`);
console.log(` ${report.file}`);
});
// Show status if requested
if (showStatus) {
console.log('');
console.log('🔍 CURRENT STATUS');
console.log('=================');
// Check for uncommitted changes
try {
const gitStatus = execSync('git status --porcelain', { encoding: 'utf8' });
if (gitStatus.trim()) {
console.log('⚠️ Uncommitted changes detected');
} else {
console.log('✅ Working directory is clean');
}
} catch (error) {
console.log('❓ Could not check git status');
}
// Check if build artifacts exist
const buildExists = fs.existsSync('dist/index.esm.js');
console.log(`${buildExists ? '✅' : '❌'} Build artifacts ${buildExists ? 'present' : 'missing'}`);
// Check if validation results exist
const validationExists = fs.existsSync(path.join(tempDir, 'validation-results.json'));
console.log(`${validationExists ? '✅' : '❌'} Validation results ${validationExists ? 'available' : 'missing'}`);
}
console.log('');
console.log('💡 QUICK ACTIONS');
console.log('================');
console.log('• Run workflow: npm run preprod:patch');
console.log('• View reports: npm run workflow:report');
console.log('• Check status: node scripts/workflow-dashboard.js --status');
console.log('• Recent runs: node scripts/workflow-dashboard.js --recent=3');
}
function getTimeAgo(date) {
const now = new Date();
const diffMs = now - date;
const diffMins = Math.floor(diffMs / 60000);
const diffHours = Math.floor(diffMins / 60);
const diffDays = Math.floor(diffHours / 24);
if (diffMins < 1) return 'just now';
if (diffMins < 60) return `${diffMins}m ago`;
if (diffHours < 24) return `${diffHours}h ago`;
return `${diffDays}d ago`;
}