package internal import ( "context" "encoding/json" "encoding/xml" "fmt" "log" "os" "os/exec" "path/filepath" "strings" "sync" "time" ) type CICDAuditConfig struct { Pipeline string ConfigFile string OutputDir string Verbose bool Parallel bool FailFast bool ReportFormat string Timeout time.Duration Stage string Environment string SlackWebhook string EmailRecipients string BaselineMode bool CompareMode bool MetricsMode bool } type CICDAuditor struct { config *CICDAuditConfig results *PipelineResults auditTools []AuditTool rootDir string startTime time.Time } type PipelineResults struct { PipelineType string `json:"pipeline_type"` Environment string `json:"environment"` OverallStatus string `json:"overall_status"` ExitCode int `json:"exit_code"` TotalAudits int `json:"total_audits"` PassedAudits int `json:"passed_audits"` FailedAudits int `json:"failed_audits"` SkippedAudits int `json:"skipped_audits"` AuditResults []AuditResult `json:"audit_results"` QualityGates []QualityGate `json:"quality_gates"` Metrics PipelineMetrics `json:"metrics"` Notifications []Notification `json:"notifications"` Artifacts []Artifact `json:"artifacts"` Timestamp time.Time `json:"timestamp"` Duration int64 `json:"duration_ms"` BuildInfo BuildInfo `json:"build_info"` DeploymentInfo DeploymentInfo `json:"deployment_info,omitempty"` } type AuditResult struct { ToolName string `json:"tool_name"` Status string `json:"status"` ExitCode int `json:"exit_code"` Duration int64 `json:"duration_ms"` OutputFile string `json:"output_file"` ErrorOutput string `json:"error_output,omitempty"` Metrics map[string]interface{} `json:"metrics,omitempty"` QualityChecks []QualityCheck `json:"quality_checks"` Recommendations []string `json:"recommendations"` Timestamp time.Time `json:"timestamp"` RetryCount int `json:"retry_count"` } type QualityGate struct { Name string `json:"name"` Type string `json:"type"` Threshold float64 `json:"threshold"` ActualValue float64 `json:"actual_value"` Status string `json:"status"` Critical bool `json:"critical"` Description string `json:"description"` Timestamp time.Time `json:"timestamp"` } type QualityCheck struct { Name string `json:"name"` Status string `json:"status"` Score float64 `json:"score"` Threshold float64 `json:"threshold"` Description string `json:"description"` Critical bool `json:"critical"` } type PipelineMetrics struct { CodeCoverage float64 `json:"code_coverage"` SecurityScore float64 `json:"security_score"` PerformanceScore float64 `json:"performance_score"` QualityScore float64 `json:"quality_score"` ComplianceScore float64 `json:"compliance_score"` OverallScore float64 `json:"overall_score"` TrendAnalysis TrendAnalysis `json:"trend_analysis"` Benchmarks map[string]float64 `json:"benchmarks"` ResourceUsage ResourceUsage `json:"resource_usage"` } type TrendAnalysis struct { ScoreTrend string `json:"score_trend"` IssuesTrend string `json:"issues_trend"` PerformanceTrend string `json:"performance_trend"` LastUpdated time.Time `json:"last_updated"` RecommendedActions []string `json:"recommended_actions"` } type ResourceUsage struct { CPUTime float64 `json:"cpu_time_seconds"` MemoryPeak int64 `json:"memory_peak_mb"` DiskUsage int64 `json:"disk_usage_mb"` NetworkRequests int `json:"network_requests"` CacheHits int `json:"cache_hits"` CacheMisses int `json:"cache_misses"` } type Notification struct { Type string `json:"type"` Target string `json:"target"` Message string `json:"message"` Status string `json:"status"` Timestamp time.Time `json:"timestamp"` } type Artifact struct { Name string `json:"name"` Path string `json:"path"` Type string `json:"type"` Size int64 `json:"size"` Hash string `json:"hash"` Timestamp time.Time `json:"timestamp"` Retention string `json:"retention"` } type BuildInfo struct { BuildNumber string `json:"build_number"` GitCommit string `json:"git_commit"` GitBranch string `json:"git_branch"` GitTag string `json:"git_tag,omitempty"` Timestamp time.Time `json:"timestamp"` TriggeredBy string `json:"triggered_by"` BuildAgent string `json:"build_agent"` Environment map[string]string `json:"environment"` } type DeploymentInfo struct { TargetEnvironment string `json:"target_environment"` DeploymentStrategy string `json:"deployment_strategy"` ConfigChanges []string `json:"config_changes"` HealthChecks []HealthCheck `json:"health_checks"` RollbackPlan string `json:"rollback_plan"` Timestamp time.Time `json:"timestamp"` } type HealthCheck struct { Name string `json:"name"` Status string `json:"status"` URL string `json:"url"` ResponseTime int64 `json:"response_time_ms"` Timestamp time.Time `json:"timestamp"` } type AuditTool struct { Name string Command string Args []string OutputFile string Stage string Critical bool Timeout time.Duration RetryCount int QualityGates []QualityGate Dependencies []string Environment map[string]string } // JUnit XML structures for CI/CD integration type JUnitTestSuites struct { XMLName xml.Name `xml:"testsuites"` Name string `xml:"name,attr"` Tests int `xml:"tests,attr"` Failures int `xml:"failures,attr"` Errors int `xml:"errors,attr"` Time float64 `xml:"time,attr"` TestSuites []JUnitTestSuite `xml:"testsuite"` } type JUnitTestSuite struct { XMLName xml.Name `xml:"testsuite"` Name string `xml:"name,attr"` Tests int `xml:"tests,attr"` Failures int `xml:"failures,attr"` Errors int `xml:"errors,attr"` Time float64 `xml:"time,attr"` TestCases []JUnitTestCase `xml:"testcase"` } type JUnitTestCase struct { XMLName xml.Name `xml:"testcase"` Name string `xml:"name,attr"` ClassName string `xml:"classname,attr"` Time float64 `xml:"time,attr"` Failure *JUnitFailure `xml:"failure,omitempty"` Error *JUnitError `xml:"error,omitempty"` Skipped *JUnitSkipped `xml:"skipped,omitempty"` } type JUnitFailure struct { Message string `xml:"message,attr"` Type string `xml:"type,attr"` Content string `xml:",chardata"` } type JUnitError struct { Message string `xml:"message,attr"` Type string `xml:"type,attr"` Content string `xml:",chardata"` } type JUnitSkipped struct { Message string `xml:"message,attr"` } func NewCICDAuditor(config *CICDAuditConfig) (*CICDAuditor, error) { rootDir, err := os.Getwd() if err != nil { return nil, fmt.Errorf("failed to get working directory: %w", err) } auditor := &CICDAuditor{ config: config, rootDir: rootDir, startTime: time.Now(), results: &PipelineResults{ PipelineType: config.Pipeline, Environment: config.Environment, AuditResults: make([]AuditResult, 0), QualityGates: make([]QualityGate, 0), Notifications: make([]Notification, 0), Artifacts: make([]Artifact, 0), Timestamp: time.Now(), }, } // Initialize audit tools based on pipeline type if err := auditor.initializeAuditTools(); err != nil { return nil, fmt.Errorf("failed to initialize audit tools: %w", err) } // Initialize build info auditor.initializeBuildInfo() return auditor, nil } func (ca *CICDAuditor) initializeAuditTools() error { // Define audit tools based on pipeline type switch ca.config.Pipeline { case "quick": ca.auditTools = ca.getQuickPipelineTools() case "standard": ca.auditTools = ca.getStandardPipelineTools() case "full": ca.auditTools = ca.getFullPipelineTools() case "custom": return ca.loadCustomPipelineTools() default: return fmt.Errorf("unsupported pipeline type: %s", ca.config.Pipeline) } // Filter tools by stage if specified if ca.config.Stage != "all" { ca.auditTools = ca.filterToolsByStage(ca.config.Stage) } ca.results.TotalAudits = len(ca.auditTools) return nil } func (ca *CICDAuditor) getQuickPipelineTools() []AuditTool { return []AuditTool{ { Name: "Math Audit", Command: "./tools/math-audit/math-audit", Args: []string{"--output", ca.config.OutputDir + "/math"}, OutputFile: "math_audit.json", Stage: "test", Critical: true, Timeout: 2 * time.Minute, RetryCount: 1, QualityGates: []QualityGate{ { Name: "Math Precision", Type: "error_rate", Threshold: 1.0, Critical: true, Description: "Mathematical calculation error rate must be below 1bp", }, }, }, { Name: "Security Scan", Command: "./tools/security-audit/security-audit", Args: []string{"--scan", "secrets", "--output", ca.config.OutputDir + "/security"}, OutputFile: "security_scan.json", Stage: "security", Critical: true, Timeout: 3 * time.Minute, RetryCount: 1, QualityGates: []QualityGate{ { Name: "Critical Vulnerabilities", Type: "count", Threshold: 0, Critical: true, Description: "No critical security vulnerabilities allowed", }, }, }, } } func (ca *CICDAuditor) getStandardPipelineTools() []AuditTool { tools := ca.getQuickPipelineTools() standardTools := []AuditTool{ { Name: "Profitability Audit", Command: "./tools/profitability-audit/profitability-audit", Args: []string{"--output", ca.config.OutputDir + "/profitability"}, OutputFile: "profitability_audit.json", Stage: "test", Critical: true, Timeout: 5 * time.Minute, RetryCount: 2, QualityGates: []QualityGate{ { Name: "Profit Accuracy", Type: "percentage", Threshold: 95.0, Critical: true, Description: "Profit calculation accuracy must be above 95%", }, }, }, { Name: "Gas Audit", Command: "./tools/gas-audit/gas-audit", Args: []string{"--network", "arbitrum", "--output", ca.config.OutputDir + "/gas"}, OutputFile: "gas_audit.json", Stage: "test", Critical: false, Timeout: 3 * time.Minute, RetryCount: 1, QualityGates: []QualityGate{ { Name: "Gas Estimation Accuracy", Type: "percentage", Threshold: 90.0, Critical: false, Description: "Gas estimation accuracy should be above 90%", }, }, }, { Name: "Exchange Integration", Command: "./tools/exchange-audit/exchange-audit", Args: []string{"--output", ca.config.OutputDir + "/exchange"}, OutputFile: "exchange_audit.json", Stage: "test", Critical: true, Timeout: 4 * time.Minute, RetryCount: 1, QualityGates: []QualityGate{ { Name: "Exchange Integration Score", Type: "percentage", Threshold: 85.0, Critical: true, Description: "Exchange integration score must be above 85%", }, }, }, } return append(tools, standardTools...) } func (ca *CICDAuditor) getFullPipelineTools() []AuditTool { tools := ca.getStandardPipelineTools() fullTools := []AuditTool{ { Name: "Performance Audit", Command: "./tools/performance-audit/performance-audit", Args: []string{"--test", "all", "--duration", "3m", "--output", ca.config.OutputDir + "/performance"}, OutputFile: "performance_audit.json", Stage: "test", Critical: false, Timeout: 10 * time.Minute, RetryCount: 1, QualityGates: []QualityGate{ { Name: "Performance Score", Type: "percentage", Threshold: 80.0, Critical: false, Description: "Overall performance score should be above 80%", }, }, }, { Name: "Opportunity Validator", Command: "./tools/opportunity-validator/opportunity-validator", Args: []string{"--test", "--output", ca.config.OutputDir + "/opportunities"}, OutputFile: "opportunity_validation.json", Stage: "test", Critical: true, Timeout: 5 * time.Minute, RetryCount: 1, QualityGates: []QualityGate{ { Name: "Opportunity Detection Rate", Type: "percentage", Threshold: 90.0, Critical: true, Description: "Opportunity detection rate must be above 90%", }, }, }, { Name: "Complete Security Scan", Command: "./tools/security-audit/security-audit", Args: []string{"--scan", "all", "--deep", "--output", ca.config.OutputDir + "/security-full"}, OutputFile: "security_full.json", Stage: "security", Critical: true, Timeout: 15 * time.Minute, RetryCount: 1, QualityGates: []QualityGate{ { Name: "Security Score", Type: "percentage", Threshold: 85.0, Critical: true, Description: "Overall security score must be above 85%", }, }, }, } return append(tools, fullTools...) } func (ca *CICDAuditor) loadCustomPipelineTools() error { // Load custom pipeline configuration from file // This would parse a YAML/JSON configuration file // For now, return the full pipeline as default ca.auditTools = ca.getFullPipelineTools() return nil } func (ca *CICDAuditor) filterToolsByStage(stage string) []AuditTool { filtered := make([]AuditTool, 0) for _, tool := range ca.auditTools { if tool.Stage == stage { filtered = append(filtered, tool) } } return filtered } func (ca *CICDAuditor) initializeBuildInfo() { ca.results.BuildInfo = BuildInfo{ BuildNumber: ca.getBuildNumber(), GitCommit: ca.getGitCommit(), GitBranch: ca.getGitBranch(), GitTag: ca.getGitTag(), Timestamp: time.Now(), TriggeredBy: ca.getTriggeredBy(), BuildAgent: ca.getBuildAgent(), Environment: ca.getBuildEnvironment(), } } func (ca *CICDAuditor) getBuildNumber() string { if buildNum := os.Getenv("BUILD_NUMBER"); buildNum != "" { return buildNum } if buildNum := os.Getenv("GITHUB_RUN_NUMBER"); buildNum != "" { return buildNum } return fmt.Sprintf("local-%d", time.Now().Unix()) } func (ca *CICDAuditor) getGitCommit() string { if commit := os.Getenv("GIT_COMMIT"); commit != "" { return commit } if commit := os.Getenv("GITHUB_SHA"); commit != "" { return commit } // Try to get from git command cmd := exec.Command("git", "rev-parse", "HEAD") if output, err := cmd.Output(); err == nil { return strings.TrimSpace(string(output)) } return "unknown" } func (ca *CICDAuditor) getGitBranch() string { if branch := os.Getenv("GIT_BRANCH"); branch != "" { return branch } if branch := os.Getenv("GITHUB_REF_NAME"); branch != "" { return branch } // Try to get from git command cmd := exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD") if output, err := cmd.Output(); err == nil { return strings.TrimSpace(string(output)) } return "unknown" } func (ca *CICDAuditor) getGitTag() string { if tag := os.Getenv("GIT_TAG"); tag != "" { return tag } if tag := os.Getenv("GITHUB_REF_NAME"); tag != "" && os.Getenv("GITHUB_REF_TYPE") == "tag" { return tag } // Try to get from git command cmd := exec.Command("git", "describe", "--tags", "--exact-match") if output, err := cmd.Output(); err == nil { return strings.TrimSpace(string(output)) } return "" } func (ca *CICDAuditor) getTriggeredBy() string { if actor := os.Getenv("GITHUB_ACTOR"); actor != "" { return actor } if user := os.Getenv("USER"); user != "" { return user } return "unknown" } func (ca *CICDAuditor) getBuildAgent() string { if agent := os.Getenv("RUNNER_NAME"); agent != "" { return agent } if agent := os.Getenv("NODE_NAME"); agent != "" { return agent } if hostname, err := os.Hostname(); err == nil { return hostname } return "unknown" } func (ca *CICDAuditor) getBuildEnvironment() map[string]string { env := make(map[string]string) // Collect relevant environment variables relevantVars := []string{ "CI", "GITHUB_ACTIONS", "JENKINS_URL", "GITLAB_CI", "BUILD_ID", "JOB_NAME", "WORKSPACE", "GO_VERSION", "NODE_VERSION", "DOCKER_VERSION", } for _, varName := range relevantVars { if value := os.Getenv(varName); value != "" { env[varName] = value } } return env } func (ca *CICDAuditor) RunCICDPipeline(ctx context.Context) (int, error) { defer func() { ca.results.Duration = time.Since(ca.startTime).Milliseconds() }() if ca.config.Verbose { fmt.Printf("Starting %s pipeline with %d audit tools\n", ca.config.Pipeline, len(ca.auditTools)) } // Build audit tools first if err := ca.buildAuditTools(ctx); err != nil { return 1, fmt.Errorf("failed to build audit tools: %w", err) } // Run audits var err error if ca.config.Parallel { err = ca.runAuditsParallel(ctx) } else { err = ca.runAuditsSequential(ctx) } if err != nil { ca.results.OverallStatus = "FAILED" ca.results.ExitCode = 1 return 1, err } // Evaluate quality gates ca.evaluateQualityGates() // Calculate metrics ca.calculatePipelineMetrics() // Send notifications ca.sendNotifications() // Generate reports if err := ca.generateAllReports(); err != nil { log.Printf("Failed to generate reports: %v", err) } // Determine exit code if ca.results.FailedAudits > 0 || ca.hasFailedCriticalGates() { ca.results.OverallStatus = "FAILED" ca.results.ExitCode = 1 return 1, nil } ca.results.OverallStatus = "PASSED" ca.results.ExitCode = 0 return 0, nil } func (ca *CICDAuditor) buildAuditTools(ctx context.Context) error { if ca.config.Verbose { fmt.Println("Building audit tools...") } // Build each audit tool buildCommands := []struct { name string cmd string args []string dir string }{ { name: "math-audit", cmd: "go", args: []string{"build", "-o", "../../tools/math-audit/math-audit", "."}, dir: "tools/math-audit", }, { name: "profitability-audit", cmd: "go", args: []string{"build", "-o", "../../tools/profitability-audit/profitability-audit", "."}, dir: "tools/profitability-audit", }, { name: "gas-audit", cmd: "go", args: []string{"build", "-o", "../../tools/gas-audit/gas-audit", "."}, dir: "tools/gas-audit", }, { name: "opportunity-validator", cmd: "go", args: []string{"build", "-o", "../../tools/opportunity-validator/opportunity-validator", "."}, dir: "tools/opportunity-validator", }, { name: "exchange-audit", cmd: "go", args: []string{"build", "-o", "../../tools/exchange-audit/exchange-audit", "."}, dir: "tools/exchange-audit", }, { name: "performance-audit", cmd: "go", args: []string{"build", "-o", "../../tools/performance-audit/performance-audit", "."}, dir: "tools/performance-audit", }, { name: "security-audit", cmd: "go", args: []string{"build", "-o", "../../tools/security-audit/security-audit", "."}, dir: "tools/security-audit", }, } for _, build := range buildCommands { if ca.config.Verbose { fmt.Printf("Building %s...\n", build.name) } buildCtx, cancel := context.WithTimeout(ctx, 2*time.Minute) cmd := exec.CommandContext(buildCtx, build.cmd, build.args...) cmd.Dir = filepath.Join(ca.rootDir, build.dir) if output, err := cmd.CombinedOutput(); err != nil { cancel() return fmt.Errorf("failed to build %s: %w\nOutput: %s", build.name, err, string(output)) } cancel() } return nil } func (ca *CICDAuditor) runAuditsParallel(ctx context.Context) error { if ca.config.Verbose { fmt.Printf("Running %d audits in parallel...\n", len(ca.auditTools)) } var wg sync.WaitGroup results := make(chan AuditResult, len(ca.auditTools)) // Use semaphore to limit concurrency maxConcurrency := 4 semaphore := make(chan struct{}, maxConcurrency) for _, tool := range ca.auditTools { wg.Add(1) go func(tool AuditTool) { defer wg.Done() // Acquire semaphore semaphore <- struct{}{} defer func() { <-semaphore }() result := ca.runSingleAudit(ctx, tool) results <- result // Check fail-fast condition if ca.config.FailFast && result.Status == "FAILED" && tool.Critical { // Cancel context to stop other audits if ca.config.Verbose { fmt.Printf("Critical audit %s failed, stopping pipeline (fail-fast mode)\n", tool.Name) } return } }(tool) } // Close results channel when all goroutines complete go func() { wg.Wait() close(results) }() // Collect results for result := range results { ca.results.AuditResults = append(ca.results.AuditResults, result) switch result.Status { case "PASSED": ca.results.PassedAudits++ case "FAILED": ca.results.FailedAudits++ case "SKIPPED": ca.results.SkippedAudits++ } if ca.config.Verbose { fmt.Printf("Audit %s: %s (%.2fs)\n", result.ToolName, result.Status, float64(result.Duration)/1000.0) } } return nil } func (ca *CICDAuditor) runAuditsSequential(ctx context.Context) error { if ca.config.Verbose { fmt.Printf("Running %d audits sequentially...\n", len(ca.auditTools)) } for _, tool := range ca.auditTools { result := ca.runSingleAudit(ctx, tool) ca.results.AuditResults = append(ca.results.AuditResults, result) switch result.Status { case "PASSED": ca.results.PassedAudits++ case "FAILED": ca.results.FailedAudits++ case "SKIPPED": ca.results.SkippedAudits++ } if ca.config.Verbose { fmt.Printf("Audit %s: %s (%.2fs)\n", result.ToolName, result.Status, float64(result.Duration)/1000.0) } // Check fail-fast condition if ca.config.FailFast && result.Status == "FAILED" && tool.Critical { if ca.config.Verbose { fmt.Printf("Critical audit %s failed, stopping pipeline (fail-fast mode)\n", tool.Name) } break } } return nil } func (ca *CICDAuditor) runSingleAudit(ctx context.Context, tool AuditTool) AuditResult { startTime := time.Now() result := AuditResult{ ToolName: tool.Name, QualityChecks: make([]QualityCheck, 0), Recommendations: make([]string, 0), Timestamp: startTime, Metrics: make(map[string]interface{}), } // Create tool-specific timeout context toolCtx, cancel := context.WithTimeout(ctx, tool.Timeout) defer cancel() // Prepare command cmd := exec.CommandContext(toolCtx, tool.Command, tool.Args...) cmd.Dir = ca.rootDir // Set environment variables cmd.Env = os.Environ() for key, value := range tool.Environment { cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", key, value)) } // Run with retry logic var err error var output []byte for attempt := 0; attempt <= tool.RetryCount; attempt++ { if attempt > 0 { if ca.config.Verbose { fmt.Printf("Retrying %s (attempt %d/%d)...\n", tool.Name, attempt+1, tool.RetryCount+1) } result.RetryCount = attempt time.Sleep(time.Duration(attempt) * 5 * time.Second) // Exponential backoff } output, err = cmd.CombinedOutput() if err == nil { break } // Check if context was cancelled if toolCtx.Err() != nil { break } } result.Duration = time.Since(startTime).Milliseconds() if err != nil { result.Status = "FAILED" result.ExitCode = 1 result.ErrorOutput = string(output) if toolCtx.Err() == context.DeadlineExceeded { result.ErrorOutput = fmt.Sprintf("Audit timed out after %v", tool.Timeout) } } else { result.Status = "PASSED" result.ExitCode = 0 // Parse output file if it exists outputPath := filepath.Join(ca.config.OutputDir, tool.OutputFile) if _, err := os.Stat(outputPath); err == nil { result.OutputFile = outputPath ca.parseAuditOutput(tool, outputPath, &result) } } // Evaluate tool-specific quality checks ca.evaluateToolQualityChecks(tool, &result) return result } func (ca *CICDAuditor) parseAuditOutput(tool AuditTool, outputPath string, result *AuditResult) { // Parse the audit output file to extract metrics and insights content, err := os.ReadFile(outputPath) if err != nil { return } // Try to parse as JSON to extract metrics var data map[string]interface{} if err := json.Unmarshal(content, &data); err == nil { // Extract common metrics based on tool type switch tool.Name { case "Math Audit": if errorRate, ok := data["max_error_bp"].(float64); ok { result.Metrics["error_rate"] = errorRate } case "Security Scan", "Complete Security Scan": if score, ok := data["metrics"].(map[string]interface{})["security_score"].(float64); ok { result.Metrics["security_score"] = score } if findings, ok := data["security_findings"].([]interface{}); ok { result.Metrics["findings_count"] = len(findings) } case "Performance Audit": if score, ok := data["overall_score"].(float64); ok { result.Metrics["performance_score"] = score } case "Profitability Audit": if accuracy, ok := data["profitability_accuracy"].(float64); ok { result.Metrics["accuracy"] = accuracy } } } } func (ca *CICDAuditor) evaluateToolQualityChecks(tool AuditTool, result *AuditResult) { for _, gate := range tool.QualityGates { check := QualityCheck{ Name: gate.Name, Threshold: gate.Threshold, Description: gate.Description, Critical: gate.Critical, } // Get actual value from metrics var actualValue float64 var found bool switch gate.Type { case "error_rate": if val, ok := result.Metrics["error_rate"].(float64); ok { actualValue = val found = true } case "percentage": // Generic percentage check - try to match by name for key, val := range result.Metrics { if strings.Contains(strings.ToLower(key), "score") || strings.Contains(strings.ToLower(key), "accuracy") { if score, ok := val.(float64); ok { actualValue = score found = true break } } } case "count": if val, ok := result.Metrics["findings_count"].(int); ok { actualValue = float64(val) found = true } } if !found { check.Status = "UNKNOWN" check.Score = 0 } else { // Evaluate based on gate type switch gate.Type { case "error_rate", "count": // Lower is better if actualValue <= gate.Threshold { check.Status = "PASSED" check.Score = 100.0 } else { check.Status = "FAILED" check.Score = 0.0 } case "percentage": // Higher is better if actualValue >= gate.Threshold { check.Status = "PASSED" check.Score = actualValue } else { check.Status = "FAILED" check.Score = actualValue } } } result.QualityChecks = append(result.QualityChecks, check) // Also add to pipeline quality gates pipelineGate := gate pipelineGate.ActualValue = actualValue pipelineGate.Status = check.Status pipelineGate.Timestamp = time.Now() ca.results.QualityGates = append(ca.results.QualityGates, pipelineGate) } } func (ca *CICDAuditor) evaluateQualityGates() { if ca.config.Verbose { fmt.Println("Evaluating quality gates...") } for i := range ca.results.QualityGates { gate := &ca.results.QualityGates[i] if gate.Status == "" { gate.Status = "NOT_EVALUATED" } } } func (ca *CICDAuditor) calculatePipelineMetrics() { if ca.config.Verbose { fmt.Println("Calculating pipeline metrics...") } // Initialize metrics metrics := PipelineMetrics{ Benchmarks: make(map[string]float64), ResourceUsage: ResourceUsage{}, } // Calculate scores from audit results var securityScores []float64 var performanceScores []float64 var qualityScores []float64 for _, result := range ca.results.AuditResults { if result.Status == "PASSED" { if score, ok := result.Metrics["security_score"].(float64); ok { securityScores = append(securityScores, score) } if score, ok := result.Metrics["performance_score"].(float64); ok { performanceScores = append(performanceScores, score) } if score, ok := result.Metrics["accuracy"].(float64); ok { qualityScores = append(qualityScores, score) } } } // Calculate average scores if len(securityScores) > 0 { metrics.SecurityScore = average(securityScores) } if len(performanceScores) > 0 { metrics.PerformanceScore = average(performanceScores) } if len(qualityScores) > 0 { metrics.QualityScore = average(qualityScores) } // Calculate overall score scores := []float64{} if metrics.SecurityScore > 0 { scores = append(scores, metrics.SecurityScore) } if metrics.PerformanceScore > 0 { scores = append(scores, metrics.PerformanceScore) } if metrics.QualityScore > 0 { scores = append(scores, metrics.QualityScore) } if len(scores) > 0 { metrics.OverallScore = average(scores) } // Simple trend analysis metrics.TrendAnalysis = TrendAnalysis{ ScoreTrend: "STABLE", // Would compare with historical data IssuesTrend: "STABLE", PerformanceTrend: "STABLE", LastUpdated: time.Now(), RecommendedActions: ca.generateMetricRecommendations(metrics), } // Resource usage (simplified) metrics.ResourceUsage = ResourceUsage{ CPUTime: float64(ca.results.Duration) / 1000.0, MemoryPeak: 512, // Simplified DiskUsage: 100, // Simplified } ca.results.Metrics = metrics } func average(scores []float64) float64 { if len(scores) == 0 { return 0 } sum := 0.0 for _, score := range scores { sum += score } return sum / float64(len(scores)) } func (ca *CICDAuditor) generateMetricRecommendations(metrics PipelineMetrics) []string { recommendations := make([]string, 0) if metrics.SecurityScore < 80.0 { recommendations = append(recommendations, "Security score is below 80%. Review and address security findings.") } if metrics.PerformanceScore < 70.0 { recommendations = append(recommendations, "Performance score is below 70%. Optimize critical paths.") } if metrics.QualityScore < 90.0 { recommendations = append(recommendations, "Quality score is below 90%. Improve test coverage and code quality.") } if ca.results.FailedAudits > 0 { recommendations = append(recommendations, "Some audits failed. Review failure causes and implement fixes.") } return recommendations } func (ca *CICDAuditor) hasFailedCriticalGates() bool { for _, gate := range ca.results.QualityGates { if gate.Critical && gate.Status == "FAILED" { return true } } return false } func (ca *CICDAuditor) sendNotifications() { if ca.config.Verbose { fmt.Println("Sending notifications...") } // Slack notification if ca.config.SlackWebhook != "" { notification := Notification{ Type: "slack", Target: ca.config.SlackWebhook, Message: ca.generateNotificationMessage(), Timestamp: time.Now(), } if err := ca.sendSlackNotification(ca.config.SlackWebhook, notification.Message); err != nil { notification.Status = "FAILED" log.Printf("Failed to send Slack notification: %v", err) } else { notification.Status = "SENT" } ca.results.Notifications = append(ca.results.Notifications, notification) } // Email notification if ca.config.EmailRecipients != "" { notification := Notification{ Type: "email", Target: ca.config.EmailRecipients, Message: ca.generateNotificationMessage(), Timestamp: time.Now(), } // Email sending would be implemented here notification.Status = "SENT" // Placeholder ca.results.Notifications = append(ca.results.Notifications, notification) } } func (ca *CICDAuditor) generateNotificationMessage() string { status := "✅ PASSED" if ca.results.OverallStatus == "FAILED" { status = "❌ FAILED" } return fmt.Sprintf(`CI/CD Audit Pipeline %s Pipeline: %s Environment: %s Build: %s Duration: %.1fs Results: - Passed: %d - Failed: %d - Skipped: %d Overall Score: %.1f%% Branch: %s Commit: %s `, status, ca.config.Pipeline, ca.config.Environment, ca.results.BuildInfo.BuildNumber, float64(ca.results.Duration)/1000.0, ca.results.PassedAudits, ca.results.FailedAudits, ca.results.SkippedAudits, ca.results.Metrics.OverallScore, ca.results.BuildInfo.GitBranch, ca.results.BuildInfo.GitCommit[:8]) } func (ca *CICDAuditor) sendSlackNotification(webhook, message string) error { // Slack notification implementation would go here // For now, just log the message if ca.config.Verbose { fmt.Printf("Slack notification: %s\n", message) } return nil } func (ca *CICDAuditor) generateAllReports() error { timestamp := time.Now().Format("2006-01-02_15-04-05") switch ca.config.ReportFormat { case "junit": return ca.generateJUnitReport(timestamp) case "json": return ca.generateJSONReport(timestamp) case "html": return ca.generateHTMLReport(timestamp) case "all": if err := ca.generateJUnitReport(timestamp); err != nil { return err } if err := ca.generateJSONReport(timestamp); err != nil { return err } return ca.generateHTMLReport(timestamp) default: return ca.generateJUnitReport(timestamp) } } func (ca *CICDAuditor) generateJUnitReport(timestamp string) error { testSuites := JUnitTestSuites{ Name: "MEV Bot Audit Pipeline", Tests: ca.results.TotalAudits, Failures: ca.results.FailedAudits, Errors: 0, Time: float64(ca.results.Duration) / 1000.0, } // Group by stage stageMap := make(map[string][]AuditResult) for _, result := range ca.results.AuditResults { stage := "default" for _, tool := range ca.auditTools { if tool.Name == result.ToolName { stage = tool.Stage break } } stageMap[stage] = append(stageMap[stage], result) } // Create test suites for each stage for stage, results := range stageMap { suite := JUnitTestSuite{ Name: fmt.Sprintf("Audit Stage: %s", stage), Tests: len(results), Time: 0, } for _, result := range results { testCase := JUnitTestCase{ Name: result.ToolName, ClassName: fmt.Sprintf("audit.%s", stage), Time: float64(result.Duration) / 1000.0, } suite.Time += testCase.Time if result.Status == "FAILED" { suite.Failures++ testCase.Failure = &JUnitFailure{ Message: fmt.Sprintf("Audit failed with exit code %d", result.ExitCode), Type: "AuditFailure", Content: result.ErrorOutput, } } else if result.Status == "SKIPPED" { testCase.Skipped = &JUnitSkipped{ Message: "Audit was skipped", } } suite.TestCases = append(suite.TestCases, testCase) } testSuites.TestSuites = append(testSuites.TestSuites, suite) } // Write JUnit XML xmlData, err := xml.MarshalIndent(testSuites, "", " ") if err != nil { return fmt.Errorf("failed to marshal JUnit XML: %w", err) } junitPath := filepath.Join(ca.config.OutputDir, fmt.Sprintf("junit-report_%s.xml", timestamp)) xmlContent := []byte(xml.Header + string(xmlData)) if err := os.WriteFile(junitPath, xmlContent, 0644); err != nil { return fmt.Errorf("failed to write JUnit report: %w", err) } if ca.config.Verbose { fmt.Printf("JUnit report generated: %s\n", junitPath) } return nil } func (ca *CICDAuditor) generateJSONReport(timestamp string) error { jsonData, err := json.MarshalIndent(ca.results, "", " ") if err != nil { return fmt.Errorf("failed to marshal JSON: %w", err) } jsonPath := filepath.Join(ca.config.OutputDir, fmt.Sprintf("pipeline-report_%s.json", timestamp)) if err := os.WriteFile(jsonPath, jsonData, 0644); err != nil { return fmt.Errorf("failed to write JSON report: %w", err) } if ca.config.Verbose { fmt.Printf("JSON report generated: %s\n", jsonPath) } return nil } func (ca *CICDAuditor) generateHTMLReport(timestamp string) error { // Generate HTML report htmlContent := ca.generateHTMLContent() htmlPath := filepath.Join(ca.config.OutputDir, fmt.Sprintf("pipeline-report_%s.html", timestamp)) if err := os.WriteFile(htmlPath, []byte(htmlContent), 0644); err != nil { return fmt.Errorf("failed to write HTML report: %w", err) } if ca.config.Verbose { fmt.Printf("HTML report generated: %s\n", htmlPath) } return nil } func (ca *CICDAuditor) generateHTMLContent() string { status := "success" statusIcon := "✅" if ca.results.OverallStatus == "FAILED" { status = "danger" statusIcon = "❌" } html := fmt.Sprintf(`
Pipeline: %s | Environment: %s | Build: %s
Generated: %s | Duration: %.1fs
Status: %s | Duration: %.2fs
Exit Code: %d
%sError: %s
", result.ErrorOutput) } return "" }()) } // Add quality gates if len(ca.results.QualityGates) > 0 { html += `| Name | Type | Threshold | Actual | Status | Critical |
|---|---|---|---|---|---|
| %s | %s | %.2f | %.2f | %s | %t |