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

Add capability of making breaking changes in update --precise #14140

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

torhovland
Copy link
Contributor

@torhovland torhovland commented Jun 25, 2024

Implements the second half of #12425.

With the unstable-options feature enabled, cargo update --precise will allow making upgrades/downgrades that require changes to be made to the manifest files, similar to cargo update --breaking.

This PR is making a change that also affects cargo update --breaking. We'll reuse ops::update_lockfile() for both breaking and non-breaking updates. The benefit is more consistent output and behaviour between the two. In particular, it addresses a task about an error output if there is nothing to upgrade. See #12425 (comment).

@rustbot
Copy link
Collaborator

rustbot commented Jun 25, 2024

r? @weihanglo

rustbot has assigned @weihanglo.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

@rustbot rustbot added A-cli Area: Command-line interface, option parsing, etc. A-manifest Area: Cargo.toml issues A-testing-cargo-itself Area: cargo's tests A-unstable Area: nightly unstable support Command-update S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 25, 2024
@torhovland torhovland force-pushed the update-precise branch 3 times, most recently from 7b753cf to 5e5b13d Compare June 25, 2024 14:49
@weihanglo
Copy link
Member

Should we wait for #14049, or this can be reviewed independently?

@torhovland
Copy link
Contributor Author

The 4 new commits here can be reviewed. I think #14049 is close to being merged anyway.

@epage
Copy link
Contributor

epage commented Jun 27, 2024

The 4 new commits here can be reviewed. I think #14049 is close to being merged anyway.

In the future, please make a blocking thing like that explicit either by making this a draft or opening an issue on an arbitrary part of the code.

tests/testsuite/update.rs Outdated Show resolved Hide resolved
@epage
Copy link
Contributor

epage commented Jun 27, 2024

In #12425 (comment), this PR was marked as addressing

Consider an error message if the command completed without doing any upgrades.

Except this PR description does not include an explanation as to why this PR addresses that and why an error is not the way forward

