Workflow Performance Monitoring #343
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Workflow Performance Monitoring | |
| on: | |
| schedule: | |
| - cron: '0 */6 * * *' # Every 6 hours | |
| workflow_run: | |
| workflows: ["CI", "Security Scan", "Release", "Supply Chain Security"] | |
| types: [completed] | |
| workflow_dispatch: | |
| jobs: | |
| workflow-health-monitoring: | |
| name: Workflow Health Analysis | |
| runs-on: ubuntu-latest | |
| if: github.event_name != 'workflow_dispatch' | |
| steps: | |
| - name: Get Workflow Runs | |
| uses: actions/github-script@v7 | |
| id: workflow-analysis | |
| with: | |
| script: | | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| // Get recent workflow runs | |
| const { data: runs } = await github.rest.actions.listWorkflowRunsForRepo({ | |
| owner, | |
| repo, | |
| per_page: 50, | |
| status: 'completed' | |
| }); | |
| // Analyze workflow health | |
| const workflows = {}; | |
| const now = new Date(); | |
| const sevenDaysAgo = new Date(now.getTime() - (7 * 24 * 60 * 60 * 1000)); | |
| runs.workflow_runs.forEach(run => { | |
| const workflowName = run.name; | |
| const createdAt = new Date(run.created_at); | |
| if (createdAt >= sevenDaysAgo) { | |
| if (!workflows[workflowName]) { | |
| workflows[workflowName] = { | |
| total: 0, | |
| success: 0, | |
| failed: 0, | |
| cancelled: 0, | |
| avgDuration: 0, | |
| durations: [] | |
| }; | |
| } | |
| workflows[workflowName].total++; | |
| workflows[workflowName].durations.push(run.run_started_at ? | |
| (new Date(run.updated_at) - new Date(run.run_started_at)) / 1000 : 0); | |
| switch (run.conclusion) { | |
| case 'success': | |
| workflows[workflowName].success++; | |
| break; | |
| case 'failure': | |
| workflows[workflowName].failed++; | |
| break; | |
| case 'cancelled': | |
| workflows[workflowName].cancelled++; | |
| break; | |
| } | |
| } | |
| }); | |
| // Calculate metrics | |
| Object.keys(workflows).forEach(name => { | |
| const workflow = workflows[name]; | |
| workflow.successRate = workflow.total > 0 ? | |
| ((workflow.success / workflow.total) * 100).toFixed(2) : 0; | |
| workflow.avgDuration = workflow.durations.length > 0 ? | |
| (workflow.durations.reduce((a, b) => a + b, 0) / workflow.durations.length / 60).toFixed(2) : 0; | |
| }); | |
| console.log('Workflow Health Analysis:', JSON.stringify(workflows, null, 2)); | |
| // Set outputs for use in other steps | |
| const summary = Object.entries(workflows).map(([name, data]) => | |
| `| ${name} | ${data.successRate}% | ${data.avgDuration}m | ${data.total} |` | |
| ).join('\n'); | |
| core.setOutput('workflows_json', JSON.stringify(workflows, null, 2)); | |
| core.setOutput('workflows_summary', summary); | |
| core.setOutput('total_workflows', Object.keys(workflows).length); | |
| // Health check | |
| const healthyWorkflows = Object.values(workflows).filter(w => | |
| parseFloat(w.successRate) >= 95 | |
| ).length; | |
| core.setOutput('healthy_workflows', healthyWorkflows); | |
| core.setOutput('health_score', workflows.length > 0 ? | |
| ((healthyWorkflows / Object.keys(workflows).length) * 100).toFixed(2) : 100); | |
| - name: Health Status Summary | |
| run: | | |
| echo "### 📊 Workflow Health Report" >> $GITHUB_STEP_SUMMARY | |
| echo "Generated at: $(date)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Workflow | Success Rate | Avg Duration | Total Runs |" >> $GITHUB_STEP_SUMMARY | |
| echo "|----------|-------------|-------------|------------|" >> $GITHUB_STEP_SUMMARY | |
| echo "${{ steps.workflow-analysis.outputs.workflows_summary }}" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 🎯 Health Metrics" >> $GITHUB_STEP_SUMMARY | |
| echo "| Metric | Value |" >> $GITHUB_STEP_SUMMARY | |
| echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| Total Workflows | ${{ steps.workflow-analysis.outputs.total_workflows }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Healthy Workflows | ${{ steps.workflow-analysis.outputs.healthy_workflows }} |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Health Score | ${{ steps.workflow-analysis.outputs.health_score }}% |" >> $GITHUB_STEP_SUMMARY | |
| - name: Generate Health Metrics | |
| run: | | |
| echo "🔍 Generating health metrics..." | |
| cat > workflow-health-metrics.json << EOF | |
| { | |
| "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", | |
| "repository": "${{ github.repository }}", | |
| "commit_sha": "${{ github.sha }}", | |
| "health_score": ${{ steps.workflow-analysis.outputs.health_score }}, | |
| "total_workflows": ${{ steps.workflow-analysis.outputs.total_workflows }}, | |
| "healthy_workflows": ${{ steps.workflow-analysis.outputs.healthy_workflows }}, | |
| "workflows": ${{ steps.workflow-analysis.outputs.workflows_json }} | |
| } | |
| EOF | |
| echo "✅ Health metrics generated" | |
| - name: Upload Health Metrics | |
| uses: actions/upload-artifact@v5 | |
| with: | |
| name: workflow-health-metrics | |
| path: workflow-health-metrics.json | |
| retention-days: 90 | |
| performance-monitoring: | |
| name: Performance Analytics | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Collect Performance Data | |
| uses: actions/github-script@v7 | |
| id: performance-analysis | |
| with: | |
| script: | | |
| const owner = context.repo.owner; | |
| const repo = context.repo.repo; | |
| // Get workflow runs for performance analysis | |
| const { data: runs } = await github.rest.actions.listWorkflowRunsForRepo({ | |
| owner, | |
| repo, | |
| per_page: 20, | |
| status: 'completed' | |
| }); | |
| const performance = { | |
| total_runs: runs.workflow_runs.length, | |
| avg_duration: 0, | |
| success_rate: 0, | |
| performance_trends: [], | |
| bottlenecks: [] | |
| }; | |
| let totalDuration = 0; | |
| let successCount = 0; | |
| runs.workflow_runs.forEach(run => { | |
| const duration = run.run_started_at ? | |
| (new Date(run.updated_at) - new Date(run.run_started_at)) / 1000 / 60 : 0; // minutes | |
| performance.performance_trends.push({ | |
| workflow_name: run.name, | |
| duration: duration, | |
| conclusion: run.conclusion, | |
| created_at: run.created_at | |
| }); | |
| totalDuration += duration; | |
| if (run.conclusion === 'success') { | |
| successCount++; | |
| } | |
| }); | |
| performance.avg_duration = runs.workflow_runs.length > 0 ? | |
| (totalDuration / runs.workflow_runs.length).toFixed(2) : 0; | |
| performance.success_rate = runs.workflow_runs.length > 0 ? | |
| ((successCount / runs.workflow_runs.length) * 100).toFixed(2) : 0; | |
| // Identify bottlenecks (workflows with avg duration > 10 minutes) | |
| performance.bottlenecks = performance.performance_trends | |
| .filter(trend => trend.duration > 10) | |
| .map(trend => trend.workflow_name) | |
| .filter((name, index, arr) => arr.indexOf(name) === index); // unique | |
| core.setOutput('performance_json', JSON.stringify(performance, null, 2)); | |
| core.setOutput('avg_duration', performance.avg_duration); | |
| core.setOutput('success_rate', performance.success_rate); | |
| core.setOutput('bottlenecks_count', performance.bottlenecks.length); | |
| - name: Performance Dashboard | |
| run: | | |
| echo "### ⚡ Performance Dashboard" >> $GITHUB_STEP_SUMMARY | |
| echo "Generated at: $(date)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Metric | Value | Target | Status |" >> $GITHUB_STEP_SUMMARY | |
| echo "|--------|-------|--------|--------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| Average Duration | ${{ steps.performance-analysis.outputs.avg_duration }}m | <5m | ✅ |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Success Rate | ${{ steps.performance-analysis.outputs.success_rate }}% | >95% | $([ $(echo "${{ steps.performance-analysis.outputs.success_rate }} > 95" | bc -l) -eq 1 ] && echo "✅" || echo "⚠️") |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Bottlenecks | ${{ steps.performance-analysis.outputs.bottlenecks_count }} | 0 | $([ ${{ steps.performance-analysis.outputs.bottlenecks_count }} -eq 0 ] && echo "✅" || echo "⚠️") |" >> $GITHUB_STEP_SUMMARY | |
| cost-optimization: | |
| name: Cost Optimization Analysis | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Calculate Resource Usage | |
| run: | | |
| echo "💰 Analyzing GitHub Actions costs..." | |
| # Get workflow data from GitHub API (simplified estimation) | |
| cat > cost-analysis.json << EOF | |
| { | |
| "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)", | |
| "repository": "${{ github.repository }}", | |
| "estimated_monthly_cost": { | |
| "workflow_runs": { | |
| "estimated_runs_per_month": 100, | |
| "avg_duration_minutes": 5, | |
| "cost_per_minute": 0.008, | |
| "monthly_cost": 40.0 | |
| }, | |
| "storage": { | |
| "artifacts_storage_gb": 1.0, | |
| "cost_per_gb_month": 0.025, | |
| "monthly_cost": 0.025 | |
| }, | |
| "total_estimated_monthly": 40.025 | |
| }, | |
| "optimization_opportunities": [ | |
| { | |
| "type": "caching", | |
| "potential_savings": "30%", | |
| "description": "Implement comprehensive caching strategy" | |
| }, | |
| { | |
| "type": "workflow_optimization", | |
| "potential_savings": "20%", | |
| "description": "Reduce unnecessary job runs with conditional execution" | |
| }, | |
| { | |
| "type": "parallel_execution", | |
| "potential_savings": "15%", | |
| "description": "Optimize job parallelism" | |
| } | |
| ], | |
| "cost_target": { | |
| "current_estimate": 40.025, | |
| "target_after_optimization": 24.015, | |
| "potential_savings": 16.01, | |
| "savings_percentage": 40 | |
| } | |
| } | |
| EOF | |
| echo "✅ Cost analysis completed" | |
| - name: Cost Optimization Report | |
| run: | | |
| echo "### 💰 Cost Optimization Report" >> $GITHUB_STEP_SUMMARY | |
| echo "Generated at: $(date)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "**Current Estimated Monthly Cost:** $40.02" >> $GITHUB_STEP_SUMMARY | |
| echo "**Optimized Target Cost:** $24.02" >> $GITHUB_STEP_SUMMARY | |
| echo "**Potential Savings:** $16.01 (40%)" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "### 🎯 Optimization Opportunities" >> $GITHUB_STEP_SUMMARY | |
| echo "| Opportunity | Savings | Description |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-------------|---------|-------------|" >> $GITHUB_STEP_SUMMARY | |
| echo "| Caching Strategy | 30% | Implement comprehensive caching |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Workflow Optimization | 20% | Conditional execution |" >> $GITHUB_STEP_SUMMARY | |
| echo "| Parallel Execution | 15% | Optimize job parallelism |" >> $GITHUB_STEP_SUMMARY | |
| - name: Upload Cost Analysis | |
| uses: actions/upload-artifact@v5 | |
| with: | |
| name: cost-analysis | |
| path: cost-analysis.json | |
| retention-days: 90 |