Feedback on `cargo-upgrade` to prepare it for merging

To clarify, my point was that minimal version handling is a wider problem with multiple, potentially overlapping solutions that we should all keep in mind rather than designing cargo upgrade around being the only solution. I see it as an input to the discussion but not a requirement.

As for the problems of --minimal-versions, I do call out the problem with it today and mentioned the proposed replacement (--minimal-versions-for-me-but-not-my-dependencies as the tracking issue puts it)

(this is kinda repeating myself but I do feel like this is a better formulation) As far as --minimal-versions goes, imho cargo update --save should keep the same default maximal version resolution as cargo update; iow cargo update --save should put the same version in the lockfile and the manifest. This is today's cargo update && cargo upgrade --to-lockfile.

I understand the logic behind not updating the manifest unnecessarily, but the version selection logic should be the same in both modes.

If you want to keep versions low, you use cargo update --minimal-for-me --save.

2 Likes

I'm not sure if multiple modes of operation of cargo update, with multiple additional switches, are ultimately easier to understand than separate update and upgrade commands. Similarity of update and upgrade is unfortunate, but OTOH it allows the two commands to have different default behaviors without seeming inconsistent.

2 Likes

Perhaps clarity would be improved by not trying to pack the meaning into single English words (whether they are update and upgrade or not). What if the extra command was named cargo major-update? That communicates immediately that it is ā€œupdate but moresoā€, and to those familiar with semver, roughly what it does.

2 Likes

as reqested in one of the following posting these here for transparency.

as is upgrade doesnt work and reported by multiple users with full steps to repro. tried multiple ways based on @epage feedback but didnt get toml updates, even with an update first. downgrading many version does work as expected and documented

Do you have any feedback on the proposed new behavior for cargo-upgrade? I understand that some people find the existing behavior confusing. We are wanting more input on the new behavior to make sure people are not unpleasantly surprised when it next changes.

I'm starting to play with what was discussed here with the twist: I'm making the tweaks as-if cargo upgrade will replace cargo update to collect feedback on the "ideal" so we can consider how we might scale back if needed.

Current changes in master:

Some items remaining

I had a couple of ideas for incompatible vs compatible.

The sets of dependencies to touch falls in

  • Compatible
  • Incompatible
  • Pinned

Proposal 1: Mutually exclusive modes

  • Compatible by default, disable if another flag is set
  • Incompatible opt-in with --incompatible, error if --pinned is also set
  • Pinned opt-in with --pinned

Proposal 2: Additive modes with a default if none specified

  • Compatible by default, require --compatible if another flag is specified
  • Incompatible opt-in with --incompatible
  • Pinned opt-in with --pinned

(open to feedback on which is the default in either)

For context, some others:

master behavior

  • Compatible always done
  • Incompatible always done
  • Pinned opt-in with --pinned

Roughly what was talked about earlier in this thread

  • Compatible always done
  • Incompatible opt-in with --incompatible
  • Pinned opt-in with --pinned
1 Like

Both of these proposals seem to treat "both compatible and incompatible" as the more unusual case, rather than a common case. (I do think only upgrading compatible versions is the right default (even if we switch commands), though.)

Looks like I left out part of my motivation and some alternatives: I was exploring alternatives to having a negative flag like --no-compatible, --compatible=false, --skip-compatible, or --only-compatible for when someone wants to only upgrade breaking changes.

We've also talked of --minimal-for-me but there are unknowns around that and I do worry that it be confusing to have a flag that is --minimal-for-me` that then bumps major as that is a bit contradictory, depending on how well we can document it.

1 Like

Could "incompatible" be the default? This is the behavior I expect from upgrades.

Then perhaps "compatible" could be --minor?

Or maybe even compatible left to cargo update exclusively, e.g. cargo update --save.

1 Like

my feedback is to restore the expected and documented behavior from 0.9.1 which match what is stated in documentation and help. otherwise it doesnt match and confusion is expected. multiple user also suggest the same from the linked issues so its not the users being confused imo.

