lib

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

commit 7538aebccf4f868fb6ac57f42e5e9a813624b11b
parent ad17b7d3455a7147cfa303d976fc5c70c3a4c0cb
Author: triesap <tyson@radroots.org>
Date:   Tue,  1 Sep 2026 02:50:46 +0000

xtask: recognize SwiftPM build output

- centralize exact build-output directory classification
- exclude SwiftPM .build from consumer manifest discovery
- retain traversal for lookalike source directory names
- cover mobile and service source-lock walkers

Diffstat:
Mtools/xtask/src/build_control.rs | 34+++++++++++++++++++++-------------
Atools/xtask/src/build_output.rs | 38++++++++++++++++++++++++++++++++++++++
Mtools/xtask/src/main.rs | 1+
Mtools/xtask/src/service_source_lock_command.rs | 28++++++++++++++++++++++++----
4 files changed, 84 insertions(+), 17 deletions(-)

diff --git a/tools/xtask/src/build_control.rs b/tools/xtask/src/build_control.rs @@ -10,6 +10,8 @@ use fs2::FileExt; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; +use crate::build_output::is_build_output_directory; + const SOURCE_LOCK_NAME: &str = "radroots.lib.source-lock.v1.toml"; const CONSUMER_MARKER: &str = ".radroots-consumer-root"; const CATALOG_RELATIVE: &str = "contracts/crates/catalog.v2.toml"; @@ -741,19 +743,9 @@ fn collect_manifests(root: &Path, current: &Path, output: &mut Vec<PathBuf>) -> } let name = entry.file_name(); if file_type.is_dir() { - if matches!( - name.to_str(), - Some( - ".git" - | ".gradle" - | ".kotlin" - | ".radroots" - | "build" - | "node_modules" - | "out" - | "target" - ) - ) { + if matches!(name.to_str(), Some(".git" | ".radroots")) + || is_build_output_directory(name.as_os_str()) + { continue; } collect_manifests(root, &entry.path(), output)?; @@ -1196,6 +1188,22 @@ mod tests { } #[test] + fn consumer_manifest_discovery_skips_swiftpm_build_output_only() { + let fixture = Fixture::new("mobile"); + let swiftpm_checkout = fixture.consumer.join(".build/checkouts/dependency"); + fs::create_dir_all(&swiftpm_checkout).expect("SwiftPM checkout directory"); + fs::write(swiftpm_checkout.join("Cargo.toml"), "not valid TOML") + .expect("SwiftPM build manifest"); + ConsumerRoot::open(&fixture.consumer).expect("SwiftPM build output is excluded"); + + let source_lookalike = fixture.consumer.join(".builder"); + fs::create_dir(&source_lookalike).expect("source lookalike directory"); + fs::write(source_lookalike.join("Cargo.toml"), "not valid TOML") + .expect("source lookalike manifest"); + assert!(ConsumerRoot::open(&fixture.consumer).is_err()); + } + + #[test] fn materialization_reuses_verified_cache_and_rejects_tampering() { let fixture = Fixture::new("sdk"); let consumer = ConsumerRoot::open(&fixture.consumer).expect("consumer"); diff --git a/tools/xtask/src/build_output.rs b/tools/xtask/src/build_output.rs @@ -0,0 +1,38 @@ +use std::ffi::OsStr; + +pub(crate) fn is_build_output_directory(name: &OsStr) -> bool { + matches!( + name.to_str(), + Some(".build" | ".gradle" | ".kotlin" | "build" | "node_modules" | "out" | "target") + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn recognizes_only_exact_build_output_directory_names() { + for name in [ + ".build", + ".gradle", + ".kotlin", + "build", + "node_modules", + "out", + "target", + ] { + assert!(is_build_output_directory(OsStr::new(name)), "{name}"); + } + for name in [ + ".builder", + ".build-output", + "builds", + "node_module", + "output", + "targets", + ] { + assert!(!is_build_output_directory(OsStr::new(name)), "{name}"); + } + } +} diff --git a/tools/xtask/src/main.rs b/tools/xtask/src/main.rs @@ -13,6 +13,7 @@ mod api_qualification; mod architecture; #[cfg_attr(coverage_nightly, coverage(off))] mod build_control; +mod build_output; #[cfg_attr(coverage_nightly, coverage(off))] mod catalog; #[cfg_attr(coverage_nightly, coverage(off))] diff --git a/tools/xtask/src/service_source_lock_command.rs b/tools/xtask/src/service_source_lock_command.rs @@ -12,6 +12,7 @@ use sha2::{Digest as _, Sha256}; use tempfile::{NamedTempFile, TempDir}; use walkdir::WalkDir; +use crate::build_output::is_build_output_directory; use crate::service_source_lock::{ ContractVersions, LIB_REPOSITORY, LOCK_FILENAME, NixMaterialParts, NixMaterialState, PREDECESSOR_LOCK_FILENAME, ServiceSourceLockParts, ServiceSourceLockV2, @@ -415,10 +416,7 @@ fn validate_cargo_manifests( .follow_links(false) .into_iter() .filter_entry(|entry| { - !matches!( - entry.file_name().to_str(), - Some(".git" | ".gradle" | ".kotlin" | "build" | "node_modules" | "out" | "target") - ) + entry.file_name() != OsStr::new(".git") && !is_build_output_directory(entry.file_name()) }); for entry in walker { entries = entries @@ -1821,6 +1819,28 @@ name = "radroots_service_host" } #[test] + fn service_manifest_discovery_skips_swiftpm_build_output_only() { + let fixture = Fixture::new(); + let swiftpm_checkout = fixture.service.join(".build/checkouts/dependency"); + fs::create_dir_all(&swiftpm_checkout).expect("SwiftPM checkout directory"); + fs::write(swiftpm_checkout.join(CARGO_MANIFEST), "not valid TOML") + .expect("SwiftPM build manifest"); + assert_eq!( + validate_cargo_manifests(&fixture.service, None), + Ok(fixture.revision) + ); + + let source_lookalike = fixture.service.join(".builder"); + fs::create_dir(&source_lookalike).expect("source lookalike directory"); + fs::write(source_lookalike.join(CARGO_MANIFEST), "not valid TOML") + .expect("source lookalike manifest"); + assert_eq!( + validate_cargo_manifests(&fixture.service, None), + Err(CommandError::InvalidCargoManifest) + ); + } + + #[test] fn source_lock_output_rejects_symlink_replacement() { let fixture = Fixture::new(); let target = fixture.root.path().join("foreign");