1use anyhow::Result;
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8use std::collections::HashMap;
9use std::path::PathBuf;
10use uuid::Uuid;
11
12use crate::{DebugConfig, DebugReport, DebugSession};
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
16pub enum CICDPlatform {
17 GitHub,
18 GitLab,
19 Jenkins,
20 CircleCI,
21 AzureDevOps,
22 BitbucketPipelines,
23 TeamCity,
24 Travis,
25 Custom(String),
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct CICDConfig {
31 pub platform: CICDPlatform,
32 pub project_id: String,
33 pub api_token: Option<String>,
34 pub base_url: Option<String>,
35 pub branch_filters: Vec<String>,
36 pub enable_regression_detection: bool,
37 pub enable_performance_tracking: bool,
38 pub enable_quality_gates: bool,
39 pub enable_automated_reports: bool,
40 pub enable_alert_systems: bool,
41 pub report_formats: Vec<ReportFormat>,
42 pub notification_channels: Vec<NotificationChannel>,
43}
44
45#[derive(Debug, Clone, Serialize, Deserialize)]
47pub enum ReportFormat {
48 JSON,
49 XML,
50 HTML,
51 Markdown,
52 JUnit,
53 SonarQube,
54 Custom(String),
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize)]
59pub enum NotificationChannel {
60 Email {
61 recipients: Vec<String>,
62 },
63 Slack {
64 webhook_url: String,
65 channel: String,
66 },
67 Teams {
68 webhook_url: String,
69 },
70 Discord {
71 webhook_url: String,
72 },
73 Webhook {
74 url: String,
75 headers: HashMap<String, String>,
76 },
77 Custom(String),
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
82pub enum PipelineStage {
83 Build,
84 Test,
85 Debug,
86 Analysis,
87 Deploy,
88 Custom(String),
89}
90
91#[derive(Debug, Clone, Serialize, Deserialize)]
93pub enum QualityGateStatus {
94 Passed,
95 Failed,
96 Warning,
97 Skipped,
98}
99
100#[derive(Debug, Clone, Serialize, Deserialize)]
102pub struct QualityGate {
103 pub name: String,
104 pub description: String,
105 pub metric: QualityMetric,
106 pub threshold: f64,
107 pub operator: ComparisonOperator,
108 pub blocking: bool,
109}
110
111#[derive(Debug, Clone, Serialize, Deserialize)]
113pub enum QualityMetric {
114 TestCoverage,
115 ModelAccuracy,
116 TrainingLoss,
117 GradientNorm,
118 MemoryUsage,
119 TrainingTime,
120 ModelSize,
121 InferenceLatency,
122 Custom(String),
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
127pub enum ComparisonOperator {
128 GreaterThan,
129 LessThan,
130 GreaterThanOrEqual,
131 LessThanOrEqual,
132 Equal,
133 NotEqual,
134}
135
136#[derive(Debug, Clone, Serialize, Deserialize)]
138pub struct RegressionResult {
139 pub detected: bool,
140 pub severity: RegressionSeverity,
141 pub metric: String,
142 pub baseline_value: f64,
143 pub current_value: f64,
144 pub change_percent: f64,
145 pub description: String,
146}
147
148#[derive(Debug, Clone, Serialize, Deserialize)]
150pub enum RegressionSeverity {
151 Critical,
152 Major,
153 Minor,
154 Info,
155}
156
157#[derive(Debug, Clone, Serialize, Deserialize)]
159pub struct PerformanceData {
160 pub timestamp: DateTime<Utc>,
161 pub commit_hash: String,
162 pub branch: String,
163 pub metrics: HashMap<String, f64>,
164 pub benchmark_results: Vec<BenchmarkResult>,
165}
166
167#[derive(Debug, Clone, Serialize, Deserialize)]
169pub struct BenchmarkResult {
170 pub name: String,
171 pub value: f64,
172 pub unit: String,
173 pub baseline: Option<f64>,
174 pub improvement_percent: Option<f64>,
175}
176
177#[derive(Debug, Clone, Serialize, Deserialize)]
179pub struct PipelineResult {
180 pub run_id: Uuid,
181 pub timestamp: DateTime<Utc>,
182 pub commit_hash: String,
183 pub branch: String,
184 pub stage: PipelineStage,
185 pub status: PipelineStatus,
186 pub debug_report: Option<DebugReport>,
187 pub quality_gate_results: Vec<QualityGateResult>,
188 pub regression_results: Vec<RegressionResult>,
189 pub performance_data: Option<PerformanceData>,
190 pub artifacts: Vec<Artifact>,
191 pub duration_ms: u64,
192}
193
194#[derive(Debug, Clone, Serialize, Deserialize)]
196pub enum PipelineStatus {
197 Success,
198 Failed,
199 Warning,
200 Cancelled,
201 Timeout,
202}
203
204impl std::fmt::Display for PipelineStatus {
205 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
206 match self {
207 PipelineStatus::Success => write!(f, "Success"),
208 PipelineStatus::Failed => write!(f, "Failed"),
209 PipelineStatus::Warning => write!(f, "Warning"),
210 PipelineStatus::Cancelled => write!(f, "Cancelled"),
211 PipelineStatus::Timeout => write!(f, "Timeout"),
212 }
213 }
214}
215
216#[derive(Debug, Clone, Serialize, Deserialize)]
218pub struct QualityGateResult {
219 pub gate: QualityGate,
220 pub status: QualityGateStatus,
221 pub actual_value: f64,
222 pub message: String,
223}
224
225#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct Artifact {
228 pub name: String,
229 pub path: PathBuf,
230 pub size_bytes: u64,
231 pub checksum: String,
232 pub artifact_type: ArtifactType,
233}
234
235#[derive(Debug, Clone, Serialize, Deserialize)]
237pub enum ArtifactType {
238 DebugReport,
239 TestResults,
240 BenchmarkResults,
241 Model,
242 Dataset,
243 Documentation,
244 Custom(String),
245}
246
247#[derive(Debug)]
249pub struct CICDIntegration {
250 config: CICDConfig,
251 quality_gates: Vec<QualityGate>,
252 baseline_metrics: HashMap<String, f64>,
253 performance_history: Vec<PerformanceData>,
254 pipeline_history: Vec<PipelineResult>,
255}
256
257impl CICDIntegration {
258 pub fn new(config: CICDConfig) -> Self {
260 Self {
261 config,
262 quality_gates: Vec::new(),
263 baseline_metrics: HashMap::new(),
264 performance_history: Vec::new(),
265 pipeline_history: Vec::new(),
266 }
267 }
268
269 pub fn add_quality_gate(&mut self, gate: QualityGate) {
271 self.quality_gates.push(gate);
272 }
273
274 pub fn set_baseline_metrics(&mut self, metrics: HashMap<String, f64>) {
276 self.baseline_metrics = metrics;
277 }
278
279 pub async fn run_debug_analysis(
281 &mut self,
282 commit_hash: String,
283 branch: String,
284 debug_config: DebugConfig,
285 ) -> Result<PipelineResult> {
286 let run_id = Uuid::new_v4();
287 let start_time = Utc::now();
288
289 tracing::info!(
290 "Starting debug analysis for commit {} on branch {}",
291 commit_hash,
292 branch
293 );
294
295 let mut debug_session = DebugSession::new(debug_config);
297 debug_session.start().await?;
298
299 tokio::time::sleep(std::time::Duration::from_millis(100)).await;
302
303 let debug_report = debug_session.stop().await?;
305
306 let metrics = self.extract_metrics_from_report(&debug_report);
308
309 let quality_gate_results = self.evaluate_quality_gates(&metrics);
311
312 let regression_results = self.detect_regressions(&metrics, &commit_hash);
314
315 let status = self.determine_pipeline_status(&quality_gate_results, ®ression_results);
317
318 let performance_data = PerformanceData {
320 timestamp: start_time,
321 commit_hash: commit_hash.clone(),
322 branch: branch.clone(),
323 metrics: metrics.clone(),
324 benchmark_results: self.generate_benchmark_results(&metrics),
325 };
326
327 let artifacts = self.generate_artifacts(&debug_report, &performance_data)?;
329
330 let duration_ms = (Utc::now() - start_time).num_milliseconds() as u64;
331
332 let result = PipelineResult {
333 run_id,
334 timestamp: start_time,
335 commit_hash,
336 branch,
337 stage: PipelineStage::Debug,
338 status: status.clone(),
339 debug_report: Some(debug_report),
340 quality_gate_results,
341 regression_results,
342 performance_data: Some(performance_data.clone()),
343 artifacts,
344 duration_ms,
345 };
346
347 self.performance_history.push(performance_data);
349 self.pipeline_history.push(result.clone());
350
351 if self.config.enable_alert_systems {
353 self.send_notifications(&result).await?;
354 }
355
356 if self.config.enable_automated_reports {
358 self.generate_reports(&result).await?;
359 }
360
361 tracing::info!("Debug analysis completed with status: {:?}", status);
362
363 Ok(result)
364 }
365
366 fn extract_metrics_from_report(&self, report: &DebugReport) -> HashMap<String, f64> {
368 let mut metrics = HashMap::new();
369
370 if let Some(ref tensor_report) = report.tensor_report {
372 metrics.insert(
373 "tensor_nan_count".to_string(),
374 tensor_report.total_nan_count() as f64,
375 );
376 metrics.insert(
377 "tensor_inf_count".to_string(),
378 tensor_report.total_inf_count() as f64,
379 );
380 }
381
382 if let Some(ref gradient_report) = report.gradient_report {
384 metrics.insert(
385 "gradient_norm".to_string(),
386 gradient_report.average_gradient_norm(),
387 );
388 metrics.insert(
389 "vanishing_gradients".to_string(),
390 gradient_report.vanishing_gradient_layers().len() as f64,
391 );
392 metrics.insert(
393 "exploding_gradients".to_string(),
394 gradient_report.exploding_gradient_layers().len() as f64,
395 );
396 }
397
398 if let Some(ref memory_report) = report.memory_profiler_report {
400 metrics.insert(
401 "peak_memory_mb".to_string(),
402 memory_report.peak_memory_usage() / (1024.0 * 1024.0),
403 );
404 metrics.insert(
405 "memory_efficiency".to_string(),
406 memory_report.memory_efficiency(),
407 );
408 }
409
410 metrics.insert(
412 "total_parameters".to_string(),
413 self.count_model_parameters() as f64,
414 );
415 metrics.insert("training_time_ms".to_string(), 1000.0); metrics
418 }
419
420 fn evaluate_quality_gates(&self, metrics: &HashMap<String, f64>) -> Vec<QualityGateResult> {
422 let mut results = Vec::new();
423
424 for gate in &self.quality_gates {
425 let metric_name = self.get_metric_name(&gate.metric);
426 let actual_value = metrics.get(&metric_name).copied().unwrap_or(0.0);
427
428 let passed = match gate.operator {
429 ComparisonOperator::GreaterThan => actual_value > gate.threshold,
430 ComparisonOperator::LessThan => actual_value < gate.threshold,
431 ComparisonOperator::GreaterThanOrEqual => actual_value >= gate.threshold,
432 ComparisonOperator::LessThanOrEqual => actual_value <= gate.threshold,
433 ComparisonOperator::Equal => (actual_value - gate.threshold).abs() < f64::EPSILON,
434 ComparisonOperator::NotEqual => {
435 (actual_value - gate.threshold).abs() >= f64::EPSILON
436 },
437 };
438
439 let status = if passed { QualityGateStatus::Passed } else { QualityGateStatus::Failed };
440
441 let message = format!(
442 "Quality gate '{}': {} {} {} (actual: {})",
443 gate.name,
444 metric_name,
445 self.operator_symbol(&gate.operator),
446 gate.threshold,
447 actual_value
448 );
449
450 results.push(QualityGateResult {
451 gate: gate.clone(),
452 status,
453 actual_value,
454 message,
455 });
456 }
457
458 results
459 }
460
461 fn detect_regressions(
463 &self,
464 metrics: &HashMap<String, f64>,
465 _commit_hash: &str,
466 ) -> Vec<RegressionResult> {
467 let mut results = Vec::new();
468
469 if !self.config.enable_regression_detection {
470 return results;
471 }
472
473 for (metric_name, ¤t_value) in metrics {
474 if let Some(&baseline_value) = self.baseline_metrics.get(metric_name) {
475 let change_percent = ((current_value - baseline_value) / baseline_value) * 100.0;
476
477 let (detected, severity) = self.analyze_regression(metric_name, change_percent);
479
480 if detected {
481 results.push(RegressionResult {
482 detected: true,
483 severity,
484 metric: metric_name.clone(),
485 baseline_value,
486 current_value,
487 change_percent,
488 description: format!(
489 "Regression detected in {}: {:.2}% change from baseline (baseline: {:.4}, current: {:.4})",
490 metric_name, change_percent, baseline_value, current_value
491 ),
492 });
493 }
494 }
495 }
496
497 results
498 }
499
500 fn analyze_regression(
502 &self,
503 metric_name: &str,
504 change_percent: f64,
505 ) -> (bool, RegressionSeverity) {
506 let abs_change = change_percent.abs();
507
508 let (minor_threshold, major_threshold, critical_threshold) = match metric_name {
510 name if name.contains("accuracy") => (2.0, 5.0, 10.0),
511 name if name.contains("loss") => (5.0, 15.0, 30.0),
512 name if name.contains("memory") => (10.0, 25.0, 50.0),
513 name if name.contains("time") => (15.0, 30.0, 60.0),
514 _ => (5.0, 15.0, 30.0), };
516
517 if abs_change >= critical_threshold {
518 (true, RegressionSeverity::Critical)
519 } else if abs_change >= major_threshold {
520 (true, RegressionSeverity::Major)
521 } else if abs_change >= minor_threshold {
522 (true, RegressionSeverity::Minor)
523 } else {
524 (false, RegressionSeverity::Info)
525 }
526 }
527
528 fn determine_pipeline_status(
530 &self,
531 quality_gate_results: &[QualityGateResult],
532 regression_results: &[RegressionResult],
533 ) -> PipelineStatus {
534 for result in quality_gate_results {
536 if result.gate.blocking && matches!(result.status, QualityGateStatus::Failed) {
537 return PipelineStatus::Failed;
538 }
539 }
540
541 for regression in regression_results {
543 if matches!(regression.severity, RegressionSeverity::Critical) {
544 return PipelineStatus::Failed;
545 }
546 }
547
548 let has_warnings = quality_gate_results
550 .iter()
551 .any(|r| matches!(r.status, QualityGateStatus::Failed))
552 || regression_results
553 .iter()
554 .any(|r| matches!(r.severity, RegressionSeverity::Major));
555
556 if has_warnings {
557 PipelineStatus::Warning
558 } else {
559 PipelineStatus::Success
560 }
561 }
562
563 fn generate_benchmark_results(&self, metrics: &HashMap<String, f64>) -> Vec<BenchmarkResult> {
565 let mut results = Vec::new();
566
567 for (name, &value) in metrics {
568 let baseline = self.baseline_metrics.get(name).copied();
569 let improvement_percent = baseline.map(|b| ((value - b) / b) * 100.0);
570
571 let unit = match name.as_str() {
572 name if name.contains("time") || name.contains("latency") => "ms",
573 name if name.contains("memory") => "MB",
574 name if name.contains("accuracy") => "%",
575 name if name.contains("loss") => "loss",
576 _ => "units",
577 };
578
579 results.push(BenchmarkResult {
580 name: name.clone(),
581 value,
582 unit: unit.to_string(),
583 baseline,
584 improvement_percent,
585 });
586 }
587
588 results
589 }
590
591 fn generate_artifacts(
593 &self,
594 debug_report: &DebugReport,
595 performance_data: &PerformanceData,
596 ) -> Result<Vec<Artifact>> {
597 let mut artifacts = Vec::new();
598
599 let debug_report_json = serde_json::to_string_pretty(debug_report)?;
601 let debug_report_path = PathBuf::from("debug_report.json");
602 std::fs::write(&debug_report_path, &debug_report_json)?;
603
604 artifacts.push(Artifact {
605 name: "Debug Report".to_string(),
606 path: debug_report_path,
607 size_bytes: debug_report_json.len() as u64,
608 checksum: format!("{:x}", md5::compute(&debug_report_json)),
609 artifact_type: ArtifactType::DebugReport,
610 });
611
612 let performance_json = serde_json::to_string_pretty(performance_data)?;
614 let performance_path = PathBuf::from("performance_data.json");
615 std::fs::write(&performance_path, &performance_json)?;
616
617 artifacts.push(Artifact {
618 name: "Performance Data".to_string(),
619 path: performance_path,
620 size_bytes: performance_json.len() as u64,
621 checksum: format!("{:x}", md5::compute(&performance_json)),
622 artifact_type: ArtifactType::BenchmarkResults,
623 });
624
625 Ok(artifacts)
626 }
627
628 async fn send_notifications(&self, result: &PipelineResult) -> Result<()> {
630 for channel in &self.config.notification_channels {
631 match channel {
632 NotificationChannel::Slack {
633 webhook_url,
634 channel: slack_channel,
635 } => {
636 self.send_slack_notification(webhook_url, slack_channel, result).await?;
637 },
638 NotificationChannel::Email { recipients } => {
639 self.send_email_notification(recipients, result).await?;
640 },
641 NotificationChannel::Teams { webhook_url } => {
642 self.send_teams_notification(webhook_url, result).await?;
643 },
644 NotificationChannel::Discord { webhook_url } => {
645 self.send_discord_notification(webhook_url, result).await?;
646 },
647 NotificationChannel::Webhook { url, headers } => {
648 self.send_webhook_notification(url, headers, result).await?;
649 },
650 NotificationChannel::Custom(_) => {
651 tracing::info!("Custom notification not implemented");
653 },
654 }
655 }
656
657 Ok(())
658 }
659
660 async fn generate_reports(&self, result: &PipelineResult) -> Result<()> {
662 for format in &self.config.report_formats {
663 match format {
664 ReportFormat::JSON => {
665 let json_report = serde_json::to_string_pretty(result)?;
666 std::fs::write("cicd_report.json", json_report)?;
667 },
668 ReportFormat::HTML => {
669 let html_report = self.generate_html_report(result)?;
670 std::fs::write("cicd_report.html", html_report)?;
671 },
672 ReportFormat::Markdown => {
673 let md_report = self.generate_markdown_report(result)?;
674 std::fs::write("cicd_report.md", md_report)?;
675 },
676 ReportFormat::JUnit => {
677 let junit_report = self.generate_junit_report(result)?;
678 std::fs::write("cicd_report.xml", junit_report)?;
679 },
680 _ => {
681 tracing::info!("Report format {:?} not implemented", format);
682 },
683 }
684 }
685
686 Ok(())
687 }
688
689 async fn send_slack_notification(
691 &self,
692 webhook_url: &str,
693 channel: &str,
694 result: &PipelineResult,
695 ) -> Result<()> {
696 let color = match result.status {
697 PipelineStatus::Success => "good",
698 PipelineStatus::Warning => "warning",
699 PipelineStatus::Failed => "danger",
700 _ => "warning",
701 };
702
703 let message = serde_json::json!({
704 "channel": channel,
705 "attachments": [{
706 "color": color,
707 "title": format!("Debug Analysis - {}", result.commit_hash),
708 "text": format!("Branch: {} | Status: {:?} | Duration: {}ms",
709 result.branch, result.status, result.duration_ms),
710 "fields": [
711 {
712 "title": "Quality Gates",
713 "value": format!("{} passed, {} failed",
714 result.quality_gate_results.iter().filter(|r| matches!(r.status, QualityGateStatus::Passed)).count(),
715 result.quality_gate_results.iter().filter(|r| matches!(r.status, QualityGateStatus::Failed)).count()),
716 "short": true
717 },
718 {
719 "title": "Regressions",
720 "value": format!("{} detected", result.regression_results.len()),
721 "short": true
722 }
723 ]
724 }]
725 });
726
727 tracing::info!("Sending Slack notification to {}: {}", webhook_url, message);
728 Ok(())
731 }
732
733 async fn send_email_notification(
734 &self,
735 recipients: &[String],
736 result: &PipelineResult,
737 ) -> Result<()> {
738 let subject = format!(
739 "Debug Analysis Report - {} ({})",
740 result.commit_hash, result.status
741 );
742 let _body = format!(
743 "Debug analysis completed for commit {} on branch {}.\n\nStatus: {:?}\nDuration: {}ms\n\nQuality Gates: {} passed, {} failed\nRegressions: {} detected",
744 result.commit_hash,
745 result.branch,
746 result.status,
747 result.duration_ms,
748 result.quality_gate_results.iter().filter(|r| matches!(r.status, QualityGateStatus::Passed)).count(),
749 result.quality_gate_results.iter().filter(|r| matches!(r.status, QualityGateStatus::Failed)).count(),
750 result.regression_results.len()
751 );
752
753 tracing::info!(
754 "Sending email notification to {:?}: {}",
755 recipients,
756 subject
757 );
758 Ok(())
761 }
762
763 async fn send_teams_notification(
764 &self,
765 webhook_url: &str,
766 _result: &PipelineResult,
767 ) -> Result<()> {
768 tracing::info!("Sending Teams notification to {}", webhook_url);
769 Ok(())
771 }
772
773 async fn send_discord_notification(
774 &self,
775 webhook_url: &str,
776 _result: &PipelineResult,
777 ) -> Result<()> {
778 tracing::info!("Sending Discord notification to {}", webhook_url);
779 Ok(())
781 }
782
783 async fn send_webhook_notification(
784 &self,
785 url: &str,
786 headers: &HashMap<String, String>,
787 _result: &PipelineResult,
788 ) -> Result<()> {
789 tracing::info!(
790 "Sending webhook notification to {} with headers: {:?}",
791 url,
792 headers
793 );
794 Ok(())
796 }
797
798 fn generate_html_report(&self, result: &PipelineResult) -> Result<String> {
800 let html = format!(r#"
801<!DOCTYPE html>
802<html>
803<head>
804 <title>Debug Analysis Report</title>
805 <style>
806 body {{ font-family: Arial, sans-serif; margin: 20px; }}
807 .status-success {{ color: green; }}
808 .status-warning {{ color: orange; }}
809 .status-failed {{ color: red; }}
810 table {{ border-collapse: collapse; width: 100%; }}
811 th, td {{ border: 1px solid #ddd; padding: 8px; text-align: left; }}
812 th {{ background-color: #f2f2f2; }}
813 </style>
814</head>
815<body>
816 <h1>Debug Analysis Report</h1>
817 <h2>Overview</h2>
818 <p><strong>Commit:</strong> {}</p>
819 <p><strong>Branch:</strong> {}</p>
820 <p><strong>Status:</strong> <span class="status-{}">{:?}</span></p>
821 <p><strong>Duration:</strong> {}ms</p>
822 <p><strong>Timestamp:</strong> {}</p>
823
824 <h2>Quality Gates</h2>
825 <table>
826 <tr><th>Gate</th><th>Status</th><th>Actual Value</th><th>Threshold</th><th>Message</th></tr>
827 {}
828 </table>
829
830 <h2>Regression Analysis</h2>
831 <table>
832 <tr><th>Metric</th><th>Severity</th><th>Change %</th><th>Baseline</th><th>Current</th><th>Description</th></tr>
833 {}
834 </table>
835</body>
836</html>
837"#,
838 result.commit_hash,
839 result.branch,
840 format!("{:?}", result.status).to_lowercase(),
841 result.status,
842 result.duration_ms,
843 result.timestamp,
844 result.quality_gate_results.iter().map(|r| format!(
845 "<tr><td>{}</td><td>{:?}</td><td>{:.4}</td><td>{:.4}</td><td>{}</td></tr>",
846 r.gate.name, r.status, r.actual_value, r.gate.threshold, r.message
847 )).collect::<Vec<_>>().join(""),
848 result.regression_results.iter().map(|r| format!(
849 "<tr><td>{}</td><td>{:?}</td><td>{:.2}%</td><td>{:.4}</td><td>{:.4}</td><td>{}</td></tr>",
850 r.metric, r.severity, r.change_percent, r.baseline_value, r.current_value, r.description
851 )).collect::<Vec<_>>().join("")
852 );
853
854 Ok(html)
855 }
856
857 fn generate_markdown_report(&self, result: &PipelineResult) -> Result<String> {
859 let status_emoji = match result.status {
860 PipelineStatus::Success => "✅",
861 PipelineStatus::Warning => "⚠️",
862 PipelineStatus::Failed => "❌",
863 _ => "❓",
864 };
865
866 let markdown = format!(
867 r#"# Debug Analysis Report
868
869## Overview
870- **Commit:** {}
871- **Branch:** {}
872- **Status:** {} {:?}
873- **Duration:** {}ms
874- **Timestamp:** {}
875
876## Quality Gates
877| Gate | Status | Actual Value | Threshold | Message |
878|------|--------|--------------|-----------|---------|
879{}
880
881## Regression Analysis
882| Metric | Severity | Change % | Baseline | Current | Description |
883|--------|----------|----------|----------|---------|-------------|
884{}
885
886## Artifacts
887{}
888"#,
889 result.commit_hash,
890 result.branch,
891 status_emoji,
892 result.status,
893 result.duration_ms,
894 result.timestamp,
895 result
896 .quality_gate_results
897 .iter()
898 .map(|r| format!(
899 "| {} | {:?} | {:.4} | {:.4} | {} |",
900 r.gate.name, r.status, r.actual_value, r.gate.threshold, r.message
901 ))
902 .collect::<Vec<_>>()
903 .join("\n"),
904 result
905 .regression_results
906 .iter()
907 .map(|r| format!(
908 "| {} | {:?} | {:.2}% | {:.4} | {:.4} | {} |",
909 r.metric,
910 r.severity,
911 r.change_percent,
912 r.baseline_value,
913 r.current_value,
914 r.description
915 ))
916 .collect::<Vec<_>>()
917 .join("\n"),
918 result
919 .artifacts
920 .iter()
921 .map(|a| format!(
922 "- **{}:** {} ({} bytes)",
923 a.name,
924 a.path.display(),
925 a.size_bytes
926 ))
927 .collect::<Vec<_>>()
928 .join("\n")
929 );
930
931 Ok(markdown)
932 }
933
934 fn generate_junit_report(&self, result: &PipelineResult) -> Result<String> {
936 let test_cases = result
937 .quality_gate_results
938 .iter()
939 .map(|r| {
940 let status = match r.status {
941 QualityGateStatus::Passed => "",
942 QualityGateStatus::Failed => {
943 r#"<failure message="Quality gate failed"></failure>"#
944 },
945 QualityGateStatus::Warning => {
946 r#"<error message="Quality gate warning"></error>"#
947 },
948 QualityGateStatus::Skipped => r#"<skipped/>"#,
949 };
950 format!(
951 r#"<testcase classname="QualityGates" name="{}" time="0">{}</testcase>"#,
952 r.gate.name, status
953 )
954 })
955 .collect::<Vec<_>>()
956 .join("\n ");
957
958 let junit = format!(
959 r#"<?xml version="1.0" encoding="UTF-8"?>
960<testsuite name="DebugAnalysis" tests="{}" failures="{}" errors="0" time="{:.3}">
961 {}
962</testsuite>
963"#,
964 result.quality_gate_results.len(),
965 result
966 .quality_gate_results
967 .iter()
968 .filter(|r| matches!(r.status, QualityGateStatus::Failed))
969 .count(),
970 result.duration_ms as f64 / 1000.0,
971 test_cases
972 );
973
974 Ok(junit)
975 }
976
977 fn get_metric_name(&self, metric: &QualityMetric) -> String {
979 match metric {
980 QualityMetric::TestCoverage => "test_coverage".to_string(),
981 QualityMetric::ModelAccuracy => "model_accuracy".to_string(),
982 QualityMetric::TrainingLoss => "training_loss".to_string(),
983 QualityMetric::GradientNorm => "gradient_norm".to_string(),
984 QualityMetric::MemoryUsage => "peak_memory_mb".to_string(),
985 QualityMetric::TrainingTime => "training_time_ms".to_string(),
986 QualityMetric::ModelSize => "total_parameters".to_string(),
987 QualityMetric::InferenceLatency => "inference_latency_ms".to_string(),
988 QualityMetric::Custom(name) => name.clone(),
989 }
990 }
991
992 fn operator_symbol(&self, op: &ComparisonOperator) -> &'static str {
993 match op {
994 ComparisonOperator::GreaterThan => ">",
995 ComparisonOperator::LessThan => "<",
996 ComparisonOperator::GreaterThanOrEqual => ">=",
997 ComparisonOperator::LessThanOrEqual => "<=",
998 ComparisonOperator::Equal => "==",
999 ComparisonOperator::NotEqual => "!=",
1000 }
1001 }
1002
1003 fn count_model_parameters(&self) -> u64 {
1004 1000000
1006 }
1007
1008 pub fn get_pipeline_history(&self) -> &[PipelineResult] {
1010 &self.pipeline_history
1011 }
1012
1013 pub fn get_performance_history(&self) -> &[PerformanceData] {
1015 &self.performance_history
1016 }
1017
1018 pub fn get_quality_gates(&self) -> &[QualityGate] {
1020 &self.quality_gates
1021 }
1022}
1023
1024impl Default for CICDConfig {
1025 fn default() -> Self {
1026 Self {
1027 platform: CICDPlatform::GitHub,
1028 project_id: "default".to_string(),
1029 api_token: None,
1030 base_url: None,
1031 branch_filters: vec!["main".to_string(), "develop".to_string()],
1032 enable_regression_detection: true,
1033 enable_performance_tracking: true,
1034 enable_quality_gates: true,
1035 enable_automated_reports: true,
1036 enable_alert_systems: true,
1037 report_formats: vec![
1038 ReportFormat::JSON,
1039 ReportFormat::HTML,
1040 ReportFormat::Markdown,
1041 ],
1042 notification_channels: Vec::new(),
1043 }
1044 }
1045}
1046
1047impl DebugReport {
1049 pub fn total_nan_count(&self) -> u32 {
1050 0
1052 }
1053
1054 pub fn total_inf_count(&self) -> u32 {
1055 0
1057 }
1058}
1059
1060impl crate::GradientDebugReport {
1061 pub fn average_gradient_norm(&self) -> f64 {
1062 1.0
1064 }
1065
1066 pub fn vanishing_gradient_layers(&self) -> Vec<String> {
1067 Vec::new()
1069 }
1070
1071 pub fn exploding_gradient_layers(&self) -> Vec<String> {
1072 Vec::new()
1074 }
1075}
1076
1077impl crate::MemoryProfilingReport {
1078 pub fn peak_memory_usage(&self) -> f64 {
1079 1024.0 * 1024.0 * 100.0 }
1082
1083 pub fn memory_efficiency(&self) -> f64 {
1084 0.85 }
1087}