On the exact defaults, I am open to ideas. My proposal was more meant to focus on the mechanism for customizing the default.

The reason I started off with compatible being the default is that is more like cargo update today and I was writing everything as-if cargo upgrade superseded cargo update.

EDIT: And my concern with calling the flag --minor is that refers to "major.minor.update" but compatible could take many shapes, like 0.breaking.compatible.

2 Likes

Something I feel that has been missing in this conversation (my fault) is a summary of all of the use cases we are considering addressing so we can make sure we are all on the same page and can consider how different proposals line up with them

I've tried to capture the main points. Let me know if I missed any!

  • Separate cargo update (for lock file) and cargo upgrade (for manifest) commands is confusing to some users
  • Some application authors want to upgrade to get the latest bug fixes and features
  • Some library authors want to keep up on breaking changes but otherwise want to keep version requirements low to allow dependents to choose the version right for them (lower audit churn with fewer upgrades, workaround bugs, MSRV, etc)
  • Frequently, users will want to defer certain breaking changes because of the level of churn or missing functionality
  • Users need to be able to force a specific minimum version (and test with it) for MSRV or another need
  • Crate authors want their manifest to match their lockfile to ensure that they do not accidentally use new functionality that breaks their users building with an old version.
    • minimal (direct?) versions is another tool in solving this
    • Stablizing #[stable] attributes and warning when an API item newer than the minimal version is being used is another tool for solving this problem
  • Crate authors want to be able to test out their changes (either a --dry-run to opt-out of changes or a --save to opt-in)
  • Crate authors want to script checking if there are any breaking changes (by checking the exit code)
  • Sometimes crate authors need to constrain (or pin) a dependency to workaround a problem and need to specifically decide when to modify the extra constraints
  • Sometimes crate authors need to rename a dependency to access an old version and a new version at the same time and don't want to upgrade the old version to the new version, negating its purpose
  • Some crate authors specifically chose their version requirement precision while authors want to ensure they use full precision always

(the initial post has been updated with this list)

1 Like

Minor note: instead of and/or in addition to the heuristics, the Cargo manifest could get additional keys to control how update/upgrade functions.

e.g. even just na = { package="nalgebra", version="0.31.1", pinned=false } would be useful/interesting, or package.update.precision = "full", or lots of other knobs which could be persistent in the manifest rather than ephemeral CLI flags.

Before I forget some of my ideas, I've gone ahead and re-wrote the proposal (mostly matches cargo-edit master branch) and listed higher-level alternatives. This was a rough first pass, more detail is needed both in how the different options would work and on the trade offs.

Was re-reading clig.dev and just saw this

Donā€™t have ambiguous or similarly-named commands. For example, having two subcommands called ā€œupdateā€ and ā€œupgradeā€ is quite confusing. You might want to use different words, or disambiguate with extra words.

That cuts.

3 Likes

Yeah, I think that's a very good high-level reason to keep update and upgrade functionality in the same command, distinguished by options (or, supposedly, nested subcommands? git has them but I know that others don't like them).

As a code reviewer, I would still like to have separate workflows for updating across semver-breaking versions vs non-breaking versions, since I feel that semver-breaking updates deserve a bit more scrutiny at review time.

2 Likes
  • The --incompatible vs --compatible false distinction is very subtle. Without the manual I could not guess what they do.

  • defaulting to whole workspace is nice (saves me from typing longish --workspace), and feels consistent with workspace-wide Cargo.lock.

  • update of Cargo.lock with upgrade is also very welcome, as I always ran cargo update afterwards anyway.

To make this conversation more concrete, I've gone ahead and updated cargo-upgrade to one combination of proposals.

See Release v0.11.0 Ā· killercup/cargo-edit Ā· GitHub for more details.

Very good point. Unsure if its just the curse of knowledge/familiarity that I hadn't noticed this.

Any thoughts on how to allow control over the different types of upgrades? I feel like --no-compatible would be the same or worse.