From 29615846e5f0d8c42be0ad540a5539d08f37bac4 Mon Sep 17 00:00:00 2001 From: Jacob Finkelman Date: Wed, 4 Oct 2023 22:29:14 +0000 Subject: [PATCH] If there's a version in the lock file only use that exact version --- src/cargo/sources/registry/index.rs | 21 ++++++++----- src/cargo/util/semver_ext.rs | 49 +++++------------------------ 2 files changed, 21 insertions(+), 49 deletions(-) diff --git a/src/cargo/sources/registry/index.rs b/src/cargo/sources/registry/index.rs index ca1cf40693f5..2e87a2daa6da 100644 --- a/src/cargo/sources/registry/index.rs +++ b/src/cargo/sources/registry/index.rs @@ -437,11 +437,13 @@ impl<'cfg> RegistryIndex<'cfg> { /// checking the integrity of a downloaded package matching the checksum in /// the index file, aka [`IndexSummary`]. pub fn hash(&mut self, pkg: PackageId, load: &mut dyn RegistryData) -> Poll> { - let req = OptVersionReq::exact(pkg.version()); + let mut req = OptVersionReq::exact(pkg.version()); + // Since crates.io allows crate versions to differ only by build metadata, + // a query using OptVersionReq::exact + next() can return nondeterministic results. + // So we `lock_to` the exact version were interested in + req.lock_to(pkg.version()); let summary = self.summaries(pkg.name(), &req, load)?; - let summary = ready!(summary) - .filter(|s| s.package_id().version() == pkg.version()) - .next(); + let summary = ready!(summary).next(); Poll::Ready(Ok(summary .ok_or_else(|| internal(format!("no hash listed for {}", pkg)))? .as_summary() @@ -698,10 +700,13 @@ impl<'cfg> RegistryIndex<'cfg> { pkg: PackageId, load: &mut dyn RegistryData, ) -> Poll> { - let req = OptVersionReq::exact(pkg.version()); - let found = ready!(self.summaries(pkg.name(), &req, load))? - .filter(|s| s.package_id().version() == pkg.version()) - .any(|s| s.is_yanked()); + let mut req = OptVersionReq::exact(pkg.version()); + + // Since crates.io allows crate versions to differ only by build metadata, + // a query using OptVersionReq::exact + next() can return nondeterministic results. + // So we `lock_to` the exact version were interested in + req.lock_to(pkg.version()); + let found = ready!(self.summaries(pkg.name(), &req, load))?.any(|s| s.is_yanked()); Poll::Ready(Ok(found)) } } diff --git a/src/cargo/util/semver_ext.rs b/src/cargo/util/semver_ext.rs index bee3b2da3ebe..bc7995d96b81 100644 --- a/src/cargo/util/semver_ext.rs +++ b/src/cargo/util/semver_ext.rs @@ -84,10 +84,14 @@ impl OptVersionReq { OptVersionReq::Any => true, OptVersionReq::Req(req) => req.matches(version), OptVersionReq::Locked(v, _) => { - v.major == version.major - && v.minor == version.minor - && v.patch == version.patch - && v.pre == version.pre + // Generally, cargo is of the opinion that semver metadata should be ignored. + // If your registry has two versions that only differing metadata you get the bugs you deserve. + // We also believe that lock files should ensure reproducibility + // and protect against mutations from the registry. + // In this circumstance these two goals are in conflict, and we pick reproducibility. + // If the lock file tells us that there is a version called `1.0.0+bar` then + // we should not silently use `1.0.0+foo` even though they have the same version. + v == version } } } @@ -311,40 +315,3 @@ fn is_req(value: &str) -> bool { }; "<>=^~".contains(first) || value.contains('*') || value.contains(',') } - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn locked_has_the_same_with_exact() { - fn test_versions(target_ver: &str, vers: &[&str]) { - let ver = Version::parse(target_ver).unwrap(); - let exact = OptVersionReq::exact(&ver); - let mut locked = exact.clone(); - locked.lock_to(&ver); - for v in vers { - let v = Version::parse(v).unwrap(); - assert_eq!(exact.matches(&v), locked.matches(&v)); - } - } - - test_versions( - "1.0.0", - &["1.0.0", "1.0.1", "0.9.9", "0.10.0", "0.1.0", "1.0.0-pre"], - ); - test_versions("0.9.0", &["0.9.0", "0.9.1", "1.9.0", "0.0.9", "0.9.0-pre"]); - test_versions("0.0.2", &["0.0.2", "0.0.1", "0.0.3", "0.0.2-pre"]); - test_versions( - "0.1.0-beta2.a", - &[ - "0.1.0-beta2.a", - "0.9.1", - "0.1.0", - "0.1.1-beta2.a", - "0.1.0-beta2", - ], - ); - test_versions("0.1.0+meta", &["0.1.0", "0.1.0+meta", "0.1.0+any"]); - } -}