commit 47656467f15b1d21b06f6b6159a8f70cb40c0d28
parent 735ecaa2d0d3a9ef4d294f3c41fdbdf3761b3c36
Author: triesap <tyson@radroots.org>
Date: Mon, 3 Aug 2026 22:31:22 +0000
release: qualify both Rust front doors
- derive both public matrices from the architecture catalog
- check default no-default individual and all-feature profiles
- gate the ordinary example behind the client feature
- consume the canonical trade json feature
Diffstat:
4 files changed, 146 insertions(+), 2 deletions(-)
diff --git a/Cargo.toml b/Cargo.toml
@@ -86,7 +86,7 @@ radroots_sdk = { path = "crates/sdk", version = "=0.1.0-alpha", default-features
radroots_sdk_sql_wasm_runtime = { path = "crates/sql_wasm_runtime", version = "=0.1.0-alpha" }
radroots_sql_core = { version = "=0.1.0-alpha", default-features = false }
radroots_trade = { package = "radroots_trade", version = "=0.1.0-alpha", default-features = false, features = [
- "serde_json",
+ "json",
"std",
] }
diff --git a/crates/radroots/Cargo.toml b/crates/radroots/Cargo.toml
@@ -48,3 +48,4 @@ full = ["radroots_sdk/full"]
[[example]]
name = "ordinary_memory"
path = "examples/ordinary_memory.rs"
+required-features = ["client"]
diff --git a/tools/xtask/src/main.rs b/tools/xtask/src/main.rs
@@ -13,6 +13,7 @@ mod manifest;
mod output;
mod package_matrix;
mod package_metadata;
+mod release_qualification;
mod smoke;
mod ts;
mod wasm;
@@ -29,6 +30,7 @@ enum CommandAction<'a> {
GenerateWasm(&'a [String]),
GeneratePackageMetadata,
Coverage(&'a [String]),
+ QualifyFeatures,
Check,
Smoke(&'a [String]),
}
@@ -61,6 +63,9 @@ fn run(args: impl IntoIterator<Item = String>) -> Result<(), String> {
CommandAction::GenerateWasm(rest) => wasm::generate(rest),
CommandAction::GeneratePackageMetadata => generate::generate_package_metadata(),
CommandAction::Coverage(rest) => coverage::run(rest),
+ CommandAction::QualifyFeatures => {
+ release_qualification::run_feature_matrix(&fs::workspace_root()?)
+ }
CommandAction::Check => check::check(),
CommandAction::Smoke(rest) => smoke::run(rest),
}
@@ -88,6 +93,9 @@ fn command_action(args: &[String]) -> Result<CommandAction<'_>, String> {
Ok(CommandAction::GeneratePackageMetadata)
}
[command, rest @ ..] if command == "coverage" => Ok(CommandAction::Coverage(rest)),
+ [command, target] if command == "release" && target == "qualify-features" => {
+ Ok(CommandAction::QualifyFeatures)
+ }
[command] if command == "check" => Ok(CommandAction::Check),
[command, rest @ ..] if command == "smoke" => Ok(CommandAction::Smoke(rest)),
[] => Err(usage()),
@@ -96,7 +104,7 @@ fn command_action(args: &[String]) -> Result<CommandAction<'_>, String> {
}
fn usage() -> String {
- "usage: cargo xtask architecture | cargo xtask architecture-ci | cargo xtask check-api-boundaries | cargo xtask check-dependency-boundaries | cargo xtask generate | cargo xtask generate ts | cargo xtask generate wasm [--package <key>] | cargo xtask generate bindings <swift|kotlin> | cargo xtask generate package-metadata | cargo xtask check | cargo xtask smoke facade-rust-local | sdk-rust-local | front-doors-rust-local | cargo xtask coverage run"
+ "usage: cargo xtask architecture | cargo xtask architecture-ci | cargo xtask check-api-boundaries | cargo xtask check-dependency-boundaries | cargo xtask generate | cargo xtask generate ts | cargo xtask generate wasm [--package <key>] | cargo xtask generate bindings <swift|kotlin> | cargo xtask generate package-metadata | cargo xtask check | cargo xtask smoke facade-rust-local | sdk-rust-local | front-doors-rust-local | cargo xtask coverage run | cargo xtask release qualify-features"
.to_owned()
}
diff --git a/tools/xtask/src/release_qualification.rs b/tools/xtask/src/release_qualification.rs
@@ -0,0 +1,135 @@
+use std::{fs, path::Path, process::Command};
+
+use serde::Deserialize;
+
+#[derive(Debug, Deserialize)]
+struct Architecture {
+ repositories: Repositories,
+ package: Vec<Package>,
+}
+
+#[derive(Debug, Deserialize)]
+struct Repositories {
+ sdk: Repository,
+}
+
+#[derive(Debug, Deserialize)]
+struct Repository {
+ packages: Vec<String>,
+}
+
+#[derive(Debug, Deserialize)]
+struct Package {
+ name: String,
+ features: Vec<String>,
+}
+
+#[derive(Debug, PartialEq, Eq)]
+struct CargoInvocation {
+ args: Vec<String>,
+}
+
+pub fn run_feature_matrix(workspace_root: &Path) -> Result<(), String> {
+ for invocation in feature_matrix(workspace_root)? {
+ eprintln!("cargo {}", invocation.args.join(" "));
+ let status = Command::new("cargo")
+ .args(&invocation.args)
+ .current_dir(workspace_root)
+ .status()
+ .map_err(|error| format!("failed to start cargo: {error}"))?;
+ if !status.success() {
+ return Err(format!(
+ "public feature qualification failed: cargo {}",
+ invocation.args.join(" ")
+ ));
+ }
+ }
+ Ok(())
+}
+
+fn feature_matrix(workspace_root: &Path) -> Result<Vec<CargoInvocation>, String> {
+ let path = workspace_root.join("docs/specs/radroots_crates_release_v1.toml");
+ let raw = fs::read_to_string(&path)
+ .map_err(|error| format!("failed to read {}: {error}", path.display()))?;
+ let architecture = toml::from_str::<Architecture>(&raw)
+ .map_err(|error| format!("failed to parse {}: {error}", path.display()))?;
+ let mut packages = architecture
+ .package
+ .into_iter()
+ .filter(|package| {
+ architecture
+ .repositories
+ .sdk
+ .packages
+ .contains(&package.name)
+ })
+ .collect::<Vec<_>>();
+ packages.sort_by(|left, right| left.name.cmp(&right.name));
+ if packages.len() != 2 {
+ return Err(format!(
+ "SDK feature qualification requires exactly 2 public packages, found {}",
+ packages.len()
+ ));
+ }
+
+ let mut invocations = Vec::new();
+ for package in packages {
+ invocations.push(check_invocation(&package.name, None, true));
+ invocations.push(check_invocation(&package.name, None, false));
+ for feature in package.features {
+ invocations.push(check_invocation(&package.name, Some(&feature), true));
+ }
+ invocations.push(CargoInvocation {
+ args: vec![
+ "check".to_owned(),
+ "-p".to_owned(),
+ package.name,
+ "--all-targets".to_owned(),
+ "--all-features".to_owned(),
+ "--locked".to_owned(),
+ ],
+ });
+ }
+ Ok(invocations)
+}
+
+fn check_invocation(
+ package: &str,
+ feature: Option<&str>,
+ no_default_features: bool,
+) -> CargoInvocation {
+ let mut args = vec!["check".to_owned(), "-p".to_owned(), package.to_owned()];
+ if no_default_features {
+ args.push("--lib".to_owned());
+ args.push("--no-default-features".to_owned());
+ } else {
+ args.push("--all-targets".to_owned());
+ }
+ if let Some(feature) = feature {
+ args.push("--features".to_owned());
+ args.push(feature.to_owned());
+ }
+ args.push("--locked".to_owned());
+ CargoInvocation { args }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::feature_matrix;
+
+ #[test]
+ fn current_catalog_generates_both_front_door_feature_matrices() {
+ let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
+ .parent()
+ .and_then(std::path::Path::parent)
+ .expect("workspace root");
+ let matrix = feature_matrix(root).expect("feature matrix");
+ assert!(matrix.len() > 20);
+ for package in ["radroots", "radroots_sdk"] {
+ assert!(matrix.iter().any(|entry| {
+ entry.args.windows(2).any(|pair| pair == ["-p", package])
+ && entry.args.iter().any(|arg| arg == "--all-features")
+ }));
+ }
+ }
+}