lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

main.rs (43005B)


      1 #![cfg_attr(coverage_nightly, feature(coverage_attribute))]
      2 #![forbid(unsafe_code)]
      3 #![recursion_limit = "256"]
      4 
      5 // These release-qualification modules are executable integration boundaries:
      6 // their governed lanes invoke external toolchains, fuzzers, package builds,
      7 // advisory scanners, SBOM generators, and target checks. They are exercised
      8 // by their dedicated release gates and must not recursively execute inside
      9 // xtask's unit-coverage process.
     10 mod advisory_snapshot;
     11 #[cfg_attr(coverage_nightly, coverage(off))]
     12 mod api_qualification;
     13 #[cfg_attr(coverage_nightly, coverage(off))]
     14 mod architecture;
     15 mod artifact_admission;
     16 mod bounded_process;
     17 #[cfg_attr(coverage_nightly, coverage(off))]
     18 mod build_control;
     19 mod build_output;
     20 #[cfg_attr(coverage_nightly, coverage(off))]
     21 mod catalog;
     22 #[cfg_attr(coverage_nightly, coverage(off))]
     23 mod consolidation;
     24 #[cfg_attr(coverage_nightly, coverage(off))]
     25 mod contract;
     26 mod coverage;
     27 #[cfg_attr(coverage_nightly, coverage(off))]
     28 mod dto_roots;
     29 mod exact_tree_archive;
     30 #[cfg_attr(coverage_nightly, coverage(off))]
     31 mod fuzz_qualification;
     32 #[cfg_attr(coverage_nightly, coverage(off))]
     33 mod generate;
     34 #[cfg_attr(coverage_nightly, coverage(off))]
     35 mod hygiene;
     36 #[cfg_attr(coverage_nightly, coverage(off))]
     37 mod portable_qualification;
     38 #[cfg_attr(coverage_nightly, coverage(off))]
     39 mod release_graph;
     40 mod release_preflight;
     41 #[cfg_attr(coverage_nightly, coverage(off))]
     42 mod release_qualification;
     43 mod rshr_202_step_298_gate;
     44 mod rshr_202_step_298_platform;
     45 mod rshr_202_step_299_gate;
     46 mod rshr_202_step_304_gate;
     47 mod rshr_202_step_305_gate;
     48 mod rshr_202_step_306_gate;
     49 mod safe_artifact_io;
     50 #[cfg_attr(coverage_nightly, coverage(off))]
     51 mod safety_qualification;
     52 #[cfg_attr(coverage_nightly, coverage(off))]
     53 mod sdk_generation;
     54 mod service_build_qualification;
     55 mod service_release_artifacts;
     56 mod service_repro_install;
     57 mod service_source_lock;
     58 mod service_source_lock_command;
     59 mod service_source_lock_v3;
     60 #[cfg_attr(coverage_nightly, coverage(off))]
     61 mod supply_chain_qualification;
     62 #[cfg_attr(coverage_nightly, coverage(off))]
     63 mod target_qualification;
     64 
     65 use clap::{Parser, Subcommand, ValueEnum};
     66 use std::env;
     67 use std::path::{Path, PathBuf};
     68 use std::process::ExitCode;
     69 
     70 #[derive(Debug, Parser)]
     71 #[command(name = "xtask", disable_help_subcommand = true)]
     72 struct Cli {
     73     #[command(subcommand)]
     74     command: XtaskCommand,
     75 }
     76 
     77 #[derive(Debug, Subcommand)]
     78 enum XtaskCommand {
     79     AdvisorySnapshotSelfTest,
     80     #[command(hide = true)]
     81     AdvisorySnapshotFixtureScanner {
     82         #[arg(long, default_value = "report")]
     83         mode: String,
     84     },
     85     Architecture,
     86     ArchitectureCi,
     87     ArchitectureSourceExportCi,
     88     BoundedProcessSelfTest,
     89     SafeArtifactIoSelfTest,
     90     CheckApiBoundaries,
     91     CheckDependencyBoundaries,
     92     Check {
     93         #[arg(long)]
     94         group: String,
     95         #[arg(long, value_enum, default_value_t = GroupOperation::Check)]
     96         operation: GroupOperation,
     97         #[arg(long)]
     98         execute: bool,
     99         #[arg(long)]
    100         include_reserved: bool,
    101     },
    102     Catalog {
    103         #[command(subcommand)]
    104         command: CatalogCommand,
    105     },
    106     #[command(trailing_var_arg = true)]
    107     Contract {
    108         #[arg(allow_hyphen_values = true)]
    109         args: Vec<String>,
    110     },
    111     #[command(trailing_var_arg = true)]
    112     Consolidation {
    113         #[arg(allow_hyphen_values = true)]
    114         args: Vec<String>,
    115     },
    116     #[command(trailing_var_arg = true)]
    117     Coverage {
    118         #[arg(allow_hyphen_values = true)]
    119         args: Vec<String>,
    120     },
    121     #[command(name = "dto-roots", trailing_var_arg = true)]
    122     DtoRoots {
    123         #[arg(allow_hyphen_values = true)]
    124         args: Vec<String>,
    125     },
    126     #[command(trailing_var_arg = true)]
    127     Generate {
    128         #[arg(allow_hyphen_values = true)]
    129         args: Vec<String>,
    130     },
    131     #[command(trailing_var_arg = true)]
    132     Hygiene {
    133         #[arg(allow_hyphen_values = true)]
    134         args: Vec<String>,
    135     },
    136     #[command(trailing_var_arg = true)]
    137     Release {
    138         #[arg(allow_hyphen_values = true)]
    139         args: Vec<String>,
    140     },
    141     #[command(name = "rshr-step-298-gate", hide = true)]
    142     RshrStep298Gate {
    143         #[arg(long)]
    144         step: u16,
    145         #[arg(long)]
    146         check_id: String,
    147         #[arg(long)]
    148         source_revision: String,
    149         #[arg(long)]
    150         source_tree: String,
    151         #[arg(long)]
    152         candidate_digest: String,
    153         #[arg(long)]
    154         platform: String,
    155         #[arg(long)]
    156         execution_request_sha256: String,
    157     },
    158     #[command(name = "rshr-step-298-platform-probe", hide = true)]
    159     RshrStep298PlatformProbe,
    160     #[command(name = "rshr-step-299-gate", hide = true)]
    161     RshrStep299Gate {
    162         #[arg(long)]
    163         step: u16,
    164         #[arg(long)]
    165         check_id: String,
    166         #[arg(long)]
    167         source_revision: String,
    168         #[arg(long)]
    169         source_tree: String,
    170         #[arg(long)]
    171         candidate_digest: String,
    172         #[arg(long)]
    173         platform: String,
    174         #[arg(long)]
    175         execution_request_sha256: String,
    176     },
    177     #[command(name = "rshr-step-304-gate", hide = true)]
    178     RshrStep304Gate {
    179         #[arg(long)]
    180         step: u16,
    181         #[arg(long)]
    182         check_id: String,
    183         #[arg(long)]
    184         source_revision: String,
    185         #[arg(long)]
    186         source_tree: String,
    187         #[arg(long)]
    188         candidate_digest: String,
    189         #[arg(long)]
    190         platform: String,
    191         #[arg(long)]
    192         execution_request_sha256: String,
    193     },
    194     #[command(name = "rshr-step-305-gate", hide = true)]
    195     RshrStep305Gate {
    196         #[arg(long)]
    197         step: u16,
    198         #[arg(long)]
    199         check_id: String,
    200         #[arg(long)]
    201         source_revision: String,
    202         #[arg(long)]
    203         source_tree: String,
    204         #[arg(long)]
    205         candidate_digest: String,
    206         #[arg(long)]
    207         platform: String,
    208         #[arg(long)]
    209         execution_request_sha256: String,
    210     },
    211     #[command(name = "rshr-step-306-gate", hide = true)]
    212     RshrStep306Gate {
    213         #[arg(long)]
    214         step: u16,
    215         #[arg(long)]
    216         check_id: String,
    217         #[arg(long)]
    218         source_revision: String,
    219         #[arg(long)]
    220         source_tree: String,
    221         #[arg(long)]
    222         candidate_digest: String,
    223         #[arg(long)]
    224         platform: String,
    225         #[arg(long)]
    226         execution_request_sha256: String,
    227     },
    228     SourceLock {
    229         #[arg(long)]
    230         consumer_root: PathBuf,
    231     },
    232     ServiceSourceLock {
    233         #[arg(long, value_enum)]
    234         mode: ServiceSourceLockMode,
    235         #[arg(long)]
    236         service_root: PathBuf,
    237         #[arg(long)]
    238         source_archive: PathBuf,
    239     },
    240     ServiceReleaseArtifacts {
    241         #[arg(long, value_enum)]
    242         mode: ServiceReleaseArtifactMode,
    243         #[arg(long)]
    244         service_root: PathBuf,
    245         #[arg(long)]
    246         lib_root: PathBuf,
    247         #[arg(long)]
    248         input_root: PathBuf,
    249         #[arg(long)]
    250         output_root: PathBuf,
    251         #[arg(long)]
    252         target: String,
    253         #[arg(long)]
    254         source_date_epoch: u32,
    255         #[arg(long)]
    256         candidate_digest: String,
    257     },
    258     ServiceReproInstall {
    259         #[arg(long)]
    260         plan: PathBuf,
    261         #[arg(long)]
    262         source_root: PathBuf,
    263         #[arg(long)]
    264         root_preimage_root: PathBuf,
    265         #[arg(long)]
    266         predecessor_artifact_root: PathBuf,
    267         #[arg(long)]
    268         git_executable: PathBuf,
    269         #[arg(long)]
    270         adapter_executable: PathBuf,
    271         #[arg(long)]
    272         output: PathBuf,
    273     },
    274     Source {
    275         #[command(subcommand)]
    276         command: SourceCommand,
    277     },
    278     Artifact {
    279         #[arg(long, value_enum)]
    280         product: ArtifactProduct,
    281         #[arg(long, value_enum)]
    282         target: ArtifactTarget,
    283         #[arg(long, value_enum)]
    284         language: ArtifactLanguage,
    285         #[arg(long, value_enum)]
    286         mode: ArtifactMode,
    287         #[arg(long)]
    288         consumer_root: PathBuf,
    289         #[arg(long)]
    290         source_root: PathBuf,
    291         #[arg(long)]
    292         output: PathBuf,
    293         #[arg(long)]
    294         source_date_epoch: u64,
    295         #[arg(long)]
    296         builder_id: String,
    297         #[arg(long, value_delimiter = ',')]
    298         features: Vec<String>,
    299     },
    300 }
    301 
    302 #[derive(Clone, Copy, Debug, Subcommand)]
    303 enum CatalogCommand {
    304     Check,
    305     Write,
    306 }
    307 
    308 #[derive(Clone, Copy, Debug, ValueEnum)]
    309 enum GroupOperation {
    310     Check,
    311     Test,
    312     Clippy,
    313 }
    314 
    315 #[derive(Clone, Copy, Debug, ValueEnum)]
    316 enum ArtifactMode {
    317     Check,
    318     Write,
    319 }
    320 
    321 #[derive(Clone, Copy, Debug, ValueEnum)]
    322 enum SourceMode {
    323     Prefetch,
    324     Offline,
    325 }
    326 
    327 #[derive(Clone, Copy, Debug, ValueEnum)]
    328 enum ServiceSourceLockMode {
    329     Check,
    330     Write,
    331 }
    332 
    333 #[derive(Clone, Copy, Debug, ValueEnum)]
    334 enum ServiceReleaseArtifactMode {
    335     Check,
    336     Write,
    337 }
    338 
    339 #[derive(Clone, Copy, Debug, ValueEnum)]
    340 enum ArtifactProduct {
    341     Sdk,
    342 }
    343 
    344 impl ArtifactProduct {
    345     fn as_str(self) -> &'static str {
    346         match self {
    347             Self::Sdk => "sdk",
    348         }
    349     }
    350 }
    351 
    352 #[derive(Clone, Copy, Debug, ValueEnum)]
    353 enum ArtifactTarget {
    354     Typescript,
    355     Wasm,
    356     Ffi,
    357     Linux,
    358     Macos,
    359     Windows,
    360 }
    361 
    362 impl ArtifactTarget {
    363     fn as_str(self) -> &'static str {
    364         match self {
    365             Self::Typescript => "typescript",
    366             Self::Wasm => "wasm",
    367             Self::Ffi => "ffi",
    368             Self::Linux => "linux",
    369             Self::Macos => "macos",
    370             Self::Windows => "windows",
    371         }
    372     }
    373 }
    374 
    375 #[derive(Clone, Copy, Debug, ValueEnum)]
    376 enum ArtifactLanguage {
    377     Typescript,
    378     Swift,
    379     Kotlin,
    380     Javascript,
    381 }
    382 
    383 impl ArtifactLanguage {
    384     fn as_str(self) -> &'static str {
    385         match self {
    386             Self::Typescript => "typescript",
    387             Self::Swift => "swift",
    388             Self::Kotlin => "kotlin",
    389             Self::Javascript => "javascript",
    390         }
    391     }
    392 }
    393 
    394 #[derive(Debug, Subcommand)]
    395 enum SourceCommand {
    396     Materialize {
    397         #[arg(long)]
    398         consumer_root: PathBuf,
    399         #[arg(long)]
    400         cache_root: PathBuf,
    401         #[arg(long, value_enum)]
    402         mode: SourceMode,
    403     },
    404     ArchiveVerify {
    405         #[arg(long)]
    406         archive: PathBuf,
    407         #[arg(long)]
    408         sha256: String,
    409     },
    410     ArchiveCreate {
    411         #[arg(long)]
    412         source_root: PathBuf,
    413         #[arg(long)]
    414         revision: String,
    415         #[arg(long)]
    416         output: PathBuf,
    417     },
    418 }
    419 
    420 fn usage() {
    421     eprintln!("usage:");
    422     eprintln!("  cargo xtask architecture");
    423     eprintln!("  cargo xtask architecture-ci");
    424     eprintln!("  cargo xtask architecture-source-export-ci");
    425     eprintln!("  cargo xtask advisory-snapshot-self-test");
    426     eprintln!("  cargo xtask bounded-process-self-test");
    427     eprintln!("  cargo xtask safe-artifact-io-self-test");
    428     eprintln!("  cargo xtask check-api-boundaries");
    429     eprintln!("  cargo xtask check-dependency-boundaries");
    430     eprintln!("  cargo xtask check --group <group> [--operation check|test|clippy] [--execute]");
    431     eprintln!("  cargo xtask catalog check|write");
    432     eprintln!("  cargo xtask contract validate");
    433     eprintln!("  cargo xtask contract event-contract-registry-v7 [--write]");
    434     eprintln!("  cargo xtask contract knowledge-manifest [--write]");
    435     eprintln!("  cargo xtask consolidation baseline");
    436     eprintln!("  cargo xtask consolidation history [--archive-root <absolute-directory>]");
    437     eprintln!("  cargo xtask consolidation history-rehearsal");
    438     eprintln!(
    439         "  cargo xtask consolidation import-verify --source <id> --source-root <absolute-directory> --filtered-root <absolute-directory> --mode <check|write>"
    440     );
    441     eprintln!("  cargo xtask dto-roots --check|--write");
    442     eprintln!("  cargo xtask generate protocol --check|--write");
    443     eprintln!("  cargo xtask release preflight");
    444     eprintln!("  cargo xtask release qualify-features");
    445     eprintln!("  cargo xtask release qualify-graph");
    446     eprintln!("  cargo xtask release qualify-api");
    447     eprintln!("  cargo xtask release qualify-fuzz");
    448     eprintln!("  cargo xtask release qualify-portable");
    449     eprintln!("  cargo xtask release qualify-safety");
    450     eprintln!("  cargo xtask release qualify-supply-chain");
    451     eprintln!("  cargo xtask release qualify-targets");
    452     eprintln!("  cargo xtask coverage run-crate --crate <crate> [--out <dir>]");
    453     eprintln!("  cargo xtask coverage required-crates");
    454     eprintln!("  cargo xtask coverage workspace-crates");
    455     eprintln!(
    456         "  cargo xtask coverage report --scope <scope> --summary <file> --lcov <file> --out <file> [--policy-gate | (--fail-under-exec-lines <pct> --fail-under-functions <pct> --fail-under-regions <pct> --fail-under-branches <pct> [--require-branches])]"
    457     );
    458     eprintln!(
    459         "  cargo xtask coverage report-missing --scope <scope> --out <file> --reason <reason>"
    460     );
    461     eprintln!(
    462         "  cargo xtask coverage refresh-summary [--reports-root <dir>] [--out <file>] [--status-out <file>]"
    463     );
    464     eprintln!("  cargo xtask hygiene forbidden-identifiers");
    465     eprintln!(
    466         "  cargo xtask hygiene prototype-contracts [--config <repo-relative-path>] [--strict|--report-only]"
    467     );
    468     eprintln!("  cargo xtask source-lock --consumer-root <absolute-directory>");
    469     eprintln!(
    470         "  cargo xtask service-source-lock --mode <check|write> --service-root <absolute-directory> --source-archive <absolute-bundle>"
    471     );
    472     eprintln!(
    473         "  cargo xtask service-release-artifacts --mode <check|write> --service-root <absolute-directory> --lib-root <absolute-directory> --input-root <absolute-directory> --output-root <absolute-directory> --target <rust-target> --source-date-epoch <seconds> --candidate-digest <sha256>"
    474     );
    475     eprintln!(
    476         "  cargo xtask service-repro-install --plan <absolute-file> --source-root <absolute-directory> --root-preimage-root <absolute-directory> --predecessor-artifact-root <absolute-directory> --git-executable <absolute-file> --adapter-executable <absolute-file> --output <absolute-file>"
    477     );
    478     eprintln!(
    479         "  cargo xtask source materialize --consumer-root <absolute-directory> --cache-root <absolute-directory> --mode <prefetch|offline>"
    480     );
    481     eprintln!("  cargo xtask source archive-verify --archive <bundle> --sha256 <digest>");
    482     eprintln!(
    483         "  cargo xtask source archive-create --source-root <absolute-directory> --revision <full-sha> --output <absolute-bundle>"
    484     );
    485     eprintln!(
    486         "  cargo xtask artifact --product sdk --target <target> --language <language> --mode <check|write> --consumer-root <absolute-directory> --source-root <absolute-directory> --output <relative-path> --source-date-epoch <seconds> --builder-id <id>"
    487     );
    488 }
    489 
    490 fn workspace_root_with_override(override_root: Option<&str>) -> PathBuf {
    491     if let Some(raw) = override_root {
    492         let trimmed = raw.trim();
    493         if !trimmed.is_empty() {
    494             return PathBuf::from(trimmed);
    495         }
    496     }
    497     let manifest_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
    498     let crates_dir = manifest_dir.parent().unwrap_or(manifest_dir);
    499     let root = crates_dir.parent().unwrap_or(crates_dir);
    500     root.to_path_buf()
    501 }
    502 
    503 fn workspace_root() -> PathBuf {
    504     let override_root = env::var("RADROOTS_WORKSPACE_ROOT").ok();
    505     workspace_root_with_override(override_root.as_deref())
    506 }
    507 
    508 fn validate_protocol_contracts() -> Result<(), String> {
    509     use radroots_protocol::{capability, event, runtime, schema};
    510 
    511     capability::v1::validate_catalog(capability::v1::CATALOG).map_err(|error| error.to_string())?;
    512     event::v1::validate_catalog(event::v1::CATALOG).map_err(|error| error.to_string())?;
    513     event::v1::validate_trade_state_vocabulary(event::v1::TRADE_STATE_VOCABULARY)
    514         .map_err(|error| error.to_string())?;
    515     runtime::v1::validate_catalog(runtime::v1::CATALOG).map_err(|error| error.to_string())?;
    516     schema::protocol_v1_registry()
    517         .map(|_| ())
    518         .map_err(|error| error.to_string())
    519 }
    520 
    521 fn validate_contract() -> Result<(), String> {
    522     validate_protocol_contracts()?;
    523     let root = workspace_root();
    524     artifact_admission::validate_contract(&root)?;
    525     service_source_lock::validate_contract(&root)?;
    526     service_build_qualification::validate_contract(&root)?;
    527     service_release_artifacts::validate_contract(&root)?;
    528     service_repro_install::validate_contract(&root)?;
    529     dto_roots::check(&root)?;
    530     generate::protocol::check(&root)?;
    531     contract::load_contract_bundle(&root)
    532         .and_then(|bundle| contract::validate_contract_bundle(&bundle))
    533         .and_then(|_| contract::validate_canonical_event_boundary(&root))
    534         .and_then(|_| contract::validate_artifact_contracts(&root))
    535 }
    536 
    537 #[cfg_attr(coverage_nightly, coverage(off))]
    538 fn release_preflight() -> Result<(), String> {
    539     let root = workspace_root();
    540     release_preflight_at(&root)
    541 }
    542 
    543 fn release_preflight_at(root: &Path) -> Result<(), String> {
    544     use release_preflight::{LaneId, LaneState};
    545 
    546     release_preflight::execute_all(|lane| {
    547         let result = match lane {
    548             LaneId::Catalog => catalog::check(root),
    549             LaneId::ServiceSourceLockContract => service_source_lock::validate_contract(root),
    550             LaneId::ServiceBuildQualificationContract => {
    551                 service_build_qualification::validate_contract(root)
    552             }
    553             LaneId::ServiceReleaseArtifactsContract => {
    554                 service_release_artifacts::validate_contract(root)
    555                     .and_then(|()| service_repro_install::validate_contract(root))
    556             }
    557             LaneId::PublicNativeGroup => build_control::group_plan(
    558                 root,
    559                 "public_native",
    560                 build_control::Operation::Check,
    561                 false,
    562             )
    563             .map(|_| ()),
    564             LaneId::PreviewGroup => {
    565                 build_control::group_plan(root, "preview", build_control::Operation::Check, false)
    566                     .map(|_| ())
    567             }
    568             LaneId::ToolsGroup => {
    569                 build_control::group_plan(root, "tools", build_control::Operation::Check, false)
    570                     .map(|_| ())
    571             }
    572             LaneId::DtoRoots => dto_roots::check(root),
    573             LaneId::ProtocolFreshness => generate::protocol::check(root),
    574             LaneId::ArtifactContracts => contract::validate_artifact_contracts(root),
    575             LaneId::ReleaseContracts => advisory_snapshot::validate_decision(root)
    576                 .and_then(|_| contract::validate_release_preflight(root)),
    577         };
    578         match result {
    579             Ok(()) => LaneState::Pass,
    580             Err(error) => {
    581                 eprintln!("release preflight {} failed: {error}", lane.as_str());
    582                 LaneState::Failed
    583             }
    584         }
    585     })
    586     .map(|_| ())
    587     .map_err(|error| error.to_string())
    588 }
    589 
    590 #[cfg_attr(coverage_nightly, coverage(off))]
    591 fn run_release(args: &[String]) -> Result<(), String> {
    592     match args.first().map(String::as_str) {
    593         Some("preflight") => release_preflight(),
    594         Some("qualify-features") => release_qualification::run_feature_matrix(&workspace_root()),
    595         Some("qualify-graph") => release_graph::run(&workspace_root()),
    596         Some("qualify-api") => api_qualification::run(&workspace_root()),
    597         Some("qualify-fuzz") => fuzz_qualification::run(&workspace_root()),
    598         Some("qualify-portable") => portable_qualification::run(&workspace_root()),
    599         Some("qualify-safety") => safety_qualification::run(&workspace_root()),
    600         Some("qualify-supply-chain") => supply_chain_qualification::run(&workspace_root()),
    601         Some("qualify-targets") => target_qualification::run(&workspace_root()),
    602         _ => Err("unknown release subcommand".to_string()),
    603     }
    604 }
    605 
    606 #[cfg_attr(coverage_nightly, coverage(off))]
    607 fn run_contract(args: &[String]) -> Result<(), String> {
    608     match args.first().map(String::as_str) {
    609         Some("validate") => validate_contract(),
    610         Some("event-contract-registry-v7") => match &args[1..] {
    611             [] => contract::validate_event_contract_registry_v7_inventory(&workspace_root()),
    612             [flag] if flag == "--write" => {
    613                 contract::write_event_contract_registry_v7_inventory(&workspace_root())
    614             }
    615             _ => Err(
    616                 "event-contract-registry-v7 accepts no arguments or exactly --write".to_string(),
    617             ),
    618         },
    619         Some("knowledge-manifest") => {
    620             if args.get(1).map(String::as_str) == Some("--write") {
    621                 contract::write_knowledge_contract_manifest(&workspace_root())
    622             } else {
    623                 contract::validate_knowledge_contract_manifest(&workspace_root())
    624             }
    625         }
    626         _ => Err("unknown contract subcommand".to_string()),
    627     }
    628 }
    629 
    630 fn run(args: &[String]) -> Result<(), String> {
    631     let cli = Cli::try_parse_from(std::iter::once("xtask").chain(args.iter().map(String::as_str)))
    632         .map_err(|error| error.to_string())?;
    633     match cli.command {
    634         XtaskCommand::AdvisorySnapshotSelfTest => advisory_snapshot::self_test(),
    635         XtaskCommand::AdvisorySnapshotFixtureScanner { mode } => {
    636             advisory_snapshot::write_fixture_scanner_output(&mode)
    637         }
    638         XtaskCommand::Architecture => architecture::validate(&workspace_root()),
    639         XtaskCommand::ArchitectureCi => {
    640             catalog::check(&workspace_root())?;
    641             architecture::validate_ci(&workspace_root())?;
    642             validate_contract()
    643         }
    644         XtaskCommand::ArchitectureSourceExportCi => {
    645             catalog::check_source_export(&workspace_root())?;
    646             architecture::validate_ci(&workspace_root())?;
    647             validate_contract()
    648         }
    649         XtaskCommand::BoundedProcessSelfTest => bounded_process::self_test(),
    650         XtaskCommand::SafeArtifactIoSelfTest => {
    651             safe_artifact_io::self_test()?;
    652             release_preflight::self_test()?;
    653             println!("safe artifact I/O self-test: ok");
    654             Ok(())
    655         }
    656         XtaskCommand::CheckApiBoundaries => {
    657             architecture::validate_api_boundaries(&workspace_root())
    658         }
    659         XtaskCommand::CheckDependencyBoundaries => {
    660             architecture::validate_dependency_boundaries(&workspace_root())
    661         }
    662         XtaskCommand::Check {
    663             group,
    664             operation,
    665             execute,
    666             include_reserved,
    667         } => {
    668             let operation = match operation {
    669                 GroupOperation::Check => build_control::Operation::Check,
    670                 GroupOperation::Test => build_control::Operation::Test,
    671                 GroupOperation::Clippy => build_control::Operation::Clippy,
    672             };
    673             let root = workspace_root();
    674             let plan = build_control::group_plan(&root, &group, operation, include_reserved)?;
    675             if execute {
    676                 build_control::execute_group_plan(&root, &plan)
    677             } else {
    678                 build_control::print_plan(&plan);
    679                 Ok(())
    680             }
    681         }
    682         XtaskCommand::Catalog { command } => match command {
    683             CatalogCommand::Check => catalog::run(&["check".to_owned()], &workspace_root()),
    684             CatalogCommand::Write => catalog::run(&["write".to_owned()], &workspace_root()),
    685         },
    686         XtaskCommand::Contract { args } => run_contract(&args),
    687         XtaskCommand::Consolidation { args } => consolidation::run(&args, &workspace_root()),
    688         XtaskCommand::Coverage { args } => coverage::run(&args),
    689         XtaskCommand::DtoRoots { args } => dto_roots::run(&args, &workspace_root()),
    690         XtaskCommand::Generate { args } => generate::run(&args, &workspace_root()),
    691         XtaskCommand::Hygiene { args } => hygiene::run(&args, &workspace_root()),
    692         XtaskCommand::Release { args } => run_release(&args),
    693         XtaskCommand::RshrStep298Gate {
    694             step,
    695             check_id,
    696             source_revision,
    697             source_tree,
    698             candidate_digest,
    699             platform,
    700             execution_request_sha256,
    701         } => rshr_202_step_298_gate::run(rshr_202_step_298_gate::Arguments {
    702             step,
    703             check_id,
    704             source_revision,
    705             source_tree,
    706             candidate_digest,
    707             platform,
    708             execution_request_sha256,
    709         }),
    710         XtaskCommand::RshrStep298PlatformProbe => rshr_202_step_298_platform::run(),
    711         XtaskCommand::RshrStep299Gate {
    712             step,
    713             check_id,
    714             source_revision,
    715             source_tree,
    716             candidate_digest,
    717             platform,
    718             execution_request_sha256,
    719         } => rshr_202_step_299_gate::run(rshr_202_step_299_gate::Arguments {
    720             step,
    721             check_id,
    722             source_revision,
    723             source_tree,
    724             candidate_digest,
    725             platform,
    726             execution_request_sha256,
    727         }),
    728         XtaskCommand::RshrStep304Gate {
    729             step,
    730             check_id,
    731             source_revision,
    732             source_tree,
    733             candidate_digest,
    734             platform,
    735             execution_request_sha256,
    736         } => rshr_202_step_304_gate::run(rshr_202_step_304_gate::Arguments {
    737             step,
    738             check_id,
    739             source_revision,
    740             source_tree,
    741             candidate_digest,
    742             platform,
    743             execution_request_sha256,
    744         }),
    745         XtaskCommand::RshrStep305Gate {
    746             step,
    747             check_id,
    748             source_revision,
    749             source_tree,
    750             candidate_digest,
    751             platform,
    752             execution_request_sha256,
    753         } => rshr_202_step_305_gate::run(rshr_202_step_305_gate::Arguments {
    754             step,
    755             check_id,
    756             source_revision,
    757             source_tree,
    758             candidate_digest,
    759             platform,
    760             execution_request_sha256,
    761         }),
    762         XtaskCommand::RshrStep306Gate {
    763             step,
    764             check_id,
    765             source_revision,
    766             source_tree,
    767             candidate_digest,
    768             platform,
    769             execution_request_sha256,
    770         } => rshr_202_step_306_gate::run(rshr_202_step_306_gate::Arguments {
    771             step,
    772             check_id,
    773             source_revision,
    774             source_tree,
    775             candidate_digest,
    776             platform,
    777             execution_request_sha256,
    778         }),
    779         XtaskCommand::SourceLock { consumer_root } => {
    780             build_control::validate_consumer(&consumer_root).map(|_| ())
    781         }
    782         XtaskCommand::ServiceSourceLock {
    783             mode,
    784             service_root,
    785             source_archive,
    786         } => service_source_lock_command::run(
    787             match mode {
    788                 ServiceSourceLockMode::Check => service_source_lock_command::CommandMode::Check,
    789                 ServiceSourceLockMode::Write => service_source_lock_command::CommandMode::Write,
    790             },
    791             &service_root,
    792             &source_archive,
    793         ),
    794         XtaskCommand::ServiceReleaseArtifacts {
    795             mode,
    796             service_root,
    797             lib_root,
    798             input_root,
    799             output_root,
    800             target,
    801             source_date_epoch,
    802             candidate_digest,
    803         } => service_release_artifacts::run(service_release_artifacts::Arguments {
    804             mode: match mode {
    805                 ServiceReleaseArtifactMode::Check => service_release_artifacts::CommandMode::Check,
    806                 ServiceReleaseArtifactMode::Write => service_release_artifacts::CommandMode::Write,
    807             },
    808             service_root: &service_root,
    809             lib_root: &lib_root,
    810             input_root: &input_root,
    811             output_root: &output_root,
    812             target: &target,
    813             source_date_epoch,
    814             candidate_digest: &candidate_digest,
    815         }),
    816         XtaskCommand::ServiceReproInstall {
    817             plan,
    818             source_root,
    819             root_preimage_root,
    820             predecessor_artifact_root,
    821             git_executable,
    822             adapter_executable,
    823             output,
    824         } => service_repro_install::run(
    825             &workspace_root(),
    826             service_repro_install::Arguments {
    827                 plan: &plan,
    828                 source_root: &source_root,
    829                 root_preimage_root: &root_preimage_root,
    830                 predecessor_artifact_root: &predecessor_artifact_root,
    831                 git_executable: &git_executable,
    832                 adapter_executable: &adapter_executable,
    833                 output: &output,
    834             },
    835         ),
    836         XtaskCommand::Source { command } => match command {
    837             SourceCommand::Materialize {
    838                 consumer_root,
    839                 cache_root,
    840                 mode,
    841             } => build_control::materialize(
    842                 &consumer_root,
    843                 &cache_root,
    844                 matches!(mode, SourceMode::Offline),
    845             )
    846             .map(|path| {
    847                 println!("{}", path.display());
    848             }),
    849             SourceCommand::ArchiveVerify { archive, sha256 } => {
    850                 build_control::verify_source_archive(&archive, &sha256)
    851             }
    852             SourceCommand::ArchiveCreate {
    853                 source_root,
    854                 revision,
    855                 output,
    856             } => build_control::create_source_archive(&source_root, &revision, &output).map(
    857                 |digest| {
    858                     println!("{digest}");
    859                 },
    860             ),
    861         },
    862         XtaskCommand::Artifact {
    863             product,
    864             target,
    865             language,
    866             mode,
    867             consumer_root,
    868             source_root,
    869             output,
    870             source_date_epoch,
    871             builder_id,
    872             features,
    873         } => {
    874             let product = product.as_str();
    875             let target = target.as_str();
    876             let language = language.as_str();
    877             let mode = match mode {
    878                 ArtifactMode::Check => build_control::Mode::Check,
    879                 ArtifactMode::Write => build_control::Mode::Write,
    880             };
    881             build_control::validate_generation_roots(
    882                 product,
    883                 target,
    884                 language,
    885                 &consumer_root,
    886                 &source_root,
    887             )?;
    888             if product == "sdk" {
    889                 sdk_generation::artifact(&source_root, &consumer_root, target, language, mode)?;
    890             }
    891             build_control::artifact(
    892                 product,
    893                 target,
    894                 language,
    895                 mode,
    896                 &consumer_root,
    897                 &source_root,
    898                 &output,
    899                 source_date_epoch,
    900                 &builder_id,
    901                 &features,
    902             )
    903         }
    904     }
    905 }
    906 
    907 fn main_with_args(args: Vec<String>) -> ExitCode {
    908     if args.is_empty() {
    909         usage();
    910         return ExitCode::from(2);
    911     }
    912     match run(&args) {
    913         Ok(()) => ExitCode::SUCCESS,
    914         Err(err) => {
    915             eprintln!("{err}");
    916             usage();
    917             ExitCode::from(2)
    918         }
    919     }
    920 }
    921 
    922 #[cfg_attr(coverage_nightly, coverage(off))]
    923 fn main() -> ExitCode {
    924     main_with_args(env::args().skip(1).collect())
    925 }
    926 
    927 #[cfg(test)]
    928 mod tests {
    929     use super::*;
    930     use std::fs;
    931     use std::sync::{Mutex, MutexGuard, OnceLock};
    932     use std::time::{SystemTime, UNIX_EPOCH};
    933 
    934     fn workspace_lock() -> &'static Mutex<()> {
    935         static LOCK: OnceLock<Mutex<()>> = OnceLock::new();
    936         LOCK.get_or_init(|| Mutex::new(()))
    937     }
    938 
    939     fn lock_workspace() -> MutexGuard<'static, ()> {
    940         match workspace_lock().lock() {
    941             Ok(guard) => guard,
    942             Err(poison) => poison.into_inner(),
    943         }
    944     }
    945 
    946     fn unique_temp_dir(prefix: &str) -> PathBuf {
    947         let ns = SystemTime::now()
    948             .duration_since(UNIX_EPOCH)
    949             .expect("system time")
    950             .as_nanos();
    951         std::env::temp_dir().join(format!("radroots_xtask_main_{prefix}_{ns}"))
    952     }
    953 
    954     #[test]
    955     fn workspace_root_resolves() {
    956         let root = workspace_root();
    957         assert!(root.join("Cargo.toml").exists());
    958     }
    959 
    960     #[test]
    961     fn workspace_root_override_takes_precedence() {
    962         let root = workspace_root_with_override(Some("/tmp/radroots-test-root"));
    963         assert_eq!(root, PathBuf::from("/tmp/radroots-test-root"));
    964 
    965         let fallback = workspace_root_with_override(Some("   "));
    966         assert!(fallback.join("Cargo.toml").exists());
    967 
    968         let default_root = workspace_root_with_override(None);
    969         assert!(default_root.join("Cargo.toml").exists());
    970     }
    971 
    972     #[test]
    973     fn typed_build_control_cli_requires_explicit_modes_and_known_values() {
    974         assert!(Cli::try_parse_from(["xtask", "bounded-process-self-test"]).is_ok());
    975         assert!(Cli::try_parse_from(["xtask", "safe-artifact-io-self-test"]).is_ok());
    976 
    977         let source_args = [
    978             "xtask",
    979             "source",
    980             "materialize",
    981             "--consumer-root",
    982             "/tmp/consumer",
    983             "--cache-root",
    984             "/tmp/cache",
    985         ];
    986         assert!(Cli::try_parse_from(source_args).is_err());
    987         assert!(Cli::try_parse_from(source_args.into_iter().chain(["--mode", "prefetch"])).is_ok());
    988 
    989         assert!(
    990             Cli::try_parse_from([
    991                 "xtask",
    992                 "service-source-lock",
    993                 "--mode",
    994                 "check",
    995                 "--service-root",
    996                 "/tmp/service",
    997             ])
    998             .is_err()
    999         );
   1000         assert!(
   1001             Cli::try_parse_from([
   1002                 "xtask",
   1003                 "service-source-lock",
   1004                 "--mode",
   1005                 "write",
   1006                 "--service-root",
   1007                 "/tmp/service",
   1008                 "--source-archive",
   1009                 "/tmp/lib.bundle",
   1010             ])
   1011             .is_ok()
   1012         );
   1013         assert!(
   1014             Cli::try_parse_from([
   1015                 "xtask",
   1016                 "service-release-artifacts",
   1017                 "--mode",
   1018                 "write",
   1019                 "--service-root",
   1020                 "/tmp/service",
   1021                 "--lib-root",
   1022                 "/tmp/lib",
   1023                 "--input-root",
   1024                 "/tmp/input",
   1025                 "--output-root",
   1026                 "/tmp/output",
   1027                 "--target",
   1028                 "x86_64-unknown-linux-gnu",
   1029             ])
   1030             .is_err()
   1031         );
   1032         assert!(
   1033             Cli::try_parse_from([
   1034                 "xtask",
   1035                 "service-release-artifacts",
   1036                 "--mode",
   1037                 "check",
   1038                 "--service-root",
   1039                 "/tmp/service",
   1040                 "--lib-root",
   1041                 "/tmp/lib",
   1042                 "--input-root",
   1043                 "/tmp/input",
   1044                 "--output-root",
   1045                 "/tmp/output",
   1046                 "--target",
   1047                 "x86_64-unknown-linux-gnu",
   1048                 "--source-date-epoch",
   1049                 "1",
   1050                 "--candidate-digest",
   1051                 "1111111111111111111111111111111111111111111111111111111111111111",
   1052             ])
   1053             .is_ok()
   1054         );
   1055         assert!(
   1056             Cli::try_parse_from([
   1057                 "xtask",
   1058                 "service-repro-install",
   1059                 "--plan",
   1060                 "/tmp/plan.json",
   1061                 "--source-root",
   1062                 "/tmp/source",
   1063                 "--root-preimage-root",
   1064                 "/tmp/root",
   1065                 "--predecessor-artifact-root",
   1066                 "/tmp/predecessor",
   1067                 "--git-executable",
   1068                 "/usr/bin/git",
   1069                 "--adapter-executable",
   1070                 "/tmp/adapter",
   1071                 "--output",
   1072                 "/tmp/result.json",
   1073             ])
   1074             .is_ok()
   1075         );
   1076 
   1077         assert!(
   1078             Cli::try_parse_from([
   1079                 "xtask",
   1080                 "artifact",
   1081                 "--product",
   1082                 "unknown",
   1083                 "--target",
   1084                 "wasm",
   1085                 "--language",
   1086                 "javascript",
   1087                 "--mode",
   1088                 "check",
   1089                 "--consumer-root",
   1090                 "/tmp/consumer",
   1091                 "--source-root",
   1092                 "/tmp/source",
   1093                 "--output",
   1094                 "generated/manifest.json",
   1095                 "--source-date-epoch",
   1096                 "1",
   1097                 "--builder-id",
   1098                 "fixture",
   1099             ])
   1100             .is_err()
   1101         );
   1102     }
   1103 
   1104     #[test]
   1105     fn artifact_cli_values_preserve_every_governed_identifier() {
   1106         assert!(ArtifactProduct::from_str("mobile", false).is_err());
   1107         assert!(ArtifactTarget::from_str("ios", false).is_err());
   1108         assert!(ArtifactTarget::from_str("android", false).is_err());
   1109         assert_eq!([ArtifactProduct::Sdk.as_str()], ["sdk"]);
   1110         assert_eq!(
   1111             [
   1112                 ArtifactTarget::Typescript.as_str(),
   1113                 ArtifactTarget::Wasm.as_str(),
   1114                 ArtifactTarget::Ffi.as_str(),
   1115                 ArtifactTarget::Linux.as_str(),
   1116                 ArtifactTarget::Macos.as_str(),
   1117                 ArtifactTarget::Windows.as_str(),
   1118             ],
   1119             ["typescript", "wasm", "ffi", "linux", "macos", "windows",]
   1120         );
   1121         assert_eq!(
   1122             [
   1123                 ArtifactLanguage::Typescript.as_str(),
   1124                 ArtifactLanguage::Swift.as_str(),
   1125                 ArtifactLanguage::Kotlin.as_str(),
   1126                 ArtifactLanguage::Javascript.as_str(),
   1127             ],
   1128             ["typescript", "swift", "kotlin", "javascript"]
   1129         );
   1130     }
   1131 
   1132     #[test]
   1133     fn run_release_and_dispatchers_cover_error_paths() {
   1134         let unknown_release =
   1135             run_release(&["unknown".to_string()]).expect_err("unknown release subcommand");
   1136         assert!(unknown_release.contains("unknown release subcommand"));
   1137 
   1138         let unknown_contract =
   1139             run_contract(&["unknown".to_string()]).expect_err("unknown contract subcommand");
   1140         assert!(unknown_contract.contains("unknown contract subcommand"));
   1141         let invalid_registry = run_contract(&[
   1142             "event-contract-registry-v7".to_string(),
   1143             "--invalid".to_string(),
   1144         ])
   1145         .expect_err("invalid registry-v7 mode");
   1146         assert!(invalid_registry.contains("exactly --write"));
   1147         let unknown_root = run(&["unknown".to_string()]).expect_err("unknown command");
   1148         assert!(unknown_root.contains("unrecognized subcommand"));
   1149 
   1150         run(&["architecture".to_string()]).expect("architecture ledger validates");
   1151 
   1152         let invalid_dto_roots =
   1153             run(&["dto-roots".to_string()]).expect_err("dto-roots requires an explicit mode");
   1154         assert!(invalid_dto_roots.contains("--check|--write"));
   1155         let invalid_generate =
   1156             run(&["generate".to_string()]).expect_err("generate requires a target and mode");
   1157         assert!(invalid_generate.contains("generate protocol --check|--write"));
   1158 
   1159         let removed_sdk = run(&["sdk".to_string(), "validate".to_string()])
   1160             .expect_err("removed sdk command namespace");
   1161         assert!(removed_sdk.contains("unrecognized subcommand"));
   1162     }
   1163 
   1164     #[test]
   1165     fn release_preflight_exhausts_the_closed_lane_inventory() {
   1166         let workspace = tempfile::TempDir::new().expect("create empty workspace");
   1167         let error = release_preflight_at(workspace.path())
   1168             .expect_err("missing required inputs must fail closed");
   1169         assert_eq!(error, "release preflight required lanes did not all pass");
   1170     }
   1171 
   1172     #[test]
   1173     fn lock_workspace_recovers_from_poisoned_mutex() {
   1174         let handle = std::thread::spawn(|| {
   1175             let _guard = workspace_lock().lock().expect("lock workspace");
   1176             panic!("poison workspace lock");
   1177         });
   1178         assert!(handle.join().is_err());
   1179 
   1180         let _guard = lock_workspace();
   1181     }
   1182 
   1183     #[test]
   1184     fn contract_and_coverage_dispatchers_execute() {
   1185         let _guard = lock_workspace();
   1186         let out_dir = unique_temp_dir("coverage_dispatch");
   1187         fs::create_dir_all(&out_dir).expect("create out dir");
   1188 
   1189         run_contract(&["validate".to_string()]).expect("validate contract");
   1190         run(&["dto-roots".to_string(), "--check".to_string()])
   1191             .expect("validate DTO root freshness");
   1192         run(&[
   1193             "generate".to_string(),
   1194             "protocol".to_string(),
   1195             "--check".to_string(),
   1196         ])
   1197         .expect("validate protocol DTO inventory freshness");
   1198         coverage::run(&["help".to_string()]).expect("coverage help");
   1199         coverage::run(&["required-crates".to_string()]).expect("coverage required crates");
   1200         coverage::run(&["workspace-crates".to_string()]).expect("coverage workspace crates");
   1201 
   1202         let summary_path = out_dir.join("summary.json");
   1203         let lcov_path = out_dir.join("coverage.info");
   1204         let gate_out = out_dir.join("gate-report.json");
   1205         fs::write(
   1206             &summary_path,
   1207             r#"{"data":[{"totals":{"functions":{"percent":100.0},"lines":{"percent":100.0},"regions":{"percent":100.0}}}]}"#,
   1208         )
   1209         .expect("write summary");
   1210         fs::write(&lcov_path, "DA:1,1\nBRDA:1,0,0,1\n").expect("write lcov");
   1211         coverage::run(&[
   1212             "report".to_string(),
   1213             "--scope".to_string(),
   1214             "main-test".to_string(),
   1215             "--summary".to_string(),
   1216             summary_path.display().to_string(),
   1217             "--lcov".to_string(),
   1218             lcov_path.display().to_string(),
   1219             "--out".to_string(),
   1220             gate_out.display().to_string(),
   1221             "--policy-gate".to_string(),
   1222         ])
   1223         .expect("coverage report");
   1224 
   1225         run(&["coverage".to_string(), "help".to_string()]).expect("root run coverage");
   1226         run(&["hygiene".to_string(), "forbidden-identifiers".to_string()])
   1227             .expect("hygiene forbidden identifiers");
   1228         run(&["hygiene".to_string(), "prototype-contracts".to_string()])
   1229             .expect("report prototype contracts");
   1230 
   1231         let _ = fs::remove_dir_all(out_dir);
   1232     }
   1233 
   1234     #[test]
   1235     fn usage_and_main_entrypoints_execute() {
   1236         usage();
   1237         let empty_code = main_with_args(Vec::new());
   1238         assert_eq!(empty_code, ExitCode::from(2));
   1239         let success_code = main_with_args(vec!["coverage".to_string(), "help".to_string()]);
   1240         assert_eq!(success_code, ExitCode::SUCCESS);
   1241         let failure_code = main_with_args(vec!["unknown".to_string()]);
   1242         assert_eq!(failure_code, ExitCode::from(2));
   1243         let _ = main();
   1244     }
   1245 
   1246     #[test]
   1247     fn run_contract_dispatches_validate_command() {
   1248         let _guard = lock_workspace();
   1249         validate_protocol_contracts().expect("final protocol contract catalogs");
   1250         run_contract(&["validate".to_string()]).expect("contract validate");
   1251         run_contract(&["event-contract-registry-v7".to_string()])
   1252             .expect("contract registry-v7 inventory");
   1253         run_contract(&["knowledge-manifest".to_string()]).expect("contract knowledge manifest");
   1254     }
   1255 }