Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't downgrade on prerelease VersionReq when update with --breaking. #14250

Merged
merged 2 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 46 additions & 3 deletions src/cargo/util/toml_mut/upgrade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,16 @@ pub(crate) fn upgrade_requirement(
// Empty matches everything, no-change.
Ok(None)
} else {
let comparators: CargoResult<Vec<_>> = raw_req
let comparators: Vec<_> = raw_req
.comparators
.into_iter()
// Don't downgrade if pre-release was used, see https://github.com/rust-lang/cargo/issues/14178 and https://github.com/rust-lang/cargo/issues/13290.
.filter(|p| p.pre.is_empty() || matches_greater(p, version))
.map(|p| set_comparator(p, version))
.collect();
let comparators = comparators?;
.collect::<CargoResult<_>>()?;
if comparators.is_empty() {
return Ok(None);
}
let new_req = semver::VersionReq { comparators };
let mut new_req_text = new_req.to_string();
if new_req_text.starts_with('^') && !req.starts_with('^') {
Expand Down Expand Up @@ -74,6 +78,33 @@ fn set_comparator(
}
}

// See https://github.com/dtolnay/semver/blob/69efd3cc770ead273a06ad1788477b3092996d29/src/eval.rs#L64-L88
fn matches_greater(cmp: &semver::Comparator, ver: &semver::Version) -> bool {
if ver.major != cmp.major {
return ver.major > cmp.major;
}

match cmp.minor {
None => return false,
Some(minor) => {
if ver.minor != minor {
return ver.minor > minor;
}
}
}

match cmp.patch {
None => return false,
Some(patch) => {
if ver.patch != patch {
return ver.patch > patch;
}
}
}

ver.pre > cmp.pre
}

fn assign_partial_req(
version: &semver::Version,
mut pred: semver::Comparator,
Expand Down Expand Up @@ -217,5 +248,17 @@ mod test {
assert_req_bump("1.1.1", "=1.0.0", "=1.1.1");
assert_req_bump("2.0.0", "=1.0.0", "=2.0.0");
}

#[test]
fn greater_prerelease() {
assert_req_bump("1.7.0", "2.0.0-beta.21", None);
linyihai marked this conversation as resolved.
Show resolved Hide resolved
assert_req_bump("1.7.0", "=2.0.0-beta.21", None);
assert_req_bump("1.7.0", "~2.0.0-beta.21", None);
assert_req_bump("2.0.0-beta.20", "2.0.0-beta.21", None);
assert_req_bump("2.0.0-beta.21", "2.0.0-beta.21", None);
assert_req_bump("2.0.0-beta.22", "2.0.0-beta.21", "2.0.0-beta.22");
assert_req_bump("2.0.0", "2.0.0-beta.21", "2.0.0");
assert_req_bump("3.0.0", "2.0.0-beta.21", "3.0.0");
}
}
}
91 changes: 91 additions & 0 deletions tests/testsuite/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2619,3 +2619,94 @@ fn update_breaking_mixed_pinning_renaming() {
"#]],
);
}

#[cargo_test]
fn update_breaking_pre_release_downgrade() {
Package::new("bar", "2.0.0-beta.21").publish();

let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.1"
edition = "2015"
authors = []

[dependencies]
bar = "2.0.0-beta.21"
"#,
)
.file("src/lib.rs", "")
.build();

p.cargo("generate-lockfile").run();

// The purpose of this test is
// to demonstrate that `update --breaking` will not try to downgrade to the latest stable version (1.7.0),
// but will rather keep the latest pre-release (2.0.0-beta.21).
Package::new("bar", "1.7.0").publish();
linyihai marked this conversation as resolved.
Show resolved Hide resolved
p.cargo("update -Zunstable-options --breaking bar")
.masquerade_as_nightly_cargo(&["update-breaking"])
.with_stderr_data(str![[r#"
[UPDATING] `dummy-registry` index

"#]])
.run();
}

#[cargo_test]
fn update_breaking_pre_release_upgrade() {
Package::new("bar", "2.0.0-beta.21").publish();

let p = project()
.file(
"Cargo.toml",
r#"
[package]
name = "foo"
version = "0.0.1"
edition = "2015"
authors = []

[dependencies]
bar = "2.0.0-beta.21"
"#,
)
.file("src/lib.rs", "")
.build();

p.cargo("generate-lockfile").run();

// TODO: `2.0.0-beta.21` can be upgraded to `2.0.0-beta.22`
Package::new("bar", "2.0.0-beta.22").publish();
p.cargo("update -Zunstable-options --breaking bar")
.masquerade_as_nightly_cargo(&["update-breaking"])
.with_stderr_data(str![[r#"
[UPDATING] `dummy-registry` index

"#]])
.run();
// TODO: `2.0.0-beta.21` can be upgraded to `2.0.0`
Package::new("bar", "2.0.0").publish();
p.cargo("update -Zunstable-options --breaking bar")
.masquerade_as_nightly_cargo(&["update-breaking"])
.with_stderr_data(str![[r#"
[UPDATING] `dummy-registry` index

"#]])
.run();

Package::new("bar", "3.0.0").publish();
p.cargo("update -Zunstable-options --breaking bar")
.masquerade_as_nightly_cargo(&["update-breaking"])
.with_stderr_data(str![[r#"
[UPDATING] `dummy-registry` index
[UPGRADING] bar ^2.0.0-beta.21 -> ^3.0.0
[LOCKING] 1 package to latest compatible version
[UPDATING] bar v2.0.0-beta.21 -> v3.0.0

"#]])
.run();
}