commit 72f618ab32502da6e820b1cf1934a0564bf409ff
parent 478cdb1b0337109e901e29df576f1530c1cad7b3
Author: triesap <tyson@radroots.org>
Date: Mon, 27 Jul 2026 13:36:42 +0000
release: resolve staged package dependencies
- model unpublished workspace crates through disposable Cargo patches
- keep normalized archives registry-only and exactly version-pinned
- write all staging configuration outside the source worktree
- test source mapping without mutating package manifests
Diffstat:
1 file changed, 59 insertions(+), 1 deletion(-)
diff --git a/tools/xtask/src/contract/release_package.rs b/tools/xtask/src/contract/release_package.rs
@@ -106,6 +106,7 @@ pub(crate) fn validate_release_packages(workspace_root: &Path) -> Result<(), Str
fs::create_dir_all(directory)
.map_err(|error| format!("create {}: {error}", directory.display()))?;
}
+ let source_patch_config = write_source_patch_config(&closure_root, &workspace_packages)?;
for package in &policy.publish_order.crates {
let package_list = cargo_package_list(workspace_root, package)?;
@@ -118,8 +119,9 @@ pub(crate) fn validate_release_packages(workspace_root: &Path) -> Result<(), Str
)
.map_err(|error| format!("write package list for {package}: {error}"))?;
- run_cargo(
+ run_cargo_with_config(
workspace_root,
+ &source_patch_config,
&["package", "--locked", "--no-verify", "-p", package],
&format!("package {package}"),
)?;
@@ -363,6 +365,29 @@ fn run_cargo(directory: &Path, args: &[&str], purpose: &str) -> Result<(), Strin
cargo_output(directory, args, purpose).map(|_| ())
}
+fn run_cargo_with_config(
+ directory: &Path,
+ config_path: &Path,
+ args: &[&str],
+ purpose: &str,
+) -> Result<(), String> {
+ let output = Command::new("cargo")
+ .args(["--config"])
+ .arg(config_path)
+ .args(args)
+ .current_dir(directory)
+ .output()
+ .map_err(|error| format!("{purpose}: run cargo {}: {error}", args.join(" ")))?;
+ if !output.status.success() {
+ return Err(format!(
+ "{purpose}: cargo {} failed: {}",
+ args.join(" "),
+ String::from_utf8_lossy(&output.stderr).trim()
+ ));
+ }
+ Ok(())
+}
+
fn cargo_output(directory: &Path, args: &[&str], purpose: &str) -> Result<Output, String> {
let output = Command::new("cargo")
.args(args)
@@ -814,6 +839,23 @@ fn load_workspace_packages(workspace_root: &Path) -> Result<Vec<PackageWorkspace
Ok(packages)
}
+fn write_source_patch_config(
+ closure_root: &Path,
+ packages: &[PackageWorkspaceEntry],
+) -> Result<PathBuf, String> {
+ let config_path = closure_root.join("source-patches.toml");
+ let mut config = String::from("[patch.crates-io]\n");
+ for package in packages {
+ config.push_str(&format!(
+ "{} = {{ path = {:?} }}\n",
+ package.name, package.source_directory
+ ));
+ }
+ fs::write(&config_path, config)
+ .map_err(|error| format!("write source patch config: {error}"))?;
+ Ok(config_path)
+}
+
fn write_archive_workspace(
workspace_root: &Path,
extracted_root: &Path,
@@ -964,4 +1006,20 @@ version = "=1.0.0-alpha.1"
fs::read_to_string(destination.join("README")).expect("read extracted README");
assert_eq!(contents, "readme");
}
+
+ #[test]
+ fn source_patch_config_maps_workspace_packages_without_touching_sources() {
+ let root = tempfile::tempdir().expect("temporary closure root");
+ let packages = [PackageWorkspaceEntry {
+ name: "radroots_fixture".to_owned(),
+ source_directory: root.path().join("source"),
+ }];
+ let config_path =
+ write_source_patch_config(root.path(), &packages).expect("write source patches");
+ let config = load_toml::<toml::Value>(&config_path).expect("parse source patches");
+ assert_eq!(
+ config["patch"]["crates-io"]["radroots_fixture"]["path"].as_str(),
+ packages[0].source_directory.to_str()
+ );
+ }
}