//! X86 CI/CD Pipeline Integration — Complete CI/CD pipeline generation, execution,
//! and management module for Clang on X86 architectures. (~9000+ lines)
//!
//! This module provides comprehensive CI/CD integration for X86-targeted Clang
//! builds, including pipeline generators for all major CI systems, compilation
//! strategies, testing strategies, quality gates, artifact management,
//! notification integration, and release automation.
//!
//! # Architecture
//!
//! - **X86CI_CD** — Master CI/CD orchestrator for X86 Clang projects
//! - **X86CIGenerator** — Unified CI configuration generator across all CI systems:
//! GitHub Actions, GitLab CI, Jenkins, CircleCI, Azure Pipelines, Travis CI,
//! Drone CI, Buildkite, TeamCity, Bitbucket Pipelines
//! - **X86BuildMatrix** — Build matrix generation (OS × compiler × build type ×
//! sanitizer × arch × opt level × C++ standard)
//! - **X86CITestIntegration** — Test integration: JUnit XML, CTest, coverage
//! (gcov/lcov/llvm-cov/source-based), performance regression, flaky test
//! detection and quarantine, result visualization
//! - **X86CIArtifactManager** — Artifact management: binary packaging (.tar.gz,
//! .zip, .deb, .rpm, .AppImage), Docker images, release automation, package
//! registry publishing (GitHub Packages, GitLab Registry), symbol server
//! upload, SBOM generation
//! - **X86CINotification** — Slack/Teams/Discord webhook notifications, email,
//! commit status updates, status badges (shields.io)
//!
//! # Integration
//!
//! Integrates with:
//! - `crate::clang::*` — Core Clang types (ClangDriver, ClangOptions, etc.)
//! - `crate::x86::*` — X86 target backend (X86TargetMachine, X86Subtarget, etc.)
//!
//! Clean-room behavioral reconstruction from published CI system documentation,
//! YAML specifications, and public API references.
use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
use crate::clang::*;
use crate::x86::*;
// ═══════════════════════════════════════════════════════════════════════════════
// Constants
// ═══════════════════════════════════════════════════════════════════════════════
/// Default CI pipeline timeout (6 hours).
pub const X86_CI_DEFAULT_TIMEOUT_MINUTES: u32 = 360;
/// Default per-job timeout (1 hour).
pub const X86_CI_JOB_TIMEOUT_MINUTES: u32 = 60;
/// Default number of parallel CI jobs.
pub const X86_CI_DEFAULT_PARALLEL: u32 = 4;
/// Maximum matrix combinations before warning.
pub const X86_CI_MAX_MATRIX_SIZE: usize = 64;
/// Default ccache storage size.
pub const X86_CI_CCACHE_SIZE: &str = "500M";
/// Default sccache storage size.
pub const X86_CI_SCCACHE_SIZE: &str = "2G";
/// Default directory for build artifacts.
pub const X86_CI_DEFAULT_ARTIFACT_DIR: &str = "ci-artifacts";
/// Default directory for test reports.
pub const X86_CI_DEFAULT_REPORT_DIR: &str = "ci-reports";
/// Default coverage threshold percentage.
pub const X86_CI_DEFAULT_COVERAGE_THRESHOLD: f64 = 80.0;
/// Default test pass rate threshold.
pub const X86_CI_DEFAULT_PASS_RATE_THRESHOLD: f64 = 100.0;
/// Default static analysis error threshold.
pub const X86_CI_DEFAULT_ANALYSIS_ERROR_MAX: u32 = 0;
/// Default performance regression threshold (percentage).
pub const X86_CI_PERF_REGRESSION_THRESHOLD: f64 = 2.0;
/// Default fuzzing duration (seconds).
pub const X86_CI_FUZZ_DEFAULT_DURATION: u64 = 3600;
/// Default retry count for flaky jobs.
pub const X86_CI_DEFAULT_RETRY_COUNT: u32 = 2;
/// X86-64 default Docker image for CI.
pub const X86_CI_DEFAULT_DOCKER_IMAGE: &str = "ubuntu:22.04";
/// Clang version tag for CI toolchain.
pub const X86_CI_CLANG_VERSION: &str = "17";
/// GCC version for comparison builds.
pub const X86_CI_GCC_VERSION: &str = "13";
/// Default build directory.
pub const X86_CI_BUILD_DIR: &str = "build";
/// Ccache cache key prefix.
pub const X86_CI_CCACHE_KEY: &str = "ccache-x86-clang";
/// Cargo cache key prefix.
pub const X86_CI_CARGO_CACHE_KEY: &str = "cargo-x86";
/// NPM cache key prefix.
pub const X86_CI_NPM_CACHE_KEY: &str = "npm-x86";
/// Default flaky test threshold (consecutive failures before quarantine).
pub const X86_CI_FLAKY_THRESHOLD: u32 = 3;
/// Default quarantine duration for flaky tests (days).
pub const X86_CI_QUARANTINE_DAYS: u32 = 7;
/// Default max flaky test quarantine list size.
pub const X86_CI_MAX_QUARANTINE_SIZE: usize = 50;
/// SBOM default format.
pub const X86_CI_SBOM_DEFAULT_FORMAT: &str = "spdx-json";
// ═══════════════════════════════════════════════════════════════════════════════
// 1. X86CI_CD — Master CI/CD Orchestrator
// ═══════════════════════════════════════════════════════════════════════════════
/// Master CI/CD orchestrator for X86-targeted Clang projects.
///
/// Coordinates all CI/CD subsystems: pipeline generation, compilation strategies,
/// testing strategies, quality gates, artifact management, and notifications.
#[derive(Debug, Clone)]
pub struct X86CI_CD {
/// Active CI system target.
pub ci_system: X86CiSystem,
/// Pipeline configuration (triggers, stages, matrix).
pub pipeline_config: X86PipelineConfig,
/// Compilation strategy.
pub compilation: X86CompilationStrategy,
/// Testing strategy.
pub testing: X86TestStrategy,
/// Quality gates for pipeline validation.
pub quality_gates: X86QualityGate,
/// Artifact management configuration.
pub artifacts: X86ArtifactManager,
/// Notification configuration.
pub notifications: X86NotificationHub,
/// GitHub Actions pipeline support.
pub github: X86GitHubSupport,
/// GitLab CI pipeline support.
pub gitlab: X86GitLabSupport,
/// Jenkins pipeline support.
pub jenkins: X86JenkinsSupport,
/// CircleCI pipeline support.
pub circleci: X86CircleCISupport,
/// Azure Pipelines support.
pub azure: X86AzureSupport,
/// Travis CI support.
pub travis: X86TravisSupport,
/// Drone CI support.
pub drone: X86DroneSupport,
/// Buildkite support.
pub buildkite: X86BuildkiteSupport,
/// TeamCity support.
pub teamcity: X86TeamCitySupport,
/// Bitbucket Pipelines support.
pub bitbucket: X86BitbucketSupport,
/// CI statistics tracking.
pub stats: X86CiStats,
}
impl Default for X86CI_CD {
fn default() -> Self {
Self {
ci_system: X86CiSystem::default(),
pipeline_config: X86PipelineConfig::default(),
compilation: X86CompilationStrategy::default(),
testing: X86TestStrategy::default(),
quality_gates: X86QualityGate::compilation_success(),
artifacts: X86ArtifactManager::default(),
notifications: X86NotificationHub::default(),
github: X86GitHubSupport::default(),
gitlab: X86GitLabSupport::default(),
jenkins: X86JenkinsSupport::default(),
circleci: X86CircleCISupport::default(),
azure: X86AzureSupport::default(),
travis: X86TravisSupport::default(),
drone: X86DroneSupport::default(),
buildkite: X86BuildkiteSupport::default(),
teamcity: X86TeamCitySupport::default(),
bitbucket: X86BitbucketSupport::default(),
stats: X86CiStats::default(),
}
}
}
impl X86CI_CD {
/// Create a new CI/CD orchestrator for the given CI system.
pub fn new(system: X86CiSystem) -> Self {
let mut ci = Self::default();
ci.ci_system = system;
ci
}
/// Generate the CI pipeline for the active CI system.
pub fn generate_pipeline(&self) -> X86PipelineResult {
let generator = X86CIGenerator::new(self);
match &self.ci_system {
X86CiSystem::GitHubActions => generator.generate_github_actions(),
X86CiSystem::GitLabCI => generator.generate_gitlab_ci(),
X86CiSystem::Jenkins => generator.generate_jenkins(),
X86CiSystem::CircleCI => generator.generate_circleci(),
X86CiSystem::AzurePipelines => generator.generate_azure_pipelines(),
X86CiSystem::TravisCI => generator.generate_travis_ci(),
X86CiSystem::DroneCI => generator.generate_drone_ci(),
X86CiSystem::Buildkite => generator.generate_buildkite(),
X86CiSystem::TeamCity => generator.generate_teamcity(),
X86CiSystem::BitbucketPipelines => generator.generate_bitbucket_pipelines(),
X86CiSystem::Bamboo => generator.generate_bamboo(),
X86CiSystem::Custom(_) => X86PipelineResult {
content: format!("# Custom CI pipeline for {}", self.ci_system),
file_name: "ci-pipeline.txt".to_string(),
ci_system: self.ci_system.clone(),
},
}
}
/// Execute the CI pipeline (simulation of build/test/analyze/deploy stages).
pub fn execute_pipeline(&self) -> X86PipelineExecution {
let start = SystemTime::now();
let mut jobs = Vec::new();
for (idx, stage) in self.pipeline_config.stages.iter().enumerate() {
let job_start = SystemTime::now();
// Simulate compilation
let compile_result = stage
.compilation
.as_ref()
.map(|comp_config| self.compilation.simulate_compile(comp_config));
// Simulate tests
let test_result = stage
.test_config
.as_ref()
.map(|test_config| self.testing.simulate_tests(test_config));
// Collect artifacts
let mut artifacts = Vec::new();
if let Some(_compile_result) = &compile_result {
let paths = self.artifacts.generate_paths(&stage.name);
for path in &paths {
artifacts.push(X86Artifact {
name: path
.file_name()
.map(|n| n.to_string_lossy().to_string())
.unwrap_or_else(|| "unknown".to_string()),
path: path.to_string_lossy().to_string(),
size_bytes: 0,
artifact_type: X86ArtifactType::Binary,
compressed: false,
checksum: None,
created_at: chrono_like_now(),
expires_at: chrono_like_now() + Duration::from_secs(86400 * 30),
});
}
}
let duration = job_start.elapsed().unwrap_or(Duration::from_secs(0));
let success = compile_result.as_ref().map(|r| r.success).unwrap_or(true)
&& test_result.as_ref().map(|r| r.all_passed()).unwrap_or(true);
jobs.push(X86CiJobExecution {
job_name: stage.name.clone(),
stage_index: idx,
success,
duration,
compile_result,
test_result,
artifacts,
logs: stage.generate_logs(),
});
}
let overall_success = jobs.iter().all(|j| j.success);
let duration = start.elapsed().unwrap_or(Duration::from_secs(0));
// Evaluate quality gates
let quality_gate_results = self.quality_gates.evaluate(&jobs, &self.artifact_summary());
let artifact_summary = self.artifacts.summarize();
X86PipelineExecution {
pipeline: self.pipeline_config.clone(),
jobs,
overall_success,
duration,
quality_gate_results,
artifact_summary,
}
}
fn artifact_summary(&self) -> X86ArtifactSummary {
self.artifacts.summarize()
}
/// Generate a build matrix from the pipeline configuration.
pub fn generate_matrix(&self) -> X86BuildMatrix {
X86BuildMatrix {
operating_systems: self.pipeline_config.os_list.clone(),
compilers: self.pipeline_config.compiler_list.clone(),
build_types: self.pipeline_config.build_types.clone(),
features: self.pipeline_config.feature_flags.clone(),
sanitizers: self.pipeline_config.sanitizer_list.clone(),
arch_variants: self.pipeline_config.arch_list.clone(),
}
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// 2. X86CIGenerator — CI Configuration Generator
// ═══════════════════════════════════════════════════════════════════════════════
/// Unified CI configuration generator supporting all major CI/CD platforms.
///
/// Generates complete pipeline configuration files (YAML, Jenkinsfile, Kotlin DSL)
/// for GitHub Actions, GitLab CI, Jenkins, CircleCI, Azure Pipelines, Travis CI,
/// Drone CI, Buildkite, TeamCity, and Bitbucket Pipelines.
pub struct X86CIGenerator {
/// Reference to the CI/CD orchestrator state.
ci_cd: Box<X86CI_CD>,
}
impl X86CIGenerator {
/// Create a new CI generator from the CI/CD orchestrator state.
pub fn new(ci_cd: &X86CI_CD) -> Self {
Self {
ci_cd: Box::new(ci_cd.clone()),
}
}
// ── GitHub Actions ─────────────────────────────────────────────────────
/// Generate a complete GitHub Actions workflow YAML with matrix builds,
/// caching (ccache/sccache), artifact upload, test reporting, code coverage,
/// static analysis integration, and release automation.
pub fn generate_github_actions(&self) -> X86PipelineResult {
self.ci_cd.github.generate_workflow(&self.ci_cd)
}
// ── GitLab CI ───────────────────────────────────────────────────────────
/// Generate a .gitlab-ci.yml with stages (build/test/analyze/deploy),
/// parallel jobs, artifacts, cache, environment deployment, review apps.
pub fn generate_gitlab_ci(&self) -> X86PipelineResult {
self.ci_cd.gitlab.generate_pipeline(&self.ci_cd)
}
// ── Jenkins ─────────────────────────────────────────────────────────────
/// Generate a Jenkinsfile (declarative + scripted) with pipeline stages,
/// parallel steps, agent labels, post-build actions, build archiving.
pub fn generate_jenkins(&self) -> X86PipelineResult {
self.ci_cd.jenkins.generate_jenkinsfile(&self.ci_cd)
}
// ── CircleCI ────────────────────────────────────────────────────────────
/// Generate a .circleci/config.yml with orbs, executors, commands, jobs,
/// workflows, caching, test splitting.
pub fn generate_circleci(&self) -> X86PipelineResult {
self.ci_cd.circleci.generate_config(&self.ci_cd)
}
// ── Azure Pipelines ────────────────────────────────────────────────────
/// Generate an azure-pipelines.yml with pool, variables, steps, matrix
/// strategy, templates.
pub fn generate_azure_pipelines(&self) -> X86PipelineResult {
self.ci_cd.azure.generate_pipeline(&self.ci_cd)
}
// ── Travis CI ───────────────────────────────────────────────────────────
/// Generate a .travis.yml with os/compiler/matrix, cache, deploy.
pub fn generate_travis_ci(&self) -> X86PipelineResult {
self.ci_cd.travis.generate_config(&self.ci_cd)
}
// ── Drone CI ────────────────────────────────────────────────────────────
/// Generate a .drone.yml with steps, plugins, secrets.
pub fn generate_drone_ci(&self) -> X86PipelineResult {
self.ci_cd.drone.generate_pipeline(&self.ci_cd)
}
// ── Buildkite ───────────────────────────────────────────────────────────
/// Generate a pipeline.yml with agents, steps, plugins.
pub fn generate_buildkite(&self) -> X86PipelineResult {
self.ci_cd.buildkite.generate_pipeline(&self.ci_cd)
}
// ── TeamCity ────────────────────────────────────────────────────────────
/// Generate a TeamCity Kotlin DSL build configuration.
pub fn generate_teamcity(&self) -> X86PipelineResult {
self.ci_cd.teamcity.generate_config(&self.ci_cd)
}
// ── Bitbucket Pipelines ─────────────────────────────────────────────────
/// Generate a bitbucket-pipelines.yml configuration.
pub fn generate_bitbucket_pipelines(&self) -> X86PipelineResult {
self.ci_cd.bitbucket.generate_pipeline(&self.ci_cd)
}
// ── Bamboo ──────────────────────────────────────────────────────────────
/// Generate an Atlassian Bamboo YAML specification.
pub fn generate_bamboo(&self) -> X86PipelineResult {
self.ci_cd.bitbucket.generate_bamboo_spec(&self.ci_cd)
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// CI System Enumeration
// ═══════════════════════════════════════════════════════════════════════════════
/// Supported CI/CD systems for X86 Clang pipelines.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum X86CiSystem {
/// GitHub Actions.
GitHubActions,
/// GitLab CI/CD.
GitLabCI,
/// Jenkins (declarative/scripted).
Jenkins,
/// CircleCI.
CircleCI,
/// Azure Pipelines.
AzurePipelines,
/// Travis CI.
TravisCI,
/// Drone CI.
DroneCI,
/// Buildkite.
Buildkite,
/// JetBrains TeamCity.
TeamCity,
/// Atlassian Bitbucket Pipelines.
BitbucketPipelines,
/// Atlassian Bamboo.
Bamboo,
/// Custom CI system with name.
Custom(String),
}
impl X86CiSystem {
/// Human-readable name of the CI system.
pub fn name(&self) -> &'static str {
match self {
Self::GitHubActions => "GitHub Actions",
Self::GitLabCI => "GitLab CI/CD",
Self::Jenkins => "Jenkins",
Self::CircleCI => "CircleCI",
Self::AzurePipelines => "Azure Pipelines",
Self::TravisCI => "Travis CI",
Self::DroneCI => "Drone CI",
Self::Buildkite => "Buildkite",
Self::TeamCity => "TeamCity",
Self::BitbucketPipelines => "Bitbucket Pipelines",
Self::Bamboo => "Atlassian Bamboo",
Self::Custom(_) => "Custom CI",
}
}
/// Default configuration file for the CI system.
pub fn config_file(&self) -> &'static str {
match self {
Self::GitHubActions => ".github/workflows/ci.yml",
Self::GitLabCI => ".gitlab-ci.yml",
Self::Jenkins => "Jenkinsfile",
Self::CircleCI => ".circleci/config.yml",
Self::AzurePipelines => "azure-pipelines.yml",
Self::TravisCI => ".travis.yml",
Self::DroneCI => ".drone.yml",
Self::Buildkite => "pipeline.yml",
Self::TeamCity => ".teamcity/settings.kts",
Self::BitbucketPipelines => "bitbucket-pipelines.yml",
Self::Bamboo => "bamboo-specs/bamboo.yml",
Self::Custom(_) => "ci-config.yml",
}
}
/// Whether this CI system supports build matrix strategies.
pub fn supports_matrix(&self) -> bool {
matches!(
self,
Self::GitHubActions
| Self::GitLabCI
| Self::Jenkins
| Self::CircleCI
| Self::AzurePipelines
| Self::TravisCI
)
}
/// Whether this CI system natively supports artifact storage.
pub fn supports_artifacts(&self) -> bool {
!matches!(self, Self::Bamboo | Self::Custom(_))
}
}
impl fmt::Display for X86CiSystem {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Custom(name) => write!(f, "Custom({})", name),
_ => write!(f, "{}", self.name()),
}
}
}
impl Default for X86CiSystem {
fn default() -> Self {
Self::GitHubActions
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// Pipeline Configuration
// ═══════════════════════════════════════════════════════════════════════════════
/// Complete pipeline configuration for X86 Clang CI/CD.
#[derive(Debug, Clone)]
pub struct X86PipelineConfig {
/// Pipeline name.
pub name: String,
/// Pipeline triggers (push, PR, schedule, etc.).
pub triggers: X86PipelineTriggers,
/// Pipeline stages (build, test, analyze, deploy).
pub stages: Vec<X86PipelineStage>,
/// Environment variables.
pub env: BTreeMap<String, String>,
/// Global timeout in minutes.
pub timeout_minutes: u32,
/// Whether to fail fast on first error.
pub fail_fast: bool,
/// Number of retries for flaky stages.
pub retry_count: u32,
/// Concurrency group name.
pub concurrency_group: Option<String>,
/// Whether to cancel in-progress runs.
pub cancel_in_progress: bool,
/// Operating systems in the build matrix.
pub os_list: Vec<String>,
/// Compilers in the build matrix.
pub compiler_list: Vec<String>,
/// Build types in the matrix.
pub build_types: Vec<String>,
/// Feature flags in the matrix.
pub feature_flags: Vec<String>,
/// Sanitizers in the matrix.
pub sanitizer_list: Vec<String>,
/// Architecture variants in the matrix.
pub arch_list: Vec<String>,
}
impl Default for X86PipelineConfig {
fn default() -> Self {
Self {
name: "X86-Clang-CI".to_string(),
triggers: X86PipelineTriggers::default(),
stages: vec![
X86PipelineStage {
name: "build".to_string(),
stage_type: X86StageType::Build,
compilation: Some(X86CompileConfig {
name: "x86_64-release".to_string(),
build_system: X86BuildSystem::CMake,
build_type: "Release".to_string(),
cmake_flags: vec![],
source_dir: ".".to_string(),
build_dir: X86_CI_BUILD_DIR.to_string(),
install_dir: "install".to_string(),
}),
test_config: None,
dependencies: vec![],
timeout_minutes: X86_CI_JOB_TIMEOUT_MINUTES,
allow_failure: false,
},
X86PipelineStage {
name: "test".to_string(),
stage_type: X86StageType::Test,
compilation: None,
test_config: Some(X86TestConfig {
name: "unit-tests".to_string(),
test_type: X86TestType::Unit,
framework: X86TestFramework::CTest,
working_dir: X86_CI_BUILD_DIR.to_string(),
extra_args: vec!["--output-on-failure".to_string()],
timeout_secs: 1800,
filter: None,
exclude: None,
}),
dependencies: vec!["build".to_string()],
timeout_minutes: X86_CI_JOB_TIMEOUT_MINUTES,
allow_failure: false,
},
X86PipelineStage {
name: "analyze".to_string(),
stage_type: X86StageType::Analyze,
compilation: None,
test_config: Some(X86TestConfig::analysis_only()),
dependencies: vec!["build".to_string()],
timeout_minutes: X86_CI_JOB_TIMEOUT_MINUTES,
allow_failure: true,
},
X86PipelineStage {
name: "deploy".to_string(),
stage_type: X86StageType::Deploy,
compilation: None,
test_config: None,
dependencies: vec!["test".to_string(), "analyze".to_string()],
timeout_minutes: X86_CI_JOB_TIMEOUT_MINUTES,
allow_failure: false,
},
],
env: BTreeMap::from([
("CC".to_string(), "clang".to_string()),
("CXX".to_string(), "clang++".to_string()),
("CCACHE_DIR".to_string(), "/tmp/ccache".to_string()),
]),
timeout_minutes: X86_CI_DEFAULT_TIMEOUT_MINUTES,
fail_fast: false,
retry_count: X86_CI_DEFAULT_RETRY_COUNT,
concurrency_group: Some("x86-clang-ci".to_string()),
cancel_in_progress: true,
os_list: vec!["ubuntu-22.04".to_string(), "ubuntu-24.04".to_string()],
compiler_list: vec![
"clang".to_string(),
"gcc".to_string(),
"clang++".to_string(),
],
build_types: vec![
"Debug".to_string(),
"Release".to_string(),
"RelWithDebInfo".to_string(),
"MinSizeRel".to_string(),
],
feature_flags: vec!["sse42".to_string(), "avx2".to_string(), "lto".to_string()],
sanitizer_list: vec![
"asan".to_string(),
"ubsan".to_string(),
"tsan".to_string(),
"msan".to_string(),
],
arch_list: vec![
"x86_64".to_string(),
"x86_64_v2".to_string(),
"x86_64_v3".to_string(),
"x86_64_v4".to_string(),
],
}
}
}
/// Pipeline triggers configuration.
#[derive(Debug, Clone)]
pub struct X86PipelineTriggers {
/// Push events trigger.
pub push: X86PushTrigger,
/// Pull request events trigger.
pub pull_request: X86PullRequestTrigger,
/// Scheduled triggers.
pub schedule: Vec<X86ScheduleTrigger>,
/// Manual workflow dispatch.
pub workflow_dispatch: bool,
/// Release events trigger.
pub release: Option<X86ReleaseTrigger>,
/// Tag events trigger.
pub tags: bool,
}
impl Default for X86PipelineTriggers {
fn default() -> Self {
Self {
push: X86PushTrigger {
branches: vec!["main".to_string(), "develop".to_string()],
paths: vec![
"src/**".to_string(),
"include/**".to_string(),
"CMakeLists.txt".to_string(),
],
paths_ignore: vec!["docs/**".to_string(), "*.md".to_string()],
},
pull_request: X86PullRequestTrigger {
branches: vec!["main".to_string()],
types: vec![
"opened".to_string(),
"synchronize".to_string(),
"reopened".to_string(),
],
paths: vec!["src/**".to_string(), "include/**".to_string()],
},
schedule: vec![X86ScheduleTrigger {
cron: "0 2 * * 0".to_string(),
display_name: Some("Weekly Sunday Build".to_string()),
}],
workflow_dispatch: true,
release: Some(X86ReleaseTrigger {
types: vec!["published".to_string(), "created".to_string()],
}),
tags: true,
}
}
}
/// Push event trigger configuration.
#[derive(Debug, Clone)]
pub struct X86PushTrigger {
pub branches: Vec<String>,
pub paths: Vec<String>,
pub paths_ignore: Vec<String>,
}
/// Pull request trigger configuration.
#[derive(Debug, Clone)]
pub struct X86PullRequestTrigger {
pub branches: Vec<String>,
pub types: Vec<String>,
pub paths: Vec<String>,
}
/// Scheduled trigger (cron).
#[derive(Debug, Clone)]
pub struct X86ScheduleTrigger {
pub cron: String,
pub display_name: Option<String>,
}
/// Release event trigger.
#[derive(Debug, Clone)]
pub struct X86ReleaseTrigger {
pub types: Vec<String>,
}
/// A pipeline stage.
#[derive(Debug, Clone)]
pub struct X86PipelineStage {
pub name: String,
pub stage_type: X86StageType,
pub compilation: Option<X86CompileConfig>,
pub test_config: Option<X86TestConfig>,
pub dependencies: Vec<String>,
pub timeout_minutes: u32,
pub allow_failure: bool,
}
impl X86PipelineStage {
fn generate_logs(&self) -> Vec<String> {
let mut logs = Vec::new();
logs.push(format!(
"[{}] Stage '{}' - Type: {:?}",
chrono_like_now_str(),
self.name,
self.stage_type
));
if self.compilation.is_some() {
logs.push(format!(
"[{}] Compilation step completed for stage '{}'.",
chrono_like_now_str(),
self.name
));
}
if self.test_config.is_some() {
logs.push(format!(
"[{}] Test step completed for stage '{}'.",
chrono_like_now_str(),
self.name
));
}
logs
}
}
/// Stage type enumeration.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86StageType {
Build,
Test,
Analyze,
Deploy,
Notify,
Custom,
}
// ═══════════════════════════════════════════════════════════════════════════════
// Architecture Variants
// ═══════════════════════════════════════════════════════════════════════════════
/// X86 architecture variants for build matrices.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86ArchVariant {
/// 32-bit x86 (i386/i686).
I386,
/// Baseline x86-64.
X86_64,
/// x86-64 with SSE2 minimum.
X86_64_SSE2,
/// x86-64-v2 (AVX, SSE4.2, POPCNT).
X86_64_V2,
/// x86-64-v3 (AVX2, BMI, FMA).
X86_64_V3,
/// x86-64-v4 (AVX-512).
X86_64_V4,
}
impl X86ArchVariant {
/// Get the target triple for this architecture.
pub fn target_triple(&self) -> &'static str {
match self {
Self::I386 => "i686-unknown-linux-gnu",
Self::X86_64
| Self::X86_64_SSE2
| Self::X86_64_V2
| Self::X86_64_V3
| Self::X86_64_V4 => "x86_64-unknown-linux-gnu",
}
}
/// Get the -march flag for this architecture.
pub fn march_flag(&self) -> &'static str {
match self {
Self::I386 => "i686",
Self::X86_64 => "x86-64",
Self::X86_64_SSE2 => "x86-64",
Self::X86_64_V2 => "x86-64-v2",
Self::X86_64_V3 => "x86-64-v3",
Self::X86_64_V4 => "x86-64-v4",
}
}
/// Get recommended default CFLAGS.
pub fn default_cflags(&self) -> &'static str {
match self {
Self::I386 => "-m32 -march=i686",
Self::X86_64 => "-m64 -march=x86-64",
Self::X86_64_SSE2 => "-m64 -march=x86-64 -msse2",
Self::X86_64_V2 => "-m64 -march=x86-64-v2",
Self::X86_64_V3 => "-m64 -march=x86-64-v3",
Self::X86_64_V4 => "-m64 -march=x86-64-v4 -mavx512f",
}
}
/// Get the number of bits (32 or 64).
pub fn bits(&self) -> u32 {
match self {
Self::I386 => 32,
_ => 64,
}
}
}
impl fmt::Display for X86ArchVariant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.march_flag())
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// 3. X86BuildMatrix — Build Matrix Generation
// ═══════════════════════════════════════════════════════════════════════════════
/// Build matrix for X86 Clang CI pipelines.
///
/// Supports matrices across: OS × Compiler × BuildType × Sanitizer ×
/// Architecture variant × Optimization level × C++ standard.
#[derive(Debug, Clone)]
pub struct X86BuildMatrix {
/// Operating systems in the matrix.
pub operating_systems: Vec<String>,
/// Compilers in the matrix (clang, gcc, msvc, icc, etc.).
pub compilers: Vec<String>,
/// Build types (Debug, Release, RelWithDebInfo, MinSizeRel).
pub build_types: Vec<String>,
/// Feature flags.
pub features: Vec<String>,
/// Sanitizers.
pub sanitizers: Vec<String>,
/// Architecture variants.
pub arch_variants: Vec<String>,
}
impl X86BuildMatrix {
/// Calculate the total number of matrix combinations.
pub fn total_combinations(&self) -> usize {
let mut total = 1;
if !self.operating_systems.is_empty() {
total *= self.operating_systems.len();
}
if !self.compilers.is_empty() {
total *= self.compilers.len();
}
if !self.build_types.is_empty() {
total *= self.build_types.len();
}
if !self.sanitizers.is_empty() {
total *= self.sanitizers.len();
}
if !self.arch_variants.is_empty() {
total *= self.arch_variants.len();
}
total
}
/// Check if the matrix is too large.
pub fn is_too_large(&self) -> bool {
self.total_combinations() > X86_CI_MAX_MATRIX_SIZE
}
/// Generate include/exclude rules for the matrix.
pub fn generate_rules(&self) -> Vec<X86MatrixRule> {
let mut rules = Vec::new();
// Add includes for all OS × compiler × build-type combos
for os in &self.operating_systems {
for compiler in &self.compilers {
for bt in &self.build_types {
rules.push(X86MatrixRule::Include {
os: os.clone(),
compiler: compiler.clone(),
build_type: bt.clone(),
sanitizer: None,
});
}
}
}
// Exclude incompatible combinations (e.g., MSVC on Linux)
for os in &self.operating_systems {
for compiler in &self.compilers {
if os.contains("linux") && (compiler == "msvc" || compiler == "icc") {
rules.push(X86MatrixRule::Exclude {
os: os.clone(),
compiler: compiler.clone(),
build_type: String::new(),
});
}
if os.contains("windows") && compiler == "clang" {
// Clang on Windows is fine, but don't exclude
}
}
}
// Exclude TSan on non-Linux platforms
if self.sanitizers.contains(&"tsan".to_string()) {
for os in &self.operating_systems {
if !os.contains("linux") {
rules.push(X86MatrixRule::Exclude {
os: os.clone(),
compiler: String::new(),
build_type: String::new(),
});
}
}
}
rules
}
}
/// Matrix include/exclude rule.
#[derive(Debug, Clone)]
pub enum X86MatrixRule {
/// Include a specific combination.
Include {
os: String,
compiler: String,
build_type: String,
sanitizer: Option<String>,
},
/// Exclude a specific combination.
Exclude {
os: String,
compiler: String,
build_type: String,
},
}
// ═══════════════════════════════════════════════════════════════════════════════
// Compilation Strategy
// ═══════════════════════════════════════════════════════════════════════════════
/// Compilation strategy for X86-targeted Clang builds.
#[derive(Debug, Clone)]
pub struct X86CompilationStrategy {
/// Build system to use.
pub build_system: X86BuildSystem,
/// Build type (Debug, Release, etc.).
pub build_type: X86BuildType,
/// Optimization level.
pub optimization: X86OptLevel,
/// Link-Time Optimization mode.
pub lto: X86LTOMode,
/// Profile-Guided Optimization config.
pub pgo: Option<X86PGOConfig>,
/// Enabled sanitizers.
pub sanitizers: Vec<X86Sanitizer>,
/// Fuzzer configuration.
pub fuzzer: Option<X86FuzzerConfig>,
/// Cross-compilation target (triple).
pub cross_compile: Option<String>,
/// Enable incremental compilation.
pub incremental: bool,
/// Extra C compiler flags.
pub cflags: Vec<String>,
/// Extra C++ compiler flags.
pub cxxflags: Vec<String>,
/// Extra linker flags.
pub ldflags: Vec<String>,
/// Number of parallel jobs.
pub parallel_jobs: u32,
/// Enable ccache.
pub ccache: bool,
/// Enable sccache.
pub sccache: bool,
/// Enable multi-config generator.
pub multi_config: bool,
/// Build shared libraries.
pub build_shared: bool,
/// Build static libraries.
pub build_static: bool,
/// C++ standard to use.
pub cxx_standard: Option<String>,
}
impl Default for X86CompilationStrategy {
fn default() -> Self {
Self {
build_system: X86BuildSystem::CMake,
build_type: X86BuildType::Release,
optimization: X86OptLevel::O2,
lto: X86LTOMode::None,
pgo: None,
sanitizers: vec![],
fuzzer: None,
cross_compile: None,
incremental: false,
cflags: vec![],
cxxflags: vec![],
ldflags: vec![],
parallel_jobs: X86_CI_DEFAULT_PARALLEL,
ccache: true,
sccache: false,
multi_config: false,
build_shared: true,
build_static: true,
cxx_standard: Some("c++17".to_string()),
}
}
}
impl X86CompilationStrategy {
/// Debug build with full symbols.
pub fn debug_full() -> Self {
Self {
build_type: X86BuildType::Debug,
build_system: X86BuildSystem::Ninja,
optimization: X86OptLevel::O0,
lto: X86LTOMode::None,
sanitizers: vec![X86Sanitizer::Address, X86Sanitizer::Undefined],
..Default::default()
}
}
/// Release build with LTO and PGO.
pub fn release_lto() -> Self {
Self {
build_type: X86BuildType::Release,
build_system: X86BuildSystem::Ninja,
optimization: X86OptLevel::O3,
lto: X86LTOMode::Thin,
pgo: Some(X86PGOConfig {
stage: X86PGOStage::Use,
profile_dir: "/tmp/pgo-profiles".to_string(),
train_data: Some("benchmark-suite".to_string()),
use_existing: true,
}),
..Default::default()
}
}
/// Build with all sanitizers enabled.
pub fn sanitizer_build(sanitizers: Vec<X86Sanitizer>) -> Self {
Self {
build_type: X86BuildType::Debug,
build_system: X86BuildSystem::Ninja,
optimization: X86OptLevel::O1,
sanitizers,
..Default::default()
}
}
/// PGO-instrumented build.
pub fn pgo_build(stage: X86PGOStage) -> Self {
Self {
build_type: X86BuildType::RelWithDebInfo,
build_system: X86BuildSystem::Ninja,
optimization: X86OptLevel::O2,
pgo: Some(X86PGOConfig {
stage,
profile_dir: "/tmp/pgo-profiles".to_string(),
train_data: Some("benchmark-suite".to_string()),
use_existing: stage == X86PGOStage::Use,
}),
..Default::default()
}
}
/// LibFuzzer-instrumented build.
pub fn fuzzer_build(target: String) -> Self {
Self {
build_type: X86BuildType::Debug,
build_system: X86BuildSystem::CMake,
optimization: X86OptLevel::O1,
sanitizers: vec![X86Sanitizer::Address, X86Sanitizer::Undefined],
fuzzer: Some(X86FuzzerConfig {
fuzzer_type: X86FuzzerType::LibFuzzer,
target,
corpus_dir: "/tmp/fuzz-corpus".to_string(),
max_len: 4096,
timeout_secs: 30,
max_total_time_secs: X86_CI_FUZZ_DEFAULT_DURATION,
}),
..Default::default()
}
}
/// Cross-compilation build.
pub fn cross_compile(target_triple: String) -> Self {
Self {
build_type: X86BuildType::Release,
build_system: X86BuildSystem::CMake,
cross_compile: Some(target_triple),
..Default::default()
}
}
/// Generate CMake flags from the compilation strategy.
pub fn to_cmake_flags(&self) -> Vec<String> {
let mut flags: Vec<String> = Vec::new();
// Build type
flags.push(format!(
"-DCMAKE_BUILD_TYPE={}",
self.build_type.cmake_name()
));
// C/C++ compilers
flags.push("-DCMAKE_C_COMPILER=clang".to_string());
flags.push("-DCMAKE_CXX_COMPILER=clang++".to_string());
// Optimization
flags.push(format!(
"-DCMAKE_C_FLAGS_RELEASE=-{}",
self.optimization.as_flag()
));
flags.push(format!(
"-DCMAKE_CXX_FLAGS_RELEASE=-{}",
self.optimization.as_flag()
));
// LTO
match self.lto {
X86LTOMode::None => {}
X86LTOMode::Full => {
flags.push("-DENABLE_LTO=ON".to_string());
flags.push("-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON".to_string());
}
X86LTOMode::Thin => {
flags.push("-DENABLE_LTO=THIN".to_string());
flags.push("-DCMAKE_INTERPROCEDURAL_OPTIMIZATION=ON".to_string());
}
}
// Sanitizers
if !self.sanitizers.is_empty() {
let san_flags: Vec<String> = self.sanitizers.iter().map(|s| s.clang_flag()).collect();
flags.push(format!(
"-DCMAKE_C_FLAGS=-fsanitize={} {}",
san_flags.join(","),
self.cflags.join(" ")
));
flags.push(format!(
"-DCMAKE_CXX_FLAGS=-fsanitize={} {}",
san_flags.join(","),
self.cxxflags.join(" ")
));
} else {
if !self.cflags.is_empty() {
flags.push(format!("-DCMAKE_C_FLAGS={}", self.cflags.join(" ")));
}
if !self.cxxflags.is_empty() {
flags.push(format!("-DCMAKE_CXX_FLAGS={}", self.cxxflags.join(" ")));
}
}
// Linker flags
if !self.ldflags.is_empty() {
flags.push(format!(
"-DCMAKE_EXE_LINKER_FLAGS={}",
self.ldflags.join(" ")
));
flags.push(format!(
"-DCMAKE_SHARED_LINKER_FLAGS={}",
self.ldflags.join(" ")
));
}
// Cross-compilation
if let Some(ref triple) = self.cross_compile {
flags.push(format!("-DCMAKE_SYSTEM_NAME=Linux"));
flags.push(format!("-DCMAKE_SYSTEM_PROCESSOR=x86_64"));
flags.push(format!("-DCMAKE_C_COMPILER_TARGET={}", triple));
flags.push(format!("-DCMAKE_CXX_COMPILER_TARGET={}", triple));
}
// C++ standard
if let Some(ref std) = self.cxx_standard {
flags.push(format!(
"-DCMAKE_CXX_STANDARD={}",
std.trim_start_matches("c++")
));
flags.push(format!("-DCMAKE_CXX_STANDARD_REQUIRED=ON"));
}
// Fuzzer
if self.fuzzer.is_some() {
flags.push("-DENABLE_FUZZING=ON".to_string());
if let Some(ref fuzz) = self.fuzzer {
if let Some(fuzz_flag) = fuzz.fuzzer_type.clang_flag() {
flags.push(format!("-DCMAKE_C_FLAGS=-fsanitize=fuzzer,{}", fuzz_flag));
flags.push(format!("-DCMAKE_CXX_FLAGS=-fsanitize=fuzzer,{}", fuzz_flag));
}
}
}
// Parallel
flags.push(format!(
"-DCMAKE_BUILD_PARALLEL_LEVEL={}",
self.parallel_jobs
));
// Shared/static
if !self.build_shared {
flags.push("-DBUILD_SHARED_LIBS=OFF".to_string());
}
if !self.build_static {
flags.push("-DBUILD_STATIC_LIBS=OFF".to_string());
}
flags
}
fn c_compiler(&self) -> &'static str {
"clang"
}
fn cxx_compiler(&self) -> &'static str {
"clang++"
}
/// Simulate a compilation run and return the result.
pub fn simulate_compile(&self, config: &X86CompileConfig) -> X86CompileResult {
let start = SystemTime::now();
let duration = start.elapsed().unwrap_or(Duration::from_millis(500));
let warnings = vec![X86CompileWarning {
message: "-Wunused-variable: 'x' defined but not used".to_string(),
file: "src/main.cpp".to_string(),
line: 42,
category: "UnusedVariable".to_string(),
}];
let success = self.sanitizers.is_empty()
|| !self.sanitizers.contains(&X86Sanitizer::Memory)
|| !config.build_type.contains("MinSizeRel");
X86CompileResult {
success,
duration,
warnings: if success { warnings } else { vec![] },
errors: if success {
vec![]
} else {
vec![X86CompileError {
message: "error: use of undeclared identifier 'undefined_var'".to_string(),
file: "src/main.cpp".to_string(),
line: 100,
column: 5,
error_code: Some("undeclared_var_use".to_string()),
}]
},
object_files: if success { 42 } else { 0 },
binary_size_bytes: if success { 15_000_000 } else { 0 },
optimization_level: self.optimization.as_flag().to_string(),
build_type: self.build_type.cmake_name().to_string(),
target_triple: "x86_64-unknown-linux-gnu".to_string(),
}
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// Build System / Build Type / Optimization Level Enums
// ═══════════════════════════════════════════════════════════════════════════════
/// Supported build systems for X86 Clang.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86BuildSystem {
CMake,
Ninja,
NinjaMultiConfig,
Make,
Bazel,
Meson,
XMake,
}
impl X86BuildSystem {
pub fn default_generator(&self) -> &'static str {
match self {
Self::CMake => "Unix Makefiles",
Self::Ninja | Self::NinjaMultiConfig => "Ninja",
Self::Make => "Unix Makefiles",
Self::Bazel => "Bazel",
Self::Meson => "Meson",
Self::XMake => "XMake",
}
}
}
/// Build type for compilation.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86BuildType {
Debug,
Release,
RelWithDebInfo,
MinSizeRel,
Custom,
}
impl X86BuildType {
pub fn cmake_name(&self) -> &'static str {
match self {
Self::Debug => "Debug",
Self::Release => "Release",
Self::RelWithDebInfo => "RelWithDebInfo",
Self::MinSizeRel => "MinSizeRel",
Self::Custom => "Custom",
}
}
pub fn has_debug_info(&self) -> bool {
matches!(self, Self::Debug | Self::RelWithDebInfo)
}
pub fn optimization_flag(&self) -> &'static str {
match self {
Self::Debug => "-O0",
Self::Release => "-O3",
Self::RelWithDebInfo => "-O2",
Self::MinSizeRel => "-Os",
Self::Custom => "-O2",
}
}
}
/// Optimization level for Clang.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86OptLevel {
O0,
O1,
O2,
O3,
Os,
Oz,
}
impl X86OptLevel {
pub fn as_flag(&self) -> &'static str {
match self {
Self::O0 => "O0",
Self::O1 => "O1",
Self::O2 => "O2",
Self::O3 => "O3",
Self::Os => "Os",
Self::Oz => "Oz",
}
}
}
/// Link-Time Optimization mode.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86LTOMode {
None,
Full,
Thin,
}
impl X86LTOMode {
pub fn clang_flag(&self) -> &'static str {
match self {
Self::None => "",
Self::Full => "-flto",
Self::Thin => "-flto=thin",
}
}
pub fn linker_plugin(&self) -> bool {
matches!(self, Self::Full | Self::Thin)
}
}
/// PGO configuration.
#[derive(Debug, Clone)]
pub struct X86PGOConfig {
pub stage: X86PGOStage,
pub profile_dir: String,
pub train_data: Option<String>,
pub use_existing: bool,
}
/// PGO stage.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86PGOStage {
Instrument,
Generate,
Use,
}
/// Sanitizer options for X86 builds.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum X86Sanitizer {
Address,
Undefined,
Thread,
Memory,
Leak,
HWAddress,
DataFlow,
SafeStack,
CFI,
}
impl X86Sanitizer {
pub fn clang_flag(&self) -> &'static str {
match self {
Self::Address => "address",
Self::Undefined => "undefined",
Self::Thread => "thread",
Self::Memory => "memory",
Self::Leak => "leak",
Self::HWAddress => "hwaddress",
Self::DataFlow => "dataflow",
Self::SafeStack => "safe-stack",
Self::CFI => "cfi",
}
}
pub fn cmake_flag(&self) -> &'static str {
match self {
Self::Address => "USE_SANITIZER_ADDRESS",
Self::Undefined => "USE_SANITIZER_UNDEFINED",
Self::Thread => "USE_SANITIZER_THREAD",
Self::Memory => "USE_SANITIZER_MEMORY",
Self::Leak => "USE_SANITIZER_LEAK",
Self::HWAddress => "USE_SANITIZER_HWADDRESS",
Self::DataFlow => "USE_SANITIZER_DF",
Self::SafeStack => "USE_SANITIZER_SAFESTACK",
Self::CFI => "USE_SANITIZER_CFI",
}
}
pub fn display_name(&self) -> &'static str {
match self {
Self::Address => "AddressSanitizer (ASan)",
Self::Undefined => "UndefinedBehaviorSanitizer (UBSan)",
Self::Thread => "ThreadSanitizer (TSan)",
Self::Memory => "MemorySanitizer (MSan)",
Self::Leak => "LeakSanitizer (LSan)",
Self::HWAddress => "HWAddressSanitizer (HWASan)",
Self::DataFlow => "DataFlowSanitizer (DFSan)",
Self::SafeStack => "SafeStack",
Self::CFI => "Control Flow Integrity (CFI)",
}
}
}
impl fmt::Display for X86Sanitizer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.clang_flag())
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// Fuzzer Configuration
// ═══════════════════════════════════════════════════════════════════════════════
/// Fuzzer configuration for CI.
#[derive(Debug, Clone)]
pub struct X86FuzzerConfig {
pub fuzzer_type: X86FuzzerType,
pub target: String,
pub corpus_dir: String,
pub max_len: u64,
pub timeout_secs: u64,
pub max_total_time_secs: u64,
}
/// Supported fuzzer types.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86FuzzerType {
LibFuzzer,
AFL,
AFLPlusPlus,
Honggfuzz,
}
impl X86FuzzerType {
pub fn clang_flag(&self) -> Option<&'static str> {
match self {
Self::LibFuzzer => Some("fuzzer-no-link"),
Self::AFL => None, // AFL uses afl-clang/afl-clang++
Self::AFLPlusPlus => None,
Self::Honggfuzz => None, // Honggfuzz uses hfuzz-clang
}
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// Compilation Configuration and Results
// ═══════════════════════════════════════════════════════════════════════════════
/// Compilation configuration for a specific build step.
#[derive(Debug, Clone)]
pub struct X86CompileConfig {
pub name: String,
pub build_system: X86BuildSystem,
pub build_type: String,
pub cmake_flags: Vec<String>,
pub source_dir: String,
pub build_dir: String,
pub install_dir: String,
}
impl Default for X86CompileConfig {
fn default() -> Self {
Self {
name: "default-build".to_string(),
build_system: X86BuildSystem::CMake,
build_type: "Release".to_string(),
cmake_flags: vec![],
source_dir: ".".to_string(),
build_dir: X86_CI_BUILD_DIR.to_string(),
install_dir: "install".to_string(),
}
}
}
/// Compilation result from a CI build step.
#[derive(Debug, Clone)]
pub struct X86CompileResult {
pub success: bool,
pub duration: Duration,
pub warnings: Vec<X86CompileWarning>,
pub errors: Vec<X86CompileError>,
pub object_files: u64,
pub binary_size_bytes: u64,
pub optimization_level: String,
pub build_type: String,
pub target_triple: String,
}
impl X86CompileResult {
pub fn success() -> Self {
Self {
success: true,
duration: Duration::from_secs(0),
warnings: vec![],
errors: vec![],
object_files: 0,
binary_size_bytes: 0,
optimization_level: "O2".to_string(),
build_type: "Release".to_string(),
target_triple: "x86_64-unknown-linux-gnu".to_string(),
}
}
}
/// Compilation warning.
#[derive(Debug, Clone)]
pub struct X86CompileWarning {
pub message: String,
pub file: String,
pub line: u32,
pub category: String,
}
/// Compilation error.
#[derive(Debug, Clone)]
pub struct X86CompileError {
pub message: String,
pub file: String,
pub line: u32,
pub column: u32,
pub error_code: Option<String>,
}
// ═══════════════════════════════════════════════════════════════════════════════
// Test Strategy and Configuration
// ═══════════════════════════════════════════════════════════════════════════════
/// Test strategy for X86 CI pipelines.
#[derive(Debug, Clone)]
pub struct X86TestStrategy {
/// Test configuration entries.
pub test_configs: Vec<X86TestConfig>,
/// Unit test framework.
pub unit_framework: X86TestFramework,
/// Integration test framework.
pub integration_framework: X86TestFramework,
/// Benchmark framework.
pub benchmark_framework: X86BenchmarkFramework,
/// Code coverage tool.
pub coverage_tool: X86CoverageTool,
/// Static analyzers to run.
pub static_analyzers: Vec<X86StaticAnalyzer>,
/// Dynamic analyzers to run.
pub dynamic_analyzers: Vec<X86DynamicAnalyzer>,
/// Fuzzing configuration.
pub fuzz_config: Option<X86FuzzTestConfig>,
/// Timeout for the test stage (minutes).
pub timeout_minutes: u32,
/// Number of parallel test jobs.
pub parallel_jobs: u32,
/// Whether to rerun failed tests.
pub rerun_failed: bool,
/// Whether to show output on failure.
pub output_on_failure: bool,
/// Test name filters.
pub test_filters: Vec<String>,
/// Test name exclusions.
pub test_exclude: Vec<String>,
}
impl Default for X86TestStrategy {
fn default() -> Self {
Self {
test_configs: vec![],
unit_framework: X86TestFramework::CTest,
integration_framework: X86TestFramework::CTest,
benchmark_framework: X86BenchmarkFramework::GoogleBenchmark,
coverage_tool: X86CoverageTool::None,
static_analyzers: vec![X86StaticAnalyzer::ClangTidy],
dynamic_analyzers: vec![],
fuzz_config: None,
timeout_minutes: X86_CI_JOB_TIMEOUT_MINUTES,
parallel_jobs: X86_CI_DEFAULT_PARALLEL,
rerun_failed: true,
output_on_failure: true,
test_filters: vec![],
test_exclude: vec![],
}
}
}
impl X86TestStrategy {
/// Create a comprehensive test strategy.
pub fn comprehensive() -> Self {
Self {
unit_framework: X86TestFramework::GTest,
integration_framework: X86TestFramework::Catch2,
benchmark_framework: X86BenchmarkFramework::GoogleBenchmark,
coverage_tool: X86CoverageTool::LLVMCov,
static_analyzers: vec![
X86StaticAnalyzer::ClangTidy,
X86StaticAnalyzer::ClangAnalyzer,
X86StaticAnalyzer::CodeQL,
],
dynamic_analyzers: vec![
X86DynamicAnalyzer::ASan,
X86DynamicAnalyzer::UBSan,
X86DynamicAnalyzer::Valgrind,
],
rerun_failed: true,
output_on_failure: true,
parallel_jobs: 8,
..Default::default()
}
}
/// Simulate test execution and return results.
pub fn simulate_tests(&self, config: &X86TestConfig) -> X86TestResult {
let start = SystemTime::now();
let duration = start.elapsed().unwrap_or(Duration::from_millis(200));
let total: u32 = 150;
let failed: u32 = if config.name.contains("analysis") {
0
} else {
2
};
let skipped: u32 = if config.exclude.is_some() { 5 } else { 0 };
let test_suites = vec![
X86TestSuiteResult {
name: "CoreTests".to_string(),
total: 50,
passed: 49,
failed: 1,
skipped: 0,
duration: Duration::from_millis(1500),
test_cases: vec![
X86TestCaseResult::passed("test_addition"),
X86TestCaseResult::passed("test_subtraction"),
X86TestCaseResult::failed("test_overflow", "integer overflow detected"),
],
},
X86TestSuiteResult {
name: "X86TargetTests".to_string(),
total: 50,
passed: 48,
failed: 1,
skipped: 1,
duration: Duration::from_millis(2500),
test_cases: vec![
X86TestCaseResult::passed("test_x86_64_encoding"),
X86TestCaseResult::passed("test_avx2_simd"),
X86TestCaseResult::failed("test_avx512_scatter", "invalid opcode"),
],
},
X86TestSuiteResult {
name: "IntegrationTests".to_string(),
total: 50,
passed: 50,
failed: 0,
skipped: 0,
duration: Duration::from_millis(5000),
test_cases: vec![X86TestCaseResult::passed("test_full_compile_pipeline")],
},
];
X86TestResult {
config_name: config.name.clone(),
test_type: config.test_type,
total,
passed: total - failed - skipped,
failed,
skipped,
errors: 0,
duration,
test_suites,
coverage: Some(X86CoverageResult {
line_coverage: 82.5,
branch_coverage: 75.0,
function_coverage: 90.3,
total_lines: 15000,
covered_lines: 12375,
total_branches: 3000,
covered_branches: 2250,
total_functions: 500,
covered_functions: 451,
}),
}
}
/// Generate a CTest command.
pub fn to_ctest_command(&self) -> String {
let mut cmd = String::from("ctest");
cmd.push_str(&format!(" -j{}", self.parallel_jobs));
if self.output_on_failure {
cmd.push_str(" --output-on-failure");
}
if self.rerun_failed {
cmd.push_str(" --rerun-failed");
}
cmd
}
/// Generate test execution commands.
pub fn to_test_command(&self) -> String {
match self.unit_framework {
X86TestFramework::CTest => self.to_ctest_command(),
X86TestFramework::GTest => {
String::from("./build/test_runner --gtest_output=xml:test-results.xml")
}
X86TestFramework::Catch2 => {
String::from("./build/test_runner -r junit -o test-results.xml")
}
X86TestFramework::BoostTest => {
String::from("./build/test_runner --log_format=JUNIT --log_level=all")
}
X86TestFramework::Doctest => String::from("./build/test_runner -s"),
}
}
}
/// Test configuration for a specific test run.
#[derive(Debug, Clone)]
pub struct X86TestConfig {
pub name: String,
pub test_type: X86TestType,
pub framework: X86TestFramework,
pub working_dir: String,
pub extra_args: Vec<String>,
pub timeout_secs: u64,
pub filter: Option<String>,
pub exclude: Option<String>,
}
impl Default for X86TestConfig {
fn default() -> Self {
Self {
name: "default-tests".to_string(),
test_type: X86TestType::Unit,
framework: X86TestFramework::CTest,
working_dir: "build".to_string(),
extra_args: vec![],
timeout_secs: 1800,
filter: None,
exclude: None,
}
}
}
impl X86TestConfig {
/// Create analysis-only test config.
pub fn analysis_only() -> Self {
Self {
name: "static-analysis".to_string(),
test_type: X86TestType::Analysis,
framework: X86TestFramework::CTest,
working_dir: "build".to_string(),
extra_args: vec![],
timeout_secs: 3600,
filter: Some("analysis".to_string()),
exclude: Some("benchmark".to_string()),
}
}
}
/// Test type categories.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86TestType {
Unit,
Integration,
Benchmark,
Fuzz,
Coverage,
Analysis,
Performance,
Smoke,
Regression,
}
/// Test frameworks.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86TestFramework {
CTest,
GTest,
Catch2,
BoostTest,
Doctest,
}
/// Benchmark frameworks.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86BenchmarkFramework {
GoogleBenchmark,
Catch2,
Criterion,
Nonius,
Celero,
}
/// Code coverage tools.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86CoverageTool {
None,
GCov,
LCov,
LLVMCov,
SanCov,
Codecov,
Coveralls,
}
impl X86CoverageTool {
/// Get compiler flags for the coverage tool.
pub fn flags(&self) -> &'static str {
match self {
Self::None => "",
Self::GCov => "--coverage",
Self::LCov => "--coverage",
Self::LLVMCov => "-fprofile-instr-generate -fcoverage-mapping",
Self::SanCov => "-fsanitize-coverage=trace-pc-guard",
Self::Codecov => "-fprofile-instr-generate -fcoverage-mapping",
Self::Coveralls => "--coverage",
}
}
/// Get the report generation command.
pub fn report_command(&self) -> Option<&'static str> {
match self {
Self::None => None,
Self::GCov => Some("gcov *.gcda"),
Self::LCov => Some("lcov --capture --directory . --output-file coverage.info && genhtml coverage.info -o coverage-html"),
Self::LLVMCov => Some("llvm-cov show -instr-profile=default.profdata -object ./build/test_runner -format=html -o coverage-html"),
Self::SanCov => Some("sancov -html-report *.sancov > coverage.html"),
Self::Codecov => Some("bash <(curl -s https://codecov.io/bash)"),
Self::Coveralls => Some("coveralls-lcov coverage.info"),
}
}
}
/// Static analyzers.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86StaticAnalyzer {
ClangTidy,
ClangAnalyzer,
Cppcheck,
PVSStudio,
Coverity,
SonarQube,
CodeQL,
Infer,
}
impl X86StaticAnalyzer {
/// Get the command to run the static analyzer.
pub fn command(&self) -> &'static str {
match self {
Self::ClangTidy => "run-clang-tidy -p build -export-fixes analysis.yaml",
Self::ClangAnalyzer => "scan-build cmake --build build",
Self::Cppcheck => "cppcheck --enable=all --xml --xml-version=2 src/ 2> cppcheck.xml",
Self::PVSStudio => "pvs-studio-analyzer analyze -o pvs.log && plog-converter -t xml -o pvs.xml pvs.log",
Self::Coverity => "cov-build --dir cov-int cmake --build build",
Self::SonarQube => "sonar-scanner -Dsonar.projectKey=my-project",
Self::CodeQL => "codeql database create codeql-db --language=cpp && codeql database analyze codeql-db --format=sarif-latest --output=codeql.sarif",
Self::Infer => "infer run -- cmake --build build",
}
}
/// Get the report format for the analyzer.
pub fn report_format(&self) -> &'static str {
match self {
Self::ClangTidy => "yaml",
Self::ClangAnalyzer => "html",
Self::Cppcheck => "xml",
Self::PVSStudio => "xml",
Self::Coverity => "html",
Self::SonarQube => "generic",
Self::CodeQL => "sarif",
Self::Infer => "json",
}
}
}
/// Dynamic analyzers.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86DynamicAnalyzer {
Valgrind,
ASan,
UBSan,
TSan,
MSan,
Helgrind,
DRD,
Massif,
Callgrind,
Cachegrind,
}
/// Fuzz test configuration.
#[derive(Debug, Clone)]
pub struct X86FuzzTestConfig {
pub fuzzer_type: X86FuzzerType,
pub targets: Vec<String>,
pub corpus_dir: String,
pub artifacts_dir: String,
pub max_len: u64,
pub timeout_secs: u64,
pub max_total_time_secs: u64,
pub runs: u64,
pub fork_count: u32,
}
impl Default for X86FuzzTestConfig {
fn default() -> Self {
Self {
fuzzer_type: X86FuzzerType::LibFuzzer,
targets: vec![],
corpus_dir: "/tmp/fuzz-corpus".to_string(),
artifacts_dir: "/tmp/fuzz-artifacts".to_string(),
max_len: 4096,
timeout_secs: 30,
max_total_time_secs: X86_CI_FUZZ_DEFAULT_DURATION,
runs: 100000,
fork_count: 4,
}
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// Test Results
// ═══════════════════════════════════════════════════════════════════════════════
/// Test result for a complete test run.
#[derive(Debug, Clone)]
pub struct X86TestResult {
pub config_name: String,
pub test_type: X86TestType,
pub total: u32,
pub passed: u32,
pub failed: u32,
pub skipped: u32,
pub errors: u32,
pub duration: Duration,
pub test_suites: Vec<X86TestSuiteResult>,
pub coverage: Option<X86CoverageResult>,
}
impl Default for X86TestResult {
fn default() -> Self {
Self {
config_name: String::new(),
test_type: X86TestType::Unit,
total: 0,
passed: 0,
failed: 0,
skipped: 0,
errors: 0,
duration: Duration::from_secs(0),
test_suites: vec![],
coverage: None,
}
}
}
impl X86TestResult {
/// Calculate the pass rate as a percentage.
pub fn pass_rate(&self) -> f64 {
if self.total == 0 {
return 100.0;
}
(self.passed as f64 / self.total as f64) * 100.0
}
/// Check if all tests passed.
pub fn all_passed(&self) -> bool {
self.failed == 0 && self.errors == 0
}
}
/// Test suite result.
#[derive(Debug, Clone)]
pub struct X86TestSuiteResult {
pub name: String,
pub total: u32,
pub passed: u32,
pub failed: u32,
pub skipped: u32,
pub duration: Duration,
pub test_cases: Vec<X86TestCaseResult>,
}
/// Individual test case result.
#[derive(Debug, Clone)]
pub struct X86TestCaseResult {
pub name: String,
pub classname: Option<String>,
pub passed: bool,
pub skipped: bool,
pub duration: Duration,
pub error_message: Option<String>,
pub stack_trace: Option<String>,
pub file: Option<String>,
pub line: Option<u32>,
}
impl X86TestCaseResult {
/// Create a passed test case.
pub fn passed(name: &str) -> Self {
Self {
name: name.to_string(),
classname: None,
passed: true,
skipped: false,
duration: Duration::from_millis(10),
error_message: None,
stack_trace: None,
file: None,
line: None,
}
}
/// Create a failed test case.
pub fn failed(name: &str, error: &str) -> Self {
Self {
name: name.to_string(),
classname: None,
passed: false,
skipped: false,
duration: Duration::from_millis(50),
error_message: Some(error.to_string()),
stack_trace: Some(format!(" at {} line 42", name)),
file: Some(format!("tests/{}.cpp", name.to_lowercase())),
line: Some(42),
}
}
/// Create a skipped test case.
pub fn skipped(name: &str) -> Self {
Self {
name: name.to_string(),
classname: None,
passed: false,
skipped: true,
duration: Duration::from_millis(0),
error_message: None,
stack_trace: None,
file: None,
line: None,
}
}
}
/// Code coverage result.
#[derive(Debug, Clone)]
pub struct X86CoverageResult {
pub line_coverage: f64,
pub branch_coverage: f64,
pub function_coverage: f64,
pub total_lines: u64,
pub covered_lines: u64,
pub total_branches: u64,
pub covered_branches: u64,
pub total_functions: u64,
pub covered_functions: u64,
}
impl X86CoverageResult {
/// Check if coverage meets the threshold.
pub fn meets_threshold(&self, threshold: f64) -> bool {
self.line_coverage >= threshold
}
/// Get a human-readable summary.
pub fn summary(&self) -> String {
format!(
"Lines: {:.1}% ({}/{}), Branches: {:.1}% ({}/{}), Functions: {:.1}% ({}/{})",
self.line_coverage,
self.covered_lines,
self.total_lines,
self.branch_coverage,
self.covered_branches,
self.total_branches,
self.function_coverage,
self.covered_functions,
self.total_functions,
)
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// 4. X86CITestIntegration — Test Integration
// ═══════════════════════════════════════════════════════════════════════════════
/// CI test integration module providing CTest/JUnit XML parsing and reporting,
/// code coverage (gcov/lcov, llvm-cov, source-based), performance regression
/// testing, flaky test detection and quarantine, and test result visualization.
#[derive(Debug, Clone)]
pub struct X86CITestIntegration {
/// Test result storage from all runs.
pub test_results_history: Vec<X86TestResult>,
/// Flaky test quarantine list.
pub quarantined_tests: HashMap<String, X86QuarantinedTest>,
/// Performance baseline for regression detection.
pub performance_baseline: Option<X86PerformanceBaseline>,
/// Coverage tool configuration.
pub coverage_tool: X86CoverageTool,
/// JUnit XML report paths.
pub junit_report_paths: Vec<String>,
/// CTest XML report paths.
pub ctest_report_paths: Vec<String>,
/// Whether to generate HTML test reports.
pub generate_html_report: bool,
/// Flaky detection threshold (consecutive failures).
pub flaky_threshold: u32,
/// Quarantine duration in days.
pub quarantine_days: u32,
}
impl Default for X86CITestIntegration {
fn default() -> Self {
Self {
test_results_history: Vec::new(),
quarantined_tests: HashMap::new(),
performance_baseline: None,
coverage_tool: X86CoverageTool::LLVMCov,
junit_report_paths: vec!["build/test-results/*.xml".to_string()],
ctest_report_paths: vec!["build/Testing/**/Test.xml".to_string()],
generate_html_report: true,
flaky_threshold: X86_CI_FLAKY_THRESHOLD,
quarantine_days: X86_CI_QUARANTINE_DAYS,
}
}
}
impl X86CITestIntegration {
/// Record a new test run result.
pub fn record_test_run(&mut self, result: X86TestResult) {
self.detect_flaky_tests(&result);
self.check_performance_regression(&result);
self.test_results_history.push(result);
}
/// Detect flaky tests from the latest test result.
///
/// A test is considered flaky if it alternates between pass and fail
/// across consecutive runs, or if it fails intermittently.
pub fn detect_flaky_tests(&mut self, result: &X86TestResult) {
let mut flaky_candidates: HashMap<String, u32> = HashMap::new();
// Track failures from current run
for suite in &result.test_suites {
for case in &suite.test_cases {
if !case.passed && !case.skipped {
let key = format!("{}::{}", suite.name, case.name);
*flaky_candidates.entry(key).or_insert(0) += 1;
}
}
}
// Check against history to identify flaky tests
for (test_name, failure_count) in &flaky_candidates {
// Check if this test passed in previous runs
let mut previous_passes = 0;
for prev_result in self.test_results_history.iter().rev().take(10) {
for suite in &prev_result.test_suites {
for case in &suite.test_cases {
let prev_key = format!("{}::{}", suite.name, case.name);
if prev_key == *test_name && case.passed {
previous_passes += 1;
}
}
}
}
// If the test has both passes and failures, it's potentially flaky
if previous_passes > 0 && *failure_count >= self.flaky_threshold {
// Quarantine the test
if self.quarantined_tests.len() < X86_CI_MAX_QUARANTINE_SIZE {
self.quarantined_tests.insert(
test_name.clone(),
X86QuarantinedTest {
test_name: test_name.clone(),
quarantined_at: chrono_like_now(),
failure_count: *failure_count,
previous_passes,
reason: "Intermittent failures detected across runs".to_string(),
},
);
}
}
}
// Remove expired quarantines
let now = chrono_like_now();
let quarantine_duration = Duration::from_secs(86400 * self.quarantine_days as u64);
self.quarantined_tests.retain(|_, q| {
now.duration_since(UNIX_EPOCH).unwrap()
< q.quarantined_at.duration_since(UNIX_EPOCH).unwrap() + quarantine_duration
});
}
/// Check for performance regressions against the baseline.
pub fn check_performance_regression(&self, result: &X86TestResult) -> Vec<X86PerfRegression> {
let mut regressions = Vec::new();
if let Some(ref baseline) = self.performance_baseline {
for suite in &result.test_suites {
if let Some(baseline_duration) = baseline.suite_durations.get(&suite.name) {
let current = suite.duration.as_secs_f64();
let previous = baseline_duration.as_secs_f64();
if previous > 0.0 {
let change_pct = ((current - previous) / previous) * 100.0;
if change_pct > X86_CI_PERF_REGRESSION_THRESHOLD {
regressions.push(X86PerfRegression {
suite_name: suite.name.clone(),
previous_duration: *baseline_duration,
current_duration: suite.duration,
change_percent: change_pct,
threshold: X86_CI_PERF_REGRESSION_THRESHOLD,
});
}
}
}
}
}
regressions
}
/// Generate a JUnit XML report from test results.
pub fn generate_junit_xml(&self, results: &[X86TestResult]) -> String {
let mut xml = String::from("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
xml.push_str("<testsuites>\n");
let mut total_tests = 0u32;
let mut total_failures = 0u32;
let mut total_errors = 0u32;
let mut total_skipped = 0u32;
let mut total_time = 0.0f64;
for result in results {
for suite in &result.test_suites {
total_tests += suite.total;
total_failures += suite.failed;
total_errors += 0;
total_skipped += suite.skipped;
total_time += suite.duration.as_secs_f64();
xml.push_str(&format!(
" <testsuite name=\"{}\" tests=\"{}\" failures=\"{}\" errors=\"0\" skipped=\"{}\" time=\"{:.3}\">\n",
xml_escape(&suite.name),
suite.total,
suite.failed,
suite.skipped,
suite.duration.as_secs_f64()
));
for case in &suite.test_cases {
let classname = case.classname.as_deref().unwrap_or(&suite.name);
xml.push_str(&format!(
" <testcase classname=\"{}\" name=\"{}\" time=\"{:.3}\"",
xml_escape(classname),
xml_escape(&case.name),
case.duration.as_secs_f64()
));
if case.skipped {
xml.push_str(">\n <skipped/>\n </testcase>\n");
} else if !case.passed {
xml.push_str(">\n");
xml.push_str(&format!(
" <failure message=\"{}\">\n",
xml_escape(case.error_message.as_deref().unwrap_or("Unknown error"))
));
if let Some(ref stack) = case.stack_trace {
xml.push_str(&format!(" {}\n", xml_escape(stack)));
}
xml.push_str(" </failure>\n </testcase>\n");
} else {
xml.push_str(" />\n");
}
}
xml.push_str(" </testsuite>\n");
}
}
xml.push_str(&format!(
" <summary tests=\"{}\" failures=\"{}\" errors=\"{}\" skipped=\"{}\" time=\"{:.3}\"/>\n",
total_tests, total_failures, total_errors, total_skipped, total_time
));
xml.push_str("</testsuites>\n");
xml
}
/// Generate a CTest XML report.
pub fn generate_ctest_xml(&self, results: &[X86TestResult]) -> String {
let mut xml = String::from("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
xml.push_str("<Site BuildName=\"x86-clang-ci\">\n");
xml.push_str(" <Testing>\n");
for result in results {
xml.push_str(&format!(" <TestList>\n"));
for suite in &result.test_suites {
xml.push_str(&format!(" <Test>{}</Test>\n", xml_escape(&suite.name)));
}
xml.push_str(" </TestList>\n");
}
xml.push_str(" </Testing>\n");
xml.push_str("</Site>\n");
xml
}
/// Generate an HTML test result visualization.
pub fn generate_html_report(&self, results: &[X86TestResult]) -> String {
let mut html = String::from(
"<!DOCTYPE html>\n<html>\n<head>\n<title>X86 Clang CI Test Report</title>\n",
);
html.push_str("<style>\nbody{font-family:monospace;background:#1e1e2e;color:#cdd6f4;}\n");
html.push_str(".pass{color:#a6e3a1;}.fail{color:#f38ba8;}.skip{color:#f9e2af;}\n");
html.push_str("table{border-collapse:collapse;width:100%;}\n");
html.push_str("td,th{border:1px solid #45475a;padding:8px;text-align:left;}\n");
html.push_str("th{background:#313244;}\n</style>\n</head>\n<body>\n");
html.push_str("<h1>X86 Clang CI Test Report</h1>\n");
let mut grand_total = 0u32;
let mut grand_passed = 0u32;
let mut grand_failed = 0u32;
let mut grand_skipped = 0u32;
for result in results {
html.push_str(&format!(
"<h2>{} ({:?})</h2>\n",
result.config_name, result.test_type
));
html.push_str("<table>\n<tr><th>Suite</th><th>Total</th><th>Passed</th><th>Failed</th><th>Skipped</th><th>Time</th></tr>\n");
for suite in &result.test_suites {
grand_total += suite.total;
grand_passed += suite.passed;
grand_failed += suite.failed;
grand_skipped += suite.skipped;
let status_class = if suite.failed > 0 { "fail" } else { "pass" };
html.push_str(&format!(
"<tr class=\"{}\"><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{}</td><td>{:.1}s</td></tr>\n",
status_class,
xml_escape(&suite.name),
suite.total,
suite.passed,
suite.failed,
suite.skipped,
suite.duration.as_secs_f64()
));
}
html.push_str("</table>\n");
}
html.push_str(&format!(
"<hr>\n<p><strong>Total:</strong> {} tests, <span class=\"pass\">{} passed</span>, <span class=\"fail\">{} failed</span>, <span class=\"skip\">{} skipped</span></p>\n",
grand_total, grand_passed, grand_failed, grand_skipped
));
html.push_str("</body>\n</html>");
html
}
/// Get the list of currently quarantined (flaky) tests.
pub fn get_quarantined_tests(&self) -> Vec<&X86QuarantinedTest> {
self.quarantined_tests.values().collect()
}
/// Check if a test is quarantined.
pub fn is_quarantined(&self, test_name: &str) -> bool {
self.quarantined_tests.contains_key(test_name)
}
/// Remove a test from quarantine.
pub fn unquarantine(&mut self, test_name: &str) -> bool {
self.quarantined_tests.remove(test_name).is_some()
}
/// Set the performance baseline from a test result.
pub fn set_performance_baseline(&mut self, result: &X86TestResult) {
let mut suite_durations = BTreeMap::new();
for suite in &result.test_suites {
suite_durations.insert(suite.name.clone(), suite.duration);
}
self.performance_baseline = Some(X86PerformanceBaseline {
created_at: chrono_like_now(),
config_name: result.config_name.clone(),
suite_durations,
});
}
}
/// A quarantined (flaky) test entry.
#[derive(Debug, Clone)]
pub struct X86QuarantinedTest {
pub test_name: String,
pub quarantined_at: Duration,
pub failure_count: u32,
pub previous_passes: u32,
pub reason: String,
}
/// Performance baseline for regression detection.
#[derive(Debug, Clone)]
pub struct X86PerformanceBaseline {
pub created_at: Duration,
pub config_name: String,
pub suite_durations: BTreeMap<String, Duration>,
}
/// Performance regression result.
#[derive(Debug, Clone)]
pub struct X86PerfRegression {
pub suite_name: String,
pub previous_duration: Duration,
pub current_duration: Duration,
pub change_percent: f64,
pub threshold: f64,
}
impl X86PerfRegression {
/// Whether the regression is significant.
pub fn is_significant(&self) -> bool {
self.change_percent.abs() > self.threshold
}
/// Description of the regression.
pub fn describe(&self) -> String {
let direction = if self.change_percent > 0.0 {
"slower"
} else {
"faster"
};
format!(
"{}: {:.1}% {} ({:.1}s → {:.1}s)",
self.suite_name,
self.change_percent.abs(),
direction,
self.previous_duration.as_secs_f64(),
self.current_duration.as_secs_f64(),
)
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// Quality Gates
// ═══════════════════════════════════════════════════════════════════════════════
/// Quality gate for CI pipeline validation.
#[derive(Debug, Clone)]
pub struct X86QualityGate {
/// Display name.
pub name: String,
/// Type of quality gate.
pub gate_type: X86QualityGateType,
/// Threshold value.
pub threshold: f64,
/// Whether this gate blocks pipeline success.
pub blocking: bool,
/// Whether this gate is enabled.
pub enabled: bool,
}
impl X86QualityGate {
/// Create a compilation success gate.
pub fn compilation_success() -> Self {
Self {
name: "CompilationSuccess".to_string(),
gate_type: X86QualityGateType::Compilation,
threshold: 1.0,
blocking: true,
enabled: true,
}
}
/// Create a test pass rate gate.
pub fn test_pass_rate(threshold_pct: f64) -> Self {
Self {
name: "TestPassRate".to_string(),
gate_type: X86QualityGateType::TestPassRate,
threshold: threshold_pct,
blocking: true,
enabled: true,
}
}
/// Create a coverage threshold gate.
pub fn coverage_threshold(threshold_pct: f64) -> Self {
Self {
name: "CoverageThreshold".to_string(),
gate_type: X86QualityGateType::Coverage,
threshold: threshold_pct,
blocking: false,
enabled: true,
}
}
/// Create a static analysis errors gate.
pub fn static_analysis_errors(max_errors: f64) -> Self {
Self {
name: "StaticAnalysisErrors".to_string(),
gate_type: X86QualityGateType::StaticAnalysis,
threshold: max_errors,
blocking: false,
enabled: true,
}
}
/// Create a fuzzing coverage gate.
pub fn fuzzing_coverage(min_blocks_pct: f64) -> Self {
Self {
name: "FuzzingCoverage".to_string(),
gate_type: X86QualityGateType::Fuzzing,
threshold: min_blocks_pct,
blocking: false,
enabled: true,
}
}
/// Create a performance regression gate.
pub fn performance_regression(max_pct: f64) -> Self {
Self {
name: "PerformanceRegression".to_string(),
gate_type: X86QualityGateType::Performance,
threshold: max_pct,
blocking: false,
enabled: true,
}
}
/// Evaluate all quality gates against pipeline execution results.
pub fn evaluate(
&self,
jobs: &[X86CiJobExecution],
artifact_summary: &X86ArtifactSummary,
) -> Vec<X86QualityGateResult> {
let mut results = Vec::new();
// Compilation success
if self.gate_type == X86QualityGateType::Compilation && self.enabled {
let all_compiled = jobs
.iter()
.all(|j| j.compile_result.as_ref().map(|r| r.success).unwrap_or(true));
results.push(X86QualityGateResult {
gate_name: self.name.clone(),
gate_type: self.gate_type,
passed: all_compiled,
threshold: self.threshold,
actual_value: if all_compiled { 1.0 } else { 0.0 },
blocking: self.blocking,
});
}
// Test pass rate
if self.gate_type == X86QualityGateType::TestPassRate && self.enabled {
let mut total = 0u32;
let mut passed = 0u32;
for job in jobs {
if let Some(ref test_result) = job.test_result {
total += test_result.total;
passed += test_result.passed;
}
}
let pass_rate = if total > 0 {
(passed as f64 / total as f64) * 100.0
} else {
100.0
};
results.push(X86QualityGateResult {
gate_name: self.name.clone(),
gate_type: self.gate_type,
passed: pass_rate >= self.threshold,
threshold: self.threshold,
actual_value: pass_rate,
blocking: self.blocking,
});
}
results
}
}
/// Quality gate type.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86QualityGateType {
Compilation,
TestPassRate,
Coverage,
StaticAnalysis,
Fuzzing,
Performance,
BinarySize,
WarningCount,
SecurityScan,
}
/// Quality gate evaluation result.
#[derive(Debug, Clone)]
pub struct X86QualityGateResult {
pub gate_name: String,
pub gate_type: X86QualityGateType,
pub passed: bool,
pub threshold: f64,
pub actual_value: f64,
pub blocking: bool,
}
impl X86QualityGateResult {
/// Status emoji for the gate result.
pub fn status_emoji(&self) -> &'static str {
if self.passed {
"✅"
} else {
"❌"
}
}
/// Whether this is a blocking failure.
pub fn is_blocking_failure(&self) -> bool {
!self.passed && self.blocking
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// 5. X86CIArtifactManager — Artifact Management
// ═══════════════════════════════════════════════════════════════════════════════
/// CI artifact manager for X86 Clang builds.
///
/// Handles binary artifact packaging (.tar.gz, .zip, .deb, .rpm, .AppImage),
/// Docker image building and publishing, release draft/publish automation,
/// package registry publishing (GitHub Packages, GitLab Registry), symbol
/// server upload (debug symbols), and SBOM generation.
#[derive(Debug, Clone)]
pub struct X86ArtifactManager {
/// Base storage path for artifacts.
pub storage_path: String,
/// Retention period in days.
pub retention_days: u32,
/// Whether to compress artifacts.
pub compress: bool,
/// Binary artifact configuration.
pub binaries: X86BinaryArtifactConfig,
/// Symbol artifact configuration.
pub symbols: X86SymbolArtifactConfig,
/// Documentation artifact configuration.
pub documentation: X86DocArtifactConfig,
/// Coverage report configuration.
pub coverage_reports: X86CoverageReportConfig,
/// Test report configuration.
pub test_reports: X86TestReportConfig,
/// Analysis report configuration.
pub analysis_reports: X86AnalysisReportConfig,
/// Docker image configuration.
pub docker: X86DockerArtifactConfig,
/// Release configuration.
pub release: X86ReleaseConfig,
/// Package registry configuration.
pub package_registry: X86PackageRegistryConfig,
/// SBOM (Software Bill of Materials) configuration.
pub sbom: X86SBOMConfig,
/// Collected artifacts from pipeline runs.
pub collected: Vec<X86Artifact>,
}
impl Default for X86ArtifactManager {
fn default() -> Self {
Self {
storage_path: X86_CI_DEFAULT_ARTIFACT_DIR.to_string(),
retention_days: 90,
compress: true,
binaries: X86BinaryArtifactConfig::default(),
symbols: X86SymbolArtifactConfig::default(),
documentation: X86DocArtifactConfig::default(),
coverage_reports: X86CoverageReportConfig::default(),
test_reports: X86TestReportConfig::default(),
analysis_reports: X86AnalysisReportConfig::default(),
docker: X86DockerArtifactConfig::default(),
release: X86ReleaseConfig::default(),
package_registry: X86PackageRegistryConfig::default(),
sbom: X86SBOMConfig::default(),
collected: Vec::new(),
}
}
}
impl X86ArtifactManager {
/// Summarize collected artifacts.
pub fn summarize(&self) -> X86ArtifactSummary {
let mut summary = X86ArtifactSummary {
total_artifacts: 0,
total_size_bytes: 0,
binary_count: 0,
library_count: 0,
symbol_count: 0,
report_count: 0,
doc_count: 0,
};
for artifact in &self.collected {
summary.total_artifacts += 1;
summary.total_size_bytes += artifact.size_bytes;
match artifact.artifact_type {
X86ArtifactType::Binary => summary.binary_count += 1,
X86ArtifactType::Library => summary.library_count += 1,
X86ArtifactType::Symbols => summary.symbol_count += 1,
X86ArtifactType::Report
| X86ArtifactType::TestResults
| X86ArtifactType::AnalysisResults => summary.report_count += 1,
X86ArtifactType::Documentation => summary.doc_count += 1,
_ => {}
}
}
summary
}
/// Generate artifact path patterns for a stage.
pub fn generate_paths(&self, stage_name: &str) -> Vec<PathBuf> {
let base = PathBuf::from(&self.storage_path);
let mut paths = Vec::new();
let sanitized = stage_name.replace(' ', "-").to_lowercase();
let stage_dir = base.join(&sanitized);
if self.binaries.enabled {
paths.push(stage_dir.join("binaries"));
}
if self.symbols.enabled {
paths.push(stage_dir.join("symbols"));
}
if self.documentation.enabled {
paths.push(stage_dir.join("docs"));
}
if self.coverage_reports.enabled {
paths.push(stage_dir.join("coverage"));
}
if self.test_reports.enabled {
paths.push(stage_dir.join("test-reports"));
}
if self.analysis_reports.enabled {
paths.push(stage_dir.join("analysis"));
}
if self.docker.enabled {
paths.push(stage_dir.join("docker"));
}
if self.sbom.enabled {
paths.push(stage_dir.join("sbom"));
}
paths
}
/// Generate artifact upload steps for a CI pipeline.
pub fn generate_upload_steps(&self, ci_system: &X86CiSystem) -> Vec<String> {
let mut steps = Vec::new();
match ci_system {
X86CiSystem::GitHubActions => {
steps.push(format!(
"- uses: actions/upload-artifact@v4\n with:\n name: x86-clang-binaries\n path: {}",
self.binaries.path_pattern
));
if self.coverage_reports.enabled {
steps.push(format!(
"- uses: actions/upload-artifact@v4\n with:\n name: coverage-report\n path: {}",
self.coverage_reports.path_pattern
));
}
}
X86CiSystem::GitLabCI => {
steps.push(format!(
"artifacts:\n paths:\n - {}\n expire_in: {} days",
self.binaries.path_pattern, self.retention_days
));
}
_ => {
steps.push(format!(
"# Upload artifacts from: {}",
self.binaries.path_pattern
));
}
}
steps
}
/// Generate SBOM (Software Bill of Materials).
pub fn generate_sbom(&self) -> X86SBOMReport {
let now = chrono_like_now_str();
X86SBOMReport {
format: self.sbom.format.clone(),
spec_version: "2.3".to_string(),
created: now.clone(),
name: self.sbom.package_name.clone(),
version: self.sbom.package_version.clone(),
supplier: self.sbom.supplier.clone(),
components: self
.sbom
.dependencies
.iter()
.map(|dep| X86SBOMComponent {
name: dep.clone(),
version: self
.sbom
.dependency_versions
.get(dep)
.cloned()
.unwrap_or_else(|| "unknown".to_string()),
purl: format!("pkg:generic/{}", dep),
license: self.sbom.dependency_licenses.get(dep).cloned(),
supplier: self.sbom.supplier.clone(),
})
.collect(),
sha256: None,
}
}
/// Generate Docker build and publish steps.
pub fn generate_docker_steps(&self) -> Vec<String> {
if !self.docker.enabled {
return vec![];
}
let mut steps = Vec::new();
steps.push(format!(
"docker build -t {} -f {} .",
self.docker.image_name, self.docker.dockerfile
));
for tag in &self.docker.tags {
steps.push(format!(
"docker tag {} {}:{}",
self.docker.image_name, self.docker.image_name, tag
));
}
if self.docker.push_to_registry {
for tag in &self.docker.tags {
steps.push(format!("docker push {}:{}", self.docker.image_name, tag));
}
}
steps
}
/// Generate release creation steps.
pub fn generate_release_steps(&self, version: &str) -> Vec<String> {
if !self.release.enabled {
return vec![];
}
let mut steps = Vec::new();
// Create release archive
let archive_name = format!(
"{}-{}-x86_64-linux.tar.gz",
self.release.artifact_name, version
);
steps.push(format!("tar czf {} build/", archive_name));
// Generate release notes
if self.release.generate_release_notes {
steps.push(
"git log $(git describe --tags --abbrev=0)..HEAD --oneline > CHANGELOG.md"
.to_string(),
);
}
// Create GitHub/GitLab release
steps.push(format!(
"# Release {} — created by X86CI_CD\ngh release create {} {} --title \"Release {}\" --notes-file CHANGELOG.md",
version, version, archive_name, version
));
steps
}
/// Generate package registry publishing steps.
pub fn generate_package_registry_steps(&self) -> Vec<String> {
if !self.package_registry.enabled {
return vec![];
}
let mut steps = Vec::new();
match self.package_registry.registry_type {
X86PackageRegistryType::GitHubPackages => {
steps.push(format!(
"docker tag {} ghcr.io/{}/{}:latest",
self.docker.image_name,
self.package_registry.namespace,
self.package_registry.package_name
));
steps.push("echo ${{ secrets.GITHUB_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin".to_string());
steps.push(format!(
"docker push ghcr.io/{}/{}:latest",
self.package_registry.namespace, self.package_registry.package_name
));
}
X86PackageRegistryType::GitLabRegistry => {
steps.push(format!(
"docker tag {} registry.gitlab.com/{}/{}:latest",
self.docker.image_name,
self.package_registry.namespace,
self.package_registry.package_name
));
steps.push("docker login registry.gitlab.com -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD".to_string());
steps.push(format!(
"docker push registry.gitlab.com/{}/{}:latest",
self.package_registry.namespace, self.package_registry.package_name
));
}
X86PackageRegistryType::DockerHub => {
steps.push(format!(
"docker tag {} {}/{}:latest",
self.docker.image_name,
self.package_registry.namespace,
self.package_registry.package_name
));
steps.push("echo ${{ secrets.DOCKERHUB_TOKEN }} | docker login -u ${{ secrets.DOCKERHUB_USERNAME }} --password-stdin".to_string());
steps.push(format!(
"docker push {}/{}:latest",
self.package_registry.namespace, self.package_registry.package_name
));
}
X86PackageRegistryType::Custom => {}
}
steps
}
/// Generate symbol server upload steps.
pub fn generate_symbol_upload_steps(&self) -> Vec<String> {
if !self.symbols.enabled || !self.symbols.upload_to_server {
return vec![];
}
let mut steps = Vec::new();
// Extract debug symbols
steps.push("find build/ -name '*.so' -o -name '*.exe' | while read f; do objcopy --only-keep-debug \"$f\" \"$f.debug\"; objcopy --add-gnu-debuglink=\"$f.debug\" \"$f\"; done".to_string());
// Upload to symbol server
if let Some(ref url) = self.symbols.server_url {
steps.push(format!(
"find build/ -name '*.debug' -exec curl -X PUT {}/{{}} --data-binary @{{}} \\;",
url
));
}
steps
}
/// Generate DEB package build steps.
pub fn generate_deb_package_steps(&self, version: &str) -> Vec<String> {
let mut steps = Vec::new();
let name = "x86-clang-project";
steps.push("mkdir -p deb/DEBIAN deb/usr/local/bin deb/usr/local/lib".to_string());
steps.push("cp build/bin/* deb/usr/local/bin/".to_string());
steps.push("cp build/lib/*.so deb/usr/local/lib/".to_string());
// Generate control file
steps.push(format!(
"echo 'Package: {}\nVersion: {}\nArchitecture: amd64\nMaintainer: CI <ci@example.com>\nDescription: X86 Clang Project' > deb/DEBIAN/control",
name, version
));
steps.push(format!(
"dpkg-deb --build deb {}-{}-amd64.deb",
name, version
));
steps
}
/// Generate RPM package build steps.
pub fn generate_rpm_package_steps(&self, version: &str) -> Vec<String> {
let mut steps = Vec::new();
let name = "x86-clang-project";
steps.push(format!(
"mkdir -p ~/rpmbuild/{{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}}"
));
// Generate spec file
steps.push(format!(
"echo 'Name: {}\nVersion: {}\nRelease: 1\nSummary: X86 Clang Project\nLicense: MIT\nBuildArch: x86_64\n\n%description\nX86 Clang project built via CI.\n\n%files\n/usr/local/bin/*\n/usr/local/lib/*.so' > ~/rpmbuild/SPECS/{}.spec",
name, version, name
));
steps.push(format!("rpmbuild -bb ~/rpmbuild/SPECS/{}.spec", name));
steps
}
/// Generate AppImage packaging steps.
pub fn generate_appimage_steps(&self, version: &str) -> Vec<String> {
let mut steps = Vec::new();
let name = "x86-clang-project";
steps.push("mkdir -p AppDir/usr/bin AppDir/usr/lib".to_string());
steps.push("cp build/bin/* AppDir/usr/bin/".to_string());
steps.push("cp build/lib/*.so AppDir/usr/lib/".to_string());
// Create desktop entry and icon
steps.push(format!(
"echo '[Desktop Entry]\nName={}\nExec={}\nType=Application' > AppDir/{}.desktop",
name, name, name
));
steps.push(format!(
"wget -q https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage -O appimagetool && chmod +x appimagetool && ./appimagetool AppDir {}-{}-x86_64.AppImage",
name, version
));
steps
}
/// Generate npm package publish steps.
pub fn generate_npm_publish_steps(&self) -> Vec<String> {
if !self.package_registry.enabled {
return vec![];
}
let mut steps = Vec::new();
steps.push("npm config set registry https://registry.npmjs.org/".to_string());
steps.push("npm publish --access public".to_string());
steps
}
}
/// Binary artifact configuration.
#[derive(Debug, Clone)]
pub struct X86BinaryArtifactConfig {
pub enabled: bool,
pub path_pattern: String,
pub include_libraries: bool,
pub include_executables: bool,
pub strip_binaries: bool,
pub platform_label: String,
}
impl Default for X86BinaryArtifactConfig {
fn default() -> Self {
Self {
enabled: true,
path_pattern: "build/bin/*".to_string(),
include_libraries: true,
include_executables: true,
strip_binaries: false,
platform_label: "x86_64-linux".to_string(),
}
}
}
/// Symbol artifact configuration.
#[derive(Debug, Clone)]
pub struct X86SymbolArtifactConfig {
pub enabled: bool,
pub include_dsym: bool,
pub include_pdb: bool,
pub include_breakpad: bool,
pub upload_to_server: bool,
pub server_url: Option<String>,
}
impl Default for X86SymbolArtifactConfig {
fn default() -> Self {
Self {
enabled: true,
include_dsym: false,
include_pdb: false,
include_breakpad: true,
upload_to_server: false,
server_url: None,
}
}
}
/// Documentation artifact configuration.
#[derive(Debug, Clone)]
pub struct X86DocArtifactConfig {
pub enabled: bool,
pub path_pattern: String,
pub generate_doxygen: bool,
pub generate_sphinx: bool,
pub publish_to_pages: bool,
}
impl Default for X86DocArtifactConfig {
fn default() -> Self {
Self {
enabled: false,
path_pattern: "docs/html/*".to_string(),
generate_doxygen: true,
generate_sphinx: false,
publish_to_pages: false,
}
}
}
/// Coverage report configuration.
#[derive(Debug, Clone)]
pub struct X86CoverageReportConfig {
pub enabled: bool,
pub path_pattern: String,
pub html_report: bool,
pub lcov_report: bool,
pub cobertura_report: bool,
}
impl Default for X86CoverageReportConfig {
fn default() -> Self {
Self {
enabled: true,
path_pattern: "coverage-html/*".to_string(),
html_report: true,
lcov_report: true,
cobertura_report: true,
}
}
}
/// Test report configuration.
#[derive(Debug, Clone)]
pub struct X86TestReportConfig {
pub enabled: bool,
pub path_pattern: String,
pub junit_xml: bool,
pub ctest_xml: bool,
}
impl Default for X86TestReportConfig {
fn default() -> Self {
Self {
enabled: true,
path_pattern: "build/test-results/*.xml".to_string(),
junit_xml: true,
ctest_xml: true,
}
}
}
/// Analysis report configuration.
#[derive(Debug, Clone)]
pub struct X86AnalysisReportConfig {
pub enabled: bool,
pub path_pattern: String,
pub sarif_report: bool,
pub plist_report: bool,
pub html_report: bool,
}
impl Default for X86AnalysisReportConfig {
fn default() -> Self {
Self {
enabled: true,
path_pattern: "analysis-results/*".to_string(),
sarif_report: true,
plist_report: false,
html_report: true,
}
}
}
/// Docker image artifact configuration.
#[derive(Debug, Clone)]
pub struct X86DockerArtifactConfig {
pub enabled: bool,
pub image_name: String,
pub dockerfile: String,
pub tags: Vec<String>,
pub push_to_registry: bool,
pub build_args: BTreeMap<String, String>,
pub platforms: Vec<String>,
}
impl Default for X86DockerArtifactConfig {
fn default() -> Self {
Self {
enabled: false,
image_name: "x86-clang-project".to_string(),
dockerfile: "Dockerfile".to_string(),
tags: vec!["latest".to_string()],
push_to_registry: false,
build_args: BTreeMap::new(),
platforms: vec!["linux/amd64".to_string()],
}
}
}
/// Release automation configuration.
#[derive(Debug, Clone)]
pub struct X86ReleaseConfig {
pub enabled: bool,
pub artifact_name: String,
pub generate_release_notes: bool,
pub draft: bool,
pub prerelease: bool,
pub publish_to: Vec<X86ReleaseTarget>,
}
impl Default for X86ReleaseConfig {
fn default() -> Self {
Self {
enabled: false,
artifact_name: "x86-clang-project".to_string(),
generate_release_notes: true,
draft: false,
prerelease: false,
publish_to: vec![X86ReleaseTarget::GitHub],
}
}
}
/// Release publishing target.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ReleaseTarget {
GitHub,
GitLab,
GitHubPackages,
GitLabRegistry,
DockerHub,
CustomRegistry,
}
/// Package registry configuration.
#[derive(Debug, Clone)]
pub struct X86PackageRegistryConfig {
pub enabled: bool,
pub registry_type: X86PackageRegistryType,
pub namespace: String,
pub package_name: String,
}
impl Default for X86PackageRegistryConfig {
fn default() -> Self {
Self {
enabled: false,
registry_type: X86PackageRegistryType::GitHubPackages,
namespace: "my-org".to_string(),
package_name: "x86-clang-project".to_string(),
}
}
}
/// Package registry type.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86PackageRegistryType {
GitHubPackages,
GitLabRegistry,
DockerHub,
Custom,
}
/// SBOM configuration.
#[derive(Debug, Clone)]
pub struct X86SBOMConfig {
pub enabled: bool,
pub format: String,
pub package_name: String,
pub package_version: String,
pub supplier: String,
pub dependencies: Vec<String>,
pub dependency_versions: BTreeMap<String, String>,
pub dependency_licenses: BTreeMap<String, String>,
}
impl Default for X86SBOMConfig {
fn default() -> Self {
Self {
enabled: false,
format: X86_CI_SBOM_DEFAULT_FORMAT.to_string(),
package_name: "x86-clang-project".to_string(),
package_version: "0.1.0".to_string(),
supplier: "X86CI_CD".to_string(),
dependencies: vec![
"llvm".to_string(),
"clang".to_string(),
"libc".to_string(),
"libc++".to_string(),
],
dependency_versions: BTreeMap::from([
("llvm".to_string(), "17.0.0".to_string()),
("clang".to_string(), "17.0.0".to_string()),
("libc".to_string(), "glibc-2.35".to_string()),
("libc++".to_string(), "17.0.0".to_string()),
]),
dependency_licenses: BTreeMap::from([
(
"llvm".to_string(),
"Apache-2.0 WITH LLVM-exception".to_string(),
),
(
"clang".to_string(),
"Apache-2.0 WITH LLVM-exception".to_string(),
),
("libc".to_string(), "LGPL-2.1".to_string()),
(
"libc++".to_string(),
"Apache-2.0 WITH LLVM-exception".to_string(),
),
]),
}
}
}
/// SBOM report.
#[derive(Debug, Clone)]
pub struct X86SBOMReport {
pub format: String,
pub spec_version: String,
pub created: String,
pub name: String,
pub version: String,
pub supplier: String,
pub components: Vec<X86SBOMComponent>,
pub sha256: Option<String>,
}
impl X86SBOMReport {
/// Generate SPDX JSON format output.
pub fn to_spdx_json(&self) -> String {
let mut json = String::from("{\n");
json.push_str(" \"spdxVersion\": \"SPDX-2.3\",\n");
json.push_str(&format!(" \"name\": \"{}\",\n", self.name));
json.push_str(&format!(" \"dataLicense\": \"CC0-1.0\",\n"));
json.push_str(&format!(" \"SPDXID\": \"SPDXRef-DOCUMENT\",\n"));
json.push_str(&format!(
" \"creationInfo\": {{\n \"created\": \"{}\",\n \"creators\": [\"Tool: X86CI_CD\"]\n }},\n",
self.created
));
json.push_str(" \"packages\": [\n");
json.push_str(&format!(
" {{\n \"name\": \"{}\",\n \"versionInfo\": \"{}\",\n \"supplier\": \"{}\"\n }}\n",
self.name, self.version, self.supplier
));
json.push_str(" ],\n");
json.push_str(" \"hasExtractedLicensingInfos\": [\n");
// List unique licenses from components
let mut seen = HashSet::new();
for component in &self.components {
if let Some(ref license) = component.license {
if seen.insert(license) {
json.push_str(&format!(
" {{\n \"licenseId\": \"{}\",\n \"name\": \"{}\"\n }},\n",
license, license
));
}
}
}
json.push_str(" ]\n");
json.push_str("}\n");
json
}
/// Generate CycloneDX JSON format output.
pub fn to_cyclonedx_json(&self) -> String {
let mut json = String::from("{\n");
json.push_str(" \"bomFormat\": \"CycloneDX\",\n");
json.push_str(" \"specVersion\": \"1.5\",\n");
json.push_str(&format!(" \"metadata\": {{\n \"component\": {{\n \"name\": \"{}\",\n \"version\": \"{}\"\n }}\n }},\n", self.name, self.version));
json.push_str(" \"components\": [\n");
for (i, component) in self.components.iter().enumerate() {
if i > 0 {
json.push_str(",\n");
}
json.push_str(&format!(
" {{\n \"type\": \"library\",\n \"name\": \"{}\",\n \"version\": \"{}\",\n \"purl\": \"{}\"\n }}",
component.name, component.version, component.purl
));
}
json.push_str("\n ]\n}\n");
json
}
}
/// SBOM component (dependency).
#[derive(Debug, Clone)]
pub struct X86SBOMComponent {
pub name: String,
pub version: String,
pub purl: String,
pub license: Option<String>,
pub supplier: String,
}
/// Artifact entry.
#[derive(Debug, Clone)]
pub struct X86Artifact {
pub name: String,
pub path: String,
pub size_bytes: u64,
pub artifact_type: X86ArtifactType,
pub compressed: bool,
pub checksum: Option<String>,
pub created_at: Duration,
pub expires_at: Duration,
}
/// Artifact type.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86ArtifactType {
Binary,
Library,
Symbols,
Documentation,
Report,
Coverage,
TestResults,
AnalysisResults,
Container,
Package,
Other,
}
/// Artifact summary.
#[derive(Debug, Clone)]
pub struct X86ArtifactSummary {
pub total_artifacts: u64,
pub total_size_bytes: u64,
pub binary_count: u64,
pub library_count: u64,
pub symbol_count: u64,
pub report_count: u64,
pub doc_count: u64,
}
// ═══════════════════════════════════════════════════════════════════════════════
// 6. X86CINotification — Notification Integration
// ═══════════════════════════════════════════════════════════════════════════════
/// Notification hub for X86 CI/CD pipelines.
///
/// Supports Slack/Teams/Discord webhook notifications, email notifications,
/// GitHub/GitLab commit status updates, and status badge generation
/// (shields.io format).
#[derive(Debug, Clone)]
pub struct X86NotificationHub {
pub enabled: bool,
pub slack: Option<X86SlackConfig>,
pub discord: Option<X86DiscordConfig>,
pub teams: Option<X86TeamsConfig>,
pub email: Option<X86EmailConfig>,
pub github_status: bool,
pub gitlab_status: bool,
pub custom_webhooks: Vec<X86WebhookConfig>,
pub notify_on: X86NotifyConditions,
}
impl Default for X86NotificationHub {
fn default() -> Self {
Self {
enabled: true,
slack: None,
discord: None,
teams: None,
email: None,
github_status: true,
gitlab_status: false,
custom_webhooks: Vec::new(),
notify_on: X86NotifyConditions::default(),
}
}
}
impl X86NotificationHub {
/// Dispatch notifications based on pipeline result.
pub fn dispatch(&self, notification: &X86CiNotification) -> Vec<String> {
if !self.enabled {
return vec!["Notifications disabled.".to_string()];
}
if !self.notify_on.should_notify(notification.success, false) {
return vec!["No notification needed for this status.".to_string()];
}
let mut messages = Vec::new();
// Slack
if let Some(ref slack) = self.slack {
let msg = self.format_slack_message(notification, slack);
messages.push(format!(
"[Slack -> {}] {}",
slack.channel.as_deref().unwrap_or("#ci"),
msg
));
}
// Discord
if let Some(ref discord) = self.discord {
let msg = self.format_discord_message(notification, discord);
messages.push(format!("[Discord] {}", msg));
}
// Teams
if let Some(ref teams) = self.teams {
messages.push(format!(
"[Teams -> {}] Build notification sent.",
teams.webhook_url
));
}
// Email
if let Some(ref email) = self.email {
messages.push(format!(
"[Email] {} -> {} recipients",
email.from_address,
email.to_addresses.len()
));
}
// GitHub commit status
if self.github_status {
let payload = self.github_status_payload(notification);
messages.push(format!("[GitHub Status] {}", payload));
}
// GitLab commit status
if self.gitlab_status {
messages.push(format!(
"[GitLab Status] Commit {} build status: {}",
notification.commit_sha,
if notification.success {
"success"
} else {
"failure"
}
));
}
// Custom webhooks
for webhook in &self.custom_webhooks {
messages.push(format!("[Webhook -> {}] {}", webhook.url, webhook.method));
}
messages
}
/// Format a Slack notification message.
pub fn format_slack_message(
&self,
notification: &X86CiNotification,
slack: &X86SlackConfig,
) -> String {
let emoji = if notification.success { "✅" } else { "❌" };
let color = if notification.success {
"#36a64f"
} else {
"#dc3545"
};
format!(
"{{\"channel\": \"{}\", \"username\": \"{}\", \"icon_emoji\": \"{}\", \"attachments\": [{{\"color\": \"{}\", \"title\": \"X86 Clang CI\", \"text\": \"{} {}\nBranch: {}\nCommit: {}\nDuration: {:.1}s\n{}/{}\", \"footer\": \"X86CI_CD\"}}]}}",
slack.channel.as_deref().unwrap_or("#ci"),
slack.username.as_deref().unwrap_or("CI Bot"),
slack.icon_emoji.as_deref().unwrap_or(":robot:"),
color,
emoji,
notification.short_summary(),
notification.branch,
¬ification.commit_sha[..8.min(notification.commit_sha.len())],
notification.duration_secs,
notification.successful_jobs,
notification.total_jobs,
)
}
/// Format a Discord notification message.
pub fn format_discord_message(
&self,
notification: &X86CiNotification,
discord: &X86DiscordConfig,
) -> String {
let emoji = if notification.success { "✅" } else { "❌" };
format!(
"{{\"username\": \"{}\", \"avatar_url\": \"{}\", \"embeds\": [{{\"title\": \"X86 Clang CI - {}\", \"description\": \"{}\\nBranch: {}\\nCommit: {}\\nDuration: {:.1}s\\n{}/{}\", \"color\": {}}}]}}",
discord.username.as_deref().unwrap_or("CI Bot"),
discord.avatar_url.as_deref().unwrap_or(""),
notification.pipeline_name,
emoji,
notification.branch,
¬ification.commit_sha[..8.min(notification.commit_sha.len())],
notification.duration_secs,
notification.successful_jobs,
notification.total_jobs,
if notification.success { 5763719 } else { 15548997 },
)
}
/// Generate GitHub commit status payload.
pub fn github_status_payload(&self, notification: &X86CiNotification) -> String {
format!(
"{{\"state\": \"{}\", \"target_url\": \"{}\", \"description\": \"{}\", \"context\": \"ci/x86-clang\"}}",
if notification.success { "success" } else { "failure" },
notification.url.as_deref().unwrap_or(""),
notification.short_summary(),
)
}
}
/// Slack notification configuration.
#[derive(Debug, Clone)]
pub struct X86SlackConfig {
pub webhook_url: String,
pub channel: Option<String>,
pub username: Option<String>,
pub icon_emoji: Option<String>,
}
/// Discord notification configuration.
#[derive(Debug, Clone)]
pub struct X86DiscordConfig {
pub webhook_url: String,
pub username: Option<String>,
pub avatar_url: Option<String>,
}
/// Microsoft Teams notification configuration.
#[derive(Debug, Clone)]
pub struct X86TeamsConfig {
pub webhook_url: String,
pub theme_color: Option<String>,
}
/// Email notification configuration.
#[derive(Debug, Clone)]
pub struct X86EmailConfig {
pub smtp_server: String,
pub smtp_port: u16,
pub from_address: String,
pub to_addresses: Vec<String>,
pub use_tls: bool,
}
/// Webhook notification configuration.
#[derive(Debug, Clone)]
pub struct X86WebhookConfig {
pub url: String,
pub method: String,
pub headers: BTreeMap<String, String>,
pub secret: Option<String>,
}
/// Notification conditions.
#[derive(Debug, Clone)]
pub struct X86NotifyConditions {
pub on_success: bool,
pub on_failure: bool,
pub on_fixed: bool,
pub on_change: bool,
pub on_always: bool,
}
impl Default for X86NotifyConditions {
fn default() -> Self {
Self {
on_success: false,
on_failure: true,
on_fixed: true,
on_change: true,
on_always: false,
}
}
}
impl X86NotifyConditions {
pub fn should_notify(&self, success: bool, is_fixed: bool) -> bool {
if self.on_always {
return true;
}
if success && self.on_success {
return true;
}
if !success && self.on_failure {
return true;
}
if is_fixed && self.on_fixed {
return true;
}
if self.on_change {
return true;
}
false
}
}
/// CI notification message.
#[derive(Debug, Clone)]
pub struct X86CiNotification {
pub ci_system: X86CiSystem,
pub pipeline_name: String,
pub success: bool,
pub duration_secs: f64,
pub total_jobs: u32,
pub successful_jobs: u32,
pub failed_jobs: u32,
pub quality_summary: Vec<String>,
pub artifact_summary: Option<X86ArtifactSummary>,
pub branch: String,
pub commit_sha: String,
pub url: Option<String>,
}
impl X86CiNotification {
/// Create from a pipeline result.
pub fn pipeline_result(execution: &X86PipelineExecution) -> Self {
Self {
ci_system: X86CiSystem::GitHubActions,
pipeline_name: execution.pipeline.name.clone(),
success: execution.overall_success,
duration_secs: execution.duration.as_secs_f64(),
total_jobs: execution.jobs.len() as u32,
successful_jobs: execution.jobs.iter().filter(|j| j.success).count() as u32,
failed_jobs: execution.jobs.iter().filter(|j| !j.success).count() as u32,
quality_summary: execution
.quality_gate_results
.iter()
.map(|g| format!("{} {}: {}", g.status_emoji(), g.gate_name, g.actual_value))
.collect(),
artifact_summary: Some(execution.artifact_summary.clone()),
branch: "main".to_string(),
commit_sha: "abcdef1234567890".to_string(),
url: None,
}
}
/// Short human-readable summary.
pub fn short_summary(&self) -> String {
format!(
"{}: {}/{} jobs successful in {:.1}s",
self.pipeline_name, self.successful_jobs, self.total_jobs, self.duration_secs
)
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// GitHub Actions Support
// ═══════════════════════════════════════════════════════════════════════════════
/// GitHub Actions pipeline support for X86 Clang CI.
#[derive(Debug, Clone)]
pub struct X86GitHubSupport {
/// Workflow name.
pub workflow_name: String,
/// Runner OS (ubuntu-latest, macos-latest, windows-latest or custom).
pub runner_os: String,
/// Additional runner labels.
pub runner_labels: Vec<String>,
/// Enable caching (ccache/sccache).
pub enable_cache: bool,
/// Enable build matrix.
pub enable_matrix: bool,
/// Enable code coverage reporting.
pub enable_coverage: bool,
/// Enable release automation.
pub enable_release: bool,
/// Custom GitHub Actions to include.
pub custom_actions: Vec<String>,
}
impl Default for X86GitHubSupport {
fn default() -> Self {
Self {
workflow_name: "X86 Clang CI".to_string(),
runner_os: "ubuntu-22.04".to_string(),
runner_labels: vec![],
enable_cache: true,
enable_matrix: true,
enable_coverage: true,
enable_release: false,
custom_actions: vec![],
}
}
}
impl X86GitHubSupport {
/// Generate a complete GitHub Actions workflow YAML.
pub fn generate_workflow(&self, ci_cd: &X86CI_CD) -> X86PipelineResult {
let mut yaml = String::new();
// Header
yaml.push_str(&format!(
"# Generated by X86CI_CD — {}\n",
self.workflow_name
));
yaml.push_str(&format!("name: {}\n\n", self.workflow_name));
// Triggers
yaml.push_str(&self.generate_triggers(ci_cd));
yaml.push('\n');
// Concurrency
if let Some(ref group) = ci_cd.pipeline_config.concurrency_group {
yaml.push_str(&format!(
"concurrency:\n group: {}\n cancel-in-progress: {}\n\n",
group, ci_cd.pipeline_config.cancel_in_progress
));
}
// Environment variables
if !ci_cd.pipeline_config.env.is_empty() {
yaml.push_str("env:\n");
for (key, value) in &ci_cd.pipeline_config.env {
yaml.push_str(&format!(" {}: {}\n", key, value));
}
yaml.push('\n');
}
// Jobs
yaml.push_str("jobs:\n");
if ci_cd.pipeline_config.stages.is_empty() {
yaml.push_str(&self.generate_default_job(ci_cd));
} else {
for stage in &ci_cd.pipeline_config.stages {
yaml.push_str(&self.generate_job_from_stage(ci_cd, stage));
}
}
// Release job (if enabled)
if self.enable_release {
yaml.push_str(&self.generate_release_job(ci_cd));
}
// Coverage job (if enabled)
if self.enable_coverage {
yaml.push_str(&self.generate_coverage_job(ci_cd));
}
// SBOM generation job
if ci_cd.artifacts.sbom.enabled {
yaml.push_str(&self.generate_sbom_job(ci_cd));
}
X86PipelineResult {
content: yaml,
file_name: ".github/workflows/ci.yml".to_string(),
ci_system: X86CiSystem::GitHubActions,
}
}
fn generate_triggers(&self, ci_cd: &X86CI_CD) -> String {
let mut yaml = String::from("on:\n");
let triggers = &ci_cd.pipeline_config.triggers;
// Push
yaml.push_str(" push:\n");
if !triggers.push.branches.is_empty() {
yaml.push_str(" branches:\n");
for branch in &triggers.push.branches {
yaml.push_str(&format!(" - {}\n", branch));
}
}
if !triggers.push.paths_ignore.is_empty() {
yaml.push_str(" paths-ignore:\n");
for path in &triggers.push.paths_ignore {
yaml.push_str(&format!(" - {}\n", path));
}
}
if triggers.tags {
yaml.push_str(" tags:\n - 'v*'\n");
}
// Pull Request
if !triggers.pull_request.branches.is_empty() {
yaml.push_str(" pull_request:\n");
yaml.push_str(" branches:\n");
for branch in &triggers.pull_request.branches {
yaml.push_str(&format!(" - {}\n", branch));
}
if !triggers.pull_request.types.is_empty() {
yaml.push_str(" types:\n");
for t in &triggers.pull_request.types {
yaml.push_str(&format!(" - {}\n", t));
}
}
}
// Schedule
for schedule in &triggers.schedule {
yaml.push_str(&format!(" schedule:\n - cron: '{}'\n", schedule.cron));
}
// Workflow dispatch
if triggers.workflow_dispatch {
yaml.push_str(" workflow_dispatch:\n inputs:\n build_type:\n description: 'Build type'\n required: true\n default: 'Release'\n type: choice\n options:\n - Debug\n - Release\n - RelWithDebInfo\n - MinSizeRel\n");
}
// Release
if let Some(ref release) = triggers.release {
yaml.push_str(" release:\n types:\n");
for t in &release.types {
yaml.push_str(&format!(" - {}\n", t));
}
}
yaml
}
fn generate_job_from_stage(&self, ci_cd: &X86CI_CD, stage: &X86PipelineStage) -> String {
let mut yaml = String::new();
let sanitized = stage.name.replace(' ', "-").to_lowercase();
yaml.push_str(&format!(" {}:\n", sanitized));
yaml.push_str(&format!(" name: {}\n", stage.name));
yaml.push_str(&format!(" runs-on: {}\n", self.runner_os));
if !stage.dependencies.is_empty() {
yaml.push_str(" needs:\n");
for dep in &stage.dependencies {
yaml.push_str(&format!(
" - {}\n",
dep.replace(' ', "-").to_lowercase()
));
}
}
yaml.push_str(&format!(" timeout-minutes: {}\n", stage.timeout_minutes));
if stage.allow_failure {
yaml.push_str(" continue-on-error: true\n");
}
// Strategy (matrix)
if self.enable_matrix && stage.stage_type == X86StageType::Build {
yaml.push_str(&self.generate_matrix_strategy(ci_cd));
}
// Steps
yaml.push_str(" steps:\n");
yaml.push_str(&self.generate_checkout_step());
yaml.push_str(&self.generate_cache_steps(ci_cd));
if let Some(ref comp) = stage.compilation {
yaml.push_str(&self.generate_build_steps(ci_cd, comp));
}
if let Some(ref test) = stage.test_config {
yaml.push_str(&self.generate_test_steps(ci_cd, test));
}
// Artifact upload
if !ci_cd.artifacts.collected.is_empty() {
for step in &ci_cd
.artifacts
.generate_upload_steps(&X86CiSystem::GitHubActions)
{
yaml.push_str(&format!(" {}\n", step));
}
}
yaml
}
fn generate_default_job(&self, ci_cd: &X86CI_CD) -> String {
let mut yaml = String::from(
" build-and-test:\n name: Build and Test\n runs-on: ubuntu-22.04\n timeout-minutes: 60\n steps:\n",
);
yaml.push_str(" - uses: actions/checkout@v4\n");
// Install dependencies
yaml.push_str(" - name: Install Dependencies\n");
yaml.push_str(" run: |\n");
yaml.push_str(" sudo apt-get update\n");
yaml.push_str(&format!(
" sudo apt-get install -y cmake ninja-build ccache clang-{} llvm-{}-dev\n",
X86_CI_CLANG_VERSION, X86_CI_CLANG_VERSION
));
// Configure
yaml.push_str(" - name: Configure CMake\n");
yaml.push_str(&format!(
" run: cmake -B {} -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++\n",
X86_CI_BUILD_DIR
));
// Build
yaml.push_str(" - name: Build\n");
yaml.push_str(" run: cmake --build build --parallel\n");
// Test
yaml.push_str(" - name: Test\n");
yaml.push_str(" run: cd build && ctest --output-on-failure -j4\n");
// Upload artifacts
yaml.push_str(" - uses: actions/upload-artifact@v4\n");
yaml.push_str(" with:\n");
yaml.push_str(" name: binaries\n");
yaml.push_str(" path: build/bin/*\n");
yaml
}
fn generate_matrix_strategy(&self, ci_cd: &X86CI_CD) -> String {
let mut yaml = String::from(" strategy:\n");
yaml.push_str(&format!(
" fail-fast: {}\n",
ci_cd.pipeline_config.fail_fast
));
let matrix = ci_cd.generate_matrix();
yaml.push_str(" matrix:\n");
if !matrix.operating_systems.is_empty() {
yaml.push_str(" os:\n");
for os in &matrix.operating_systems {
yaml.push_str(&format!(" - {}\n", os));
}
}
if !matrix.compilers.is_empty() {
yaml.push_str(" compiler:\n");
for compiler in &matrix.compilers {
yaml.push_str(&format!(" - {}\n", compiler));
}
}
if !matrix.build_types.is_empty() {
yaml.push_str(" build-type:\n");
for bt in &matrix.build_types {
yaml.push_str(&format!(" - {}\n", bt));
}
}
if !matrix.sanitizers.is_empty() {
yaml.push_str(" sanitizer:\n");
for s in &matrix.sanitizers {
yaml.push_str(&format!(" - {}\n", s));
}
}
if !matrix.arch_variants.is_empty() {
yaml.push_str(" arch:\n");
for arch in &matrix.arch_variants {
yaml.push_str(&format!(" - {}\n", arch));
}
}
// Exclude rules
let rules = matrix.generate_rules();
let excludes: Vec<_> = rules
.iter()
.filter_map(|r| match r {
X86MatrixRule::Exclude {
os,
compiler,
build_type,
} => Some((os, compiler, build_type)),
_ => None,
})
.collect();
if !excludes.is_empty() {
yaml.push_str(" exclude:\n");
for (os, compiler, build_type) in &excludes {
yaml.push_str(&format!(
" - os: {}\n compiler: {}\n",
os, compiler
));
if !build_type.is_empty() {
yaml.push_str(&format!(" build-type: {}\n", build_type));
}
}
}
yaml
}
fn generate_checkout_step(&self) -> String {
" - uses: actions/checkout@v4\n with:\n fetch-depth: 0\n".to_string()
}
fn generate_cache_steps(&self, ci_cd: &X86CI_CD) -> String {
if !self.enable_cache {
return String::new();
}
let mut steps = String::new();
// ccache
if ci_cd.compilation.ccache {
steps.push_str(" - name: Setup ccache\n");
steps.push_str(" uses: hendrikmuhs/ccache-action@v1\n");
steps.push_str(" with:\n");
steps.push_str(&format!(" key: {}-${{{{ matrix.os }}}}-${{{{ matrix.compiler }}}}-${{{{ matrix.build-type }}}}\n", X86_CI_CCACHE_KEY));
steps.push_str(" max-size: 500M\n");
}
// sccache
if ci_cd.compilation.sccache {
steps.push_str(" - name: Setup sccache\n");
steps.push_str(" uses: mozilla-actions/sccache-action@v0.0.3\n");
steps.push_str(" with:\n");
steps.push_str(" version: v0.5.4\n");
}
steps
}
fn generate_build_steps(&self, ci_cd: &X86CI_CD, config: &X86CompileConfig) -> String {
let mut steps = String::new();
steps.push_str(&format!(
" - name: Configure ({})\n run: |\n cmake -B {} -G Ninja \\\n",
config.name, config.build_dir
));
let cmake_flags = ci_cd.compilation.to_cmake_flags();
for flag in &cmake_flags {
steps.push_str(&format!(" {} \\\n", flag));
}
steps.push_str(" .\n");
steps.push_str(&format!(
" - name: Build ({})\n run: cmake --build {} --parallel ${{{{ env.NUM_JOBS }}}}\n",
config.name, config.build_dir
));
steps
}
fn generate_test_steps(&self, ci_cd: &X86CI_CD, config: &X86TestConfig) -> String {
let mut steps = String::new();
steps.push_str(&format!(
" - name: Test ({})\n run: |\n cd {}\n {}\n",
config.name,
config.working_dir,
ci_cd.testing.to_ctest_command()
));
steps.push_str(" - name: Upload Test Results\n");
steps.push_str(" uses: actions/upload-artifact@v4\n");
steps.push_str(" with:\n");
steps.push_str(" name: test-results\n");
steps.push_str(" path: build/test-results/*.xml\n");
steps
}
fn generate_release_job(&self, ci_cd: &X86CI_CD) -> String {
let mut yaml = String::from(
" release:\n name: Create Release\n runs-on: ubuntu-22.04\n if: startsWith(github.ref, 'refs/tags/v')\n needs: [build, test]\n steps:\n",
);
yaml.push_str(" - uses: actions/checkout@v4\n");
yaml.push_str(" - uses: actions/download-artifact@v4\n with:\n name: binaries\n path: build/bin/\n");
// Create release
yaml.push_str(" - name: Create Release\n");
yaml.push_str(" uses: softprops/action-gh-release@v1\n");
yaml.push_str(" with:\n");
yaml.push_str(" files: |\n");
yaml.push_str(" build/bin/*\n");
yaml.push_str(" generate_release_notes: true\n");
// Docker publish (if enabled)
if ci_cd.artifacts.docker.enabled {
yaml.push_str(" - name: Build and Push Docker Image\n");
yaml.push_str(" run: |\n");
for step in &ci_cd.artifacts.generate_docker_steps() {
yaml.push_str(&format!(" {}\n", step));
}
}
yaml
}
fn generate_coverage_job(&self, ci_cd: &X86CI_CD) -> String {
let mut yaml = String::from(
" coverage:\n name: Code Coverage\n runs-on: ubuntu-22.04\n needs: [test]\n steps:\n",
);
yaml.push_str(" - uses: actions/checkout@v4\n");
yaml.push_str(" - name: Generate Coverage Report\n");
yaml.push_str(" run: |\n");
yaml.push_str(&format!(
" {}\n",
ci_cd
.testing
.coverage_tool
.report_command()
.unwrap_or("echo 'No coverage tool configured'")
));
yaml.push_str(" - name: Upload Coverage\n");
yaml.push_str(" uses: actions/upload-artifact@v4\n");
yaml.push_str(" with:\n");
yaml.push_str(" name: coverage-report\n");
yaml.push_str(" path: coverage-html/\n");
yaml
}
fn generate_sbom_job(&self, ci_cd: &X86CI_CD) -> String {
let mut yaml = String::from(
" sbom:\n name: Generate SBOM\n runs-on: ubuntu-22.04\n needs: [build]\n steps:\n",
);
yaml.push_str(" - uses: actions/checkout@v4\n");
yaml.push_str(" - name: Generate SBOM\n");
yaml.push_str(" run: |\n");
let sbom = ci_cd.artifacts.generate_sbom();
yaml.push_str(&format!(
" echo '{}' > sbom.spdx.json\n",
sbom.to_spdx_json().replace('\n', "\\n")
));
yaml.push_str(" - uses: actions/upload-artifact@v4\n");
yaml.push_str(" with:\n");
yaml.push_str(" name: sbom\n");
yaml.push_str(" path: sbom.spdx.json\n");
yaml
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// GitLab CI Support
// ═══════════════════════════════════════════════════════════════════════════════
/// GitLab CI/CD pipeline support for X86 Clang CI.
#[derive(Debug, Clone)]
pub struct X86GitLabSupport {
/// Default Docker image for jobs.
pub default_image: String,
/// Pipeline stages.
pub stages: Vec<String>,
/// Enable caching.
pub enable_cache: bool,
/// Enable container registry.
pub enable_registry: bool,
/// Enable review apps (environment deployments).
pub enable_review_apps: bool,
/// Enable Kubernetes integration.
pub kubernetes_enabled: bool,
}
impl Default for X86GitLabSupport {
fn default() -> Self {
Self {
default_image: X86_CI_DEFAULT_DOCKER_IMAGE.to_string(),
stages: vec![
"build".to_string(),
"test".to_string(),
"analyze".to_string(),
"deploy".to_string(),
],
enable_cache: true,
enable_registry: false,
enable_review_apps: false,
kubernetes_enabled: false,
}
}
}
impl X86GitLabSupport {
/// Generate a complete .gitlab-ci.yml pipeline.
pub fn generate_pipeline(&self, ci_cd: &X86CI_CD) -> X86PipelineResult {
let mut yaml = String::from("# Generated by X86CI_CD — GitLab CI\n\n");
// Default image
yaml.push_str(&format!("default:\n image: {}\n\n", self.default_image));
// Stages
yaml.push_str("stages:\n");
for stage in &self.stages {
yaml.push_str(&format!(" - {}\n", stage));
}
yaml.push('\n');
// Cache
if self.enable_cache {
yaml.push_str("cache:\n key: \"$CI_COMMIT_REF_SLUG\"\n paths:\n - .ccache/\n - build/\n\n");
}
// Variables
yaml.push_str("variables:\n CC: clang\n CXX: clang++\n BUILD_TYPE: Release\n MAKEFLAGS: \"-j$(nproc)\"\n\n");
// Build jobs
yaml.push_str(&self.generate_build_jobs(ci_cd));
// Test jobs
yaml.push_str(&self.generate_test_jobs(ci_cd));
// Analyze jobs
yaml.push_str(&self.generate_analyze_jobs(ci_cd));
// Deploy jobs
yaml.push_str(&self.generate_deploy_jobs(ci_cd));
X86PipelineResult {
content: yaml,
file_name: ".gitlab-ci.yml".to_string(),
ci_system: X86CiSystem::GitLabCI,
}
}
fn generate_build_jobs(&self, ci_cd: &X86CI_CD) -> String {
let mut yaml = String::new();
yaml.push_str("build:\n");
yaml.push_str(" stage: build\n");
yaml.push_str(" script:\n");
yaml.push_str(" - cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_C_COMPILER=${CC} -DCMAKE_CXX_COMPILER=${CXX}\n");
yaml.push_str(" - cmake --build build --parallel\n");
yaml.push_str(" artifacts:\n");
yaml.push_str(" paths:\n");
yaml.push_str(" - build/bin/\n");
yaml.push_str(" - build/lib/\n");
yaml.push_str(" expire_in: 7 days\n\n");
// Sanitizer builds (parallel matrix)
for sanitizer in &ci_cd.compilation.sanitizers {
yaml.push_str(&format!(
"build-{}:\n stage: build\n variables:\n BUILD_TYPE: Debug\n SANITIZER_FLAGS: -fsanitize={}\n script:\n - cmake -B build-san -G Ninja -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_C_FLAGS=\"$SANITIZER_FLAGS\" -DCMAKE_CXX_FLAGS=\"$SANITIZER_FLAGS\"\n - cmake --build build-san --parallel\n allow_failure: true\n\n",
sanitizer.clang_flag(),
sanitizer.clang_flag()
));
}
yaml
}
fn generate_test_jobs(&self, ci_cd: &X86CI_CD) -> String {
let mut yaml = String::new();
yaml.push_str("test:\n");
yaml.push_str(" stage: test\n");
yaml.push_str(" needs: [\"build\"]\n");
yaml.push_str(" script:\n");
yaml.push_str(&format!(
" - cd build && {}\n",
ci_cd.testing.to_ctest_command()
));
yaml.push_str(" artifacts:\n");
yaml.push_str(" reports:\n");
yaml.push_str(" junit: build/test-results/*.xml\n");
yaml.push_str(" when: always\n\n");
// Coverage test
if ci_cd.testing.coverage_tool != X86CoverageTool::None {
yaml.push_str("coverage:\n");
yaml.push_str(" stage: test\n");
yaml.push_str(" needs: [\"build\"]\n");
yaml.push_str(" script:\n");
yaml.push_str(&format!(
" - cmake -B build-cov -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_FLAGS=\"{}\" -DCMAKE_CXX_FLAGS=\"{}\"\n",
ci_cd.testing.coverage_tool.flags(),
ci_cd.testing.coverage_tool.flags()
));
yaml.push_str(" - cmake --build build-cov --parallel\n");
yaml.push_str(" - cd build-cov && ctest --output-on-failure\n");
yaml.push_str(&format!(
" - {}\n",
ci_cd.testing.coverage_tool.report_command().unwrap_or("")
));
yaml.push_str(" artifacts:\n");
yaml.push_str(" reports:\n");
yaml.push_str(" coverage_report:\n");
yaml.push_str(" coverage_format: cobertura\n");
yaml.push_str(" path: coverage.xml\n\n");
}
yaml
}
fn generate_analyze_jobs(&self, ci_cd: &X86CI_CD) -> String {
let mut yaml = String::new();
for analyzer in &ci_cd.testing.static_analyzers {
yaml.push_str(&format!("analyze-{}:\n", analyzer.report_format()));
yaml.push_str(" stage: analyze\n");
yaml.push_str(" needs: [\"build\"]\n");
yaml.push_str(" allow_failure: true\n");
yaml.push_str(" script:\n");
yaml.push_str(&format!(" - {}\n", analyzer.command()));
yaml.push_str(" artifacts:\n");
yaml.push_str(" reports:\n");
yaml.push_str(&format!(
" sast: analysis-results/{}.*\n",
analyzer.report_format()
));
yaml.push_str(" when: always\n\n");
}
yaml
}
fn generate_deploy_jobs(&self, ci_cd: &X86CI_CD) -> String {
let mut yaml = String::new();
// Review app (if enabled)
if self.enable_review_apps {
yaml.push_str("review:\n");
yaml.push_str(" stage: deploy\n");
yaml.push_str(" needs: [\"test\"]\n");
yaml.push_str(" script:\n");
yaml.push_str(" - echo \"Deploying review app for $CI_COMMIT_REF_NAME\"\n");
yaml.push_str(" environment:\n");
yaml.push_str(" name: review/$CI_COMMIT_REF_SLUG\n");
yaml.push_str(" url: https://$CI_COMMIT_REF_SLUG.example.com\n");
yaml.push_str(" on_stop: stop_review\n");
yaml.push_str(" only:\n");
yaml.push_str(" - merge_requests\n\n");
yaml.push_str("stop_review:\n");
yaml.push_str(" stage: deploy\n");
yaml.push_str(" script:\n");
yaml.push_str(" - echo \"Stopping review app\"\n");
yaml.push_str(" environment:\n");
yaml.push_str(" name: review/$CI_COMMIT_REF_SLUG\n");
yaml.push_str(" action: stop\n");
yaml.push_str(" when: manual\n\n");
}
yaml
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// Jenkins Support
// ═══════════════════════════════════════════════════════════════════════════════
/// Jenkins pipeline support for X86 Clang CI.
#[derive(Debug, Clone)]
pub struct X86JenkinsSupport {
pub pipeline_type: X86JenkinsPipelineType,
pub agent_label: String,
pub docker_image: Option<String>,
pub enable_declarative: bool,
pub enable_scripted: bool,
pub enable_blue_ocean: bool,
}
impl Default for X86JenkinsSupport {
fn default() -> Self {
Self {
pipeline_type: X86JenkinsPipelineType::Declarative,
agent_label: "x86_64-linux".to_string(),
docker_image: Some("ubuntu:22.04".to_string()),
enable_declarative: true,
enable_scripted: true,
enable_blue_ocean: false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86JenkinsPipelineType {
Declarative,
Scripted,
}
impl X86JenkinsSupport {
/// Generate a Jenkinsfile (Declarative or Scripted).
pub fn generate_jenkinsfile(&self, ci_cd: &X86CI_CD) -> X86PipelineResult {
match self.pipeline_type {
X86JenkinsPipelineType::Declarative => self.generate_declarative(ci_cd),
X86JenkinsPipelineType::Scripted => self.generate_scripted(ci_cd),
}
}
fn generate_declarative(&self, ci_cd: &X86CI_CD) -> X86PipelineResult {
let mut jf = String::new();
jf.push_str("// Generated by X86CI_CD — Jenkins Declarative Pipeline\n\n");
jf.push_str("pipeline {\n");
// Agent
if let Some(ref image) = self.docker_image {
jf.push_str(" agent {\n");
jf.push_str(" docker {\n");
jf.push_str(&format!(" image '{}'\n", image));
jf.push_str(
" args '--cap-add SYS_PTRACE --volume /tmp/ccache:/tmp/ccache'\n",
);
jf.push_str(" }\n");
jf.push_str(" }\n");
} else {
jf.push_str(&format!(" agent {{ label '{}' }}\n", self.agent_label));
}
// Environment
jf.push_str("\n environment {\n");
jf.push_str(" CC = 'clang'\n");
jf.push_str(" CXX = 'clang++'\n");
jf.push_str(" BUILD_TYPE = 'Release'\n");
jf.push_str(" CCACHE_DIR = '/tmp/ccache'\n");
jf.push_str(" }\n");
// Options
jf.push_str("\n options {\n");
jf.push_str(" timeout(time: 6, unit: 'HOURS')\n");
jf.push_str(" timestamps()\n");
jf.push_str(" disableConcurrentBuilds()\n");
if ci_cd.pipeline_config.retry_count > 0 {
jf.push_str(&format!(
" retry({})\n",
ci_cd.pipeline_config.retry_count
));
}
jf.push_str(" }\n");
// Parameters
jf.push_str("\n parameters {\n");
jf.push_str(" string(name: 'BUILD_TYPE', defaultValue: 'Release', description: 'Build type (Debug, Release, RelWithDebInfo, MinSizeRel)')\n");
jf.push_str(" choice(name: 'COMPILER', choices: ['clang', 'clang++', 'gcc', 'g++'], description: 'Compiler to use')\n");
jf.push_str(" booleanParam(name: 'RUN_TESTS', defaultValue: true, description: 'Run the test suite')\n");
jf.push_str(" booleanParam(name: 'RUN_ANALYSIS', defaultValue: true, description: 'Run static analysis')\n");
jf.push_str(" }\n");
// Stages
jf.push_str("\n stages {\n");
// Build stage
jf.push_str(" stage('Build') {\n");
jf.push_str(" steps {\n");
jf.push_str(" sh '''\n");
jf.push_str(" cmake -B build -G Ninja \\\n");
jf.push_str(" -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \\\n");
jf.push_str(" -DCMAKE_C_COMPILER=${COMPILER} \\\n");
jf.push_str(" -DCMAKE_CXX_COMPILER=${COMPILER}++ \\\n");
jf.push_str(" -DENABLE_TESTS=ON\n");
jf.push_str(" cmake --build build --parallel\n");
jf.push_str(" '''\n");
jf.push_str(" }\n");
jf.push_str(" }\n\n");
// Test stage
jf.push_str(" stage('Test') {\n");
jf.push_str(" when { expression { params.RUN_TESTS } }\n");
jf.push_str(" steps {\n");
jf.push_str(" sh 'cd build && ctest --output-on-failure -j4'\n");
jf.push_str(" }\n");
jf.push_str(" post {\n");
jf.push_str(" always {\n");
jf.push_str(" junit 'build/test-results/*.xml'\n");
jf.push_str(" }\n");
jf.push_str(" }\n");
jf.push_str(" }\n\n");
// Static Analysis stage (parallel)
jf.push_str(" stage('Static Analysis') {\n");
jf.push_str(" when { expression { params.RUN_ANALYSIS } }\n");
jf.push_str(" parallel {\n");
jf.push_str(" stage('Clang-Tidy') {\n");
jf.push_str(" steps {\n");
jf.push_str(
" sh 'run-clang-tidy -p build -export-fixes analysis.yaml'\n",
);
jf.push_str(" }\n");
jf.push_str(" }\n");
jf.push_str(" stage('Cppcheck') {\n");
jf.push_str(" steps {\n");
jf.push_str(" sh 'cppcheck --enable=all --xml --xml-version=2 src/ 2> cppcheck.xml'\n");
jf.push_str(" }\n");
jf.push_str(" }\n");
jf.push_str(" }\n");
jf.push_str(" }\n\n");
// Deploy stage
jf.push_str(" stage('Deploy') {\n");
jf.push_str(" when {\n");
jf.push_str(" branch 'main'\n");
jf.push_str(" }\n");
jf.push_str(" steps {\n");
jf.push_str(" sh 'echo \"Deploying artifacts...\"'\n");
jf.push_str(
" archiveArtifacts artifacts: 'build/bin/**', fingerprint: true\n",
);
jf.push_str(" }\n");
jf.push_str(" }\n");
jf.push_str(" }\n");
// Post
jf.push_str("\n post {\n");
jf.push_str(" success {\n");
jf.push_str(" emailext subject: \"Build Successful: ${env.JOB_NAME} #${env.BUILD_NUMBER}\",\n");
jf.push_str(" body: \"The build completed successfully.\",\n");
jf.push_str(" to: 'team@example.com'\n");
jf.push_str(" }\n");
jf.push_str(" failure {\n");
jf.push_str(" emailext subject: \"Build Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}\",\n");
jf.push_str(" body: \"The build has failed. Please investigate.\",\n");
jf.push_str(" to: 'team@example.com'\n");
jf.push_str(" }\n");
jf.push_str(" }\n");
jf.push_str("}\n");
X86PipelineResult {
content: jf,
file_name: "Jenkinsfile".to_string(),
ci_system: X86CiSystem::Jenkins,
}
}
fn generate_scripted(&self, ci_cd: &X86CI_CD) -> X86PipelineResult {
let mut jf = String::new();
jf.push_str("// Generated by X86CI_CD — Jenkins Scripted Pipeline\n\n");
jf.push_str("node('x86_64-linux') {\n");
jf.push_str(" stage('Checkout') {\n");
jf.push_str(" checkout scm\n");
jf.push_str(" }\n\n");
jf.push_str(" stage('Build') {\n");
jf.push_str(" sh '''\n");
jf.push_str(" cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release\n");
jf.push_str(" cmake --build build --parallel\n");
jf.push_str(" '''\n");
jf.push_str(" }\n\n");
jf.push_str(" stage('Test') {\n");
jf.push_str(" try {\n");
jf.push_str(" sh 'cd build && ctest --output-on-failure -j4'\n");
jf.push_str(" } finally {\n");
jf.push_str(" junit 'build/test-results/*.xml'\n");
jf.push_str(" }\n");
jf.push_str(" }\n\n");
jf.push_str(" stage('Archive') {\n");
jf.push_str(" archiveArtifacts artifacts: 'build/bin/**', fingerprint: true\n");
jf.push_str(" }\n");
jf.push_str("}\n");
X86PipelineResult {
content: jf,
file_name: "Jenkinsfile".to_string(),
ci_system: X86CiSystem::Jenkins,
}
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// CircleCI Support
// ═══════════════════════════════════════════════════════════════════════════════
/// CircleCI pipeline support for X86 Clang CI.
#[derive(Debug, Clone)]
pub struct X86CircleCISupport {
pub version: String,
pub executor_type: X86CircleCIExecutor,
pub enable_orbs: bool,
pub enable_workspace: bool,
pub enable_test_splitting: bool,
}
impl Default for X86CircleCISupport {
fn default() -> Self {
Self {
version: "2.1".to_string(),
executor_type: X86CircleCIExecutor::Docker,
enable_orbs: true,
enable_workspace: true,
enable_test_splitting: true,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86CircleCIExecutor {
Docker,
Machine,
MacOS,
Windows,
}
impl X86CircleCISupport {
/// Generate a complete .circleci/config.yml.
pub fn generate_config(&self, ci_cd: &X86CI_CD) -> X86PipelineResult {
let mut yaml = String::new();
yaml.push_str(&format!(
"# Generated by X86CI_CD — CircleCI\nversion: {}\n\n",
self.version
));
// Orbs
if self.enable_orbs {
yaml.push_str("orbs:\n codecov: codecov/codecov@3.2.4\n\n");
}
// Executors
yaml.push_str("executors:\n");
yaml.push_str(&self.generate_executor());
// Commands
yaml.push_str("commands:\n setup-build-env:\n steps:\n - run:\n name: Install Dependencies\n command: |\n apt-get update && apt-get install -y cmake ninja-build ccache clang lld\n - run:\n name: Setup ccache\n command: |\n mkdir -p .ccache\n ccache -M 500M -F 0\n\n");
// Jobs
yaml.push_str("jobs:\n");
yaml.push_str(&self.generate_build_job(ci_cd));
yaml.push_str(&self.generate_test_job(ci_cd));
yaml.push_str(&self.generate_analyze_job(ci_cd));
yaml.push_str(&self.generate_deploy_job(ci_cd));
// Workflows
yaml.push_str("workflows:\n version: 2\n build-test-deploy:\n jobs:\n");
yaml.push_str(" - build\n");
yaml.push_str(" - test:\n requires:\n - build\n");
yaml.push_str(" - analyze:\n requires:\n - build\n");
yaml.push_str(" - deploy:\n requires:\n - test\n - analyze\n filters:\n branches:\n only: main\n");
X86PipelineResult {
content: yaml,
file_name: ".circleci/config.yml".to_string(),
ci_system: X86CiSystem::CircleCI,
}
}
fn generate_executor(&self) -> String {
let mut yaml = String::from(" x86-clang-executor:\n");
match self.executor_type {
X86CircleCIExecutor::Docker => {
yaml.push_str(" docker:\n");
yaml.push_str(" - image: cimg/base:2023.09\n");
}
X86CircleCIExecutor::Machine => {
yaml.push_str(" machine:\n");
yaml.push_str(" image: ubuntu-2204:2023.07.1\n");
}
X86CircleCIExecutor::MacOS => {
yaml.push_str(" macos:\n");
yaml.push_str(" xcode: 15.0.0\n");
}
X86CircleCIExecutor::Windows => {
yaml.push_str(" machine:\n");
yaml.push_str(" image: windows-server-2022:current\n");
}
}
yaml.push_str(" resource_class: large\n");
yaml.push_str(" working_directory: ~/project\n");
yaml
}
fn generate_build_job(&self, ci_cd: &X86CI_CD) -> String {
let mut yaml = String::new();
yaml.push_str(" build:\n");
yaml.push_str(" executor: x86-clang-executor\n");
yaml.push_str(" steps:\n");
yaml.push_str(" - checkout\n");
yaml.push_str(" - setup-build-env\n");
yaml.push_str(" - run:\n name: Configure\n command: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++\n");
yaml.push_str(" - run:\n name: Build\n command: cmake --build build --parallel\n");
if self.enable_workspace {
yaml.push_str(" - persist_to_workspace:\n root: .\n paths:\n - build/\n");
}
yaml.push_str(" - store_artifacts:\n path: build/bin/\n destination: binaries\n\n");
yaml
}
fn generate_test_job(&self, ci_cd: &X86CI_CD) -> String {
let mut yaml = String::new();
yaml.push_str(" test:\n");
yaml.push_str(" executor: x86-clang-executor\n");
if self.enable_test_splitting {
yaml.push_str(" parallelism: 4\n");
}
yaml.push_str(" steps:\n");
yaml.push_str(" - checkout\n");
if self.enable_workspace {
yaml.push_str(" - attach_workspace:\n at: .\n");
}
yaml.push_str(" - run:\n name: Run Tests\n");
if self.enable_test_splitting {
yaml.push_str(" command: |\n cd build && ctest --output-on-failure -j4 --schedule-random\n");
} else {
yaml.push_str(" command: cd build && ctest --output-on-failure -j4\n");
}
yaml.push_str(" - store_test_results:\n path: build/test-results/\n");
if ci_cd.testing.coverage_tool != X86CoverageTool::None {
yaml.push_str(
" - run:\n name: Generate Coverage\n command: |\n",
);
yaml.push_str(&format!(
" {}\n",
ci_cd.testing.coverage_tool.report_command().unwrap_or(":")
));
if self.enable_orbs {
yaml.push_str(" - codecov/upload\n");
}
}
yaml
}
fn generate_analyze_job(&self, ci_cd: &X86CI_CD) -> String {
let mut yaml = String::from(
" analyze:\n executor: x86-clang-executor\n steps:\n - checkout\n - setup-build-env\n - run:\n name: Static Analysis\n command: |\n run-clang-tidy -p build -export-fixes analysis.yaml\n cppcheck --enable=all --xml --xml-version=2 src/ 2> cppcheck.xml\n - store_artifacts:\n path: analysis.yaml\n destination: analysis\n - store_artifacts:\n path: cppcheck.xml\n destination: analysis\n\n",
);
yaml
}
fn generate_deploy_job(&self, ci_cd: &X86CI_CD) -> String {
let mut yaml = String::from(
" deploy:\n executor: x86-clang-executor\n steps:\n - checkout\n - setup-build-env\n - run:\n name: Deploy\n command: |\n echo \"Deploying build artifacts...\"\n tar czf x86-clang-build.tar.gz build/\n",
);
if self.enable_orbs {
yaml.push_str(" - run:\n name: Create GitHub Release\n command: gh release create v1.0.0 x86-clang-build.tar.gz --generate-notes\n");
}
yaml
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// Azure Pipelines Support
// ═══════════════════════════════════════════════════════════════════════════════
/// Azure Pipelines support for X86 Clang CI.
#[derive(Debug, Clone)]
pub struct X86AzureSupport {
pub vm_image: String,
pub pool_name: Option<String>,
pub enable_multi_stage: bool,
pub enable_multi_platform: bool,
}
impl Default for X86AzureSupport {
fn default() -> Self {
Self {
vm_image: "ubuntu-22.04".to_string(),
pool_name: None,
enable_multi_stage: true,
enable_multi_platform: false,
}
}
}
impl X86AzureSupport {
/// Generate an azure-pipelines.yml.
pub fn generate_pipeline(&self, ci_cd: &X86CI_CD) -> X86PipelineResult {
if self.enable_multi_stage {
return self.generate_multi_stage(ci_cd);
}
self.generate_single_stage(ci_cd)
}
fn generate_multi_stage(&self, ci_cd: &X86CI_CD) -> X86PipelineResult {
let mut yaml = String::from("# Generated by X86CI_CD — Azure Pipelines (Multi-Stage)\n\n");
// Trigger
yaml.push_str("trigger:\n branches:\n include:\n - main\n - develop\n paths:\n include:\n - src/**\n\n");
// PR trigger
yaml.push_str("pr:\n branches:\n include:\n - main\n\n");
// Pool
if let Some(ref pool) = self.pool_name {
yaml.push_str(&format!("pool:\n name: {}\n\n", pool));
} else {
yaml.push_str(&format!("pool:\n vmImage: {}\n\n", self.vm_image));
}
// Variables
yaml.push_str("variables:\n CC: clang\n CXX: clang++\n BUILD_TYPE: Release\n\n");
// Stages
yaml.push_str("stages:\n");
// Build stage
yaml.push_str("- stage: Build\n displayName: 'Build Stage'\n jobs:\n - job: Build\n displayName: 'Build X86 Clang'\n strategy:\n matrix:\n");
if self.enable_multi_platform {
yaml.push_str(" x86_64_linux:\n imageName: ubuntu-22.04\n ARCH: x86_64\n i686_linux:\n imageName: ubuntu-22.04\n ARCH: i686\n");
} else {
yaml.push_str(" x86_64_release:\n IMAGE: ubuntu-22.04\n BUILD_TYPE: Release\n");
}
yaml.push_str(" pool:\n vmImage: $(IMAGE)\n");
yaml.push_str(" steps:\n");
yaml.push_str(" - checkout: self\n submodules: true\n");
yaml.push_str(" - script: sudo apt-get update && sudo apt-get install -y cmake ninja-build ccache clang\n");
yaml.push_str(" displayName: 'Install Dependencies'\n");
yaml.push_str(" - script: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=$(BUILD_TYPE) -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++\n");
yaml.push_str(" displayName: 'Configure'\n");
yaml.push_str(" - script: cmake --build build --parallel\n");
yaml.push_str(" displayName: 'Build'\n");
yaml.push_str(" - publish: build/bin\n artifact: binaries-$(BUILD_TYPE)\n");
// Test stage
yaml.push_str("- stage: Test\n displayName: 'Test Stage'\n dependsOn: Build\n jobs:\n - job: Test\n displayName: 'Run Tests'\n pool:\n vmImage: ubuntu-22.04\n steps:\n");
yaml.push_str(" - download: current\n artifact: binaries-Release\n");
yaml.push_str(" - script: cd build && ctest --output-on-failure -j4\n");
yaml.push_str(" displayName: 'Run Tests'\n");
yaml.push_str(" - task: PublishTestResults@2\n inputs:\n testResultsFormat: JUnit\n testResultsFiles: '**/test-results/*.xml'\n testRunTitle: 'X86 Clang Tests'\n");
// Analyze stage
yaml.push_str("- stage: Analyze\n displayName: 'Analysis Stage'\n dependsOn: Build\n jobs:\n - job: Analyze\n displayName: 'Static Analysis'\n pool:\n vmImage: ubuntu-22.04\n steps:\n");
yaml.push_str(" - script: |\n run-clang-tidy -p build -export-fixes analysis.yaml\n cppcheck --enable=all --xml --xml-version=2 src/ 2> cppcheck.xml\n");
yaml.push_str(" displayName: 'Run Analyzers'\n");
yaml.push_str(" - publish: analysis.yaml\n artifact: analysis-reports\n");
X86PipelineResult {
content: yaml,
file_name: "azure-pipelines.yml".to_string(),
ci_system: X86CiSystem::AzurePipelines,
}
}
fn generate_single_stage(&self, ci_cd: &X86CI_CD) -> X86PipelineResult {
let mut yaml = String::from("# Generated by X86CI_CD — Azure Pipelines\n\n");
yaml.push_str("trigger:\n- main\n\n");
if let Some(ref pool) = self.pool_name {
yaml.push_str(&format!("pool:\n name: {}\n\n", pool));
} else {
yaml.push_str(&format!("pool:\n vmImage: {}\n\n", self.vm_image));
}
yaml.push_str("steps:\n");
yaml.push_str("- checkout: self\n");
yaml.push_str("- script: sudo apt-get update && sudo apt-get install -y cmake ninja-build ccache clang\n displayName: 'Install Dependencies'\n");
yaml.push_str("- script: cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release\n displayName: 'Configure'\n");
yaml.push_str("- script: cmake --build build --parallel\n displayName: 'Build'\n");
yaml.push_str("- script: cd build && ctest --output-on-failure\n displayName: 'Test'\n");
yaml.push_str("- publish: build/bin\n artifact: binaries\n");
X86PipelineResult {
content: yaml,
file_name: "azure-pipelines.yml".to_string(),
ci_system: X86CiSystem::AzurePipelines,
}
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// Travis CI Support
// ═══════════════════════════════════════════════════════════════════════════════
/// Travis CI support for X86 Clang CI.
#[derive(Debug, Clone)]
pub struct X86TravisSupport {
pub language: String,
pub os_list: Vec<String>,
pub dist: String,
pub enable_stages: bool,
}
impl Default for X86TravisSupport {
fn default() -> Self {
Self {
language: "cpp".to_string(),
os_list: vec!["linux".to_string()],
dist: "jammy".to_string(),
enable_stages: true,
}
}
}
impl X86TravisSupport {
/// Generate a complete .travis.yml.
pub fn generate_config(&self, ci_cd: &X86CI_CD) -> X86PipelineResult {
let mut yaml = String::from("# Generated by X86CI_CD — Travis CI\n\n");
yaml.push_str(&format!("language: {}\n", self.language));
// OS matrix
yaml.push_str("os:\n");
for os in &self.os_list {
yaml.push_str(&format!(" - {}\n", os));
}
yaml.push_str(&format!("dist: {}\n\n", self.dist));
// Compiler matrix
yaml.push_str("compiler:\n");
for compiler in &ci_cd.pipeline_config.compiler_list {
yaml.push_str(&format!(" - {}\n", compiler));
}
yaml.push('\n');
// Environment matrix
yaml.push_str("env:\n");
for bt in &ci_cd.pipeline_config.build_types {
yaml.push_str(&format!(" - BUILD_TYPE={}\n", bt));
}
yaml.push('\n');
// Cache
yaml.push_str("cache:\n ccache: true\n directories:\n - $HOME/.ccache\n\n");
// Addons
yaml.push_str("addons:\n apt:\n sources:\n - sourceline: 'deb http://apt.llvm.org/jammy/ llvm-toolchain-jammy-17 main'\n key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'\n packages:\n - cmake\n - ninja-build\n - ccache\n - clang-17\n - llvm-17-dev\n\n");
// Before install
yaml.push_str("before_install:\n - export CC=clang-17\n - export CXX=clang++-17\n - ccache -M 500M -F 0\n\n");
// Install
yaml.push_str("install:\n - cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DCMAKE_C_COMPILER=${CC} -DCMAKE_CXX_COMPILER=${CXX}\n - cmake --build build --parallel\n\n");
// Script
yaml.push_str("script:\n - cd build && ctest --output-on-failure -j4\n\n");
// After success
yaml.push_str("after_success:\n - bash <(curl -s https://codecov.io/bash) || echo \"Coverage upload skipped\"\n\n");
// Deploy
if ci_cd.artifacts.release.enabled {
yaml.push_str("deploy:\n provider: releases\n file: build/bin/*\n skip_cleanup: true\n on:\n tags: true\n branch: main\n\n");
}
X86PipelineResult {
content: yaml,
file_name: ".travis.yml".to_string(),
ci_system: X86CiSystem::TravisCI,
}
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// Drone CI Support
// ═══════════════════════════════════════════════════════════════════════════════
/// Drone CI support for X86 Clang CI.
#[derive(Debug, Clone)]
pub struct X86DroneSupport {
pub kind: String,
pub os: String,
pub arch: String,
pub enable_matrix: bool,
}
impl Default for X86DroneSupport {
fn default() -> Self {
Self {
kind: "pipeline".to_string(),
os: "linux".to_string(),
arch: "amd64".to_string(),
enable_matrix: false,
}
}
}
impl X86DroneSupport {
/// Generate a complete .drone.yml pipeline.
pub fn generate_pipeline(&self, ci_cd: &X86CI_CD) -> X86PipelineResult {
let mut yaml = String::from("# Generated by X86CI_CD — Drone CI\n\n");
yaml.push_str(&format!("kind: {}\n", self.kind));
yaml.push_str("type: docker\n");
yaml.push_str(&format!("name: x86-clang-ci\n\n"));
if self.enable_matrix {
yaml.push_str(&self.generate_drone_matrix(ci_cd));
}
// Platform
yaml.push_str(&format!(
"platform:\n os: {}\n arch: {}\n\n",
self.os, self.arch
));
// Trigger
yaml.push_str("trigger:\n branch:\n - main\n - develop\n event:\n - push\n - pull_request\n\n");
// Steps
yaml.push_str("steps:\n");
// Build step
yaml.push_str("- name: build\n image: ubuntu:22.04\n commands:\n");
yaml.push_str(
" - apt-get update && apt-get install -y cmake ninja-build ccache clang\n",
);
yaml.push_str(" - cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++\n");
yaml.push_str(" - cmake --build build --parallel\n\n");
// Test step
yaml.push_str("- name: test\n image: ubuntu:22.04\n commands:\n");
yaml.push_str(" - cd build && ctest --output-on-failure -j4\n");
yaml.push_str(" depends_on:\n - build\n\n");
// Analyze step
yaml.push_str("- name: analyze\n image: ubuntu:22.04\n commands:\n");
yaml.push_str(" - apt-get install -y clang-tidy cppcheck || true\n");
yaml.push_str(" - run-clang-tidy -p build -export-fixes analysis.yaml || true\n");
yaml.push_str(" depends_on:\n - build\n failure: ignore\n\n");
// Deploy step
if ci_cd.artifacts.release.enabled {
yaml.push_str("- name: deploy\n image: plugins/github-release\n settings:\n");
yaml.push_str(" api_key:\n from_secret: github_token\n");
yaml.push_str(" files: build/bin/*\n");
yaml.push_str(" depends_on:\n - test\n");
yaml.push_str(" when:\n branch: main\n event: tag\n\n");
}
X86PipelineResult {
content: yaml,
file_name: ".drone.yml".to_string(),
ci_system: X86CiSystem::DroneCI,
}
}
fn generate_drone_matrix(&self, ci_cd: &X86CI_CD) -> String {
let mut yaml = String::from("---\nkind: pipeline\ntype: docker\nname: x86-clang-ci\n\n");
// Include multiple build types via Drone's matrix trigger
yaml.push_str("trigger:\n branch:\n include:\n - main\n\n");
yaml.push_str("steps:\n- name: build-matrix\n image: ubuntu:22.04\n commands:\n");
for bt in &ci_cd.pipeline_config.build_types {
yaml.push_str(&format!(
" - cmake -B build-{} -DCMAKE_BUILD_TYPE={}\n - cmake --build build-{}\n",
bt.to_lowercase(),
bt,
bt.to_lowercase()
));
}
yaml.push('\n');
yaml
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// Buildkite Support
// ═══════════════════════════════════════════════════════════════════════════════
/// Buildkite pipeline support for X86 Clang CI.
#[derive(Debug, Clone)]
pub struct X86BuildkiteSupport {
pub agent_queue: String,
pub enable_plugins: bool,
pub enable_artifacts: bool,
}
impl Default for X86BuildkiteSupport {
fn default() -> Self {
Self {
agent_queue: "x86_64-linux".to_string(),
enable_plugins: true,
enable_artifacts: true,
}
}
}
impl X86BuildkiteSupport {
/// Generate a complete Buildkite pipeline.yml.
pub fn generate_pipeline(&self, ci_cd: &X86CI_CD) -> X86PipelineResult {
let mut yaml = String::from("# Generated by X86CI_CD — Buildkite\n\n");
// Env
yaml.push_str("env:\n CC: clang\n CXX: clang++\n BUILD_TYPE: Release\n\n");
// Agents
yaml.push_str(&format!("agents:\n queue: {}\n\n", self.agent_queue));
// Steps
yaml.push_str("steps:\n");
// Build step
yaml.push_str(&format!(
" - label: \":hammer: Build\"\n key: build\n command: |\n sudo apt-get update && sudo apt-get install -y cmake ninja-build ccache clang\n cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=$BUILD_TYPE\n cmake --build build --parallel\n"
));
if self.enable_artifacts {
yaml.push_str(
" artifact_paths:\n - build/bin/**/*\n - build/lib/**/*.so\n",
);
}
yaml.push('\n');
// Test step
yaml.push_str(" - label: \":test_tube: Test\"\n key: test\n depends_on: build\n");
yaml.push_str(" command: cd build && ctest --output-on-failure -j4\n");
if self.enable_plugins {
yaml.push_str(" plugins:\n - test-collector#v1.10.2:\n files: \"build/test-results/*.xml\"\n format: junit\n");
}
yaml.push('\n');
// Analyze step
yaml.push_str(" - label: \":mag: Analyze\"\n key: analyze\n depends_on: build\n allow_dependency_failure: true\n");
yaml.push_str(" command: |\n sudo apt-get install -y clang-tidy || true\n run-clang-tidy -p build -export-fixes analysis.yaml || true\n");
yaml.push('\n');
// Deploy step
yaml.push_str(" - label: \":rocket: Deploy\"\n key: deploy\n depends_on:\n - test\n - analyze\n if: build.branch == \"main\"\n");
yaml.push_str(" command: |\n echo \"Deploying build artifacts\"\n");
if self.enable_artifacts {
yaml.push_str(" buildkite-agent artifact upload \"build/bin/**/*\"\n");
}
yaml.push('\n');
// Notify
yaml.push_str("notify:\n - slack:\n channels:\n - \"#ci\"\n");
X86PipelineResult {
content: yaml,
file_name: "pipeline.yml".to_string(),
ci_system: X86CiSystem::Buildkite,
}
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// TeamCity Support
// ═══════════════════════════════════════════════════════════════════════════════
/// TeamCity pipeline support for X86 Clang CI.
#[derive(Debug, Clone)]
pub struct X86TeamCitySupport {
pub project_id: String,
pub use_kotlin_dsl: bool,
pub enable_vcs_trigger: bool,
}
impl Default for X86TeamCitySupport {
fn default() -> Self {
Self {
project_id: "X86ClangCI".to_string(),
use_kotlin_dsl: true,
enable_vcs_trigger: true,
}
}
}
impl X86TeamCitySupport {
/// Generate a TeamCity Kotlin DSL build configuration.
pub fn generate_config(&self, ci_cd: &X86CI_CD) -> X86PipelineResult {
let mut dsl = String::from("// Generated by X86CI_CD — TeamCity Kotlin DSL\n\n");
dsl.push_str("package _Self.buildTypes\n\n");
dsl.push_str("import jetbrains.buildServer.configs.kotlin.v2019_2.*\n");
dsl.push_str("import jetbrains.buildServer.configs.kotlin.v2019_2.buildSteps.*\n");
dsl.push_str("import jetbrains.buildServer.configs.kotlin.v2019_2.triggers.*\n");
dsl.push_str("import jetbrains.buildServer.configs.kotlin.v2019_2.buildFeatures.*\n\n");
// Project
dsl.push_str(&format!(
"object X86ClangProject : Project({{\n id(\"{}\")\n name = \"X86 Clang CI\"\n description = \"Continuous Integration for X86 Clang builds\"\n\n",
self.project_id
));
// VCS root
dsl.push_str(" vcsRoot(X86ClangVcsRoot)\n\n");
// Build type
dsl.push_str(" buildType(X86ClangBuild)\n\n");
// Sub-projects
dsl.push_str(" subProject(X86SanitizerBuilds)\n\n");
// Templates
dsl.push_str(" buildTypesOrder = arrayListOf(X86ClangBuild)\n");
dsl.push_str(" params {\n");
dsl.push_str(" param(\"env.CC\", \"clang\")\n");
dsl.push_str(" param(\"env.CXX\", \"clang++\")\n");
dsl.push_str(" param(\"env.BUILD_TYPE\", \"Release\")\n");
dsl.push_str(" }\n");
dsl.push_str("}})\n\n");
// Build type
dsl.push_str(&format!(
"object X86ClangBuild : BuildType({{\n name = \"Build and Test\"\n description = \"Full CI pipeline for X86 Clang\"\n\n"
));
if self.enable_vcs_trigger {
dsl.push_str(" vcs {\n");
dsl.push_str(" root(X86ClangVcsRoot)\n");
dsl.push_str(" }\n\n");
}
// Build steps
dsl.push_str(" steps {\n");
dsl.push_str(" script {\n");
dsl.push_str(" name = \"Install Dependencies\"\n");
dsl.push_str(" scriptContent = \"\"\"\n");
dsl.push_str(
" apt-get update && apt-get install -y cmake ninja-build ccache clang\n",
);
dsl.push_str(" \"\"\".trimIndent()\n");
dsl.push_str(" }\n");
dsl.push_str(" script {\n");
dsl.push_str(" name = \"Configure and Build\"\n");
dsl.push_str(" scriptContent = \"\"\"\n");
dsl.push_str(
" cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=%env.BUILD_TYPE%\n",
);
dsl.push_str(" cmake --build build --parallel\n");
dsl.push_str(" \"\"\".trimIndent()\n");
dsl.push_str(" }\n");
dsl.push_str(" script {\n");
dsl.push_str(" name = \"Run Tests\"\n");
dsl.push_str(" scriptContent = \"cd build && ctest --output-on-failure -j4\"\n");
dsl.push_str(" }\n");
dsl.push_str(" }\n\n");
// Triggers
if self.enable_vcs_trigger {
dsl.push_str(" triggers {\n");
dsl.push_str(" vcs {\n");
dsl.push_str(" branchFilter = \"\"\"\n");
dsl.push_str(" +:*\n");
dsl.push_str(" \"\"\".trimIndent()\n");
dsl.push_str(" }\n");
dsl.push_str(" }\n\n");
}
// Features
dsl.push_str(" features {\n");
dsl.push_str(" feature {\n");
dsl.push_str(" type = \"commit-status-publisher\"\n");
dsl.push_str(" }\n");
dsl.push_str(" feature {\n");
dsl.push_str(" type = \"xml-report-plugin\"\n");
dsl.push_str(" param(\"xmlReportParsing.reportType\", \"junit\")\n");
dsl.push_str(
" param(\"xmlReportParsing.rules\", \"build/test-results/*.xml\")\n",
);
dsl.push_str(" }\n");
dsl.push_str(" }\n\n");
dsl.push_str("}})\n\n");
// VCS root definition
dsl.push_str(&format!(
"object X86ClangVcsRoot : GitVcsRoot({{\n name = \"X86 Clang Repository\"\n url = \"https://github.com/org/x86-clang-project.git\"\n branch = \"refs/heads/main\"\n branchSpec = \"+:*\"\n authMethod = anonymous()\n}})\n\n"
));
// Sanitizer builds sub-project
dsl.push_str("object X86SanitizerBuilds : Project({\n id(\"X86ClangCI_Sanitizers\")\n name = \"Sanitizer Builds\"\n description = \"Sanitizer-enabled CI builds\"\n})\n");
X86PipelineResult {
content: dsl,
file_name: ".teamcity/settings.kts".to_string(),
ci_system: X86CiSystem::TeamCity,
}
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// Bitbucket Pipelines Support
// ═══════════════════════════════════════════════════════════════════════════════
/// Bitbucket Pipelines support for X86 Clang CI (includes Bamboo).
#[derive(Debug, Clone)]
pub struct X86BitbucketSupport {
/// Docker image for pipelines.
pub image: String,
/// Pipeline definitions.
pub definitions: Vec<X86BitbucketDefinition>,
/// Enable parallel steps.
pub enable_parallel: bool,
/// Enable deployments.
pub enable_deployments: bool,
/// Bamboo project key.
pub bamboo_project_key: String,
/// Bamboo plan key.
pub bamboo_plan_key: String,
}
impl Default for X86BitbucketSupport {
fn default() -> Self {
Self {
image: "atlassian/default-image:3".to_string(),
definitions: vec![],
enable_parallel: true,
enable_deployments: false,
bamboo_project_key: "X86CLANG".to_string(),
bamboo_plan_key: "CI".to_string(),
}
}
}
impl X86BitbucketSupport {
/// Generate a bitbucket-pipelines.yml.
pub fn generate_pipeline(&self, ci_cd: &X86CI_CD) -> X86PipelineResult {
let mut yaml = String::from("# Generated by X86CI_CD — Bitbucket Pipelines\n\n");
yaml.push_str(&format!("image: {}\n\n", self.image));
// Pipelines
yaml.push_str("pipelines:\n");
yaml.push_str(" default:\n");
if self.enable_parallel {
yaml.push_str(" - parallel:\n");
let indent = " ";
yaml.push_str(&format!("{} - step:\n{} name: Build\n{} script:\n{} - apt-get update && apt-get install -y cmake ninja-build ccache clang\n{} - cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++\n{} - cmake --build build --parallel\n", indent, indent, indent, indent, indent, indent));
yaml.push_str(&format!("{} - step:\n{} name: Test\n{} script:\n{} - cd build && ctest --output-on-failure -j4\n", indent, indent, indent, indent));
} else {
yaml.push_str(" - step:\n name: Build and Test\n script:\n - apt-get update && apt-get install -y cmake ninja-build ccache clang\n - cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++\n - cmake --build build --parallel\n - cd build && ctest --output-on-failure -j4\n");
}
// Branch-specific pipelines
yaml.push_str(" branches:\n main:\n");
yaml.push_str(" - step:\n name: Build, Test, and Deploy\n");
yaml.push_str(" script:\n");
yaml.push_str(
" - apt-get update && apt-get install -y cmake ninja-build ccache clang\n",
);
yaml.push_str(" - cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++\n");
yaml.push_str(" - cmake --build build --parallel\n");
yaml.push_str(" - cd build && ctest --output-on-failure -j4\n");
yaml.push_str(" - echo \"Deploying...\"\n");
// Pull request pipelines
yaml.push_str(" pull-requests:\n");
yaml.push_str(" '**':\n");
yaml.push_str(" - step:\n name: Build and Test\n");
yaml.push_str(" script:\n");
yaml.push_str(
" - apt-get update && apt-get install -y cmake ninja-build ccache clang\n",
);
yaml.push_str(" - cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++\n");
yaml.push_str(" - cmake --build build --parallel\n");
yaml.push_str(" - cd build && ctest --output-on-failure -j4\n");
// Deployments
if self.enable_deployments {
yaml.push_str(" custom:\n deploy:\n - step:\n name: Deploy to Production\n deployment: production\n script:\n - echo \"Deploying to production...\"\n - tar czf x86-clang-build.tar.gz build/\n");
}
// Artifacts
yaml.push_str("definitions:\n artifacts:\n binaries:\n path: build/bin/\n\n");
X86PipelineResult {
content: yaml,
file_name: "bitbucket-pipelines.yml".to_string(),
ci_system: X86CiSystem::BitbucketPipelines,
}
}
/// Generate a Bamboo YAML specification.
pub fn generate_bamboo_spec(&self, ci_cd: &X86CI_CD) -> X86PipelineResult {
let mut yaml = String::from("# Generated by X86CI_CD — Bamboo YAML Specs\n\n");
yaml.push_str(&format!(
"version: 2\nplan:\n project-key: {}\n key: {}\n name: X86 Clang CI Build\n\n",
self.bamboo_project_key, self.bamboo_plan_key
));
// Repositories
yaml.push_str("repositories:\n - x86-clang:\n type: git\n url: https://github.com/org/x86-clang-project.git\n branch: main\n\n");
// Stages
yaml.push_str("stages:\n - Build Stage:\n jobs:\n - Build Job\n - Test Stage:\n jobs:\n - Test Job\n - Deploy Stage:\n jobs:\n - Deploy Job\n\n");
// Build Job
yaml.push_str("Build Job:\n tasks:\n - script:\n interpreter: SHELL\n scripts:\n - |\n apt-get update\n apt-get install -y cmake ninja-build ccache clang\n cmake -B build -G Ninja -DCMAKE_BUILD_TYPE=Release\n cmake --build build --parallel\n artifacts:\n - name: binaries\n pattern: build/bin/**\n\n");
// Test Job
yaml.push_str("Test Job:\n tasks:\n - script:\n interpreter: SHELL\n scripts:\n - cd build && ctest --output-on-failure -j4\n requirements:\n - TestRequirement\n\n");
// Deploy Job
yaml.push_str("Deploy Job:\n tasks:\n - script:\n interpreter: SHELL\n scripts:\n - tar czf x86-clang-build.tar.gz build/\n - echo \"Deployment complete\"\n\n");
X86PipelineResult {
content: yaml,
file_name: "bamboo-specs/bamboo.yml".to_string(),
ci_system: X86CiSystem::Bamboo,
}
}
}
/// Bitbucket pipeline custom definition.
#[derive(Debug, Clone)]
pub struct X86BitbucketDefinition {
pub name: String,
pub image: Option<String>,
pub script: Vec<String>,
pub artifacts: Vec<String>,
pub caches: Vec<String>,
}
// ═══════════════════════════════════════════════════════════════════════════════
// Pipeline Result Types
// ═══════════════════════════════════════════════════════════════════════════════
/// Result of generating a pipeline configuration.
#[derive(Debug, Clone)]
pub struct X86PipelineResult {
pub content: String,
pub file_name: String,
pub ci_system: X86CiSystem,
}
impl X86PipelineResult {
/// Write the pipeline configuration to a file.
pub fn write_to(&self, base_dir: &Path) -> io::Result<()> {
let path = base_dir.join(&self.file_name);
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
fs::write(&path, &self.content)?;
Ok(())
}
/// Get a preview of the pipeline content.
pub fn preview(&self) -> String {
let lines: Vec<&str> = self.content.lines().take(20).collect();
format!(
"File: {}\nSystem: {}\n---\n{}\n...",
self.file_name,
self.ci_system,
lines.join("\n")
)
}
}
/// Job execution result from a pipeline run.
#[derive(Debug, Clone)]
pub struct X86CiJobExecution {
pub job_name: String,
pub stage_index: usize,
pub success: bool,
pub duration: Duration,
pub compile_result: Option<X86CompileResult>,
pub test_result: Option<X86TestResult>,
pub artifacts: Vec<X86Artifact>,
pub logs: Vec<String>,
}
/// Pipeline execution result.
#[derive(Debug, Clone)]
pub struct X86PipelineExecution {
pub pipeline: X86PipelineConfig,
pub jobs: Vec<X86CiJobExecution>,
pub overall_success: bool,
pub duration: Duration,
pub quality_gate_results: Vec<X86QualityGateResult>,
pub artifact_summary: X86ArtifactSummary,
}
impl X86PipelineExecution {
/// Check if all quality gates passed.
pub fn all_gates_passed(&self) -> bool {
self.quality_gate_results
.iter()
.all(|g| g.passed || !g.blocking)
}
/// Generate a human-readable summary.
pub fn summary(&self) -> String {
let mut s = format!(
"Pipeline: {}\nOverall: {}\nDuration: {:.1}s\nJobs: {}/{} successful\n",
self.pipeline.name,
if self.overall_success {
"✅ SUCCESS"
} else {
"❌ FAILURE"
},
self.duration.as_secs_f64(),
self.jobs.iter().filter(|j| j.success).count(),
self.jobs.len(),
);
for gate in &self.quality_gate_results {
s.push_str(&format!(
" {} {}: {} (threshold: {}, actual: {:.1})\n",
gate.status_emoji(),
gate.gate_name,
if gate.passed { "PASSED" } else { "FAILED" },
gate.threshold,
gate.actual_value,
));
}
s
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// CI Statistics
// ═══════════════════════════════════════════════════════════════════════════════
/// CI statistics tracking.
#[derive(Debug, Clone)]
pub struct X86CiStats {
pub total_executions: u64,
pub successful_executions: u64,
pub failed_executions: u64,
pub total_duration: Duration,
pub avg_duration: Duration,
pub avg_build_time: Duration,
pub avg_test_time: Duration,
pub flaky_jobs: Vec<String>,
pub slowest_jobs: Vec<String>,
pub cache_hit_rate: f64,
}
impl Default for X86CiStats {
fn default() -> Self {
Self {
total_executions: 0,
successful_executions: 0,
failed_executions: 0,
total_duration: Duration::from_secs(0),
avg_duration: Duration::from_secs(0),
avg_build_time: Duration::from_secs(0),
avg_test_time: Duration::from_secs(0),
flaky_jobs: Vec::new(),
slowest_jobs: Vec::new(),
cache_hit_rate: 0.0,
}
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// Status Badges
// ═══════════════════════════════════════════════════════════════════════════════
/// CI status badge (shields.io format).
#[derive(Debug, Clone)]
pub struct X86CiBadge {
pub label: String,
pub message: String,
pub color: X86CiBadgeColor,
pub style: X86CiBadgeStyle,
pub logo: Option<String>,
pub url: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86CiBadgeColor {
BrightGreen,
Green,
YellowGreen,
Yellow,
Orange,
Red,
LightGrey,
Blue,
}
impl X86CiBadgeColor {
pub fn as_hex(&self) -> &'static str {
match self {
Self::BrightGreen => "#4c1",
Self::Green => "#97CA00",
Self::YellowGreen => "#a4a61d",
Self::Yellow => "#dfb317",
Self::Orange => "#fe7d37",
Self::Red => "#e05d44",
Self::LightGrey => "#9f9f9f",
Self::Blue => "#007ec6",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86CiBadgeStyle {
Plastic,
Flat,
FlatSquare,
ForTheBadge,
}
/// Generate a shields.io badge URL.
pub fn x86_generate_badge_url(label: &str, message: &str, color: &str, style: &str) -> String {
format!(
"https://img.shields.io/badge/{}-{}-{}.svg?style={}",
urlencode(label),
urlencode(message),
urlencode(color),
style
)
}
/// Generate a build status badge.
pub fn x86_build_status_badge(passed: bool, style: &str) -> String {
let (message, color) = if passed {
("passing", "brightgreen")
} else {
("failing", "red")
};
x86_generate_badge_url("X86 Clang CI", message, color, style)
}
/// Generate a code coverage badge.
pub fn x86_coverage_badge(coverage_pct: f64, style: &str) -> String {
let message = format!("{:.0}%", coverage_pct);
let color = if coverage_pct >= 80.0 {
"brightgreen"
} else if coverage_pct >= 60.0 {
"yellow"
} else {
"red"
};
x86_generate_badge_url("coverage", &message, color, style)
}
// ═══════════════════════════════════════════════════════════════════════════════
// CI Dashboard
// ═══════════════════════════════════════════════════════════════════════════════
/// CI dashboard showing pipeline statuses.
#[derive(Debug, Clone)]
pub struct X86CiDashboard {
pub title: String,
pub pipelines: Vec<X86PipelineStatus>,
pub last_updated: Duration,
pub refresh_interval_secs: u64,
}
/// Pipeline status entry for the dashboard.
#[derive(Debug, Clone)]
pub struct X86PipelineStatus {
pub pipeline_name: String,
pub ci_system: X86CiSystem,
pub branch: String,
pub status: X86CiJobStatus,
pub last_build_time: Duration,
pub duration_secs: f64,
pub test_summary: Option<String>,
pub coverage_pct: Option<f64>,
pub url: Option<String>,
}
/// CI job status.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86CiJobStatus {
Success,
Failed,
Running,
Pending,
Cancelled,
Skipped,
Unknown,
}
impl X86CiJobStatus {
pub fn emoji(&self) -> &'static str {
match self {
Self::Success => "✅",
Self::Failed => "❌",
Self::Running => "🔄",
Self::Pending => "⏳",
Self::Cancelled => "🚫",
Self::Skipped => "⏭️",
Self::Unknown => "❓",
}
}
}
impl X86CiDashboard {
/// Generate an HTML dashboard.
pub fn to_html(&self) -> String {
let mut html = String::from("<!DOCTYPE html>\n<html>\n<head>\n");
html.push_str(&format!("<title>{}</title>\n", self.title));
html.push_str("<meta http-equiv=\"refresh\" content=\"");
html.push_str(&self.refresh_interval_secs.to_string());
html.push_str("\">\n");
html.push_str("<style>\n");
html.push_str("body{font-family:-apple-system,system-ui,sans-serif;background:#0d1117;color:#c9d1d9;}\n");
html.push_str("table{border-collapse:collapse;width:100%;}\n");
html.push_str("td,th{border:1px solid #30363d;padding:8px 12px;}\n");
html.push_str("th{background:#161b22;}\n");
html.push_str(".success{color:#3fb950;}.failed{color:#f85149;}.running{color:#58a6ff;}.pending{color:#d29922;}\n");
html.push_str("</style>\n</head>\n<body>\n");
html.push_str(&format!("<h1>{}</h1>\n", self.title));
html.push_str("<table>\n<tr><th>Pipeline</th><th>CI System</th><th>Branch</th><th>Status</th><th>Duration</th><th>Coverage</th></tr>\n");
for pipeline in &self.pipelines {
let status_class = match pipeline.status {
X86CiJobStatus::Success => "success",
X86CiJobStatus::Failed => "failed",
X86CiJobStatus::Running => "running",
X86CiJobStatus::Pending => "pending",
_ => "",
};
let url_link = pipeline.url.as_deref().map_or_else(
|| pipeline.pipeline_name.clone(),
|u| {
format!(
"<a href=\"{}\" style=\"color:#58a6ff;\">{}</a>",
u, pipeline.pipeline_name
)
},
);
html.push_str(&format!(
"<tr><td>{}</td><td>{}</td><td>{}</td><td class=\"{}\">{} {}</td><td>{:.1}s</td><td>{}</td></tr>\n",
url_link,
pipeline.ci_system.name(),
pipeline.branch,
status_class,
pipeline.status.emoji(),
format!("{:?}", pipeline.status),
pipeline.duration_secs,
pipeline.coverage_pct.map_or("-".to_string(), |c| format!("{:.1}%", c)),
));
}
html.push_str("</table>\n</body>\n</html>");
html
}
/// Generate a plain-text table.
pub fn to_table(&self) -> String {
let mut table = format!(
"{:<30} {:<20} {:<12} {:<10} {:>10}\n",
"Pipeline", "CI System", "Branch", "Status", "Duration"
);
table.push_str(&"-".repeat(90));
table.push('\n');
for pipeline in &self.pipelines {
table.push_str(&format!(
"{:<30} {:<20} {:<12} {} {:<10} {:>8.1}s\n",
pipeline.pipeline_name,
pipeline.ci_system.name(),
pipeline.branch,
pipeline.status.emoji(),
format!("{:?}", pipeline.status),
pipeline.duration_secs,
));
}
table
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// CI Migration Helper
// ═══════════════════════════════════════════════════════════════════════════════
/// Helper for migrating between CI systems.
#[derive(Debug, Clone)]
pub struct X86CiMigration {
pub source_system: X86CiSystem,
pub target_system: X86CiSystem,
pub preserve_triggers: bool,
pub preserve_variables: bool,
pub preserve_caching: bool,
pub preserve_artifacts: bool,
}
impl Default for X86CiMigration {
fn default() -> Self {
Self {
source_system: X86CiSystem::TravisCI,
target_system: X86CiSystem::GitHubActions,
preserve_triggers: true,
preserve_variables: true,
preserve_caching: true,
preserve_artifacts: true,
}
}
}
impl X86CiMigration {
pub fn migrate(&self) -> Vec<String> {
vec![format!(
"Migrating from {} to {}...",
self.source_system.name(),
self.target_system.name()
)]
}
pub fn compare_features(&self) -> X86CiFeatureComparison {
X86CiFeatureComparison {
source: self.source_system.clone(),
target: self.target_system.clone(),
source_matrix: self.source_system.supports_matrix(),
target_matrix: self.target_system.supports_matrix(),
source_artifacts: self.source_system.supports_artifacts(),
target_artifacts: self.target_system.supports_artifacts(),
}
}
}
#[derive(Debug, Clone)]
pub struct X86CiFeatureComparison {
pub source: X86CiSystem,
pub target: X86CiSystem,
pub source_matrix: bool,
pub target_matrix: bool,
pub source_artifacts: bool,
pub target_artifacts: bool,
}
impl fmt::Display for X86CiFeatureComparison {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(
f,
"{} -> {} Feature Comparison:",
self.source.name(),
self.target.name()
)?;
writeln!(
f,
" Matrix: {} -> {}",
if self.source_matrix { "✅" } else { "❌" },
if self.target_matrix { "✅" } else { "❌" }
)?;
writeln!(
f,
" Artifacts: {} -> {}",
if self.source_artifacts { "✅" } else { "❌" },
if self.target_artifacts { "✅" } else { "❌" }
)
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// Utility Functions
// ═══════════════════════════════════════════════════════════════════════════════
fn urlencode(input: &str) -> String {
input
.replace(' ', "%20")
.replace('-', "--")
.replace('_', "__")
}
fn xml_escape(input: &str) -> String {
input
.replace('&', "&")
.replace('<', "<")
.replace('>', ">")
.replace('"', """)
.replace('\'', "'")
}
fn chrono_like_now() -> Duration {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or(Duration::from_secs(1700000000))
}
fn chrono_like_now_str() -> String {
let secs = chrono_like_now().as_secs();
// Simple ISO 8601 format
let days_since_epoch = secs / 86400;
let time_of_day = secs % 86400;
let hours = time_of_day / 3600;
let minutes = (time_of_day % 3600) / 60;
let seconds = time_of_day % 60;
format!(
"2024-{:02}-{:02}T{:02}:{:02}:{:02}Z",
days_since_epoch % 365 / 30 + 1,
days_since_epoch % 30 + 1,
hours,
minutes,
seconds
)
}
// ═══════════════════════════════════════════════════════════════════════════════
// CI Cache Manager
// ═══════════════════════════════════════════════════════════════════════════════
/// CI cache manager for build acceleration across X86 CI pipelines.
#[derive(Debug, Clone)]
pub struct X86CICacheManager {
pub ccache_enabled: bool,
pub ccache_max_size: String,
pub sccache_enabled: bool,
pub sccache_max_size: String,
pub cargo_cache: bool,
pub npm_cache: bool,
pub gradle_cache: bool,
pub pip_cache: bool,
pub docker_layer_cache: bool,
}
impl Default for X86CICacheManager {
fn default() -> Self {
Self {
ccache_enabled: true,
ccache_max_size: X86_CI_CCACHE_SIZE.to_string(),
sccache_enabled: false,
sccache_max_size: X86_CI_SCCACHE_SIZE.to_string(),
cargo_cache: false,
npm_cache: false,
gradle_cache: false,
pip_cache: false,
docker_layer_cache: true,
}
}
}
impl X86CICacheManager {
/// Generate cache configuration for GitHub Actions.
pub fn generate_cache_actions_config(&self) -> String {
let mut config = String::new();
if self.ccache_enabled {
config.push_str(&format!(
"- name: Cache ccache\n uses: actions/cache@v3\n with:\n path: ~/.ccache\n key: {}-${{{{ runner.os }}}}-${{{{ hashFiles('**/*.cpp', '**/*.c', '**/*.h') }}}}\n restore-keys: {}-${{{{ runner.os }}}}-\n",
X86_CI_CCACHE_KEY, X86_CI_CCACHE_KEY
));
}
if self.sccache_enabled {
config.push_str(&format!(
"- name: Cache sccache\n uses: actions/cache@v3\n with:\n path: ~/.cache/sccache\n key: sccache-${{{{ runner.os }}}}\n"
));
}
config
}
/// Generate GitHub-specific cache steps.
pub fn generate_github_cache_steps(&self) -> Vec<String> {
let mut steps = Vec::new();
if self.ccache_enabled {
steps.push(format!(
"- name: Setup ccache\n uses: hendrikmuhs/ccache-action@v1\n with:\n key: {}\n max-size: {}",
X86_CI_CCACHE_KEY, self.ccache_max_size
));
}
if self.cargo_cache {
steps.push(format!(
"- name: Cache Cargo\n uses: actions/cache@v3\n with:\n path: ~/.cargo\n key: cargo-${{{{ runner.os }}}}"
));
}
steps
}
/// Generate GitLab CI cache configuration.
pub fn generate_gitlab_cache_config(&self) -> String {
let mut config = String::from("cache:\n key: \"$CI_COMMIT_REF_SLUG\"\n paths:\n");
if self.ccache_enabled {
config.push_str(" - .ccache/\n");
}
if self.cargo_cache {
config.push_str(" - .cargo/\n");
}
if self.pip_cache {
config.push_str(" - .cache/pip/\n");
}
config.push_str(" policy: pull-push\n");
config
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// CI Security Scan
// ═══════════════════════════════════════════════════════════════════════════════
/// CI security scanning integration for X86 Clang pipelines.
#[derive(Debug, Clone)]
pub struct X86CISecurityScan {
pub sast_enabled: bool,
pub secret_scanning: bool,
pub dependency_scanning: bool,
pub container_scanning: bool,
pub license_compliance: bool,
pub codeql_languages: Vec<String>,
pub trivy_enabled: bool,
pub grype_enabled: bool,
}
impl Default for X86CISecurityScan {
fn default() -> Self {
Self {
sast_enabled: true,
secret_scanning: true,
dependency_scanning: false,
container_scanning: false,
license_compliance: false,
codeql_languages: vec!["cpp".to_string(), "python".to_string()],
trivy_enabled: false,
grype_enabled: false,
}
}
}
impl X86CISecurityScan {
/// Generate security scanning steps for GitHub Actions.
pub fn generate_github_security_steps(&self) -> Vec<String> {
let mut steps = Vec::new();
if self.sast_enabled {
steps.push("- name: Initialize CodeQL\n uses: github/codeql-action/init@v2\n with:\n languages: cpp".to_string());
steps.push(
"- name: Perform CodeQL Analysis\n uses: github/codeql-action/analyze@v2"
.to_string(),
);
}
if self.secret_scanning {
steps.push("- name: Secret Scanning\n uses: aquasecurity/trivy-action@master\n with:\n scan-type: fs\n scanners: secret".to_string());
}
if self.dependency_scanning {
steps.push(
"- name: Dependency Review\n uses: actions/dependency-review-action@v3"
.to_string(),
);
}
if self.container_scanning {
steps.push("- name: Container Scan\n uses: aquasecurity/trivy-action@master\n with:\n image-ref: x86-clang-project:latest\n format: sarif\n output: trivy-results.sarif".to_string());
}
steps
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// CI Environment Manager
// ═══════════════════════════════════════════════════════════════════════════════
/// CI environment manager for X86 build environments.
#[derive(Debug, Clone)]
pub struct X86CIEnvironmentManager {
pub default_runner: String,
pub environments: Vec<X86CIEnvironment>,
}
impl Default for X86CIEnvironmentManager {
fn default() -> Self {
Self {
default_runner: "ubuntu-22.04".to_string(),
environments: vec![
X86CIEnvironment {
name: "linux-x86_64".to_string(),
os: "linux".to_string(),
arch: "x86_64".to_string(),
runner: "ubuntu-22.04".to_string(),
compiler: "clang".to_string(),
docker_image: Some("ubuntu:22.04".to_string()),
},
X86CIEnvironment {
name: "linux-i686".to_string(),
os: "linux".to_string(),
arch: "i686".to_string(),
runner: "ubuntu-22.04".to_string(),
compiler: "clang".to_string(),
docker_image: Some("i386/ubuntu:22.04".to_string()),
},
X86CIEnvironment {
name: "macos-x86_64".to_string(),
os: "macos".to_string(),
arch: "x86_64".to_string(),
runner: "macos-13".to_string(),
compiler: "clang".to_string(),
docker_image: None,
},
X86CIEnvironment {
name: "windows-x86_64".to_string(),
os: "windows".to_string(),
arch: "x86_64".to_string(),
runner: "windows-2022".to_string(),
compiler: "clang-cl".to_string(),
docker_image: None,
},
],
}
}
}
impl X86CIEnvironmentManager {
/// Get an environment by name.
pub fn get_environment(&self, name: &str) -> Option<&X86CIEnvironment> {
self.environments.iter().find(|e| e.name == name)
}
/// Get the GitHub runner label for an OS and arch.
pub fn github_runner_for(&self, os: &str, arch: &str) -> String {
self.environments
.iter()
.find(|e| e.os == os && e.arch == arch)
.map(|e| e.runner.clone())
.unwrap_or_else(|| self.default_runner.clone())
}
}
/// A CI environment definition.
#[derive(Debug, Clone)]
pub struct X86CIEnvironment {
pub name: String,
pub os: String,
pub arch: String,
pub runner: String,
pub compiler: String,
pub docker_image: Option<String>,
}
// ═══════════════════════════════════════════════════════════════════════════════
// CI Pipeline Analytics
// ═══════════════════════════════════════════════════════════════════════════════
/// CI pipeline analytics for tracking trends and metrics.
#[derive(Debug, Clone)]
pub struct X86CIPipelineAnalytics {
pub runs: Vec<X86CIPipelineAnalyticsEntry>,
}
impl Default for X86CIPipelineAnalytics {
fn default() -> Self {
Self { runs: Vec::new() }
}
}
impl X86CIPipelineAnalytics {
/// Record a pipeline execution.
pub fn record_run(&mut self, execution: &X86PipelineExecution) {
self.runs.push(X86CIPipelineAnalyticsEntry {
success: execution.overall_success,
duration: execution.duration,
job_count: execution.jobs.len(),
timestamp: chrono_like_now(),
});
}
/// Calculate success rate.
pub fn success_rate(&self) -> f64 {
if self.runs.is_empty() {
return 0.0;
}
let successful = self.runs.iter().filter(|r| r.success).count();
(successful as f64 / self.runs.len() as f64) * 100.0
}
/// Calculate Mean Time To Recovery (MTTR).
pub fn mean_time_to_recovery(&self) -> Option<Duration> {
let failures: Vec<_> = self.runs.iter().filter(|r| !r.success).collect();
if failures.is_empty() {
return None;
}
let total_duration: Duration = failures.iter().map(|r| r.duration).sum();
Some(total_duration / failures.len() as u32)
}
/// Calculate deployment velocity (runs per day).
pub fn deployment_velocity(&self) -> Option<f64> {
if self.runs.len() < 2 {
return None;
}
let first = self.runs.first().unwrap().timestamp;
let last = self.runs.last().unwrap().timestamp;
let timespan = Duration::from_secs((last.as_secs().saturating_sub(first.as_secs())).max(1));
let days = timespan.as_secs_f64() / 86400.0;
if days < 0.01 {
return None;
}
Some(self.runs.len() as f64 / days)
}
}
/// An analytics entry for a single pipeline run.
#[derive(Debug, Clone)]
pub struct X86CIPipelineAnalyticsEntry {
pub success: bool,
pub duration: Duration,
pub job_count: usize,
pub timestamp: Duration,
}
// ═══════════════════════════════════════════════════════════════════════════════
// CI Release Manager
// ═══════════════════════════════════════════════════════════════════════════════
/// CI release manager for automated versioning and publishing.
#[derive(Debug, Clone)]
pub struct X86CIReleaseManager {
pub draft: bool,
pub prerelease: bool,
pub generate_release_notes: bool,
pub publish_to: Vec<X86ReleaseTarget>,
pub version_prefix: String,
}
impl Default for X86CIReleaseManager {
fn default() -> Self {
Self {
draft: false,
prerelease: false,
generate_release_notes: true,
publish_to: vec![X86ReleaseTarget::GitHub],
version_prefix: "v".to_string(),
}
}
}
impl X86CIReleaseManager {
/// Generate a release notes template.
pub fn generate_release_notes_template(&self, version: &str) -> String {
format!(
"# Release {}v{}\n\n## Changelog\n\n### Added\n- \n\n### Changed\n- \n\n### Fixed\n- \n\n### Security\n- \n\n## Artifacts\n\n| Platform | Format | File |\n|----------|--------|------|\n| x86_64-linux | .tar.gz | x86-clang-project-{}-x86_64-linux.tar.gz |\n| x86_64-linux | .deb | x86-clang-project_{}_amd64.deb |\n| x86_64-linux | .rpm | x86-clang-project-{}-1.x86_64.rpm |\n| x86_64-linux | .AppImage | x86-clang-project-{}-x86_64.AppImage |\n\n## SBOM\n\nSPDX SBOM is included in the release assets.\n",
self.version_prefix, version, version, version, version, version
)
}
/// Bump a semantic version.
pub fn bump_version(&self, current: &str, bump: X86VersionBump) -> String {
let parts: Vec<&str> = current.split('.').collect();
if parts.len() != 3 {
return current.to_string();
}
let major: u32 = parts[0].parse().unwrap_or(0);
let minor: u32 = parts[1].parse().unwrap_or(0);
let patch: u32 = parts[2].parse().unwrap_or(0);
match bump {
X86VersionBump::Major => format!("{}.0.0", major + 1),
X86VersionBump::Minor => format!("{}.{}.0", major, minor + 1),
X86VersionBump::Patch => format!("{}.{}.{}", major, minor, patch + 1),
}
}
}
/// Version bump type for semantic versioning.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum X86VersionBump {
Major,
Minor,
Patch,
}
// ═══════════════════════════════════════════════════════════════════════════════
// Multi-Arch Build Config
// ═══════════════════════════════════════════════════════════════════════════════
/// Multi-architecture build configuration for X86 targets.
#[derive(Debug, Clone)]
pub struct X86MultiArchConfig {
pub architectures: Vec<String>,
pub docker_buildx: bool,
pub qemu_emulation: bool,
}
impl Default for X86MultiArchConfig {
fn default() -> Self {
Self {
architectures: vec!["x86_64".to_string(), "i686".to_string()],
docker_buildx: true,
qemu_emulation: false,
}
}
}
impl X86MultiArchConfig {
/// Get Docker platform strings for the configured architectures.
pub fn docker_platforms(&self) -> Vec<String> {
self.architectures
.iter()
.map(|arch| match arch.as_str() {
"x86_64" => "linux/amd64".to_string(),
"i686" | "i386" => "linux/386".to_string(),
a => format!("linux/{}", a),
})
.collect()
}
/// Generate build steps for multi-arch compilation.
pub fn generate_build_steps(&self) -> Vec<String> {
let mut steps = Vec::new();
if self.docker_buildx {
steps.push("docker buildx create --name x86-multiarch --use".to_string());
for platform in self.docker_platforms() {
steps.push(format!(
"docker buildx build --platform {} --load -t x86-clang-project:{}",
platform,
platform.replace('/', "-")
));
}
steps.push("docker buildx rm x86-multiarch".to_string());
}
steps
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// Pipeline Templates
// ═══════════════════════════════════════════════════════════════════════════════
/// Predefined pipeline template for common X86 CI configurations.
#[derive(Debug, Clone)]
pub struct X86PipelineTemplate {
pub name: String,
pub description: String,
pub stages: Vec<String>,
pub compilation: X86CompilationStrategy,
pub testing: X86TestStrategy,
}
impl Default for X86PipelineTemplate {
fn default() -> Self {
Self {
name: "x86-clang-standard".to_string(),
description: "Standard CI pipeline for X86 Clang builds".to_string(),
stages: vec![
"build".to_string(),
"test".to_string(),
"analyze".to_string(),
"deploy".to_string(),
],
compilation: X86CompilationStrategy::default(),
testing: X86TestStrategy::default(),
}
}
}
impl X86PipelineTemplate {
/// Add a stage to the template.
pub fn add_stage(&mut self, stage: &str) {
if !self.stages.contains(&stage.to_string()) {
self.stages.push(stage.to_string());
}
}
/// Convert the template into a full X86CI_CD configuration.
pub fn into_ci_cd(&self, system: &X86CiSystem) -> X86CI_CD {
let mut ci = X86CI_CD::new(system.clone());
ci.compilation = self.compilation.clone();
ci.testing = self.testing.clone();
ci.pipeline_config.stages = self
.stages
.iter()
.enumerate()
.map(|(i, name)| X86PipelineStage {
name: name.clone(),
stage_type: match i {
0 => X86StageType::Build,
1 => X86StageType::Test,
2 => X86StageType::Analyze,
3 => X86StageType::Deploy,
_ => X86StageType::Custom,
},
compilation: if i == 0 {
Some(X86CompileConfig::default())
} else {
None
},
test_config: if i == 1 {
Some(X86TestConfig::default())
} else {
None
},
dependencies: if i > 0 {
vec![self.stages[i - 1].clone()]
} else {
vec![]
},
timeout_minutes: X86_CI_JOB_TIMEOUT_MINUTES,
allow_failure: i == 2,
})
.collect();
ci
}
/// Get all predefined templates.
pub fn predefined_templates() -> Vec<Self> {
vec![
Self::default(),
Self {
name: "x86-clang-sanitizer".to_string(),
description: "Sanitizer-heavy CI pipeline".to_string(),
compilation: X86CompilationStrategy::debug_full(),
testing: X86TestStrategy::comprehensive(),
..Default::default()
},
Self {
name: "x86-clang-release".to_string(),
description: "Release-focused CI pipeline with LTO and PGO".to_string(),
compilation: X86CompilationStrategy::release_lto(),
testing: X86TestStrategy {
coverage_tool: X86CoverageTool::LLVMCov,
..X86TestStrategy::comprehensive()
},
..Default::default()
},
Self {
name: "x86-clang-minimal".to_string(),
description: "Minimal CI pipeline for quick checks".to_string(),
stages: vec!["build".to_string(), "test".to_string()],
..Default::default()
},
]
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// CI Schedule
// ═══════════════════════════════════════════════════════════════════════════════
/// CI schedule configuration with cron expression.
#[derive(Debug, Clone)]
pub struct X86CISchedule {
pub cron: String,
pub description: String,
}
impl X86CISchedule {
/// Check if the cron expression is valid (basic validation).
pub fn is_valid_cron(&self) -> bool {
let fields: Vec<&str> = self.cron.split_whitespace().collect();
fields.len() == 5
}
/// Return a human-readable description of the schedule.
pub fn human_readable(&self) -> &str {
if !self.is_valid_cron() {
return "Invalid cron expression";
}
let fields = self.cron_fields();
// Simple mapping for common patterns
if fields[0] == "0" && fields[4] == "0" {
return "At midnight on Sunday";
}
if fields[0] == "0" && fields[1] == "2" && fields[4] == "0" {
return "At 02:00 on Sunday";
}
if fields[0] == "*/15" {
return "Every 15 minutes";
}
if fields[0] == "0" && fields[1] == "0" {
return "Daily at midnight";
}
&self.description
}
/// Extract cron fields.
pub fn cron_fields(&self) -> Vec<&str> {
self.cron.split_whitespace().collect()
}
}
// ═══════════════════════════════════════════════════════════════════════════════
// CI Run History
// ═══════════════════════════════════════════════════════════════════════════════
/// CI run history for tracking pipeline executions over time.
#[derive(Debug, Clone)]
pub struct X86CIRunHistory {
pub entries: Vec<X86CIRunHistoryEntry>,
}
impl Default for X86CIRunHistory {
fn default() -> Self {
Self {
entries: Vec::new(),
}
}
}
impl X86CIRunHistory {
/// Record a pipeline execution.
pub fn record(&mut self, execution: &X86PipelineExecution, system: &X86CiSystem) {
self.entries.push(X86CIRunHistoryEntry {
timestamp: chrono_like_now(),
ci_system: system.clone(),
success: execution.overall_success,
duration_secs: execution.duration.as_secs_f64(),
job_count: execution.jobs.len(),
failed_job_count: execution.jobs.iter().filter(|j| !j.success).count(),
branch: "main".to_string(),
commit_sha: "unknown".to_string(),
});
}
/// Get the latest run entry.
pub fn latest(&self) -> Option<&X86CIRunHistoryEntry> {
self.entries.last()
}
/// Identify potentially flaky jobs from history.
pub fn identify_flaky_jobs(&self, threshold: usize) -> Vec<String> {
let mut failure_counts: HashMap<String, usize> = HashMap::new();
let mut success_counts: HashMap<String, usize> = HashMap::new();
for entry in &self.entries {
let key = format!("{}@{}", entry.ci_system.name(), entry.branch);
if entry.success {
*success_counts.entry(key.clone()).or_insert(0) += 1;
} else {
*failure_counts.entry(key.clone()).or_insert(0) += 1;
}
}
let mut flaky = Vec::new();
for (key, failures) in &failure_counts {
let successes = success_counts.get(key).copied().unwrap_or(0);
if *failures >= threshold && successes > 0 {
flaky.push(key.clone());
}
}
flaky
}
}
/// A single run history entry.
#[derive(Debug, Clone)]
pub struct X86CIRunHistoryEntry {
pub timestamp: Duration,
pub ci_system: X86CiSystem,
pub success: bool,
pub duration_secs: f64,
pub job_count: usize,
pub failed_job_count: usize,
pub branch: String,
pub commit_sha: String,
}
// ═══════════════════════════════════════════════════════════════════════════════
// 7. Test Suite
// ═══════════════════════════════════════════════════════════════════════════════
#[cfg(test)]
mod tests {
use super::*;
// ── CI System Tests ────────────────────────────────────────────────────
#[test]
fn test_ci_system_names() {
assert_eq!(X86CiSystem::GitHubActions.name(), "GitHub Actions");
assert_eq!(X86CiSystem::GitLabCI.name(), "GitLab CI/CD");
assert_eq!(X86CiSystem::Jenkins.name(), "Jenkins");
assert_eq!(X86CiSystem::CircleCI.name(), "CircleCI");
assert_eq!(
X86CiSystem::BitbucketPipelines.name(),
"Bitbucket Pipelines"
);
assert_eq!(X86CiSystem::Bamboo.name(), "Atlassian Bamboo");
assert_eq!(X86CiSystem::Custom("test".to_string()).name(), "Custom CI");
}
#[test]
fn test_ci_system_config_files() {
assert_eq!(
X86CiSystem::GitHubActions.config_file(),
".github/workflows/ci.yml"
);
assert_eq!(X86CiSystem::GitLabCI.config_file(), ".gitlab-ci.yml");
assert_eq!(X86CiSystem::Jenkins.config_file(), "Jenkinsfile");
assert_eq!(
X86CiSystem::BitbucketPipelines.config_file(),
"bitbucket-pipelines.yml"
);
assert_eq!(X86CiSystem::Bamboo.config_file(), "bamboo-specs/bamboo.yml");
}
#[test]
fn test_ci_system_matrix_support() {
assert!(X86CiSystem::GitHubActions.supports_matrix());
assert!(X86CiSystem::GitLabCI.supports_matrix());
assert!(X86CiSystem::Jenkins.supports_matrix());
assert!(X86CiSystem::CircleCI.supports_matrix());
assert!(X86CiSystem::AzurePipelines.supports_matrix());
assert!(X86CiSystem::TravisCI.supports_matrix());
assert!(!X86CiSystem::Bamboo.supports_matrix());
}
#[test]
fn test_custom_ci_system() {
let custom = X86CiSystem::Custom("MyCustomCI".to_string());
assert_eq!(custom.to_string(), "Custom(MyCustomCI)");
assert!(!custom.supports_matrix());
}
// ── X86CI_CD Tests ─────────────────────────────────────────────────────
#[test]
fn test_x86_ci_cd_default() {
let ci = X86CI_CD::default();
assert_eq!(ci.ci_system, X86CiSystem::GitHubActions);
assert_eq!(ci.pipeline_config.stages.len(), 4);
assert_eq!(ci.pipeline_config.retry_count, X86_CI_DEFAULT_RETRY_COUNT);
}
#[test]
fn test_x86_ci_cd_new() {
let ci = X86CI_CD::new(X86CiSystem::GitLabCI);
assert_eq!(ci.ci_system, X86CiSystem::GitLabCI);
}
#[test]
fn test_x86_ci_cd_generate_pipeline() {
let ci = X86CI_CD::new(X86CiSystem::GitHubActions);
let result = ci.generate_pipeline();
assert!(!result.content.is_empty());
assert!(result.content.contains("name: X86 Clang CI"));
assert!(result.content.contains("jobs:"));
}
#[test]
fn test_x86_ci_cd_generate_pipeline_all_systems() {
let systems = vec![
X86CiSystem::GitHubActions,
X86CiSystem::GitLabCI,
X86CiSystem::Jenkins,
X86CiSystem::CircleCI,
X86CiSystem::AzurePipelines,
X86CiSystem::TravisCI,
X86CiSystem::DroneCI,
X86CiSystem::Buildkite,
X86CiSystem::TeamCity,
X86CiSystem::BitbucketPipelines,
X86CiSystem::Bamboo,
];
for system in systems {
let ci = X86CI_CD::new(system);
let result = ci.generate_pipeline();
assert!(
!result.content.is_empty(),
"Empty pipeline for {:?}",
result.ci_system
);
assert!(
!result.file_name.is_empty(),
"Empty filename for {:?}",
result.ci_system
);
}
}
#[test]
fn test_x86_ci_cd_execute_pipeline() {
let ci = X86CI_CD::new(X86CiSystem::GitHubActions);
let execution = ci.execute_pipeline();
assert!(!execution.jobs.is_empty());
assert_eq!(execution.jobs.len(), ci.pipeline_config.stages.len());
}
// ── Build Matrix Tests ─────────────────────────────────────────────────
#[test]
fn test_build_matrix_generation() {
let ci = X86CI_CD::default();
let matrix = ci.generate_matrix();
assert_eq!(matrix.operating_systems.len(), 2);
assert_eq!(matrix.compilers.len(), 3);
assert_eq!(matrix.build_types.len(), 4);
assert_eq!(matrix.sanitizers.len(), 4);
assert_eq!(matrix.arch_variants.len(), 4);
}
#[test]
fn test_build_matrix_rules() {
let ci = X86CI_CD::default();
let matrix = ci.generate_matrix();
let rules = matrix.generate_rules();
assert!(!rules.is_empty());
}
#[test]
fn test_build_matrix_too_large() {
let mut ci = X86CI_CD::default();
ci.pipeline_config.os_list = (0..10).map(|i| format!("os-{}", i)).collect();
ci.pipeline_config.compiler_list = (0..10).map(|i| format!("compiler-{}", i)).collect();
let matrix = ci.generate_matrix();
assert!(matrix.total_combinations() > X86_CI_MAX_MATRIX_SIZE);
assert!(matrix.is_too_large());
}
#[test]
fn test_build_matrix_combinations_count() {
let ci = X86CI_CD::default();
let matrix = ci.generate_matrix();
let expected = 2 * 3 * 4 * 4 * 4; // os × compiler × build_type × sanitizer × arch
assert_eq!(matrix.total_combinations(), expected);
}
// ── Architecture Tests ─────────────────────────────────────────────────
#[test]
fn test_arch_variant_triple() {
assert_eq!(
X86ArchVariant::X86_64.target_triple(),
"x86_64-unknown-linux-gnu"
);
assert_eq!(
X86ArchVariant::I386.target_triple(),
"i686-unknown-linux-gnu"
);
}
#[test]
fn test_arch_variant_march() {
assert_eq!(X86ArchVariant::X86_64_V3.march_flag(), "x86-64-v3");
assert_eq!(X86ArchVariant::X86_64_V4.march_flag(), "x86-64-v4");
}
#[test]
fn test_arch_variant_default_cflags() {
assert!(X86ArchVariant::X86_64_V2
.default_cflags()
.contains("x86-64-v2"));
assert!(X86ArchVariant::X86_64_V4
.default_cflags()
.contains("avx512"));
}
#[test]
fn test_arch_variant_bits() {
assert_eq!(X86ArchVariant::I386.bits(), 32);
assert_eq!(X86ArchVariant::X86_64.bits(), 64);
assert_eq!(X86ArchVariant::X86_64_V4.bits(), 64);
}
// ── Compilation Strategy Tests ─────────────────────────────────────────
#[test]
fn test_compilation_strategy_debug() {
let strategy = X86CompilationStrategy::debug_full();
assert_eq!(strategy.build_type, X86BuildType::Debug);
assert_eq!(strategy.optimization, X86OptLevel::O0);
assert!(strategy.sanitizers.contains(&X86Sanitizer::Address));
}
#[test]
fn test_compilation_strategy_release_lto() {
let strategy = X86CompilationStrategy::release_lto();
assert_eq!(strategy.build_type, X86BuildType::Release);
assert_eq!(strategy.optimization, X86OptLevel::O3);
assert_eq!(strategy.lto, X86LTOMode::Thin);
}
#[test]
fn test_compilation_strategy_sanitizer() {
let strategy =
X86CompilationStrategy::sanitizer_build(vec![X86Sanitizer::Thread, X86Sanitizer::Leak]);
assert_eq!(strategy.build_type, X86BuildType::Debug);
assert_eq!(strategy.sanitizers.len(), 2);
}
#[test]
fn test_compilation_strategy_fuzzer() {
let strategy = X86CompilationStrategy::fuzzer_build("my_fuzz_target".to_string());
assert_eq!(strategy.fuzzer.unwrap().target, "my_fuzz_target");
}
#[test]
fn test_compilation_strategy_cross_compile() {
let strategy = X86CompilationStrategy::cross_compile("i686-unknown-linux-gnu".to_string());
assert_eq!(strategy.cross_compile.unwrap(), "i686-unknown-linux-gnu");
}
#[test]
fn test_compilation_strategy_pgo() {
let strategy = X86CompilationStrategy::pgo_build(X86PGOStage::Generate);
assert!(strategy.pgo.is_some());
assert_eq!(strategy.pgo.unwrap().stage, X86PGOStage::Generate);
}
// ── Build Type / Opt Level Tests ───────────────────────────────────────
#[test]
fn test_build_type_cmake_name() {
assert_eq!(X86BuildType::Debug.cmake_name(), "Debug");
assert_eq!(X86BuildType::Release.cmake_name(), "Release");
assert_eq!(X86BuildType::RelWithDebInfo.cmake_name(), "RelWithDebInfo");
assert_eq!(X86BuildType::MinSizeRel.cmake_name(), "MinSizeRel");
}
#[test]
fn test_build_type_debug_info() {
assert!(X86BuildType::Debug.has_debug_info());
assert!(X86BuildType::RelWithDebInfo.has_debug_info());
assert!(!X86BuildType::Release.has_debug_info());
}
#[test]
fn test_opt_level_flags() {
assert_eq!(X86OptLevel::O0.as_flag(), "O0");
assert_eq!(X86OptLevel::O3.as_flag(), "O3");
assert_eq!(X86OptLevel::Os.as_flag(), "Os");
assert_eq!(X86OptLevel::Oz.as_flag(), "Oz");
}
#[test]
fn test_lto_mode_flags() {
assert_eq!(X86LTOMode::None.clang_flag(), "");
assert_eq!(X86LTOMode::Full.clang_flag(), "-flto");
assert_eq!(X86LTOMode::Thin.clang_flag(), "-flto=thin");
}
// ── Sanitizer Tests ────────────────────────────────────────────────────
#[test]
fn test_sanitizer_flags() {
assert_eq!(X86Sanitizer::Address.clang_flag(), "address");
assert_eq!(X86Sanitizer::Thread.clang_flag(), "thread");
assert_eq!(X86Sanitizer::Memory.clang_flag(), "memory");
assert_eq!(X86Sanitizer::CFI.clang_flag(), "cfi");
}
#[test]
fn test_sanitizer_cmake_flags() {
assert_eq!(X86Sanitizer::Address.cmake_flag(), "USE_SANITIZER_ADDRESS");
assert_eq!(
X86Sanitizer::Undefined.cmake_flag(),
"USE_SANITIZER_UNDEFINED"
);
}
#[test]
fn test_sanitizer_display_names() {
assert!(X86Sanitizer::Address.display_name().contains("ASan"));
assert!(X86Sanitizer::Thread.display_name().contains("TSan"));
}
#[test]
fn test_sanitizer_all_variants() {
let sanitizers = vec![
X86Sanitizer::Address,
X86Sanitizer::Undefined,
X86Sanitizer::Thread,
X86Sanitizer::Memory,
X86Sanitizer::Leak,
X86Sanitizer::HWAddress,
X86Sanitizer::DataFlow,
X86Sanitizer::SafeStack,
X86Sanitizer::CFI,
];
for s in &sanitizers {
assert!(!s.clang_flag().is_empty());
assert!(!s.display_name().is_empty());
let _ = format!("{}", s);
}
}
// ── Compile Simulation Tests ───────────────────────────────────────────
#[test]
fn test_compile_simulate() {
let strategy = X86CompilationStrategy::default();
let config = X86CompileConfig::default();
let result = strategy.simulate_compile(&config);
assert!(result.success);
assert!(result.object_files > 0);
assert!(result.binary_size_bytes > 0);
}
#[test]
fn test_cmake_flags_generation() {
let strategy = X86CompilationStrategy::release_lto();
let flags = strategy.to_cmake_flags();
assert!(flags.iter().any(|f| f.contains("Release")));
assert!(flags.iter().any(|f| f.contains("LTO")));
}
#[test]
fn test_cmake_flags_lto() {
let mut strategy = X86CompilationStrategy::default();
strategy.lto = X86LTOMode::Thin;
let flags = strategy.to_cmake_flags();
assert!(flags.iter().any(|f| f.contains("THIN")));
}
#[test]
fn test_cmake_flags_sanitizer() {
let mut strategy = X86CompilationStrategy::default();
strategy.sanitizers = vec![X86Sanitizer::Address, X86Sanitizer::Undefined];
let flags = strategy.to_cmake_flags();
assert!(flags.iter().any(|f| f.contains("sanitize")));
}
// ── Fuzzer Tests ───────────────────────────────────────────────────────
#[test]
fn test_fuzzer_types() {
assert!(X86FuzzerType::LibFuzzer.clang_flag().is_some());
assert!(X86FuzzerType::AFL.clang_flag().is_none());
assert!(X86FuzzerType::AFLPlusPlus.clang_flag().is_none());
assert!(X86FuzzerType::Honggfuzz.clang_flag().is_none());
}
// ── Test Strategy Tests ────────────────────────────────────────────────
#[test]
fn test_test_strategy_comprehensive() {
let strategy = X86TestStrategy::comprehensive();
assert_eq!(strategy.unit_framework, X86TestFramework::GTest);
assert_eq!(strategy.integration_framework, X86TestFramework::Catch2);
assert_eq!(strategy.coverage_tool, X86CoverageTool::LLVMCov);
assert!(strategy.static_analyzers.len() >= 3);
assert!(strategy.dynamic_analyzers.len() >= 2);
}
#[test]
fn test_test_strategy_simulate() {
let strategy = X86TestStrategy::comprehensive();
let config = X86TestConfig::default();
let result = strategy.simulate_tests(&config);
assert!(result.total > 0);
assert!(result.passed > 0);
assert!(result.pass_rate() > 0.0);
}
#[test]
fn test_test_strategy_ctest_command() {
let strategy = X86TestStrategy::comprehensive();
let cmd = strategy.to_ctest_command();
assert!(cmd.contains("ctest"));
assert!(cmd.contains("--output-on-failure"));
}
#[test]
fn test_test_framework_commands() {
let mut strategy = X86TestStrategy::comprehensive();
strategy.unit_framework = X86TestFramework::GTest;
assert!(strategy.to_test_command().contains("gtest"));
strategy.unit_framework = X86TestFramework::Catch2;
assert!(strategy.to_test_command().contains("junit"));
strategy.unit_framework = X86TestFramework::CTest;
assert!(strategy.to_test_command().contains("ctest"));
}
// ── Test Result Tests ──────────────────────────────────────────────────
#[test]
fn test_test_result_pass_rate() {
let result = X86TestResult {
total: 100,
passed: 95,
failed: 5,
skipped: 0,
..Default::default()
};
assert_eq!(result.pass_rate(), 95.0);
}
#[test]
fn test_test_result_all_passed() {
let result = X86TestResult {
total: 50,
passed: 50,
failed: 0,
..Default::default()
};
assert!(result.all_passed());
}
#[test]
fn test_test_result_not_all_passed() {
let result = X86TestResult {
total: 50,
passed: 49,
failed: 1,
..Default::default()
};
assert!(!result.all_passed());
}
#[test]
fn test_test_config_analysis_only() {
let config = X86TestConfig::analysis_only();
assert_eq!(config.test_type, X86TestType::Analysis);
assert_eq!(config.filter, Some("analysis".to_string()));
}
// ── Coverage Tests ─────────────────────────────────────────────────────
#[test]
fn test_coverage_tool_flags() {
assert_eq!(X86CoverageTool::None.flags(), "");
assert_eq!(X86CoverageTool::GCov.flags(), "--coverage");
assert!(X86CoverageTool::LLVMCov.flags().contains("fprofile"));
}
#[test]
fn test_coverage_tool_report_command() {
assert!(X86CoverageTool::None.report_command().is_none());
assert!(X86CoverageTool::GCov.report_command().is_some());
assert!(X86CoverageTool::LLVMCov
.report_command()
.unwrap()
.contains("llvm-cov"));
}
#[test]
fn test_coverage_result_threshold() {
let coverage = X86CoverageResult {
line_coverage: 85.0,
branch_coverage: 75.0,
function_coverage: 90.0,
total_lines: 1000,
covered_lines: 850,
total_branches: 200,
covered_branches: 150,
total_functions: 100,
covered_functions: 90,
};
assert!(coverage.meets_threshold(80.0));
assert!(!coverage.meets_threshold(90.0));
assert!(coverage.summary().contains("85.0%"));
}
// ── Quality Gate Tests ─────────────────────────────────────────────────
#[test]
fn test_quality_gate_compilation() {
let gate = X86QualityGate::compilation_success();
assert!(gate.blocking);
assert_eq!(gate.gate_type, X86QualityGateType::Compilation);
}
#[test]
fn test_quality_gate_test_pass_rate() {
let gate = X86QualityGate::test_pass_rate(95.0);
assert_eq!(gate.threshold, 95.0);
}
#[test]
fn test_quality_gate_coverage() {
let gate = X86QualityGate::coverage_threshold(80.0);
assert!(!gate.blocking);
}
#[test]
fn test_quality_gate_static_analysis() {
let gate = X86QualityGate::static_analysis_errors(0.0);
assert_eq!(gate.threshold, 0.0);
}
#[test]
fn test_quality_gate_fuzzing() {
let gate = X86QualityGate::fuzzing_coverage(60.0);
assert_eq!(gate.threshold, 60.0);
}
#[test]
fn test_quality_gate_performance() {
let gate = X86QualityGate::performance_regression(2.0);
assert_eq!(gate.threshold, 2.0);
}
#[test]
fn test_quality_gate_evaluate() {
let ci = X86CI_CD::default();
let execution = ci.execute_pipeline();
let summary = X86ArtifactSummary {
total_artifacts: 0,
total_size_bytes: 0,
binary_count: 0,
library_count: 0,
symbol_count: 0,
report_count: 0,
doc_count: 0,
};
let gate = X86QualityGate::compilation_success();
let results = gate.evaluate(&execution.jobs, &summary);
for result in &results {
assert!(result.passed);
}
}
#[test]
fn test_quality_gate_result_status() {
let passed = X86QualityGateResult {
gate_name: "Test".to_string(),
gate_type: X86QualityGateType::Compilation,
passed: true,
threshold: 1.0,
actual_value: 1.0,
blocking: true,
};
assert_eq!(passed.status_emoji(), "✅");
assert!(!passed.is_blocking_failure());
let failed = X86QualityGateResult {
gate_name: "Test".to_string(),
gate_type: X86QualityGateType::Compilation,
passed: false,
threshold: 1.0,
actual_value: 0.0,
blocking: true,
};
assert_eq!(failed.status_emoji(), "❌");
assert!(failed.is_blocking_failure());
}
// ── Artifact Manager Tests ─────────────────────────────────────────────
#[test]
fn test_artifact_manager_default() {
let manager = X86ArtifactManager::default();
assert_eq!(manager.storage_path, X86_CI_DEFAULT_ARTIFACT_DIR);
assert!(manager.binaries.enabled);
}
#[test]
fn test_artifact_manager_generate_paths() {
let manager = X86ArtifactManager::default();
let paths = manager.generate_paths("build");
assert!(!paths.is_empty());
}
#[test]
fn test_artifact_manager_upload_steps() {
let manager = X86ArtifactManager::default();
let steps = manager.generate_upload_steps(&X86CiSystem::GitHubActions);
assert!(!steps.is_empty());
assert!(steps[0].contains("upload-artifact"));
}
#[test]
fn test_artifact_manager_summary() {
let mut manager = X86ArtifactManager::default();
manager.collected.push(X86Artifact {
name: "test_binary".to_string(),
path: "build/bin/test".to_string(),
size_bytes: 50000,
artifact_type: X86ArtifactType::Binary,
compressed: false,
checksum: None,
created_at: chrono_like_now(),
expires_at: chrono_like_now() + Duration::from_secs(86400),
});
let summary = manager.summarize();
assert_eq!(summary.total_artifacts, 1);
assert_eq!(summary.binary_count, 1);
}
// ── SBOM Tests ─────────────────────────────────────────────────────────
#[test]
fn test_sbom_generation() {
let manager = X86ArtifactManager::default();
let sbom = manager.generate_sbom();
assert_eq!(sbom.format, "spdx-json");
assert_eq!(sbom.name, "x86-clang-project");
assert!(!sbom.components.is_empty());
}
#[test]
fn test_sbom_to_spdx_json() {
let manager = X86ArtifactManager::default();
let sbom = manager.generate_sbom();
let json = sbom.to_spdx_json();
assert!(json.contains("SPDX-2.3"));
assert!(json.contains("x86-clang-project"));
}
#[test]
fn test_sbom_to_cyclonedx_json() {
let manager = X86ArtifactManager::default();
let sbom = manager.generate_sbom();
let json = sbom.to_cyclonedx_json();
assert!(json.contains("CycloneDX"));
assert!(json.contains("bomFormat"));
}
// ── Docker Artifact Tests ──────────────────────────────────────────────
#[test]
fn test_docker_steps() {
let mut manager = X86ArtifactManager::default();
manager.docker.enabled = true;
manager.docker.push_to_registry = true;
let steps = manager.generate_docker_steps();
assert!(!steps.is_empty());
assert!(steps.iter().any(|s| s.contains("docker build")));
assert!(steps.iter().any(|s| s.contains("docker push")));
}
// ── Release Tests ──────────────────────────────────────────────────────
#[test]
fn test_release_steps() {
let mut manager = X86ArtifactManager::default();
manager.release.enabled = true;
let steps = manager.generate_release_steps("1.0.0");
assert!(!steps.is_empty());
assert!(steps.iter().any(|s| s.contains("tar czf")));
}
// ── Package Registry Tests ─────────────────────────────────────────────
#[test]
fn test_package_registry_steps_github() {
let mut manager = X86ArtifactManager::default();
manager.package_registry.enabled = true;
manager.package_registry.registry_type = X86PackageRegistryType::GitHubPackages;
let steps = manager.generate_package_registry_steps();
assert!(!steps.is_empty());
assert!(steps.iter().any(|s| s.contains("ghcr.io")));
}
#[test]
fn test_package_registry_steps_gitlab() {
let mut manager = X86ArtifactManager::default();
manager.package_registry.enabled = true;
manager.package_registry.registry_type = X86PackageRegistryType::GitLabRegistry;
let steps = manager.generate_package_registry_steps();
assert!(!steps.is_empty());
assert!(steps.iter().any(|s| s.contains("registry.gitlab.com")));
}
#[test]
fn test_package_registry_steps_dockerhub() {
let mut manager = X86ArtifactManager::default();
manager.package_registry.enabled = true;
manager.package_registry.registry_type = X86PackageRegistryType::DockerHub;
let steps = manager.generate_package_registry_steps();
assert!(!steps.is_empty());
}
// ── Symbol Server Tests ────────────────────────────────────────────────
#[test]
fn test_symbol_upload_steps() {
let mut manager = X86ArtifactManager::default();
manager.symbols.upload_to_server = true;
manager.symbols.server_url = Some("https://symbols.example.com".to_string());
let steps = manager.generate_symbol_upload_steps();
assert!(!steps.is_empty());
assert!(steps.iter().any(|s| s.contains("objcopy")));
}
#[test]
fn test_symbol_upload_disabled() {
let manager = X86ArtifactManager::default();
let steps = manager.generate_symbol_upload_steps();
assert!(steps.is_empty());
}
// ── Package Generation Tests ───────────────────────────────────────────
#[test]
fn test_deb_package_steps() {
let manager = X86ArtifactManager::default();
let steps = manager.generate_deb_package_steps("1.0.0");
assert!(!steps.is_empty());
assert!(steps.iter().any(|s| s.contains("dpkg-deb")));
}
#[test]
fn test_rpm_package_steps() {
let manager = X86ArtifactManager::default();
let steps = manager.generate_rpm_package_steps("1.0.0");
assert!(!steps.is_empty());
assert!(steps.iter().any(|s| s.contains("rpmbuild")));
}
#[test]
fn test_appimage_steps() {
let manager = X86ArtifactManager::default();
let steps = manager.generate_appimage_steps("1.0.0");
assert!(!steps.is_empty());
assert!(steps.iter().any(|s| s.contains("AppImage")));
}
// ── CI Test Integration Tests ──────────────────────────────────────────
#[test]
fn test_ci_test_integration_default() {
let integration = X86CITestIntegration::default();
assert_eq!(integration.coverage_tool, X86CoverageTool::LLVMCov);
assert!(integration.quarantined_tests.is_empty());
}
#[test]
fn test_ci_test_integration_record_run() {
let mut integration = X86CITestIntegration::default();
let result = X86TestResult {
total: 10,
passed: 9,
failed: 1,
test_suites: vec![X86TestSuiteResult {
name: "Suite1".to_string(),
total: 10,
passed: 9,
failed: 1,
skipped: 0,
duration: Duration::from_secs(1),
test_cases: vec![X86TestCaseResult::failed("test_x", "error")],
}],
..Default::default()
};
integration.record_test_run(result);
assert!(!integration.test_results_history.is_empty());
}
#[test]
fn test_ci_test_integration_flaky_detection() {
let mut integration = X86CITestIntegration::default();
// Record multiple runs where a test alternates pass/fail
for i in 0..5 {
let passed = i % 2 == 0;
let result = X86TestResult {
total: 1,
passed: if passed { 1 } else { 0 },
failed: if passed { 0 } else { 1 },
test_suites: vec![X86TestSuiteResult {
name: "Suite1".to_string(),
total: 1,
passed: if passed { 1 } else { 0 },
failed: if passed { 0 } else { 1 },
skipped: 0,
duration: Duration::from_secs(1),
test_cases: vec![if passed {
X86TestCaseResult::passed("test_flaky")
} else {
X86TestCaseResult::failed("test_flaky", "intermittent")
}],
}],
..Default::default()
};
integration.record_test_run(result);
}
// Flaky tests might be detected (depends on threshold)
// At minimum, history should be recorded
assert_eq!(integration.test_results_history.len(), 5);
}
#[test]
fn test_ci_test_integration_junit_xml() {
let integration = X86CITestIntegration::default();
let results = vec![X86TestResult {
total: 5,
passed: 4,
failed: 1,
test_suites: vec![X86TestSuiteResult {
name: "Suite1".to_string(),
total: 5,
passed: 4,
failed: 1,
skipped: 0,
duration: Duration::from_secs(2),
test_cases: vec![
X86TestCaseResult::passed("test_a"),
X86TestCaseResult::failed("test_b", "assertion failed"),
],
}],
..Default::default()
}];
let xml = integration.generate_junit_xml(&results);
assert!(xml.contains("<?xml"));
assert!(xml.contains("testsuites"));
assert!(xml.contains("test_a"));
assert!(xml.contains("test_b"));
}
#[test]
fn test_ci_test_integration_ctest_xml() {
let integration = X86CITestIntegration::default();
let results = vec![X86TestResult::default()];
let xml = integration.generate_ctest_xml(&results);
assert!(xml.contains("Site"));
assert!(xml.contains("Testing"));
}
#[test]
fn test_ci_test_integration_html_report() {
let integration = X86CITestIntegration::default();
let results = vec![X86TestResult {
total: 1,
passed: 1,
failed: 0,
test_suites: vec![X86TestSuiteResult {
name: "Suite1".to_string(),
total: 1,
passed: 1,
failed: 0,
skipped: 0,
duration: Duration::from_secs(1),
test_cases: vec![X86TestCaseResult::passed("test_a")],
}],
..Default::default()
}];
let html = integration.generate_html_report(&results);
assert!(html.contains("<!DOCTYPE html>"));
assert!(html.contains("Suite1"));
assert!(html.contains("passed"));
}
#[test]
fn test_ci_test_integration_quarantine() {
let mut integration = X86CITestIntegration::default();
let test_name = "Suite1::test_flaky";
assert!(!integration.is_quarantined(test_name));
assert!(integration.get_quarantined_tests().is_empty());
// Add a test to quarantine manually
integration.quarantined_tests.insert(
test_name.to_string(),
X86QuarantinedTest {
test_name: test_name.to_string(),
quarantined_at: chrono_like_now(),
failure_count: 5,
previous_passes: 3,
reason: "Intermittent".to_string(),
},
);
assert!(integration.is_quarantined(test_name));
assert_eq!(integration.get_quarantined_tests().len(), 1);
}
#[test]
fn test_ci_test_integration_unquarantine() {
let mut integration = X86CITestIntegration::default();
let test_name = "Suite1::test_flaky";
integration.quarantined_tests.insert(
test_name.to_string(),
X86QuarantinedTest {
test_name: test_name.to_string(),
quarantined_at: chrono_like_now(),
failure_count: 3,
previous_passes: 2,
reason: "Intermittent".to_string(),
},
);
assert!(integration.unquarantine(test_name));
assert!(!integration.is_quarantined(test_name));
}
#[test]
fn test_performance_regression_detection() {
let mut integration = X86CITestIntegration::default();
// Set baseline
let baseline = X86TestResult {
config_name: "baseline".to_string(),
test_suites: vec![X86TestSuiteResult {
name: "SlowSuite".to_string(),
total: 1,
passed: 1,
failed: 0,
skipped: 0,
duration: Duration::from_secs(1),
test_cases: vec![],
}],
..Default::default()
};
integration.set_performance_baseline(&baseline);
// Now test for regression with slower result
let slower_result = X86TestResult {
config_name: "current".to_string(),
test_suites: vec![X86TestSuiteResult {
name: "SlowSuite".to_string(),
total: 1,
passed: 1,
failed: 0,
skipped: 0,
duration: Duration::from_secs(3),
test_cases: vec![],
}],
..Default::default()
};
let regressions = integration.check_performance_regression(&slower_result);
// 3s vs 1s = 200% change > 2% threshold
assert!(!regressions.is_empty());
assert!(regressions[0].is_significant());
assert!(regressions[0].describe().contains("slower"));
}
// ── Notification Tests ─────────────────────────────────────────────────
#[test]
fn test_notification_hub_default() {
let hub = X86NotificationHub::default();
assert!(hub.enabled);
assert!(hub.github_status);
assert!(!hub.gitlab_status);
}
#[test]
fn test_notification_format_slack() {
let hub = X86NotificationHub::default();
let slack = X86SlackConfig {
webhook_url: "https://hooks.slack.com/test".to_string(),
channel: Some("#ci".to_string()),
username: Some("CI Bot".to_string()),
icon_emoji: Some(":robot:".to_string()),
};
let notification = X86CiNotification {
ci_system: X86CiSystem::GitHubActions,
pipeline_name: "X86 Clang CI".to_string(),
success: true,
duration_secs: 120.0,
total_jobs: 4,
successful_jobs: 4,
failed_jobs: 0,
quality_summary: vec![],
artifact_summary: None,
branch: "main".to_string(),
commit_sha: "abcdef1234567890".to_string(),
url: None,
};
let msg = hub.format_slack_message(¬ification, &slack);
assert!(msg.contains("✅"));
assert!(msg.contains("#ci"));
}
#[test]
fn test_notification_format_discord() {
let hub = X86NotificationHub::default();
let discord = X86DiscordConfig {
webhook_url: "https://discord.com/api/webhooks/test".to_string(),
username: Some("CI Bot".to_string()),
avatar_url: None,
};
let notification = X86CiNotification {
ci_system: X86CiSystem::GitHubActions,
pipeline_name: "X86 Clang CI".to_string(),
success: false,
duration_secs: 30.0,
total_jobs: 4,
successful_jobs: 3,
failed_jobs: 1,
quality_summary: vec![],
artifact_summary: None,
branch: "main".to_string(),
commit_sha: "abcdef1234567890".to_string(),
url: None,
};
let msg = hub.format_discord_message(¬ification, &discord);
assert!(msg.contains("❌"));
assert!(msg.contains("15548997")); // red color
}
#[test]
fn test_github_status_payload() {
let hub = X86NotificationHub::default();
let notification = X86CiNotification {
ci_system: X86CiSystem::GitHubActions,
pipeline_name: "test".to_string(),
success: true,
duration_secs: 0.0,
total_jobs: 0,
successful_jobs: 0,
failed_jobs: 0,
quality_summary: vec![],
artifact_summary: None,
branch: "main".to_string(),
commit_sha: "abc".to_string(),
url: Some("https://github.com".to_string()),
};
let payload = hub.github_status_payload(¬ification);
assert!(payload.contains("success"));
}
#[test]
fn test_notify_conditions() {
let conditions = X86NotifyConditions::default();
assert!(!conditions.should_notify(true, false));
assert!(conditions.should_notify(false, false));
assert!(conditions.should_notify(false, true));
}
#[test]
fn test_notification_short_summary() {
let notification = X86CiNotification {
ci_system: X86CiSystem::GitHubActions,
pipeline_name: "X86 Clang CI".to_string(),
success: true,
duration_secs: 120.0,
total_jobs: 4,
successful_jobs: 4,
failed_jobs: 0,
quality_summary: vec![],
artifact_summary: None,
branch: "main".to_string(),
commit_sha: "abcdef1234567890".to_string(),
url: None,
};
let summary = notification.short_summary();
assert!(summary.contains("X86 Clang CI"));
assert!(summary.contains("4/4"));
}
#[test]
fn test_notification_hub_dispatch() {
let hub = X86NotificationHub {
enabled: true,
slack: Some(X86SlackConfig {
webhook_url: "https://hooks.slack.com/test".to_string(),
channel: Some("#ci".to_string()),
username: None,
icon_emoji: None,
}),
..Default::default()
};
let notification = X86CiNotification {
ci_system: X86CiSystem::GitHubActions,
pipeline_name: "test".to_string(),
success: false,
duration_secs: 0.0,
total_jobs: 0,
successful_jobs: 0,
failed_jobs: 1,
quality_summary: vec![],
artifact_summary: None,
branch: "main".to_string(),
commit_sha: "abc".to_string(),
url: None,
};
let messages = hub.dispatch(¬ification);
assert!(!messages.is_empty());
}
// ── GitHub Actions Generation Tests ────────────────────────────────────
#[test]
fn test_github_workflow_generation() {
let ci = X86CI_CD::new(X86CiSystem::GitHubActions);
let result = ci.generate_pipeline();
assert!(result.content.contains("name: X86 Clang CI"));
assert!(result.content.contains("on:"));
assert!(result.content.contains("jobs:"));
assert_eq!(result.file_name, ".github/workflows/ci.yml");
}
#[test]
fn test_github_workflow_matrix() {
let ci = X86CI_CD::new(X86CiSystem::GitHubActions);
let result = ci.generate_pipeline();
assert!(result.content.contains("strategy"));
assert!(result.content.contains("matrix"));
}
#[test]
fn test_github_workflow_triggers() {
let ci = X86CI_CD::new(X86CiSystem::GitHubActions);
let result = ci.generate_pipeline();
assert!(result.content.contains("push"));
assert!(result.content.contains("pull_request"));
}
#[test]
fn test_github_workflow_release() {
let mut ci = X86CI_CD::new(X86CiSystem::GitHubActions);
ci.github.enable_release = true;
let result = ci.generate_pipeline();
assert!(result.content.contains("release"));
}
#[test]
fn test_github_workflow_coverage() {
let mut ci = X86CI_CD::new(X86CiSystem::GitHubActions);
ci.github.enable_coverage = true;
ci.testing.coverage_tool = X86CoverageTool::LLVMCov;
let result = ci.generate_pipeline();
assert!(result.content.contains("coverage"));
}
#[test]
fn test_github_sbom_job() {
let mut ci = X86CI_CD::new(X86CiSystem::GitHubActions);
ci.artifacts.sbom.enabled = true;
let result = ci.generate_pipeline();
assert!(result.content.contains("sbom"));
}
// ── GitLab CI Generation Tests ─────────────────────────────────────────
#[test]
fn test_gitlab_pipeline_generation() {
let ci = X86CI_CD::new(X86CiSystem::GitLabCI);
let result = ci.generate_pipeline();
assert!(result.content.contains("stages:"));
assert!(result.content.contains("GitLab CI"));
assert_eq!(result.file_name, ".gitlab-ci.yml");
}
#[test]
fn test_gitlab_review_apps() {
let mut ci = X86CI_CD::new(X86CiSystem::GitLabCI);
ci.gitlab.enable_review_apps = true;
let result = ci.generate_pipeline();
assert!(result.content.contains("review"));
}
// ── Jenkins Generation Tests ───────────────────────────────────────────
#[test]
fn test_jenkinsfile_declarative() {
let ci = X86CI_CD::new(X86CiSystem::Jenkins);
let result = ci.generate_pipeline();
assert!(result.content.contains("pipeline {"));
assert!(result.content.contains("stages {"));
assert_eq!(result.file_name, "Jenkinsfile");
}
#[test]
fn test_jenkinsfile_scripted() {
let mut ci = X86CI_CD::new(X86CiSystem::Jenkins);
ci.jenkins.pipeline_type = X86JenkinsPipelineType::Scripted;
let result = ci.generate_pipeline();
assert!(result.content.contains("node("));
}
// ── CircleCI Generation Tests ──────────────────────────────────────────
#[test]
fn test_circleci_config_generation() {
let ci = X86CI_CD::new(X86CiSystem::CircleCI);
let result = ci.generate_pipeline();
assert!(result.content.contains("version:"));
assert!(result.content.contains("workflows:"));
assert_eq!(result.file_name, ".circleci/config.yml");
}
#[test]
fn test_circleci_test_splitting() {
let mut ci = X86CI_CD::new(X86CiSystem::CircleCI);
ci.circleci.enable_test_splitting = true;
let result = ci.generate_pipeline();
assert!(result.content.contains("parallelism"));
}
// ── Azure Pipelines Generation Tests ───────────────────────────────────
#[test]
fn test_azure_pipeline_generation() {
let ci = X86CI_CD::new(X86CiSystem::AzurePipelines);
let result = ci.generate_pipeline();
assert!(result.content.contains("stages:"));
assert!(result.content.contains("Azure Pipelines"));
}
#[test]
fn test_azure_pipeline_single_stage() {
let mut ci = X86CI_CD::new(X86CiSystem::AzurePipelines);
ci.azure.enable_multi_stage = false;
let result = ci.generate_pipeline();
assert!(!result.content.contains("stages:"));
}
// ── Travis CI Generation Tests ─────────────────────────────────────────
#[test]
fn test_travis_yml_generation() {
let ci = X86CI_CD::new(X86CiSystem::TravisCI);
let result = ci.generate_pipeline();
assert!(result.content.contains("language: cpp"));
assert!(result.content.contains("compiler:"));
assert_eq!(result.file_name, ".travis.yml");
}
// ── Drone CI Generation Tests ──────────────────────────────────────────
#[test]
fn test_drone_yml_generation() {
let ci = X86CI_CD::new(X86CiSystem::DroneCI);
let result = ci.generate_pipeline();
assert!(result.content.contains("kind:"));
assert!(result.content.contains("steps:"));
assert_eq!(result.file_name, ".drone.yml");
}
// ── Buildkite Generation Tests ─────────────────────────────────────────
#[test]
fn test_buildkite_pipeline_generation() {
let ci = X86CI_CD::new(X86CiSystem::Buildkite);
let result = ci.generate_pipeline();
assert!(result.content.contains("steps:"));
assert!(result.content.contains("agents:"));
assert!(result.content.contains("queue:"));
}
// ── TeamCity Generation Tests ──────────────────────────────────────────
#[test]
fn test_teamcity_config_generation() {
let ci = X86CI_CD::new(X86CiSystem::TeamCity);
let result = ci.generate_pipeline();
assert!(result.content.contains("package _Self.buildTypes"));
assert!(result.content.contains("Project({"));
}
// ── Bitbucket Pipelines Generation Tests ───────────────────────────────
#[test]
fn test_bitbucket_pipelines_generation() {
let ci = X86CI_CD::new(X86CiSystem::BitbucketPipelines);
let result = ci.generate_pipeline();
assert!(result.content.contains("pipelines:"));
assert!(result.content.contains("image:"));
assert_eq!(result.file_name, "bitbucket-pipelines.yml");
}
#[test]
fn test_bitbucket_pipelines_deployments() {
let mut ci = X86CI_CD::new(X86CiSystem::BitbucketPipelines);
ci.bitbucket.enable_deployments = true;
let result = ci.generate_pipeline();
assert!(result.content.contains("deployment:"));
}
// ── Bamboo Generation Tests ────────────────────────────────────────────
#[test]
fn test_bamboo_spec_generation() {
let ci = X86CI_CD::new(X86CiSystem::Bamboo);
let result = ci.generate_pipeline();
assert!(result.content.contains("plan:"));
assert!(result.content.contains("stages:"));
assert_eq!(result.file_name, "bamboo-specs/bamboo.yml");
}
// ── Pipeline Result Tests ──────────────────────────────────────────────
#[test]
fn test_pipeline_result_write() {
let result = X86PipelineResult {
content: "# Test pipeline".to_string(),
file_name: "test-ci.yml".to_string(),
ci_system: X86CiSystem::GitHubActions,
};
let dir = std::env::temp_dir().join("x86-ci-test");
let _ = fs::create_dir_all(&dir);
assert!(result.write_to(&dir).is_ok());
let _ = fs::remove_dir_all(&dir);
}
#[test]
fn test_pipeline_execution_summary() {
let ci = X86CI_CD::new(X86CiSystem::GitHubActions);
let execution = ci.execute_pipeline();
let summary = execution.summary();
assert!(summary.contains("Pipeline:"));
assert!(summary.contains("Duration:"));
}
#[test]
fn test_pipeline_execution_all_gates_passed() {
let ci = X86CI_CD::new(X86CiSystem::GitHubActions);
let execution = ci.execute_pipeline();
assert!(execution.all_gates_passed());
}
// ── Badge Tests ────────────────────────────────────────────────────────
#[test]
fn test_build_status_badge() {
let badge = x86_build_status_badge(true, "flat");
assert!(badge.contains("shields.io"));
assert!(badge.contains("passing"));
assert!(badge.contains("brightgreen"));
}
#[test]
fn test_coverage_badge() {
let badge = x86_coverage_badge(85.0, "flat");
assert!(badge.contains("shields.io"));
assert!(badge.contains("85%"));
assert!(badge.contains("brightgreen"));
}
#[test]
fn test_badge_url_generation() {
let url = x86_generate_badge_url("build", "passing", "green", "flat");
assert!(url.contains("shields.io"));
assert!(url.contains("passing"));
}
// ── Dashboard Tests ────────────────────────────────────────────────────
#[test]
fn test_dashboard_html() {
let dashboard = X86CiDashboard {
title: "CI Dashboard".to_string(),
pipelines: vec![X86PipelineStatus {
pipeline_name: "X86 Clang CI".to_string(),
ci_system: X86CiSystem::GitHubActions,
branch: "main".to_string(),
status: X86CiJobStatus::Success,
last_build_time: Duration::from_secs(0),
duration_secs: 120.0,
test_summary: Some("All tests passed".to_string()),
coverage_pct: Some(85.0),
url: Some("https://github.com".to_string()),
}],
last_updated: Duration::from_secs(0),
refresh_interval_secs: 30,
};
let html = dashboard.to_html();
assert!(html.contains("<!DOCTYPE html>"));
assert!(html.contains("X86 Clang CI"));
assert!(html.contains("GitHub Actions"));
assert!(html.contains("success"));
}
#[test]
fn test_dashboard_table() {
let dashboard = X86CiDashboard {
title: "CI Dashboard".to_string(),
pipelines: vec![X86PipelineStatus {
pipeline_name: "X86 Clang CI".to_string(),
ci_system: X86CiSystem::GitHubActions,
branch: "main".to_string(),
status: X86CiJobStatus::Success,
last_build_time: Duration::from_secs(0),
duration_secs: 120.0,
test_summary: None,
coverage_pct: None,
url: None,
}],
last_updated: Duration::from_secs(0),
refresh_interval_secs: 30,
};
let table = dashboard.to_table();
assert!(table.contains("Pipeline"));
assert!(table.contains("X86 Clang CI"));
}
// ── Migration Tests ────────────────────────────────────────────────────
#[test]
fn test_ci_migration_generate() {
let migration = X86CiMigration::default();
let msgs = migration.migrate();
assert!(!msgs.is_empty());
assert!(msgs[0].contains("Travis CI"));
assert!(msgs[0].contains("GitHub Actions"));
}
#[test]
fn test_ci_feature_comparison() {
let migration = X86CiMigration::default();
let comparison = migration.compare_features();
let display = format!("{}", comparison);
assert!(display.contains("Matrix:"));
assert!(display.contains("Artifacts:"));
}
// ── Pipeline Config Tests ──────────────────────────────────────────────
#[test]
fn test_pipeline_config_default_stages() {
let config = X86PipelineConfig::default();
assert_eq!(config.stages.len(), 4);
assert_eq!(config.stages[0].stage_type, X86StageType::Build);
assert_eq!(config.stages[1].stage_type, X86StageType::Test);
assert_eq!(config.stages[2].stage_type, X86StageType::Analyze);
assert_eq!(config.stages[3].stage_type, X86StageType::Deploy);
}
#[test]
fn test_pipeline_triggers_default() {
let triggers = X86PipelineTriggers::default();
assert_eq!(triggers.push.branches.len(), 2);
assert_eq!(triggers.pull_request.branches.len(), 1);
assert_eq!(triggers.schedule.len(), 1);
assert!(triggers.workflow_dispatch);
}
#[test]
fn test_build_system_generators() {
assert_eq!(X86BuildSystem::CMake.default_generator(), "Unix Makefiles");
assert_eq!(X86BuildSystem::Ninja.default_generator(), "Ninja");
assert_eq!(X86BuildSystem::Meson.default_generator(), "Meson");
}
// ── Stats Tests ────────────────────────────────────────────────────────
#[test]
fn test_stats_default() {
let stats = X86CiStats::default();
assert_eq!(stats.total_executions, 0);
assert_eq!(stats.successful_executions, 0);
}
// ── E2E Tests ──────────────────────────────────────────────────────────
#[test]
fn test_e2e_full_pipeline() {
let mut ci = X86CI_CD {
compilation: X86CompilationStrategy::release_lto(),
testing: X86TestStrategy::comprehensive(),
notifications: X86NotificationHub {
enabled: true,
slack: Some(X86SlackConfig {
webhook_url: "https://hooks.slack.com/test".to_string(),
channel: Some("#ci".to_string()),
username: Some("CI Bot".to_string()),
icon_emoji: Some(":robot:".to_string()),
}),
..Default::default()
},
..X86CI_CD::default()
};
// Generate pipeline for all systems
for system in &[
X86CiSystem::GitHubActions,
X86CiSystem::GitLabCI,
X86CiSystem::Jenkins,
X86CiSystem::CircleCI,
X86CiSystem::AzurePipelines,
X86CiSystem::TravisCI,
X86CiSystem::DroneCI,
X86CiSystem::Buildkite,
X86CiSystem::TeamCity,
X86CiSystem::BitbucketPipelines,
X86CiSystem::Bamboo,
] {
ci.ci_system = system.clone();
let result = ci.generate_pipeline();
assert!(!result.content.is_empty(), "Empty output for {:?}", system);
assert!(!result.file_name.is_empty());
}
// Execute pipeline
ci.ci_system = X86CiSystem::GitHubActions;
let execution = ci.execute_pipeline();
assert!(!execution.jobs.is_empty());
}
// ── Static Analyzer Tests ──────────────────────────────────────────────
#[test]
fn test_static_analyzer_commands() {
let analyzers = vec![
X86StaticAnalyzer::ClangTidy,
X86StaticAnalyzer::ClangAnalyzer,
X86StaticAnalyzer::Cppcheck,
X86StaticAnalyzer::CodeQL,
X86StaticAnalyzer::SonarQube,
X86StaticAnalyzer::PVSStudio,
X86StaticAnalyzer::Coverity,
X86StaticAnalyzer::Infer,
];
for a in &analyzers {
assert!(!a.command().is_empty(), "Empty command for {:?}", a);
assert!(!a.report_format().is_empty(), "Empty format for {:?}", a);
}
}
// ── Custom CI Tests ────────────────────────────────────────────────────
#[test]
fn test_custom_ci_pipeline() {
let ci = X86CI_CD::new(X86CiSystem::Custom("MyEnterpriseCI".to_string()));
let result = ci.generate_pipeline();
assert!(!result.content.is_empty());
}
// ── X86CIGenerator Tests ───────────────────────────────────────────────
#[test]
fn test_x86_ci_generator_factory() {
let ci = X86CI_CD::new(X86CiSystem::GitHubActions);
let generator = X86CIGenerator::new(&ci);
let result = generator.generate_github_actions();
assert!(result.content.contains("GitHub"));
assert_eq!(result.ci_system, X86CiSystem::GitHubActions);
let result = generator.generate_gitlab_ci();
assert!(result.content.contains("GitLab"));
let result = generator.generate_jenkins();
assert!(result.content.contains("pipeline"));
let result = generator.generate_circleci();
assert!(result.content.contains("version:"));
let result = generator.generate_azure_pipelines();
assert!(result.content.contains("Azure"));
let result = generator.generate_travis_ci();
assert!(result.content.contains("language:"));
let result = generator.generate_drone_ci();
assert!(result.content.contains("kind:"));
let result = generator.generate_buildkite();
assert!(result.content.contains("steps:"));
let result = generator.generate_teamcity();
assert!(result.content.contains("Kotlin"));
let result = generator.generate_bitbucket_pipelines();
assert!(result.content.contains("pipelines:"));
let result = generator.generate_bamboo();
assert!(result.content.contains("plan:"));
}
// ── Misc Tests ─────────────────────────────────────────────────────────
#[test]
fn test_xml_escape() {
assert_eq!(xml_escape("<>&\"'"), "<>&"'");
}
#[test]
fn test_urlencode() {
assert_eq!(urlencode("hello world"), "hello%20world");
}
#[test]
fn test_artifact_type_coverage() {
let types = vec![
X86ArtifactType::Binary,
X86ArtifactType::Library,
X86ArtifactType::Symbols,
X86ArtifactType::Documentation,
X86ArtifactType::Report,
X86ArtifactType::Coverage,
X86ArtifactType::TestResults,
X86ArtifactType::AnalysisResults,
X86ArtifactType::Container,
X86ArtifactType::Package,
X86ArtifactType::Other,
];
assert_eq!(types.len(), 11);
}
#[test]
fn test_case_result_constructors() {
let passed = X86TestCaseResult::passed("test_ok");
assert!(passed.passed);
assert!(!passed.skipped);
let failed = X86TestCaseResult::failed("test_bad", "error_msg");
assert!(!failed.passed);
assert_eq!(failed.error_message, Some("error_msg".to_string()));
let skipped = X86TestCaseResult::skipped("test_skip");
assert!(skipped.skipped);
}
#[test]
fn test_pipeline_result_preview() {
let result = X86PipelineResult {
content: "line1\nline2\nline3\nline4\nline5".to_string(),
file_name: "test.yml".to_string(),
ci_system: X86CiSystem::GitHubActions,
};
let preview = result.preview();
assert!(preview.contains("test.yml"));
assert!(preview.contains("line1"));
assert!(preview.contains("..."));
}
#[test]
fn test_perf_regression_describe() {
let regression = X86PerfRegression {
suite_name: "SlowSuite".to_string(),
previous_duration: Duration::from_secs(1),
current_duration: Duration::from_secs(2),
change_percent: 100.0,
threshold: 2.0,
};
assert!(regression.is_significant());
assert!(regression.describe().contains("100.0%"));
assert!(regression.describe().contains("slower"));
}
// ── CI Cache Manager Tests ─────────────────────────────────────────────
#[test]
fn test_cache_manager_default() {
let cache = X86CICacheManager::default();
assert!(cache.ccache_enabled);
assert_eq!(cache.ccache_max_size, X86_CI_CCACHE_SIZE);
}
#[test]
fn test_cache_manager_generate_config() {
let cache = X86CICacheManager::default();
let config = cache.generate_cache_actions_config();
assert!(!config.is_empty());
assert!(config.contains("ccache"));
}
#[test]
fn test_cache_manager_all_backends() {
let cache = X86CICacheManager::default();
let gh = cache.generate_github_cache_steps();
let gl = cache.generate_gitlab_cache_config();
assert!(!gh.is_empty());
assert!(!gl.is_empty());
}
// ── CI Security Scan Tests ─────────────────────────────────────────────
#[test]
fn test_security_scan_default() {
let scan = X86CISecurityScan::default();
assert!(scan.sast_enabled);
assert!(scan.secret_scanning);
assert!(!scan.container_scanning);
}
#[test]
fn test_security_scan_generate_steps() {
let scan = X86CISecurityScan::default();
let steps = scan.generate_github_security_steps();
assert!(!steps.is_empty());
assert!(steps.iter().any(|s| s.contains("codeql")));
}
#[test]
fn test_security_scan_all_tools() {
let scan = X86CISecurityScan {
sast_enabled: true,
secret_scanning: true,
dependency_scanning: true,
container_scanning: true,
license_compliance: true,
..Default::default()
};
let steps = scan.generate_github_security_steps();
assert!(steps.len() >= 3);
}
// ── CI Environment Manager Tests ───────────────────────────────────────
#[test]
fn test_env_manager_default() {
let env_mgr = X86CIEnvironmentManager::default();
assert_eq!(env_mgr.default_runner, "ubuntu-22.04");
assert!(!env_mgr.environments.is_empty());
}
#[test]
fn test_env_manager_get_environment() {
let env_mgr = X86CIEnvironmentManager::default();
let env = env_mgr.get_environment("linux-x86_64");
assert!(env.is_some());
let env = env.unwrap();
assert_eq!(env.os, "linux");
assert_eq!(env.arch, "x86_64");
}
#[test]
fn test_env_manager_runner_for() {
let env_mgr = X86CIEnvironmentManager::default();
let runner = env_mgr.github_runner_for("linux", "x86_64");
assert_eq!(runner, "ubuntu-22.04");
}
#[test]
fn test_env_manager_unknown_environment() {
let env_mgr = X86CIEnvironmentManager::default();
let env = env_mgr.get_environment("nonexistent-os");
assert!(env.is_none());
}
// ── CI Pipeline Analytics Tests ────────────────────────────────────────
#[test]
fn test_pipeline_analytics_default() {
let analytics = X86CIPipelineAnalytics::default();
assert!(analytics.runs.is_empty());
assert_eq!(analytics.success_rate(), 0.0);
}
#[test]
fn test_pipeline_analytics_record() {
let mut analytics = X86CIPipelineAnalytics::default();
let ci = X86CI_CD::default();
let execution = ci.execute_pipeline();
analytics.record_run(&execution);
assert_eq!(analytics.runs.len(), 1);
assert!(analytics.success_rate() >= 0.0);
}
#[test]
fn test_pipeline_analytics_mttr() {
let mut analytics = X86CIPipelineAnalytics::default();
let ci = X86CI_CD::default();
for _ in 0..5 {
let execution = ci.execute_pipeline();
analytics.record_run(&execution);
}
let mttr = analytics.mean_time_to_recovery();
assert!(mttr.is_some() || analytics.runs.iter().all(|r| r.success));
}
#[test]
fn test_pipeline_analytics_velocity() {
let mut analytics = X86CIPipelineAnalytics::default();
let ci = X86CI_CD::default();
let execution = ci.execute_pipeline();
analytics.record_run(&execution);
let velocity = analytics.deployment_velocity();
assert!(velocity.is_some());
}
// ── Release Manager Tests ──────────────────────────────────────────────
#[test]
fn test_release_manager_default() {
let mgr = X86CIReleaseManager::default();
assert!(!mgr.draft);
assert!(mgr.generate_release_notes);
assert!(mgr.publish_to.contains(&X86ReleaseTarget::GitHub));
}
#[test]
fn test_release_manager_draft_release() {
let mgr = X86CIReleaseManager {
draft: true,
prerelease: true,
..Default::default()
};
assert!(mgr.draft);
assert!(mgr.prerelease);
}
#[test]
fn test_release_manager_generate_notes() {
let mgr = X86CIReleaseManager::default();
let notes = mgr.generate_release_notes_template("1.0.0");
assert!(notes.contains("1.0.0"));
assert!(notes.contains("Changelog"));
}
#[test]
fn test_release_manager_version_bump() {
let mgr = X86CIReleaseManager::default();
assert_eq!(mgr.bump_version("1.0.0", X86VersionBump::Patch), "1.0.1");
assert_eq!(mgr.bump_version("1.0.0", X86VersionBump::Minor), "1.1.0");
assert_eq!(mgr.bump_version("1.0.0", X86VersionBump::Major), "2.0.0");
}
#[test]
fn test_release_manager_version_bump_complex() {
let mgr = X86CIReleaseManager::default();
assert_eq!(mgr.bump_version("2.1.3", X86VersionBump::Patch), "2.1.4");
assert_eq!(mgr.bump_version("2.1.9", X86VersionBump::Minor), "2.2.0");
assert_eq!(mgr.bump_version("9.9.9", X86VersionBump::Major), "10.0.0");
}
// ── Multi-Arch Build Tests ─────────────────────────────────────────────
#[test]
fn test_multi_arch_config() {
let config = X86MultiArchConfig::default();
assert!(config.architectures.contains(&"x86_64".to_string()));
assert!(config.architectures.contains(&"i686".to_string()));
}
#[test]
fn test_multi_arch_docker_platforms() {
let config = X86MultiArchConfig::default();
let platforms = config.docker_platforms();
assert!(platforms.contains(&"linux/amd64".to_string()));
assert!(platforms.contains(&"linux/386".to_string()));
}
#[test]
fn test_multi_arch_build_steps() {
let config = X86MultiArchConfig::default();
let steps = config.generate_build_steps();
assert!(!steps.is_empty());
}
// ── Pipeline Templates Tests ───────────────────────────────────────────
#[test]
fn test_pipeline_template_default() {
let template = X86PipelineTemplate::default();
assert_eq!(template.name, "x86-clang-standard");
assert!(!template.stages.is_empty());
}
#[test]
fn test_pipeline_template_customize() {
let mut template = X86PipelineTemplate::default();
template.add_stage("security-scan");
assert_eq!(template.stages.len(), 5);
assert!(template.stages.contains(&"security-scan".to_string()));
}
#[test]
fn test_pipeline_template_generate_for_system() {
let template = X86PipelineTemplate::default();
let ci = template.into_ci_cd(&X86CiSystem::GitHubActions);
let result = ci.generate_pipeline();
assert!(!result.content.is_empty());
}
#[test]
fn test_pipeline_template_predefined_templates() {
let templates = X86PipelineTemplate::predefined_templates();
assert!(!templates.is_empty());
assert!(templates.iter().any(|t| t.name == "x86-clang-standard"));
assert!(templates.iter().any(|t| t.name == "x86-clang-sanitizer"));
assert!(templates.iter().any(|t| t.name == "x86-clang-release"));
}
// ── CI Schedule Cron Tests ─────────────────────────────────────────────
#[test]
fn test_schedule_parser() {
let schedule = X86CISchedule {
cron: "0 2 * * 0".to_string(),
description: "Weekly".to_string(),
};
assert!(schedule.is_valid_cron());
assert_eq!(schedule.human_readable(), "At 02:00 on Sunday");
}
#[test]
fn test_schedule_invalid_cron() {
let schedule = X86CISchedule {
cron: "invalid".to_string(),
description: "Bad".to_string(),
};
assert!(!schedule.is_valid_cron());
}
#[test]
fn test_schedule_cron_fields() {
let schedule = X86CISchedule {
cron: "*/15 * * * *".to_string(),
description: "Every 15 minutes".to_string(),
};
let fields = schedule.cron_fields();
assert_eq!(fields.len(), 5);
assert_eq!(fields[0], "*/15");
}
// ── CI Run History Tests ───────────────────────────────────────────────
#[test]
fn test_run_history_default() {
let history = X86CIRunHistory::default();
assert!(history.entries.is_empty());
}
#[test]
fn test_run_history_record() {
let mut history = X86CIRunHistory::default();
let ci = X86CI_CD::default();
let execution = ci.execute_pipeline();
history.record(&execution, &ci.ci_system);
assert_eq!(history.entries.len(), 1);
}
#[test]
fn test_run_history_flaky_detection_over_time() {
let mut history = X86CIRunHistory::default();
let ci = X86CI_CD::default();
for _ in 0..10 {
let execution = ci.execute_pipeline();
history.record(&execution, &ci.ci_system);
}
let flaky = history.identify_flaky_jobs(3);
// Not expecting specific results, just testing the API works
assert!(flaky.len() >= 0);
}
#[test]
fn test_run_history_latest() {
let mut history = X86CIRunHistory::default();
assert!(history.latest().is_none());
let ci = X86CI_CD::default();
let execution = ci.execute_pipeline();
history.record(&execution, &ci.ci_system);
assert!(history.latest().is_some());
}
// ── Integration Tests Between Subsystems ───────────────────────────────
#[test]
fn test_integration_artifact_manager_with_notification() {
let mut ci = X86CI_CD::default();
ci.artifacts.sbom.enabled = true;
ci.artifacts.docker.enabled = true;
let sbom = ci.artifacts.generate_sbom();
let docker_steps = ci.artifacts.generate_docker_steps();
let release_steps = ci.artifacts.generate_release_steps("1.0.0");
assert!(!sbom.components.is_empty());
assert!(!docker_steps.is_empty());
assert!(!release_steps.is_empty());
}
#[test]
fn test_integration_test_integration_with_quality_gates() {
let mut integration = X86CITestIntegration::default();
let strategy = X86TestStrategy::comprehensive();
let result = strategy.simulate_tests(&X86TestConfig::default());
integration.record_test_run(result);
assert!(!integration.test_results_history.is_empty());
assert!(integration.quarantined_tests.len() >= 0);
}
#[test]
fn test_integration_full_build_test_notify_cycle() {
let mut ci = X86CI_CD {
compilation: X86CompilationStrategy::debug_full(),
testing: X86TestStrategy::comprehensive(),
notifications: X86NotificationHub {
enabled: true,
github_status: true,
..Default::default()
},
..X86CI_CD::default()
};
// Generate pipeline
ci.ci_system = X86CiSystem::GitHubActions;
let pipeline = ci.generate_pipeline();
assert!(pipeline.content.contains("jobs:"));
// Execute
let execution = ci.execute_pipeline();
assert!(!execution.jobs.is_empty());
// Notify
let notification = X86CiNotification::pipeline_result(&execution);
let messages = ci.notifications.dispatch(¬ification);
assert!(!messages.is_empty());
}
// ── Stress / Edge Case Tests ───────────────────────────────────────────
#[test]
fn test_large_matrix_handling() {
let mut ci = X86CI_CD::default();
ci.pipeline_config.os_list = (0..10).map(|i| format!("os-{}", i)).collect();
ci.pipeline_config.compiler_list = (0..10).map(|i| format!("compiler-{}", i)).collect();
ci.pipeline_config.build_types = (0..5).map(|i| format!("bt-{}", i)).collect();
let matrix = ci.generate_matrix();
assert!(matrix.is_too_large());
// Should still generate pipeline without crashing
let result = ci.generate_pipeline();
assert!(!result.content.is_empty());
}
#[test]
fn test_empty_pipeline_config() {
let mut ci = X86CI_CD::default();
ci.pipeline_config.stages.clear();
ci.pipeline_config.os_list.clear();
ci.pipeline_config.compiler_list.clear();
ci.pipeline_config.build_types.clear();
let result = ci.generate_pipeline();
assert!(!result.content.is_empty());
let execution = ci.execute_pipeline();
assert!(execution.jobs.is_empty());
}
#[test]
fn test_all_sanitizers_enabled() {
let mut ci = X86CI_CD::default();
ci.compilation.sanitizers = vec![
X86Sanitizer::Address,
X86Sanitizer::Undefined,
X86Sanitizer::Thread,
X86Sanitizer::Memory,
X86Sanitizer::Leak,
X86Sanitizer::HWAddress,
X86Sanitizer::DataFlow,
X86Sanitizer::SafeStack,
X86Sanitizer::CFI,
];
let flags = ci.compilation.to_cmake_flags();
assert!(flags.iter().any(|f| f.contains("sanitize")));
}
#[test]
fn test_all_static_analyzers_enabled() {
let mut ci = X86CI_CD::default();
ci.testing.static_analyzers = vec![
X86StaticAnalyzer::ClangTidy,
X86StaticAnalyzer::ClangAnalyzer,
X86StaticAnalyzer::Cppcheck,
X86StaticAnalyzer::PVSStudio,
X86StaticAnalyzer::Coverity,
X86StaticAnalyzer::SonarQube,
X86StaticAnalyzer::CodeQL,
X86StaticAnalyzer::Infer,
];
for analyzer in &ci.testing.static_analyzers {
assert!(!analyzer.command().is_empty());
}
}
#[test]
fn test_all_dynamic_analyzers_listed() {
let analyzers = vec![
X86DynamicAnalyzer::Valgrind,
X86DynamicAnalyzer::ASan,
X86DynamicAnalyzer::UBSan,
X86DynamicAnalyzer::TSan,
X86DynamicAnalyzer::MSan,
X86DynamicAnalyzer::Helgrind,
X86DynamicAnalyzer::DRD,
X86DynamicAnalyzer::Massif,
X86DynamicAnalyzer::Callgrind,
X86DynamicAnalyzer::Cachegrind,
];
assert_eq!(analyzers.len(), 10);
}
#[test]
fn test_all_coverage_tools_have_commands() {
let tools = vec![
X86CoverageTool::GCov,
X86CoverageTool::LCov,
X86CoverageTool::LLVMCov,
X86CoverageTool::SanCov,
X86CoverageTool::Codecov,
X86CoverageTool::Coveralls,
];
for tool in &tools {
if *tool != X86CoverageTool::None {
assert!(!tool.flags().is_empty(), "No flags for {:?}", tool);
}
}
}
#[test]
fn test_benchmark_framework_coverage() {
let frameworks = vec![
X86BenchmarkFramework::GoogleBenchmark,
X86BenchmarkFramework::Catch2,
X86BenchmarkFramework::Criterion,
X86BenchmarkFramework::Nonius,
X86BenchmarkFramework::Celero,
];
assert_eq!(frameworks.len(), 5);
}
#[test]
fn test_test_framework_coverage() {
let frameworks = vec![
X86TestFramework::CTest,
X86TestFramework::GTest,
X86TestFramework::Catch2,
X86TestFramework::BoostTest,
X86TestFramework::Doctest,
];
assert_eq!(frameworks.len(), 5);
}
#[test]
fn test_test_type_coverage() {
let types = vec![
X86TestType::Unit,
X86TestType::Integration,
X86TestType::Benchmark,
X86TestType::Fuzz,
X86TestType::Coverage,
X86TestType::Analysis,
X86TestType::Performance,
X86TestType::Smoke,
X86TestType::Regression,
];
assert_eq!(types.len(), 9);
}
#[test]
fn test_build_type_coverage() {
let types = vec![
X86BuildType::Debug,
X86BuildType::Release,
X86BuildType::RelWithDebInfo,
X86BuildType::MinSizeRel,
X86BuildType::Custom,
];
assert_eq!(types.len(), 5);
}
#[test]
fn test_opt_level_coverage() {
let levels = vec![
X86OptLevel::O0,
X86OptLevel::O1,
X86OptLevel::O2,
X86OptLevel::O3,
X86OptLevel::Os,
X86OptLevel::Oz,
];
assert_eq!(levels.len(), 6);
for level in &levels {
assert!(!level.as_flag().is_empty());
}
}
#[test]
fn test_stage_type_coverage() {
let stages = vec![
X86StageType::Build,
X86StageType::Test,
X86StageType::Analyze,
X86StageType::Deploy,
X86StageType::Notify,
X86StageType::Custom,
];
assert_eq!(stages.len(), 6);
}
#[test]
fn test_quality_gate_type_coverage() {
let gates = vec![
X86QualityGateType::Compilation,
X86QualityGateType::TestPassRate,
X86QualityGateType::Coverage,
X86QualityGateType::StaticAnalysis,
X86QualityGateType::Fuzzing,
X86QualityGateType::Performance,
X86QualityGateType::BinarySize,
X86QualityGateType::WarningCount,
X86QualityGateType::SecurityScan,
];
assert_eq!(gates.len(), 9);
}
#[test]
fn test_build_system_coverage() {
let systems = vec![
X86BuildSystem::CMake,
X86BuildSystem::Ninja,
X86BuildSystem::NinjaMultiConfig,
X86BuildSystem::Make,
X86BuildSystem::Bazel,
X86BuildSystem::Meson,
X86BuildSystem::XMake,
];
assert_eq!(systems.len(), 7);
for sys in &systems {
assert!(!sys.default_generator().is_empty());
}
}
#[test]
fn test_arch_variant_coverage() {
let variants = vec![
X86ArchVariant::I386,
X86ArchVariant::X86_64,
X86ArchVariant::X86_64_SSE2,
X86ArchVariant::X86_64_V2,
X86ArchVariant::X86_64_V3,
X86ArchVariant::X86_64_V4,
];
assert_eq!(variants.len(), 6);
for v in &variants {
assert!(!v.march_flag().is_empty());
assert!(!v.default_cflags().is_empty());
let _ = format!("{}", v);
}
}
#[test]
fn test_release_target_coverage() {
let targets = vec![
X86ReleaseTarget::GitHub,
X86ReleaseTarget::GitLab,
X86ReleaseTarget::GitHubPackages,
X86ReleaseTarget::GitLabRegistry,
X86ReleaseTarget::DockerHub,
X86ReleaseTarget::CustomRegistry,
];
assert_eq!(targets.len(), 6);
}
#[test]
fn test_package_registry_type_coverage() {
let types = vec![
X86PackageRegistryType::GitHubPackages,
X86PackageRegistryType::GitLabRegistry,
X86PackageRegistryType::DockerHub,
X86PackageRegistryType::Custom,
];
assert_eq!(types.len(), 4);
}
#[test]
fn test_ci_badge_color_coverage() {
let colors = vec![
X86CiBadgeColor::BrightGreen,
X86CiBadgeColor::Green,
X86CiBadgeColor::YellowGreen,
X86CiBadgeColor::Yellow,
X86CiBadgeColor::Orange,
X86CiBadgeColor::Red,
X86CiBadgeColor::LightGrey,
X86CiBadgeColor::Blue,
];
assert_eq!(colors.len(), 8);
for c in &colors {
assert!(!c.as_hex().is_empty());
}
}
#[test]
fn test_ci_badge_style_variants() {
let styles = vec![
X86CiBadgeStyle::Plastic,
X86CiBadgeStyle::Flat,
X86CiBadgeStyle::FlatSquare,
X86CiBadgeStyle::ForTheBadge,
];
assert_eq!(styles.len(), 4);
}
#[test]
fn test_ci_job_status_coverage() {
let statuses = vec![
X86CiJobStatus::Success,
X86CiJobStatus::Failed,
X86CiJobStatus::Running,
X86CiJobStatus::Pending,
X86CiJobStatus::Cancelled,
X86CiJobStatus::Skipped,
X86CiJobStatus::Unknown,
];
assert_eq!(statuses.len(), 7);
for s in &statuses {
assert!(!s.emoji().is_empty());
}
}
#[test]
fn test_ci_system_display_and_default() {
assert_eq!(format!("{}", X86CiSystem::GitHubActions), "GitHub Actions");
assert_eq!(format!("{}", X86CiSystem::default()), "GitHub Actions");
}
// ── Serialization / Deserialization Tests ──────────────────────────────
#[test]
fn test_pipeline_result_roundtrip() {
let ci = X86CI_CD::new(X86CiSystem::GitHubActions);
let result = ci.generate_pipeline();
let preview = result.preview();
assert!(preview.starts_with("File:"));
}
#[test]
fn test_sbom_roundtrip_formats() {
let manager = X86ArtifactManager::default();
let sbom = manager.generate_sbom();
let spdx = sbom.to_spdx_json();
let cyclonedx = sbom.to_cyclonedx_json();
assert!(spdx.contains("SPDX"));
assert!(cyclonedx.contains("CycloneDX"));
// Both formats should reference the same package name
assert!(spdx.contains(&sbom.name));
assert!(cyclonedx.contains(&sbom.name));
}
// ── CI Migration Comprehensive Tests ───────────────────────────────────
#[test]
fn test_migration_all_combinations() {
let sources = vec![
X86CiSystem::TravisCI,
X86CiSystem::CircleCI,
X86CiSystem::Jenkins,
];
let targets = vec![X86CiSystem::GitHubActions, X86CiSystem::GitLabCI];
for source in &sources {
for target in &targets {
if source != target {
let migration = X86CiMigration {
source_system: source.clone(),
target_system: target.clone(),
..Default::default()
};
let comparison = migration.compare_features();
let display = format!("{}", comparison);
assert!(display.contains("Feature Comparison"));
}
}
}
}
// ── Constants Validation Tests ─────────────────────────────────────────
#[test]
fn test_all_constants_are_non_empty() {
assert!(!X86_CI_DEFAULT_DOCKER_IMAGE.is_empty());
assert!(!X86_CI_CLANG_VERSION.is_empty());
assert!(!X86_CI_GCC_VERSION.is_empty());
assert!(!X86_CI_BUILD_DIR.is_empty());
assert!(!X86_CI_CCACHE_KEY.is_empty());
assert!(!X86_CI_CARGO_CACHE_KEY.is_empty());
assert!(!X86_CI_NPM_CACHE_KEY.is_empty());
assert!(!X86_CI_SBOM_DEFAULT_FORMAT.is_empty());
}
#[test]
fn test_threshold_constants_are_reasonable() {
assert!(
X86_CI_DEFAULT_COVERAGE_THRESHOLD > 0.0 && X86_CI_DEFAULT_COVERAGE_THRESHOLD <= 100.0
);
assert!(
X86_CI_DEFAULT_PASS_RATE_THRESHOLD > 0.0 && X86_CI_DEFAULT_PASS_RATE_THRESHOLD <= 100.0
);
assert!(X86_CI_PERF_REGRESSION_THRESHOLD > 0.0);
assert!(X86_CI_DEFAULT_RETRY_COUNT > 0);
assert!(X86_CI_FLAKY_THRESHOLD > 0);
assert!(X86_CI_MAX_QUARANTINE_SIZE > 0);
}
#[test]
fn test_pgo_stage_coverage() {
let stages = vec![
X86PGOStage::Instrument,
X86PGOStage::Generate,
X86PGOStage::Use,
];
assert_eq!(stages.len(), 3);
}
#[test]
fn test_circleci_executor_coverage() {
let executors = vec![
X86CircleCIExecutor::Docker,
X86CircleCIExecutor::Machine,
X86CircleCIExecutor::MacOS,
X86CircleCIExecutor::Windows,
];
assert_eq!(executors.len(), 4);
}
#[test]
fn test_jenkins_pipeline_type_coverage() {
let types = vec![
X86JenkinsPipelineType::Declarative,
X86JenkinsPipelineType::Scripted,
];
assert_eq!(types.len(), 2);
}
#[test]
fn test_lto_mode_coverage() {
let modes = vec![X86LTOMode::None, X86LTOMode::Full, X86LTOMode::Thin];
assert_eq!(modes.len(), 3);
}
#[test]
fn test_notify_always() {
let conditions = X86NotifyConditions {
on_always: true,
..Default::default()
};
assert!(conditions.should_notify(true, false));
assert!(conditions.should_notify(false, false));
}
#[test]
fn test_notify_only_success() {
let conditions = X86NotifyConditions {
on_success: true,
on_failure: false,
on_fixed: false,
on_change: false,
on_always: false,
};
assert!(conditions.should_notify(true, false));
assert!(!conditions.should_notify(false, false));
}
#[test]
fn test_notify_on_fixed() {
let conditions = X86NotifyConditions {
on_fixed: true,
on_success: false,
on_failure: false,
on_change: false,
on_always: false,
};
assert!(!conditions.should_notify(true, false)); // Not fixed
assert!(conditions.should_notify(true, true)); // Fixed
}
// ── X86CI_CD Builder Pattern Tests ─────────────────────────────────────
#[test]
fn test_builder_pattern_chain() {
let ci = X86CI_CD::new(X86CiSystem::GitHubActions);
assert_eq!(ci.ci_system, X86CiSystem::GitHubActions);
let mut ci = ci;
ci.compilation = X86CompilationStrategy::release_lto();
ci.testing = X86TestStrategy::comprehensive();
ci.artifacts.sbom.enabled = true;
ci.notifications.github_status = true;
let result = ci.generate_pipeline();
assert!(!result.content.is_empty());
}
#[test]
fn test_compile_result_success_factory() {
let result = X86CompileResult::success();
assert!(result.success);
assert!(result.warnings.is_empty());
assert!(result.errors.is_empty());
}
#[test]
fn test_coverage_result_summary_format() {
let cov = X86CoverageResult {
line_coverage: 92.5,
branch_coverage: 88.3,
function_coverage: 95.7,
total_lines: 10000,
covered_lines: 9250,
total_branches: 2000,
covered_branches: 1766,
total_functions: 500,
covered_functions: 478,
};
let summary = cov.summary();
assert!(summary.contains("92.5%"));
assert!(summary.contains("88.3%"));
assert!(summary.contains("95.7%"));
}
#[test]
fn test_perf_regression_zero_baseline() {
let mut integration = X86CITestIntegration::default();
let baseline = X86TestResult {
config_name: "baseline".to_string(),
test_suites: vec![X86TestSuiteResult {
name: "EmptySuite".to_string(),
total: 0,
passed: 0,
failed: 0,
skipped: 0,
duration: Duration::from_secs(0),
test_cases: vec![],
}],
..Default::default()
};
integration.set_performance_baseline(&baseline);
// Zero baseline duration should not cause division by zero
let current = X86TestResult {
config_name: "current".to_string(),
test_suites: vec![X86TestSuiteResult {
name: "EmptySuite".to_string(),
total: 0,
passed: 0,
failed: 0,
skipped: 0,
duration: Duration::from_secs(5),
test_cases: vec![],
}],
..Default::default()
};
let regressions = integration.check_performance_regression(¤t);
// With zero baseline, the change is undefined; the implementation should handle this gracefully
assert!(regressions.is_empty() || !regressions.is_empty());
}
#[test]
fn test_pipeline_template_into_ci_cd_for_all_systems() {
let template = X86PipelineTemplate::default();
for system in &[
X86CiSystem::GitHubActions,
X86CiSystem::GitLabCI,
X86CiSystem::Jenkins,
X86CiSystem::CircleCI,
] {
let ci = template.clone().into_ci_cd(system);
let result = ci.generate_pipeline();
assert!(
!result.content.is_empty(),
"Empty pipeline for {:?}",
system
);
}
}
#[test]
fn test_stage_logs_contain_stage_name() {
let stage = X86PipelineStage {
name: "my-custom-stage".to_string(),
stage_type: X86StageType::Build,
compilation: Some(X86CompileConfig::default()),
test_config: None,
dependencies: vec![],
timeout_minutes: 30,
allow_failure: false,
};
let logs = stage.generate_logs();
assert!(logs.iter().any(|l| l.contains("my-custom-stage")));
assert!(logs.iter().any(|l| l.contains("Compilation")));
}
#[test]
fn test_fuzz_config_defaults() {
let config = X86FuzzTestConfig::default();
assert_eq!(config.fuzzer_type, X86FuzzerType::LibFuzzer);
assert_eq!(config.max_total_time_secs, X86_CI_FUZZ_DEFAULT_DURATION);
assert!(config.runs > 0);
assert!(config.fork_count > 0);
}
#[test]
fn test_compile_config_defaults() {
let config = X86CompileConfig::default();
assert_eq!(config.build_type, "Release");
assert_eq!(config.build_system, X86BuildSystem::CMake);
}
#[test]
fn test_notification_hub_disabled() {
let hub = X86NotificationHub {
enabled: false,
..Default::default()
};
let notification = X86CiNotification {
ci_system: X86CiSystem::GitHubActions,
pipeline_name: "test".to_string(),
success: false,
duration_secs: 0.0,
total_jobs: 0,
successful_jobs: 0,
failed_jobs: 1,
quality_summary: vec![],
artifact_summary: None,
branch: "main".to_string(),
commit_sha: "abc".to_string(),
url: None,
};
let messages = hub.dispatch(¬ification);
assert_eq!(messages.len(), 1);
assert!(messages[0].contains("disabled"));
}
#[test]
fn test_case_result_all_fields() {
let case = X86TestCaseResult {
name: "test_complete".to_string(),
classname: Some("TestSuite".to_string()),
passed: true,
skipped: false,
duration: Duration::from_millis(15),
error_message: None,
stack_trace: None,
file: Some("tests/test.cpp".to_string()),
line: Some(100),
};
assert!(case.passed);
assert_eq!(case.name, "test_complete");
assert_eq!(case.classname, Some("TestSuite".to_string()));
assert_eq!(case.file, Some("tests/test.cpp".to_string()));
assert_eq!(case.line, Some(100));
}
#[test]
fn test_artifact_with_checksum() {
let artifact = X86Artifact {
name: "libtest.so".to_string(),
path: "build/lib/libtest.so".to_string(),
size_bytes: 2048576,
artifact_type: X86ArtifactType::Library,
compressed: false,
checksum: Some(
"sha256:abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890"
.to_string(),
),
created_at: chrono_like_now(),
expires_at: chrono_like_now() + Duration::from_secs(86400 * 30),
};
assert_eq!(artifact.artifact_type, X86ArtifactType::Library);
assert!(artifact.checksum.is_some());
assert!(artifact.size_bytes > 0);
}
}