lib

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

commit e791ed375864725dc5ba915eb43230a1d1e49cc7
parent 078492acdc0e4ea44186a45e6a728e841fe2dbe2
Author: triesap <tyson@radroots.org>
Date:   Tue, 14 Jul 2026 06:49:48 +0000

release: guard DTO tooling sources

- reject publishable DTO tooling git and path sources during release preflight
- resolve workspace-inherited DTO dependencies before source checks
- keep internal-only DTO tooling dependencies outside the public publication gate
- cover direct path and workspace git dependency failures in xtask tests

Diffstat:
Mtools/xtask/src/contract.rs | 185++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 184 insertions(+), 1 deletion(-)

diff --git a/tools/xtask/src/contract.rs b/tools/xtask/src/contract.rs @@ -40,6 +40,12 @@ const EVENT_BOUNDARY_MATRIX_ENV: &str = "RADROOTS_EVENT_BOUNDARY_MATRIX"; const COVERAGE_REQUIRED_THRESHOLD: f64 = 100.0; const COVERAGE_REQUIRED_THRESHOLD_LABEL: &str = "100/100/100/100"; const COVERAGE_REPORT_EPSILON: f64 = 0.000_001; +const DTO_TOOLING_DEPENDENCIES: [&str; 4] = [ + "dto_bindgen", + "dto_bindgen_backend_ts", + "dto_bindgen_core", + "dto_bindgen_macros", +]; const EVENT_BOUNDARY_MATRIX_RELATIVES: [&str; 1] = [ "docs/platform/canonical/open_source/radroots_v1_spec/02_public_contract_and_runtime/08_event_boundary_matrix.md", ]; @@ -2557,6 +2563,94 @@ fn read_workspace_package_dependencies( Ok(deps) } +fn validate_publishable_dto_tooling_sources( + workspace_root: &Path, + public_crates: &BTreeSet<String>, +) -> Result<(), String> { + let workspace_manifest_value = parse_toml::<toml::Value>(&workspace_root.join("Cargo.toml"))?; + let package_records = workspace_package_records(workspace_root)?; + + for record in package_records { + if !public_crates.contains(&record.name) { + continue; + } + for section in ["dependencies", "build-dependencies"] { + let Some(dependencies) = record + .manifest_value + .get(section) + .and_then(toml::Value::as_table) + else { + continue; + }; + for dependency_name in DTO_TOOLING_DEPENDENCIES { + let Some(dependency_value) = dependencies.get(dependency_name) else { + continue; + }; + validate_publishable_dto_dependency_source( + workspace_manifest_value.as_table(), + record.name.as_str(), + section, + dependency_name, + dependency_value, + )?; + } + } + } + + Ok(()) +} + +fn validate_publishable_dto_dependency_source( + workspace_manifest: Option<&toml::value::Table>, + crate_name: &str, + section: &str, + dependency_name: &str, + dependency_value: &toml::Value, +) -> Result<(), String> { + let resolved = + resolve_workspace_dependency_source(workspace_manifest, dependency_name, dependency_value) + .unwrap_or(dependency_value); + if dependency_has_source_key(resolved, "git") { + return Err(format!( + "public crate {crate_name} {section}.{dependency_name} must use a crates.io DTO tooling dependency, not a git source" + )); + } + if dependency_has_source_key(resolved, "path") { + return Err(format!( + "public crate {crate_name} {section}.{dependency_name} must use a crates.io DTO tooling dependency, not a path source" + )); + } + Ok(()) +} + +fn resolve_workspace_dependency_source<'a>( + workspace_manifest: Option<&'a toml::value::Table>, + dependency_name: &str, + dependency_value: &toml::Value, +) -> Option<&'a toml::Value> { + if !dependency_has_workspace_true(dependency_value) { + return None; + } + workspace_manifest? + .get("workspace")? + .as_table()? + .get("dependencies")? + .as_table()? + .get(dependency_name) +} + +fn dependency_has_workspace_true(value: &toml::Value) -> bool { + value + .as_table() + .and_then(|table| table.get("workspace")) + .and_then(toml::Value::as_bool) + .unwrap_or(false) +} + +fn dependency_has_source_key(value: &toml::Value, key: &str) -> bool { + value.as_table().and_then(|table| table.get(key)).is_some() +} + fn join_set(items: &BTreeSet<String>) -> String { items.iter().cloned().collect::<Vec<_>>().join(", ") } @@ -3529,7 +3623,6 @@ fn validate_release_publish_policy( )); } } - Ok(()) } @@ -3564,6 +3657,7 @@ pub fn validate_release_preflight_with_override( .expect("validated contract includes required crates"); let required_crates = collect_unique_set(&required_crate_list, "required.crates") .expect("validated contract enforces unique required.crates"); + validate_publishable_dto_tooling_sources(workspace_root, &publish_crates)?; validate_publish_package_metadata(workspace_root, &publish_crates)?; validate_required_coverage_summary_with_policy(workspace_root, &required_crates, &policy)?; Ok(()) @@ -5632,6 +5726,95 @@ edition = "2024" } #[test] + fn release_preflight_rejects_public_dto_tooling_git_or_path_sources() { + let root = create_synthetic_workspace("release_policy_dto_tooling_sources"); + + write_file( + &root.join("crates").join("a").join("Cargo.toml"), + r#"[package] +name = "radroots_a" +publish = ["crates-io"] +version = "0.1.0" +edition = "2024" +description = "crate a" +repository = "https://example.com/a" +homepage = "https://example.com/a" +documentation = "https://docs.example.com/a" +readme = "README" + +[dependencies] +dto_bindgen_core = { path = "../../dto_bindgen_core", version = "0.1.0", optional = true } +"#, + ); + let path_err = validate_release_preflight(&root).expect_err("public path DTO dependency"); + assert!(path_err.contains("radroots_a dependencies.dto_bindgen_core")); + assert!(path_err.contains("not a path source")); + + write_file( + &root.join("Cargo.toml"), + r#"[workspace] +members = ["crates/a", "crates/b"] +resolver = "2" + +[workspace.dependencies] +dto_bindgen = { version = "0.1.0", git = "https://example.com/dto_bindgen", rev = "abc123" } +"#, + ); + write_file( + &root.join("crates").join("a").join("Cargo.toml"), + r#"[package] +name = "radroots_a" +publish = ["crates-io"] +version = "0.1.0" +edition = "2024" +description = "crate a" +repository = "https://example.com/a" +homepage = "https://example.com/a" +documentation = "https://docs.example.com/a" +readme = "README" + +[dependencies] +dto_bindgen = { workspace = true, optional = true } +"#, + ); + let git_err = + validate_release_preflight(&root).expect_err("public workspace git DTO dependency"); + assert!(git_err.contains("radroots_a dependencies.dto_bindgen")); + assert!(git_err.contains("not a git source")); + + write_file( + &root.join("crates").join("a").join("Cargo.toml"), + r#"[package] +name = "radroots_a" +publish = ["crates-io"] +version = "0.1.0" +edition = "2024" +description = "crate a" +repository = "https://example.com/a" +homepage = "https://example.com/a" +documentation = "https://docs.example.com/a" +readme = "README" +"#, + ); + write_file( + &root.join("crates").join("b").join("Cargo.toml"), + r#"[package] +name = "radroots_b" +version = "0.1.0" +edition = "2024" +publish = false + +[dependencies] +dto_bindgen = { workspace = true, optional = true } +"#, + ); + validate_release_preflight(&root) + .expect("internal DTO tooling source does not block public publish policy"); + + let _ = fs::remove_dir_all(root); + } + + #[test] fn validate_contract_bundle_reports_required_field_errors() { let root = create_synthetic_workspace("contract_bundle_errors");