From 24ed3f32c0bbf48d0cd0adf4257578acda5b2cb0 Mon Sep 17 00:00:00 2001 From: Xiaoyu Zhang <35585791+BBuf@users.noreply.github.com> Date: Mon, 20 Oct 2025 09:08:34 +0800 Subject: [PATCH] fix(ci): Fix CI Monitor limit parameter and add CI Analysis to summary (#11832) --- .github/workflows/ci-monitor.yml | 4 +- scripts/ci_monitor/ci_analyzer.py | 134 +++++++++++++++++++++++++ scripts/ci_monitor/ci_analyzer_perf.py | 4 +- 3 files changed, 138 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci-monitor.yml b/.github/workflows/ci-monitor.yml index d3d6d364a..89d7ec877 100644 --- a/.github/workflows/ci-monitor.yml +++ b/.github/workflows/ci-monitor.yml @@ -44,7 +44,7 @@ jobs: PYTHONIOENCODING: utf-8 run: | cd scripts/ci_monitor - python ci_analyzer.py --token $GITHUB_TOKEN --limit ${{ github.event.inputs.limit || '1000' }} --output ci_analysis_$(date +%Y%m%d_%H%M%S).json + python ci_analyzer.py --token $GITHUB_TOKEN --limit ${{ inputs.limit || '1000' }} --output ci_analysis_$(date +%Y%m%d_%H%M%S).json - name: Run Performance Analysis env: @@ -53,7 +53,7 @@ jobs: PYTHONIOENCODING: utf-8 run: | cd scripts/ci_monitor - python ci_analyzer_perf.py --token $GITHUB_TOKEN --limit ${{ github.event.inputs.limit || '1000' }} --output-dir performance_tables_$(date +%Y%m%d_%H%M%S) --upload-to-github + python ci_analyzer_perf.py --token $GITHUB_TOKEN --limit ${{ inputs.limit || '1000' }} --output-dir performance_tables_$(date +%Y%m%d_%H%M%S) --upload-to-github - name: Upload Analysis Results uses: actions/upload-artifact@v4 diff --git a/scripts/ci_monitor/ci_analyzer.py b/scripts/ci_monitor/ci_analyzer.py index 3dab9a9e8..922f3ef44 100755 --- a/scripts/ci_monitor/ci_analyzer.py +++ b/scripts/ci_monitor/ci_analyzer.py @@ -395,6 +395,137 @@ class SGLangCIAnalyzer: json.dump(stats, f, ensure_ascii=False, indent=2) print(f"\nDetailed report saved to: {output_file}") + def generate_github_summary(self, stats: Dict): + """Generate GitHub Actions summary""" + try: + github_step_summary = os.environ.get("GITHUB_STEP_SUMMARY") + if not github_step_summary: + print("â„šī¸ Not running in GitHub Actions, skipping summary generation") + return + + print("📊 Generating GitHub Actions summary for CI Analysis...") + + summary_lines = [] + summary_lines.append("# 🔍 SGLang CI Analysis Report (CUDA Only)") + summary_lines.append("") + + # Overall statistics + total = stats["total_runs"] + failed = stats["failed_runs"] + success = stats["successful_runs"] + cancelled = stats["cancelled_runs"] + skipped = stats["skipped_runs"] + success_rate = (success / total * 100) if total > 0 else 0 + + summary_lines.append("## 📊 Overall Statistics") + summary_lines.append("") + summary_lines.append("| Metric | Count | Percentage |") + summary_lines.append("|--------|-------|------------|") + summary_lines.append(f"| Total Runs | {total} | 100% |") + summary_lines.append( + f"| ✅ Successful | {success} | {success/total*100:.1f}% |" + ) + summary_lines.append(f"| ❌ Failed | {failed} | {failed/total*100:.1f}% |") + summary_lines.append( + f"| đŸšĢ Cancelled | {cancelled} | {cancelled/total*100:.1f}% |" + ) + summary_lines.append( + f"| â­ī¸ Skipped | {skipped} | {skipped/total*100:.1f}% |" + ) + summary_lines.append(f"| **Success Rate** | **{success_rate:.1f}%** | - |") + summary_lines.append("") + + # Category failure statistics + if stats["category_failures"]: + summary_lines.append("## 📁 Category Failure Statistics") + summary_lines.append("") + summary_lines.append("| Category | Failures |") + summary_lines.append("|----------|----------|") + for category, count in sorted( + stats["category_failures"].items(), key=lambda x: x[1], reverse=True + ): + summary_lines.append(f"| {category} | {count} |") + summary_lines.append("") + + # Most frequently failed jobs (Top 20) + if stats["job_failures"]: + summary_lines.append("## 🔴 Most Frequently Failed Jobs (Top 20)") + summary_lines.append("") + + top_failures = sorted( + stats["job_failures"].items(), key=lambda x: x[1], reverse=True + )[:20] + + for i, (job, count) in enumerate(top_failures, 1): + summary_lines.append(f"### {i}. `{job}` ({count} failures)") + summary_lines.append("") + + # Show last successful run + if job in stats["job_last_success"]: + last_success = stats["job_last_success"][job] + success_date = datetime.fromisoformat( + last_success["created_at"].replace("Z", "+00:00") + ) + pr_info = last_success["pr_info"] + + pr_text = "" + if pr_info["pr_number"]: + pr_text = ( + f" (PR #{pr_info['pr_number']} by {pr_info['author']})" + ) + else: + pr_text = f" by {pr_info['author']}" + + summary_lines.append( + f"✅ **Last Success:** [Run #{last_success['run_number']}]({last_success['url']}) ({success_date.strftime('%Y-%m-%d %H:%M')}){pr_text}" + ) + summary_lines.append("") + + # Show recent failure links + if ( + job in stats["job_failure_links"] + and stats["job_failure_links"][job] + ): + summary_lines.append("❌ **Recent Failures:**") + for link_info in stats["job_failure_links"][job]: + created_at = datetime.fromisoformat( + link_info["created_at"].replace("Z", "+00:00") + ) + + pr_info = link_info.get("pr_info", {}) + pr_text = "" + if pr_info.get("pr_number"): + pr_text = f" (PR #{pr_info['pr_number']} by {pr_info.get('author', 'Unknown')})" + else: + pr_text = f" by {pr_info.get('author', 'Unknown')}" + + summary_lines.append( + f"- [Run #{link_info['run_number']}]({link_info['url']}) ({created_at.strftime('%Y-%m-%d %H:%M')}){pr_text}" + ) + summary_lines.append("") + + # Failure pattern analysis + if stats["failure_patterns"]: + summary_lines.append("## đŸ”Ŧ Failure Pattern Analysis") + summary_lines.append("") + summary_lines.append("| Pattern | Count |") + summary_lines.append("|---------|-------|") + for pattern, count in sorted( + stats["failure_patterns"].items(), key=lambda x: x[1], reverse=True + ): + summary_lines.append(f"| {pattern} | {count} |") + summary_lines.append("") + + # Write summary to GitHub Actions + with open(github_step_summary, "w", encoding="utf-8") as f: + f.write("\n".join(summary_lines)) + f.write("\n\n---\n\n") # Add separator between reports + + print("✅ GitHub Actions summary generated successfully") + + except Exception as e: + print(f"❌ Failed to generate GitHub Actions summary: {e}") + def main(): parser = argparse.ArgumentParser(description="SGLang CI Analyzer") @@ -440,6 +571,9 @@ def main(): # Save detailed report analyzer.save_detailed_report(stats, args.output) + # Generate GitHub summary + analyzer.generate_github_summary(stats) + except Exception as e: print(f"Error during analysis: {e}") sys.exit(1) diff --git a/scripts/ci_monitor/ci_analyzer_perf.py b/scripts/ci_monitor/ci_analyzer_perf.py index ba0defebb..923c3b11b 100755 --- a/scripts/ci_monitor/ci_analyzer_perf.py +++ b/scripts/ci_monitor/ci_analyzer_perf.py @@ -1304,8 +1304,8 @@ class SGLangPerfAnalyzer: summary_lines.append("---") summary_lines.append("") - # Write summary to GitHub Actions - with open(github_step_summary, "w", encoding="utf-8") as f: + # Write summary to GitHub Actions (append mode to preserve CI Analysis report) + with open(github_step_summary, "a", encoding="utf-8") as f: f.write("\n".join(summary_lines)) print("✅ GitHub Actions summary generated successfully")