(btw probably best not to mark things as done when they aren't merged as things can change especially when the "solution" is not decided yet)

@@ -786,6 +786,7 @@ unstable_cli_options!(
target_applies_to_host: bool = ("Enable the `target-applies-to-host` key in the .cargo/config.toml file"),
trim_paths: bool = ("Enable the `trim-paths` option in profiles"),
unstable_options: bool = ("Allow the usage of unstable options"),
update_precise_breaking: bool = ("Allow `update --precise` to do breaking upgrades"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why a new unstable option rather than unstable_options?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because since there isn't any difference between the breaking command cargo update foo --precise 2.0.1 and its non-breaking counterpart, I thought we needed a feature.

Should the breaking instead be cargo update foo --breaking --precise 2.0.1?

Or is it fine to just use unstable-options to allow the breaking update?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I view this as a new supported value within the option which could be used with -Zunstable-options. If there is doing more than turning error cases into success cases, then we might want to consider a more explicit opt-in.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

Comment on lines 1 to 5
//! Duplicating tests for `cargo update --precise` with the
//! update-precise-breaking feature enabled. When the feature is stabilized,
//! this file can be deleted.

#![allow(deprecated)]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still trying to decide what the best approach is here but in the mean time, could you split this into two commits

  • One that duplicates the tests
  • One that enables the unstable feature

That way it shows what behavior change happened (or not) with the feature.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of duplicating tests, one approach we could take is using a test only env var to activate the unstable behavior, for example __CARGO_USE_GITOXIDE_INSTEAD_OF_GIT2.

(Yes I admit that this opens a door for people without -Zunstable-options, though we already have __CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS hence 😬)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__CARGO_USE_GITOXIDE_INSTEAD_OF_GIT2 is used from tests to modify Cargo behaviour. You would still need duplicate tests if you wanted to run Cargo with both options.

I was thinking the duplicate test file is OK, as it is meant to be temporary and can be deleted later.

I have put it in a separate commit, and it didn't need any modification after implementing the feature.

tests/testsuite/update.rs Outdated Show resolved Hide resolved
Comment on lines 2033 to 2170
// The lockfile update is failing for the same reason a non-breaking
// update would. There is still a [email protected] here that didn't get
// upgraded (the transitive dependency within bar), and we are now
// asking the lockfile update to update it to 0.2.0, which it cannot do.
.with_stderr_data(str![[r#"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like with --breaking, should we limit this to just the ones that can be upgraded?

Like other cases, this can be broken out into your task list

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious what you think the correct behaviour should be. We are upgrading shared, so we are not trying to keep it unchanged. So the update tries to do its thing, but fails. Anyway, I'll add this to the task list.

Comment on lines 2316 to 2452
.with_stderr_data(str![[r#"
[ERROR] unknown `-Z` flag specified: update-precise-breaking

For available unstable features, see https://doc.rust-lang.org/nightly/cargo/reference/unstable.html
If you intended to use an unstable rustc feature, try setting `RUSTFLAGS="-Zupdate-precise-breaking"`
[UPDATING] `dummy-registry` index
[ERROR] failed to select a version for the requirement `pinned = "=0.1"`
candidate versions found which didn't match: 0.2.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So --precise will error if the mentioned item can't upgrade but --breaking won't...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike with --breaking, there is no command line difference between a breaking and a non-breaking precise update. So we just try to do any upgrades, and whether or not we did any, the same lockfile update is run. So a precise update may fall back to being non-breaking, and may fail in the same way as a precise update could break before.

If we changed the API to --breaking --precise we could stop if there aren't any upgrades.

Comment on lines +1592 to +1594
#[cargo_test]
fn update_precise_breaking_incompatible() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a rest that makes sure cargo update incompatible --precise 2.3.4 sets the version req correctly when 2.5.7 is present?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does adding 0.4.5 here cover it?

    Package::new("incompatible", "0.3.0").publish();
    Package::new("incompatible", "0.3.1").publish();
    Package::new("incompatible", "0.4.5").publish();

    p.cargo("update -Zunstable-options -p [email protected] --precise 0.3.0")
        .with_stderr_data(str![[r#"
[UPGRADING] incompatible ^0.1 -> ^0.3
[UPDATING] incompatible v0.1.0 -> v0.3.0

@@ -1,8 +1,11 @@
use std::collections::HashMap;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only did a quick glance at the tests; haven't gotten to the implementation yet.

.masquerade_as_nightly_cargo(&[])
.with_status(101)
.with_stderr_data(str![[r#"
[ERROR] expected a version like "1.32"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case my comment gets lost track of on a merged pr

Hadn't notice this before. This error message is bad. We should at least be calling out that we are parsing a package id spec

Existing behavior:

$ cargo update clap@foo
error: invalid package ID specification: `clap@foo`

Caused by:
  expected a version like "1.32"

(again, task is list is fine)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added to list.

@torhovland
Copy link
Contributor Author

Except this PR description does not include an explanation as to why this PR addresses that and why an error is not the way forward

Description updated.

(btw probably best not to mark things as done when they aren't merged as things can change especially when the "solution" is not decided yet)

Got it.

@epage
Copy link
Contributor

epage commented Jun 27, 2024

Description updated.

I'm still not seeing the description talk to

why an error is not the way forward

btw it would be good to focus on user impact and not implementation. The paragraph that alludes to this starts off by discussing the implementation which someone is likely to gloss over when looking for user impact.

@bors
Copy link
Collaborator

bors commented Jun 27, 2024

☔ The latest upstream changes (presumably #14049) made this pull request unmergeable. Please resolve the merge conflicts.

@torhovland torhovland force-pushed the update-precise branch 2 times, most recently from 0097bf8 to 93c2ede Compare June 27, 2024 13:16
.fail_if_stable_opt("--breaking", 12425)?;

let upgrades = ops::upgrade_manifests(&mut ws, &update_opts.to_update)?;
ops::resolve_ws(&ws, update_opts.dry_run)?;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this change, it is now possible to revert the introduction of the dry_run argument in resolve_ws. Let me know what you think.

@torhovland torhovland changed the title Add update-precise-breaking feature. Add capability of making breaking changes in update --precise Jun 28, 2024
@rustbot rustbot added the A-semver Area: semver specifications, version matching, etc. label Jun 28, 2024
@torhovland torhovland force-pushed the update-precise branch 2 times, most recently from 95dddc4 to 48dc39e Compare June 28, 2024 12:04
if you are looking for the prerelease package it needs to be specified explicitly
pre = { version = "0.2.0-beta" }
perhaps a crate was updated and forgotten to be re-vendored?
[ERROR] New requirement ^0.2 is invalid, because it doesn't match 0.2.0-beta
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@epage I'm curious what you think of this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind changing the first letter to lower case? There is a convention we do that. (I know old message didnt' follow the convention…)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have another unstable --precise <pre-release>, which we may want to allow here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lower casing fixed. Awaiting feedback on the error case.

return self.matches(&version);
let mut version_cleaned = version.clone();
version_cleaned.pre = semver::Prerelease::EMPTY;
return self.matches(&version) || self.matches(&version_cleaned);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs more looking into. A test is failing. I'll also move this and related changes to a separate commit.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two commits inserted at the beginning of the PR.

if you are looking for the prerelease package it needs to be specified explicitly
pre = { version = "0.2.0-beta" }
perhaps a crate was updated and forgotten to be re-vendored?
[ERROR] New requirement ^0.2 is invalid, because it doesn't match 0.2.0-beta
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you mind changing the first letter to lower case? There is a convention we do that. (I know old message didnt' follow the convention…)

if you are looking for the prerelease package it needs to be specified explicitly
pre = { version = "0.2.0-beta" }
perhaps a crate was updated and forgotten to be re-vendored?
[ERROR] New requirement ^0.2 is invalid, because it doesn't match 0.2.0-beta
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have another unstable --precise <pre-release>, which we may want to allow here?

@@ -14,6 +14,7 @@ use crate::util::toml_mut::manifest::LocalManifest;
use crate::util::toml_mut::upgrade::upgrade_requirement;
use crate::util::{style, OptVersionReq};
use crate::util::{CargoResult, VersionExt};
use anyhow::Context;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Context is a name so general that may conflict with other structs.

Suggested change
use anyhow::Context;
use anyhow::Context as _;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.


// Will upgrade the direct dependency
p.cargo("update -Zunstable-options --breaking [email protected]")
.masquerade_as_nightly_cargo(&["update-breaking"])
.with_stderr_data(str![[r#"
[UPDATING] `[..]` index
[UPGRADING] dep ^1.0 -> ^2.0
[UPGRADING] dep ^1.0 -> ^3.0
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is discrepancy between the normal update and breaking update.

  • With and without v prefix
  • Show SemVer requirement operator or not.

Not a blocker for this PR, but we might want track this somewhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was discussed here: #13979 (comment)

I'll add it to the task list.

"#,
)
.file("bar/src/lib.rs", "")
.build();

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

Package::new("dep", "1.1.1").publish();
Package::new("dep", "2.0.0").publish();
Package::new("dep", "2.0.1").publish();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the motivation behind 45bc28c?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this test (update_breaking_spec_version_transitive) I wanted to specifically target foo.dep but not bar.dep. I can do that if foo.dep=1.0 and bar.dep=2.0:

    p.cargo("update -Zunstable-options --breaking [email protected]")
        .with_stderr_data(str![[r#"
[UPGRADING] dep ^1.0 -> ^3.0
[UPDATING] dep v1.0.0 -> v3.0.0

But not if bar.dep=1.1, because that makes both foo.dep and bar.dep resolve to 1.1.

.file("src/lib.rs", "")
.build();

p.cargo("update -p incompatible --precise 2.0.0")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

-p is not needed anymore :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

[ERROR] failed to select a version for the requirement `renamed-from = "^1.0"`
candidate versions found which didn't match: 2.0.0
location searched: `dummy-registry` index (which is replacing registry `crates-io`)
required by package `foo v0.0.1 ([..]/foo)`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[..] glob is not necessary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

candidate versions found which didn't match: 2.0.0
location searched: `dummy-registry` index (which is replacing registry `crates-io`)
required by package `foo v0.0.1 ([ROOT]/foo)`
perhaps a crate was updated and forgotten to be re-vendored?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh no this error message is bad 😢

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep in mind this is without the feature implemented. In the next commit this becomes:

[UPGRADING] pre ^1.0.0-alpha -> ^2.0.0
[UPDATING] `dummy-registry` index
[UPDATING] pre v1.0.0-alpha -> v2.0.0

Comment on lines 1 to 5
//! Duplicating tests for `cargo update --precise` with the
//! update-precise-breaking feature enabled. When the feature is stabilized,
//! this file can be deleted.

#![allow(deprecated)]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of duplicating tests, one approach we could take is using a test only env var to activate the unstable behavior, for example __CARGO_USE_GITOXIDE_INSTEAD_OF_GIT2.

(Yes I admit that this opens a door for people without -Zunstable-options, though we already have __CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS hence 😬)

pub fn update_lockfile(
ws: &Workspace<'_>,
opts: &UpdateOptions<'_>,
upgrades: &UpgradeMap,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we document this type alias? It's not immediately clear what should be included in this HashMap. This LockedMap is a good reference:

/// A map of all "locked packages" which is filled in when parsing a lock file
/// and is used to guide dependency resolution by altering summaries as they're
/// queried from this source.
///
/// This map can be thought of as a glorified `Vec<MySummary>` where `MySummary`
/// has a `PackageId` for which package it represents as well as a list of
/// `PackageId` for the resolved dependencies. The hash map is otherwise
/// structured though for easy access throughout this registry.
type LockedMap = HashMap<
// The first level of key-ing done in this hash map is the source that
// dependencies come from, identified by a `SourceId`.
// The next level is keyed by the name of the package...
(SourceId, InternedString),
// ... and the value here is a list of tuples. The first element of each
// tuple is a package which has the source/name used to get to this
// point. The second element of each tuple is the list of locked
// dependencies that the first element has.
Vec<(PackageId, Vec<PackageId>)>,
>;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed.

@torhovland
Copy link
Contributor Author

Description updated.

I'm still not seeing the description talk to

why an error is not the way forward

I have updated the task list. Hope that's OK.

@torhovland
Copy link
Contributor Author

This is ready for another look now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-cli Area: Command-line interface, option parsing, etc. A-manifest Area: Cargo.toml issues A-semver Area: semver specifications, version matching, etc. A-testing-cargo-itself Area: cargo's tests A-unstable Area: nightly unstable support Command-update S-waiting-on-review Status: Awaiting review from the assignee but also interested parties.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants