This is an automated email from the ASF dual-hosted git repository.

xuanwo pushed a commit to branch xuanwo/release-tooling-sync
in repository https://gitbox.apache.org/repos/asf/opendal.git

commit 463b019a4dd68bbd28b7da607080188b12c5d2b6
Author: Xuanwo <[email protected]>
AuthorDate: Tue Mar 31 18:03:58 2026 +0800

    fix(dev): sync release package versions
---
 dev/src/release/package.rs | 300 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 276 insertions(+), 24 deletions(-)

diff --git a/dev/src/release/package.rs b/dev/src/release/package.rs
index 9fcbbe6b7..e90b46664 100644
--- a/dev/src/release/package.rs
+++ b/dev/src/release/package.rs
@@ -19,6 +19,7 @@ use crate::workspace_dir;
 use semver::Version;
 use std::path::{Path, PathBuf};
 use std::str::FromStr;
+use toml_edit::{DocumentMut, Item, TableLike};
 
 #[derive(Debug, Clone)]
 pub struct Package {
@@ -65,7 +66,7 @@ pub fn all_packages() -> Vec<Package> {
     // Integrations
     let dav_server = make_package("integrations/dav-server", "0.7.0", 
vec![core.clone()]);
     let object_store = make_package("integrations/object_store", "0.55.0", 
vec![core.clone()]);
-    let parquet = make_package("integrations/parquet", "0.7.0", 
vec![core.clone()]);
+    let parquet = make_package("integrations/parquet", "0.8.0", 
vec![core.clone()]);
     let unftp_sbe = make_package("integrations/unftp-sbe", "0.4.0", 
vec![core.clone()]);
 
     // Binaries moved to separate repositories; no longer released from this 
repo
@@ -93,17 +94,25 @@ pub fn all_packages() -> Vec<Package> {
 
 pub fn update_package_version(package: &Package) -> bool {
     match package.name.as_str() {
-        "core" => update_cargo_version(&package.path, &package.version),
-        "integrations/dav-server" => update_cargo_version(&package.path, 
&package.version),
-        "integrations/object_store" => update_cargo_version(&package.path, 
&package.version),
-        "integrations/parquet" => update_cargo_version(&package.path, 
&package.version),
-        "integrations/unftp-sbe" => update_cargo_version(&package.path, 
&package.version),
+        "core" => update_cargo_version(&package.path, &package.version, 
package.dependencies()),
+        "integrations/dav-server" => {
+            update_cargo_version(&package.path, &package.version, 
package.dependencies())
+        }
+        "integrations/object_store" => {
+            update_cargo_version(&package.path, &package.version, 
package.dependencies())
+        }
+        "integrations/parquet" => {
+            update_cargo_version(&package.path, &package.version, 
package.dependencies())
+        }
+        "integrations/unftp-sbe" => {
+            update_cargo_version(&package.path, &package.version, 
package.dependencies())
+        }
 
         "bindings/c" => false,   // C bindings has no version to update
         "bindings/cpp" => false, // C++ bindings has no version to update
         "bindings/lua" => false, // Lua bindings has no version to update
 
-        "bindings/python" => update_cargo_version(&package.path, 
&package.version),
+        "bindings/python" => update_cargo_version(&package.path, 
&package.version, &[]),
         "bindings/java" => update_maven_version(&package.path, 
&package.version),
         "bindings/nodejs" => update_nodejs_version(&package.path, 
&package.version),
 
@@ -111,28 +120,148 @@ pub fn update_package_version(package: &Package) -> bool 
{
     }
 }
 
-fn update_cargo_version(path: &Path, version: &Version) -> bool {
+fn update_cargo_version(path: &Path, version: &Version, dependencies: 
&[Package]) -> bool {
     let path = path.join("Cargo.toml");
     let manifest = std::fs::read_to_string(&path).unwrap();
-    let mut manifest = 
toml_edit::DocumentMut::from_str(manifest.as_str()).unwrap();
+    let mut manifest = DocumentMut::from_str(manifest.as_str()).unwrap();
+    let mut updated = update_manifest_version(&mut manifest, version, &path);
 
-    let old_version = match manifest["package"]["version"].as_str() {
-        Some(version) => Version::parse(version).unwrap(),
-        None => panic!("missing version for package: {}", path.display()),
-    };
+    for dependency in dependencies {
+        updated |= update_dependency_version(&mut manifest, dependency);
+    }
 
-    if &old_version != version {
-        manifest["package"]["version"] = toml_edit::value(version.to_string());
+    if updated {
         std::fs::write(&path, manifest.to_string()).unwrap();
-        println!(
-            "updating version for package: {} from {} to {}",
-            path.display(),
-            old_version,
-            version
-        );
-        true
-    } else {
-        false
+    }
+
+    updated
+}
+
+fn update_manifest_version(manifest: &mut DocumentMut, version: &Version, 
path: &Path) -> bool {
+    if let Some(old_version) = manifest["package"]["version"]
+        .as_str()
+        .map(Version::parse)
+        .transpose()
+        .unwrap()
+    {
+        if &old_version != version {
+            manifest["package"]["version"] = 
toml_edit::value(version.to_string());
+            println!(
+                "updating version for package: {} from {} to {}",
+                path.display(),
+                old_version,
+                version
+            );
+            return true;
+        }
+
+        return false;
+    }
+
+    if let Some(old_version) = manifest["workspace"]["package"]["version"]
+        .as_str()
+        .map(Version::parse)
+        .transpose()
+        .unwrap()
+    {
+        if &old_version != version {
+            manifest["workspace"]["package"]["version"] = 
toml_edit::value(version.to_string());
+            println!(
+                "updating version for package: {} from {} to {}",
+                path.display(),
+                old_version,
+                version
+            );
+            return true;
+        }
+
+        return false;
+    }
+
+    panic!("missing version for package: {}", path.display());
+}
+
+fn update_dependency_version(manifest: &mut DocumentMut, dependency: &Package) 
-> bool {
+    let Some(crate_name) = dependency.crate_name() else {
+        return false;
+    };
+
+    update_dependency_version_in_table(manifest.as_table_mut(), crate_name, 
dependency)
+}
+
+fn update_dependency_version_in_table(
+    table: &mut dyn TableLike,
+    crate_name: &str,
+    dependency: &Package,
+) -> bool {
+    let mut updated = false;
+
+    for table_name in ["dependencies", "dev-dependencies", 
"build-dependencies"] {
+        let Some(dependencies) = 
table.get_mut(table_name).and_then(Item::as_table_like_mut) else {
+            continue;
+        };
+
+        updated |= update_dependency_version_in_dependencies(dependencies, 
crate_name, dependency);
+    }
+
+    let Some(targets) = 
table.get_mut("target").and_then(Item::as_table_like_mut) else {
+        return updated;
+    };
+
+    for (_, target) in targets.iter_mut() {
+        let Some(target) = target.as_table_like_mut() else {
+            continue;
+        };
+        updated |= update_dependency_version_in_table(target, crate_name, 
dependency);
+    }
+
+    updated
+}
+
+fn update_dependency_version_in_dependencies(
+    dependencies: &mut dyn TableLike,
+    crate_name: &str,
+    dependency: &Package,
+) -> bool {
+    let Some(entry) = dependencies.get_mut(crate_name) else {
+        return false;
+    };
+    let Some(entry) = entry.as_table_like_mut() else {
+        return false;
+    };
+    let Some("../../core") = entry.get("path").and_then(Item::as_str) else {
+        return false;
+    };
+    let Some(value) = entry.get_mut("version") else {
+        return false;
+    };
+
+    let old_version = match value.as_str() {
+        Some(version) => match Version::parse(version) {
+            Ok(version) => version,
+            Err(_) => return false,
+        },
+        None => panic!("missing dependency version for crate: {crate_name}"),
+    };
+
+    if old_version == dependency.version {
+        return false;
+    }
+
+    *value = toml_edit::value(dependency.version.to_string());
+    println!(
+        "updating dependency version for crate: {} from {} to {}",
+        crate_name, old_version, dependency.version
+    );
+    true
+}
+
+impl Package {
+    fn crate_name(&self) -> Option<&'static str> {
+        match self.name.as_str() {
+            "core" => Some("opendal"),
+            _ => None,
+        }
     }
 }
 
@@ -193,3 +322,126 @@ fn update_nodejs_version(path: &Path, version: &Version) 
-> bool {
 
     updated
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    fn temp_test_dir(name: &str) -> PathBuf {
+        let nanos = std::time::SystemTime::now()
+            .duration_since(std::time::UNIX_EPOCH)
+            .unwrap()
+            .as_nanos();
+        std::env::temp_dir().join(format!("odev-package-{name}-{nanos}"))
+    }
+
+    #[test]
+    fn parquet_release_version_matches_manifest() {
+        let parquet = all_packages()
+            .into_iter()
+            .find(|package| package.name() == "integrations/parquet")
+            .unwrap();
+
+        assert_eq!(parquet.version, Version::parse("0.8.0").unwrap());
+    }
+
+    #[test]
+    fn update_manifest_version_supports_workspace_package_version() {
+        let mut manifest = DocumentMut::from_str(
+            r#"
+[workspace.package]
+version = "0.55.0"
+
+[package]
+name = "opendal"
+version = { workspace = true }
+"#,
+        )
+        .unwrap();
+
+        let updated = update_manifest_version(
+            &mut manifest,
+            &Version::parse("0.56.0").unwrap(),
+            Path::new("core/Cargo.toml"),
+        );
+
+        assert!(updated);
+        assert_eq!(
+            manifest["workspace"]["package"]["version"].as_str(),
+            Some("0.56.0")
+        );
+        assert!(!manifest["package"]["version"].is_str());
+    }
+
+    #[test]
+    fn 
update_dependency_version_only_touches_release_managed_core_constraints() {
+        let dir = temp_test_dir("dependency-sync");
+        std::fs::create_dir_all(&dir).unwrap();
+
+        let manifest_path = dir.join("Cargo.toml");
+        std::fs::write(
+            &manifest_path,
+            r#"[package]
+name = "demo"
+version = "0.8.0"
+
+[dependencies]
+opendal = { version = "0.55.0", path = "../../core" }
+serde = "1"
+
+[dev-dependencies]
+opendal = { version = "0.55.0", path = "../../core", features = 
["services-memory"] }
+
+[build-dependencies]
+opendal = { version = ">=0", path = "../../core" }
+
+[target.'cfg(unix)'.dependencies]
+opendal = { version = "0.55.0", path = "../../core" }
+
+[target.'cfg(windows)'.dependencies]
+opendal = { version = "0.55.0", path = "../core" }
+"#,
+        )
+        .unwrap();
+
+        let dependency = Package {
+            name: "core".to_string(),
+            path: PathBuf::from("core"),
+            version: Version::parse("0.56.0").unwrap(),
+            dependencies: vec![],
+        };
+
+        let updated = update_cargo_version(
+            dir.as_path(),
+            &Version::parse("0.8.0").unwrap(),
+            &[dependency],
+        );
+        assert!(updated);
+
+        let manifest = std::fs::read_to_string(&manifest_path).unwrap();
+        let manifest = DocumentMut::from_str(&manifest).unwrap();
+
+        assert_eq!(
+            manifest["dependencies"]["opendal"]["version"].as_str(),
+            Some("0.56.0")
+        );
+        assert_eq!(
+            manifest["dev-dependencies"]["opendal"]["version"].as_str(),
+            Some("0.56.0")
+        );
+        assert_eq!(
+            
manifest["target"]["cfg(unix)"]["dependencies"]["opendal"]["version"].as_str(),
+            Some("0.56.0")
+        );
+        assert_eq!(
+            manifest["build-dependencies"]["opendal"]["version"].as_str(),
+            Some(">=0")
+        );
+        assert_eq!(
+            
manifest["target"]["cfg(windows)"]["dependencies"]["opendal"]["version"].as_str(),
+            Some("0.55.0")
+        );
+
+        std::fs::remove_dir_all(dir).unwrap();
+    }
+}

Reply via email to