commit 646e3c75b633b03aac6d1828632dd1719ed3b8fc
parent 6b5512f76de02819b2db90ddc4f3f0bc1b17af6a
Author: triesap <tyson@radroots.org>
Date: Tue, 4 Aug 2026 11:38:03 +0000
build(release): stage sdk crate publication
- enable only radroots_sdk and the radroots facade
- preserve private sdk workspace publication blocks
- record sdk before facade in the local order
- validate staged registry flags and policy order
Diffstat:
4 files changed, 47 insertions(+), 7 deletions(-)
diff --git a/crates/radroots/Cargo.toml b/crates/radroots/Cargo.toml
@@ -13,7 +13,7 @@ documentation = "https://docs.rs/radroots"
keywords = ["radroots", "local-food", "sdk", "nostr"]
categories = ["api-bindings", "asynchronous", "network-programming"]
include = ["src/**", "examples/**", "Cargo.toml", "README.md", "LICENSE-APACHE", "LICENSE-MIT"]
-publish = false
+publish = ["crates-io"]
autoexamples = false
[package.metadata.docs.rs]
diff --git a/crates/sdk/Cargo.toml b/crates/sdk/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "radroots_sdk"
-publish = false
+publish = ["crates-io"]
version.workspace = true
edition.workspace = true
authors.workspace = true
diff --git a/tools/sdk_xtask_import/src/architecture.rs b/tools/sdk_xtask_import/src/architecture.rs
@@ -443,9 +443,13 @@ fn validate_public_package_metadata(
"public package {name} must use an existing crate-local {PUBLIC_README}"
));
}
- if package.get("publish").and_then(toml::Value::as_bool) != Some(false) {
+ let publish_registries = package
+ .get("publish")
+ .and_then(toml::Value::as_array)
+ .ok_or_else(|| format!("public package {name} must set publish = [\"crates-io\"]"))?;
+ if publish_registries.len() != 1 || publish_registries[0].as_str() != Some("crates-io") {
return Err(format!(
- "public package {name} must remain publish = false during migration"
+ "public package {name} must set publish = [\"crates-io\"]"
));
}
let inherits_lints = manifest
@@ -1387,7 +1391,7 @@ adr_required = false
complete_workspace_manifest("\"crates/radroots\""),
)
.expect("write workspace manifest");
- let manifest = "[package]\nname = \"radroots\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nhomepage.workspace = true\nauthors.workspace = true\nreadme = \"README.md\"\npublish = false\n\n[lints]\nworkspace = true\n";
+ let manifest = "[package]\nname = \"radroots\"\nversion.workspace = true\nedition.workspace = true\nrust-version.workspace = true\nlicense.workspace = true\nrepository.workspace = true\nhomepage.workspace = true\nauthors.workspace = true\nreadme = \"README.md\"\npublish = [\"crates-io\"]\n\n[lints]\nworkspace = true\n";
fs::write(root.join("crates/radroots/Cargo.toml"), manifest)
.expect("write public manifest");
fs::write(root.join("crates/radroots/README.md"), "fixture\n")
diff --git a/tools/sdk_xtask_import/src/check.rs b/tools/sdk_xtask_import/src/check.rs
@@ -93,6 +93,13 @@ struct CargoMetadataPackage {
struct PublicationPolicyFile {
publication: PublicationPolicy,
workspace_classification: WorkspacePublicationClassification,
+ #[serde(default)]
+ publish_order: PublishOrder,
+}
+
+#[derive(Debug, Default, Deserialize)]
+struct PublishOrder {
+ crates: Vec<String>,
}
#[derive(Debug, Deserialize)]
@@ -198,8 +205,12 @@ fn check_radroots_facade_conformance(root: &Path) -> Result<(), String> {
));
}
}
- if package.get("publish").and_then(toml::Value::as_bool) != Some(false) {
- return Err("radroots facade must remain publish = false during the scaffold".to_owned());
+ let publish = package
+ .get("publish")
+ .and_then(toml::Value::as_array)
+ .ok_or_else(|| "radroots facade must set publish = [\"crates-io\"]".to_owned())?;
+ if publish.len() != 1 || publish[0].as_str() != Some("crates-io") {
+ return Err("radroots facade must set publish = [\"crates-io\"]".to_owned());
}
for field in [
"version",
@@ -408,6 +419,7 @@ fn check_publication_policy(root: &Path) -> Result<(), String> {
.map_err(|error| format!("failed to read {}: {error}", policy_path.display()))?;
let policy_file = toml::from_str::<PublicationPolicyFile>(&raw)
.map_err(|error| format!("failed to parse {}: {error}", policy_path.display()))?;
+ let publish_order = policy_file.publish_order.crates.clone();
let policy = policy_file.publication;
if policy.registry != "crates-io" {
return Err("publication.registry must be crates-io".to_owned());
@@ -490,6 +502,24 @@ fn check_publication_policy(root: &Path) -> Result<(), String> {
"local and external package ownership must partition approved packages".to_owned(),
);
}
+ if policy.frozen {
+ if !publish_order.is_empty() {
+ return Err(
+ "publish_order.crates must remain empty while publication is frozen".to_owned(),
+ );
+ }
+ } else {
+ let publish_order_set = policy_set(publish_order.iter().cloned(), "publish_order.crates")?;
+ if publish_order_set != local {
+ return Err(
+ "publish_order.crates must contain exactly the approved local packages when publication is enabled"
+ .to_owned(),
+ );
+ }
+ if publish_order != ["radroots_sdk", "radroots"] {
+ return Err("publish_order.crates must place radroots_sdk before radroots".to_owned());
+ }
+ }
let mut workspace_packages = BTreeSet::new();
for path in sdk_manifest_paths(root)?.into_iter().skip(1) {
@@ -2526,6 +2556,11 @@ mod tests {
private: &[&str],
build_codegen: &[&str],
) -> String {
+ let publish_order = if frozen {
+ String::new()
+ } else {
+ "\n[publish_order]\ncrates = [\"radroots_sdk\", \"radroots\"]\n".to_owned()
+ };
format!(
r#"[publication]
frozen = {frozen}
@@ -2542,6 +2577,7 @@ build_codegen = [{}]
test_support = []
preview = []
retired = []
+{publish_order}
"#,
toml_strings(approved),
toml_strings(&APPROVED_CRATES[..17]),