From edb54e51e4ee1e79e746c9fe84bfdbae2bb8a715 Mon Sep 17 00:00:00 2001 From: Zeke Foppa <196249+bfops@users.noreply.github.com> Date: Mon, 23 Feb 2026 13:12:56 -0800 Subject: [PATCH] Fix version upgrade check for prerelease versions (#4407) # Description of Changes Fix our version compatibility checking for prereleases. Fixes https://github.com/clockworklabs/SpacetimeDB/issues/4405. # API and ABI breaking changes None. # Expected complexity level and risk 1 # Testing I've added more unit tests for the upgrade behavior surrounding prereleases (upgrading to a prerelease, upgrading from a prerelease, and upgrading between prereleases). --------- Co-authored-by: Zeke Foppa --- crates/core/src/config.rs | 60 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 59 insertions(+), 1 deletion(-) diff --git a/crates/core/src/config.rs b/crates/core/src/config.rs index 0be76a675..4441c48e0 100644 --- a/crates/core/src/config.rs +++ b/crates/core/src/config.rs @@ -63,6 +63,12 @@ impl MetadataFile { previous.edition, ); + // This is mostly redundant with the caret comparison below, but + // pre-releases make it annoying. + if previous.version == current.version { + return Ok(()); + } + // Special-case: SpacetimeDB 2.x can run 1.x databases. if previous.version.major == 1 && current.version.major == 2 { return Ok(()); @@ -73,7 +79,8 @@ impl MetadataFile { major: previous.version.major, minor: Some(previous.version.minor), patch: None, - pre: previous.version.pre.clone(), + // We deal with pre-releases separately above. + pre: semver::Prerelease::new("").unwrap(), }; if cmp.matches(¤t.version) { @@ -170,6 +177,16 @@ mod tests { semver::Version::new(major, minor, patch) } + fn mkver_pre(major: u64, minor: u64, patch: u64, pre: &str) -> semver::Version { + semver::Version { + major, + minor, + patch, + pre: semver::Prerelease::new(pre).unwrap(), + build: semver::BuildMetadata::EMPTY, + } + } + fn mkmeta(major: u64, minor: u64, patch: u64) -> MetadataFile { MetadataFile { version: mkver(major, minor, patch), @@ -178,6 +195,14 @@ mod tests { } } + fn mkmeta_pre(major: u64, minor: u64, patch: u64, pre: &str) -> MetadataFile { + MetadataFile { + version: mkver_pre(major, minor, patch, pre), + edition: "standalone".to_owned(), + client_connection_id: None, + } + } + #[test] fn check_metadata_compatibility_checking() { assert_eq!( @@ -212,4 +237,37 @@ mod tests { .check_compatibility_and_update(mkmeta(3, 0, 0)) .unwrap_err(); } + + #[test] + fn check_metadata_compatibility_prerelease() { + mkmeta(1, 9, 0) + .check_compatibility_and_update(mkmeta_pre(2, 0, 0, "rc1")) + .unwrap(); + + mkmeta_pre(2, 0, 0, "rc1") + .check_compatibility_and_update(mkmeta_pre(2, 0, 0, "rc1")) + .unwrap(); + + mkmeta_pre(2, 0, 0, "rc1") + .check_compatibility_and_update(mkmeta(2, 0, 1)) + .unwrap(); + + mkmeta_pre(2, 0, 0, "rc1") + .check_compatibility_and_update(mkmeta(2, 0, 0)) + .unwrap(); + + // Now check some failures.. + + mkmeta_pre(2, 0, 0, "rc1") + .check_compatibility_and_update(mkmeta_pre(2, 0, 0, "rc2")) + .unwrap_err(); + + mkmeta_pre(2, 0, 0, "rc2") + .check_compatibility_and_update(mkmeta_pre(2, 0, 0, "rc1")) + .unwrap_err(); + + mkmeta(2, 0, 0) + .check_compatibility_and_update(mkmeta_pre(2, 1, 0, "rc1")) + .unwrap_err(); + } }