mehcode / config-rs

⚙️ Layered configuration system for Rust applications (with strong support for 12-factor applications).
Apache License 2.0
2.43k stars 206 forks source link

[release-0.13.x] fix(CI): Commit an MSRV compatible `Cargo.lock` #492

Closed ijackson closed 7 months ago

ijackson commented 8 months ago

See individual commit messages for rationale, and how I made this lockfile.

ijackson commented 8 months ago

I put the recipe for generate the lockfile in the commit message, rather than in a script to run from CI, because, sadly, it will rot. As newer dependencies get published, the list of things to downgrade will grow (and possibly things will get more complicated).

The -Z minimal-versions generate-lockfile approach wouldn't have suffered from that problem.

polarathene commented 8 months ago

Commit: https://github.com/mehcode/config-rs/pull/492/commits/f1445bc16d099392207db8c9725cb9469a2dcafb Emprically, 1.30 requires a newer Rust than our MSRV. Therefore, request 1.29.

Typo: Empirically

Commit: https://github.com/mehcode/config-rs/pull/492/commits/5c4c72d96c1d69f45bc91142e9764902baf645db My atteempts at starting with the minimal versions and upgrading crates as needed, were not successful.

Typo: attempts


Do I understand this correctly, instead of installing the semver version in Cargo.toml as the minimum supported dependency, preferring the newest in semver, you're generating a lock with the minimal version?

You note though that this is a bit tricky to manage this way, and the main goal is to ensure the Cargo.lock respects a supported MSRV?


The PR and commits lack reference to what the target MSRV is. I'm assuming it's the one referenced in the README for the 0.13.x branch, which is 1.56.1. Using the cargo-msrv tool to verify:

$ cargo-msrv

Fetching index
Determining the Minimum Supported Rust Version (MSRV) for toolchain x86_64-unknown-linux-gnu
Using check command cargo check

Check for toolchain '1.52.1-x86_64-unknown-linux-gnu' failed with:
┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Updating crates.io index                                                                                                                          │
│  Downloading crates ...                                                                                                                           │
│ error: failed to download `syn v2.0.38`                                                                                                           │
│                                                                                                                                                   │
│ Caused by:                                                                                                                                        │
│   unable to get packages from source                                                                                                              │
│                                                                                                                                                   │
│ Caused by:                                                                                                                                        │
│   failed to parse manifest at `/home/xiaobu/.cargo/registry/src/github.com-1ecc6299db9ec823/syn-2.0.38/Cargo.toml`                                │
│                                                                                                                                                   │
│ Caused by:                                                                                                                                        │
│   feature `edition2021` is required                                                                                                               │
│                                                                                                                                                   │
│   this Cargo does not support nightly features, but if you                                                                                        │
│   switch to nightly channel you can add                                                                                                           │
│   `cargo-features = ["edition2021"]` to enable this feature                                                                                       │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Check for toolchain '1.63.0-x86_64-unknown-linux-gnu' succeeded
Check for toolchain '1.57.0-x86_64-unknown-linux-gnu' succeeded

Check for toolchain '1.54.0-x86_64-unknown-linux-gnu' failed with:
┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ error: failed to download `async-trait v0.1.74`                                                                                                   │
│                                                                                                                                                   │
│ Caused by:                                                                                                                                        │
│   unable to get packages from source                                                                                                              │
│                                                                                                                                                   │
│ Caused by:                                                                                                                                        │
│   failed to parse manifest at `/home/xiaobu/.cargo/registry/src/github.com-1ecc6299db9ec823/async-trait-0.1.74/Cargo.toml`                        │
│                                                                                                                                                   │
│ Caused by:                                                                                                                                        │
│   feature `edition2021` is required                                                                                                               │
│                                                                                                                                                   │
│   this Cargo does not support nightly features, but if you                                                                                        │
│   switch to nightly channel you can add                                                                                                           │
│   `cargo-features = ["edition2021"]` to enable this feature                                                                                       │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

Check for toolchain '1.55.0-x86_64-unknown-linux-gnu' failed with:
┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ error: failed to download `async-trait v0.1.74`                                                                                                   │
│                                                                                                                                                   │
│ Caused by:                                                                                                                                        │
│   unable to get packages from source                                                                                                              │
│                                                                                                                                                   │
│ Caused by:                                                                                                                                        │
│   failed to parse manifest at `/home/xiaobu/.cargo/registry/src/github.com-1ecc6299db9ec823/async-trait-0.1.74/Cargo.toml`                        │
│                                                                                                                                                   │
│ Caused by:                                                                                                                                        │
│   feature `edition2021` is required                                                                                                               │
│                                                                                                                                                   │
│ The package requires the Cargo feature called `edition2021`, but that feature is not stabilized in this version of Cargo (1.55.0 (32da73ab1       │
│ 2021-08-23)).                                                                                                                                     │
│   Consider trying a newer version of Cargo (this may require the nightly release).                                                                │
│   See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#edition-2021 for more information about the status of this feature.         │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
Check for toolchain '1.56.1-x86_64-unknown-linux-gnu' succeeded
   Finished The MSRV is: 1.56.1   ██████████████████████████████████████████████████████████████████████████████████████████████████████████ 00:02:03

It also confirms that 1.56.1 is the MSRV.

I am not sure what the issue was with the tokio version? With an MSRV of 1.56.1 it is compatible with tokio v1.33.0? 🤔

$ cargo +1.56.1 tree --package tokio

tokio v1.33.0
├── bytes v1.5.0
├── libc v0.2.149
├── mio v0.8.9
│   └── libc v0.2.149
├── num_cpus v1.16.0
│   └── libc v0.2.149
├── pin-project-lite v0.2.13
├── socket2 v0.5.5
│   └── libc v0.2.149
└── tokio-macros v2.1.0 (proc-macro)
    ├── proc-macro2 v1.0.69
    │   └── unicode-ident v1.0.12
    ├── quote v1.0.33
    │   └── proc-macro2 v1.0.69 (*)
    └── syn v2.0.38
        ├── proc-macro2 v1.0.69 (*)
        ├── quote v1.0.33 (*)
        └── unicode-ident v1.0.12

# No issues with this MSRV to build:
$ cargo +1.56.1 check
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s

$ cargo +1.56.1 build
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s

# With `rust-version = "1.56.1" in Cargo.toml:
$ cargo-msrv verify

Fetching index
Verifying the Minimum Supported Rust Version (MSRV) for toolchain x86_64-unknown-linux-gnu
Using check command cargo check
   Finished Satisfied MSRV check: 1.56.1 ███████████████████████████████████████████████████████████████████████████████████████████████████ 00:00:00
polarathene commented 8 months ago

I put the recipe for generate the lockfile in the commit message, rather than in a script to run from CI, because, sadly, it will rot. As newer dependencies get published, the list of things to downgrade will grow (and possibly things will get more complicated).

Assuming the MSRV is intended as 1.56.1, it would seem more reliable to use cargo-msrv (as I have suggested earlier) to determine this than the approach you've noted will be a maintenance burden?

ijackson commented 8 months ago

Do I understand this correctly, instead of installing the semver version in Cargo.toml as the minimum supported dependency, preferring the newest in semver, you're generating a lock with the minimal version?

You note though that this is a bit tricky to manage this way, and the main goal is to ensure the Cargo.lock respects a supported MSRV?

I'm not quite sure I follow the question but I will try to answer it anyway :-).

The problem I am trying to solve with this MR is that CI is failing with the declared MSRV of 1.56. We want to test our MSRV in CI as otherwise it's almost certain we'll accidentally violate it.

Proper approach (rejected)

In principle the correct solution to testing with the MSRV is as follows:

  1. Make sure that all dependency definitions are accurate: specifically, that we can actually build with the earliest version of each dependency, that is permitted by the relevant Cargo.tomls.
  2. cargo +nightly update -Z minimal-versions to generate a lockfile using the lowest version permitted by our Cargo.toml

This is what I do in (for example) derive-adhoc.

However, 1 must be done transitively: if any of our dependencies have too-relaxed versions, then we need to get the dependency updated; and we would then need to update our dependency edge to declare a requirement on the version which is fixed. And this applies all the way to the bottom of the stack.

This might involve patch releases of multiple crates throughout our dependency stack. My investigations (or random flailing, depending how you look at it) led me to suspect that this proper approach wouldn't yield success, at least not for the 0.13.x release branch of config-rs.

It's easier to do this kind of cleanup to all of our upstreams if we'd be working on their mainlines. But, the job is a big one and might involve starting at the bottom. Realistically, a good place to start would be to try to get pest to build with -Z minimal-versions.

Minor bodging (attempted, failed)

The next most principled approach is to generate a lockfile with -Z minimal-versions and then update individual crates (deep in our stack) that, in practice, are broken. (For direct dependencies, we could easily, and should, just change our Cargo.toml.)

I tried this. But it got badly out of hand.

Major bodging

So instead, what I did was, by hook or by crook, generate a Cargo.lock that can be used to test that we do build with our MSRV. That will prevent us from adding MSRV breaking to config-rs.

Downstreams with a comparable MSRV might have to redo some of this work. My particular prompt is the Arti project, where we have an MSRV of 1.65, and we have already adopted the "minor bodging" approach described above. In practice that seems to be working for us.

But, at least in a release branch, I don't think we will often want to update our Cargo.lock in CI.

ijackson commented 8 months ago

@polarathene

Assuming the MSRV is intended as 1.56.1, it would seem more reliable to use cargo-msrv (as I have suggested earlier) to determine this than the approach you've noted will be a maintenance burden?

Sorry for not replying to that suggestion earlier. I had read it, but it didn't seem directly on-point. Let me explain why. (I have read enough of the cargo-msrv docs to feel I know what it does.)

Firstly, I don't think we are trying to determine our MSRV. We are trying to assure that we do not break our intended MSRV. The basic principle is that we should advance our MSRV deliberately, and ideally avoid doing that on a release branch.

To do that, we do not need to build and test with many different toolchains. Nor do we need to inspect the rust-version value in any of our (transitive) dependencies. (Those are the things cargo-msrv can do that aren't easy without it.)

We just need to test with our intended MSRV. If that passes, then the Rust projects' compatibility guarantees mean that we should build with later versions too (and, in practice, that always is the case).

And, whether we use cargo-msrv or just test with our MSRV in CI, we still need a suitable lockfile. Since cargo dosn't do MSRV-aware resolution, this is not straightforward.

I am not sure what the issue was with the tokio version? With an MSRV of 1.56.1 it is compatible with tokio v1.33.0? 🤔

According to Tokio's README the MSRV of Tokio 1.30 onwards is 1.63.

Empirically, cargo +1.59 clippy --locked --offline --all-targets --all-features -- -D warnings failed with Tokio 1.30.

Note that in order to properly check our MSRV, the CI doesn't just build the package. It runs the tests too. I think this is correct.

polarathene commented 8 months ago

Thanks for taking the time to give a detailed reply! ❤️

Building the crate is successful for 1.56.1, which is the documented MSRV.

It seems that when you throw tests into the mix that is not the case, and I could not seem to find much information at a quick glance with some search queries if MSRV is meant to also cover dev-dependencies, or only the release crate that would be pulled in as a dependency to other projects. I'd assume that's what MSRV is generally intended for though?

I have seen some projects document MSRV for running, and MSRV for building/testing, so perhaps that distinction is worthwhile? cargo-msrv seems to lack the ability to take the dev-dependencies context into account with cargo check --tests :(

$ cargo +1.56.1 check --tests
error: package `rustix v0.38.9` cannot be built because it requires rustc 1.63 or newer, while the currently active rustc version is 1.56.1

I understand now I think, thanks! 👍


Original response > The problem I am trying to solve with this MR is that CI is failing with the declared MSRV of 1.56. We want to test our MSRV in CI as otherwise it's almost certain we'll accidentally violate it. Then it's important to understand why it's failing due to that? I was able to clone the branch and build with 1.56.1, are you sure it built with that toolchain? What could cause the difference in success? > * Make sure that all dependency definitions are accurate: specifically, that we can actually build with the earliest version of each dependency, that is permitted by the relevant `Cargo.toml`s. > * `cargo +nightly update -Z minimal-versions` to generate a lockfile using the _lowest_ version permitted by our `Cargo.toml` Ok, but at the same time we could also identify at what point lowering the rust toolchain version fails, then try to identify what crate is causing that. This is what `cargo-msrv` was doing for me automatically. > Firstly, I don't think we are trying to _determine_ our MSRV. We are trying to _assure_ that we do not break our _intended_ MSRV. The basic principle is that we should advance our MSRV deliberately, and ideally avoid doing that on a release branch. While I agree with that, I think I had only seen it documented in the README which is where MSRV bumps were raised. There often wasn't much context provided as to why when that happened, so it seemed somewhat arbitrary, unclear if it was due to policy, dev-dependencies or an actual need to raise for release builds. I used `cargo-msrv` to confirm what the actual MSRV was, and it seemed to do a pretty good job of matching both this release branch and main branch IIRC. I've not seen MSRV documented lower than `1.56.1` for this release branch, or discussion here of the intended MSRV being any lower than that, so I'd assume that was the goal. > We just need to test with our intended MSRV. If that passes, then the Rust projects' compatibility guarantees mean that we should build with later versions too (and, in practice, that always is the case). I feel that if there is an actual MSRV that the project should build with such as `1.56.1`, then that should be documented in the `Cargo.toml`? That's what `rust-version` is for right? As I noted, I was able to build the project with that MSRV, so I'm interested in why the CI was having a problem. > And, whether we use cargo-msrv or just test with our MSRV in CI, we still need a suitable lockfile. Since cargo dosn't do MSRV-aware resolution, this is not straightforward. That I can't comment on, but how do you build with a lockfile on an MSRV that is lower than `cargo-msrv` would detect? Is this due to the crate install using the latest version a dependency semver permits, vs your minimal versions approach? (_which seemed problematic, at least with this release branch_) That would be lowering the crates to a MSRV lower than 1.56.1? You didn't document how much lower that went 🤔 --- > > I am not sure what the issue was with the tokio version? With an MSRV of 1.56.1 it is compatible with tokio v1.33.0? 🤔 > > According to [Tokio's README](https://lib.rs/crates/tokio) the MSRV of Tokio 1.30 onwards is 1.63. > > Empirically, `cargo +1.59 clippy --locked --offline --all-targets --all-features -- -D warnings` failed with Tokio 1.30. > > Note that in order to properly check our MSRV, the CI doesn't just _build_ the package. It runs the tests too. I think this is correct. Ah ok, I didn't run the tests, perhaps that was the difference 😅 (**EDIT:** Confirmed)
ijackson commented 8 months ago

It seems that when you throw tests into the mix that is not the case, and I could not seem to find much information at a quick glance with some search queries if MSRV is meant to also cover dev-dependencies, or only the release crate that would be pulled in as a dependency to other projects. I'd assume that's what MSRV is generally intended for though?

Downstreams don't run the tests so they might be OK with it just building. But we want to detect semantic as well as syntactic problems in our CI. That will help assure that downstream users get not only a build that completes, but also working programs.

Declaring the MSRV with rust-version in the Cargo.toml is probably a good idea, but IMO (i) probably not on the stable release branch, where we should avoid unecessary changes (and especially, build system changes) and (ii) even on the main branch, not until we have checked that the documented MSRV isn't unecessarily high (since adding the rust-version will break things that might have previously been working).

ijackson commented 8 months ago

@matthiasbeyer I think this is just blocking on your decision about https://github.com/mehcode/config-rs/pull/492#discussion_r1370437830

matthiasbeyer commented 8 months ago

No time to read this all now, because I am traveling until Sunday. Just wanted to say that I don't mind typos in commit messages or doc comments! So these should not block merges! I don't think anyone intended to block a merge here because of commits, I just wanted this to be noted so everyone knows! :laughing:

Thanks for your contributions, all of you! I hope I can read through all of the above next week.

polarathene commented 8 months ago

I don't think anyone intended to block a merge here because of commits

I'm not blocking on anything, this isn't an area that I am confident in so leave the discussion between you two 👍

I only mentioned the typos since once you merge, rewriting history isn't usually viable if the typos were a concern.

Thanks for your contributions, all of you! I hope I can read through all of the above next week.

You're welcome, I still plan to get back to my contributions here ASAP 😅

EDIT: Accidentally marked this as off-topic, meant to be "outdated" to reduce noise in the discussion, but I can't seem to change that or unhide the comment 🤷‍♂️

polarathene commented 8 months ago

Been writing this up for a while, fairly tedious and I don't expect anyone else to read through it all, but it serves as documentation on issues being tackled/discussed by the PR.


json5 (pest) with -Z minimal-versions.

Comment:

Realistically, a good place to start would be to try to get pest to build with -Z minimal-versions.

This actually seems to be due to json5 (parent dependency) pulling in pest allowing a semver with minimum versions that are incompatible with the MSRV of config-rs?

The json5 crate does not appear to be actively maintained anymore, so trying to address that may not be successful?

Perhaps the issue does bubble further down into the pest crates.. EDIT: pest_meta min version was the problem


Nightly resolver options

-Z msrv-policy vs -Z minimal-versions:

You could manage minimal versions for only direct dependencies (only those directly in our Cargo.toml) with cargo +nightly update -Z direct-minimal-versions?

Hidden as outdated `json5` will also resolve to an MSRV compatible release with `cargo +1.56.1 update` that in this case is equivalent (_**EDIT:** This was due to Rust `< 1.60.0` resolver incompatibility_) to `cargo +nightly update -Z msrv-policy` (_when `rust-version = "1.56.1"` is set in `Cargo.toml`_): ```console # Only uses minimal versions declared in Cargo.toml, # resolves remaining implicit deps normally for the specified toolchain (nightly): $ cargo +nightly update -Z direct-minimal-versions # Pest version is too new: $ cargo tree app v0.1.0 (/app) └── json5 v0.4.0 ├── pest v2.7.5 │ ├── memchr v2.6.4 │ ├── thiserror v1.0.50 │ │ └── thiserror-impl v1.0.50 (proc-macro) │ │ ├── proc-macro2 v1.0.69 │ │ │ └── unicode-ident v1.0.12 │ │ ├── quote v1.0.33 │ │ │ └── proc-macro2 v1.0.69 (*) │ │ └── syn v2.0.38 │ │ ├── proc-macro2 v1.0.69 (*) │ │ ├── quote v1.0.33 (*) │ │ └── unicode-ident v1.0.12 │ └── ucd-trie v0.1.6 ├── pest_derive v2.7.5 (proc-macro) │ ├── pest v2.7.5 (*) │ └── pest_generator v2.7.5 │ ├── pest v2.7.5 (*) │ ├── pest_meta v2.7.5 │ │ ├── once_cell v1.18.0 │ │ └── pest v2.7.5 (*) │ │ [build-dependencies] │ │ └── sha2 v0.10.8 │ │ ├── cfg-if v1.0.0 │ │ ├── cpufeatures v0.2.11 │ │ └── digest v0.10.7 │ │ ├── block-buffer v0.10.4 │ │ │ └── generic-array v0.14.7 │ │ │ └── typenum v1.17.0 │ │ │ [build-dependencies] │ │ │ └── version_check v0.9.4 │ │ └── crypto-common v0.1.6 │ │ ├── generic-array v0.14.7 (*) │ │ └── typenum v1.17.0 │ ├── proc-macro2 v1.0.69 (*) │ ├── quote v1.0.33 (*) │ └── syn v2.0.38 (*) └── serde v1.0.190 # Failure: $ cargo +1.56.1 check Updating crates.io index error: failed to select a version for the requirement `pest = "=2.7.5"` candidate versions found which didn't match: 2.5.7, 2.5.6, 2.5.5, ... location searched: crates.io index required by package `json5 v0.4.0` ... which satisfies dependency `json5 = "=0.4.0"` of package `app v0.1.0 (/app)` # Compatible versions for the MSRV toolchain are resolved (best effort, not always reliable): # EDIT: Disregard this, crates published with 1.60.0 Cargo.toml features are dismissed by the resolver $ cargo +1.56.1 update Updating crates.io index Updating json5 v0.4.0 -> v0.4.1 Removing memchr v2.6.4 Updating once_cell v1.18.0 -> v1.17.2 Updating pest v2.7.5 -> v2.5.7 Updating pest_derive v2.7.5 -> v2.5.7 Updating pest_generator v2.7.5 -> v2.5.7 Updating pest_meta v2.7.5 -> v2.5.7 # No adjustments, (`-Z msrv-policy` resolver does not stop at parents with compatible `rust-version`): # EDIT: "stop at parents" resolver behaviour was a misunderstanding on my part, as other edits point out $ cargo +nightly update -Z msrv-policy Updating crates.io index ```
EDIT: Inaccurate as this was only true for `-Z msrv-policy`, see next section instead [`pest 2.5.x` declares `rust-version = "1.56.0"`](https://github.com/pest-parser/pest/blob/v2.5.7/pest/Cargo.toml#L14) (_introduced in [`pest 2.2.0`](https://github.com/pest-parser/pest/blob/v2.2.0/pest/Cargo.toml)_), this [carries over to `2.6.x` series](https://github.com/pest-parser/pest/blob/v2.6.1/pest/Cargo.toml#L14) (_but [that series was yanked](https://crates.io/crates/pest/2.6.1)_), meanwhile [`2.7.0` introduces an MSRV bump with `rust-version = "1.60.0"`](https://github.com/pest-parser/pest/blob/047c27e06231937c558ff8bf2ff2227c4a09dc1b/pest/Cargo.toml#L14). This is why `2.5.7` is resolved despite the wider `2.0.0` semver range. While the other sibling dependency (`serde`) currently resolves to `1.0.190` (_latest release_) also [declares a `1.31.0` MSRV via `rust-version`](https://github.com/serde-rs/serde/blob/edb1a586d83ceaf4a61980ce890bc77a02e00787/serde/Cargo.toml#L15) that still appears compatible / valid (_the bump was [since `1.0.180`, where it was previously Rust `1.19.0` for `serde 1.0.179`](https://github.com/serde-rs/serde/blob/c2b16bfbb0a5b662aa97c9a8a5d5bf7d958b85b8/serde/Cargo.toml#L14)_).

Breakdown of problematic dependency resolutions

json5 => pest

pest 2.5.7 is the last valid release resolved (ignoring the 2.6.x yanked series) due to this Cargo.toml line:

Minimal versions:

# `-Z minimal-versions` compatibility for json5:

json5 = "0.4.0"
# json5 relies on these dep versions at a minimum:
pest = "2.0.2"
serde = "1.0.69"
# 2.0.0 min version brings in 1.x pest versions, avoid that:
pest_meta = "2.0.3"

# Pinning this dep additionally supports earlier than Rust 1.56 (down to 1.31):
# 0.1.3 requires 1.31 (edition 2018)
# 0.1.6 requires 1.56 (edition 2021)
#ucd-trie = "=0.1.2"

rust-ini => ordered-multimap => hashbrown => ahash

Meanwhile ahash 0.7.6 also adopted the dep: syntax feature too. ahash 0.7.6 got yanked, so 0.7.7 is the earliest and current release with that change.

And that is how the resolving happened 😮‍💨

Latest rust-ini 0.20.0 bumps to ordered-multimap 0.7 which currently requires Rust 1.71.1 with no use of rust-version by either crate to guard against that. Meanwhile rust-ini 0.19.0 bumped to ordered-multimap 0.6 which has an MSRV of 1.64 (due to dlv-list 0.5).


TL;DR

I initially investigated with cargo-minimal-versions and learned about -Z msrv-policy as a better way of resolving a Cargo.lock that is compatible with the MSRV, but not foolproof (even with broader adoption, see the hashbrown 0.14.2 example at the link).

Thankfully pest has been declaring rust-version since their 2.2.0 release with 1.56.0, and bumping that appropriately to 1.60.0 for the 2.7.0 release.

Unfortunately, since rust-version is not widely adopted yet, -Z msrv-policy can fail to resolve Cargo.lock in a compatible manner (as noted earlier with ahash).

Due to the resolver bug with crates that adopted 1.60.0 weak (?) or namespaced (dep:) feature syntax:

Dev dependencies fail to resolve correctly for the intended MSRV of 1.56.1 however

polarathene commented 8 months ago

Comment:

I put the recipe for generate the lockfile in the commit message, rather than in a script to run from CI, because, sadly, it will rot. As newer dependencies get published, the list of things to downgrade will grow (and possibly things will get more complicated).

The -Z minimal-versions generate-lockfile approach wouldn't have suffered from that problem.

Reference: Commit message with mentioned recipe Commit: https://github.com/mehcode/config-rs/pull/492/commits/5c4c72d96c1d69f45bc91142e9764902baf645db This is not so trivial. In theory, `cargo +nightly update -Z minimal-versions generate-lockfile` ought to work. - However, there are rather many places in our dependency tree where some crate A depends on crate B, version V, but it actually doesn't build with version V, but requires something considerably newer. - There seemed to be particular problems in the parts of the dependency graph near "pest". My attempts at starting with the minimal versions and upgrading crates as needed, were not successful. - However, I was able to converge reasonably quickly from the other end. Starting with the lockfile generated by 1.59, using recent versions, and then repeatedly downgrading crates whose (actual, or declared) MSRV was too high. The following recipe generates a working lockfile: ```bash cargo +1.59 generate-lockfile cargo +nightly update -Z minimal-versions -p linux-raw-sys cargo +nightly update -Z minimal-versions -p rustix cargo +nightly update -Z minimal-versions -p tempfile cargo +nightly update -Z minimal-versions -p reqwest cargo +nightly update -Z minimal-versions -p byteorder cargo +nightly update -Z minimal-versions -p h2 cargo +nightly update -Z minimal-versions -p log cargo +nightly update -Z minimal-versions -p tokio cargo +nightly update -Z minimal-versions -p tokio-util@0.7.9 ```

With a slight adjustment to Cargo.toml (which still respects the MSRV constraints), we can then minimize this down to:

# Minimal versions in Cargo.toml only, resolve the rest by rust-version
cargo +nightly update -Z direct-minimal-versions -Z msrv-policy

# Pins required after `-Z msrv-policy` due to lack of implicit deps using rust-version:
# nom, warp => multipart, futures-util (many parent deps):
cargo update --package memchr --precise 2.5.0
# rust-ini:
cargo update --package ordered-multimap --precise 0.4.0

Review comment:

Is this really necessary? I wouldn't mind this change at all on master, but I am way more conservative with patch releases.

Reference: Associated commit with message [Earlier commit with `tokio` min version set to `1.29`](https://github.com/mehcode/config-rs/pull/492/commits/f1445bc16d099392207db8c9725cb9469a2dcafb): > _We want a later version than `1.0` since earlier versions have various bugs, and also dependency versions which are insufficient to rule out bugs in lower crates._

It's a dev dependency, so I really don't see any issue with that especially when it's within the MSRV.

It should be a minimum version of 1.27, but could be up to 1.29.1 (last supported tokio release for MSRV of 1.56). That would require some extra explicit pinning, thus preferring tokio ="1.27" is better. Next paragraph with bullet points explains why.

Unfortunately from tokio 1.28 the resolver will then allow an incompatibility due to h2 => tokio-util.

warp can be bumped up to 0.3.5 which makes it easier to pin tokio-util 0.7.8:


Comment:

The problem I am trying to solve with this MR is that CI is failing with the declared MSRV of 1.56. We want to test our MSRV in CI as otherwise it's almost certain we'll accidentally violate it.

I'm not sure if I agree with the need to run tests on the MSRV:

Presently, the main dependencies build fine without a lockfile, but this is only because newer releases of some crates are ignored as the MSRV considers the Cargo.toml invalid. At some point an implicit dependency bump could break that and require maintaining a Cargo.lock that the downstream needs to manage to maintain the MSRV.

Many downstream aren't likely stuck on 1.56.1, so while that possibility could occur at some point, the impact on downstream users is unclear.

The main concern isn't so much upstreams breaking MSRV then:

Since running the examples on the target MSRV can require version pinning with Cargo.lock, the user would need to look at how we manage that (if we document it), or figure it out for themselves.

As opposed to keeping examples and tests independent of that MSRV constraint, and just focusing on correctness which is far less of a maintenance burden?

I've managed to satisfy the MSRV with reduced effort, but it appears quite a few implicit deps made changes in recent months that would have been missed if this was tackled before then, so I don't consider it worthwhile as opposed to an MSRV bump.


Proper approach (rejected)

Agree, this is not really feasible to resolve properly, not for MSRV of 1.56.0. It's more at risk of breaking again too if upstreams aren't careful with their updates.

Minor bodging (attempted, failed) Major bodging

Not too different vs "Proper approach", but easier to implement, however maintenance burden may be a bit concerning (could be a signal for bumping MSRV).

Not sure what the difference between the two bodging descriptions is?

The concern with downstreams appears to be the same as with MSRV bumps that commonly ripple up the chain, bumping MSRVs or requiring those with older MSRV requirements to enjoy maintaining a Cargo.lock (as version pinning from Cargo.toml is strongly discouraged from what I've seen).


Comment:

In principle the correct solution to testing with the MSRV is as follows:

This seems good 👍

I think given an MSRV as a baseline, Cargo.toml can then express the minimum versions semver as what satisfies the MSRV, no earlier since that's already a hard requirement? (at least edition sets a hard boundary on MSRV, and the rust-version field can too although opt-out is supported)

This would not require implicit dependencies to build with -Z minimal-versions - especially when rust-version info can be leveraged by the resolver (EDIT: Only valid with -Z msrv-policy).

UPDATE: Might be best served with: cargo +nightly update -Z direct-minimal-versions -Z msrv-policy

ijackson commented 7 months ago

tl;dr:

@matthiasbeyer Please would you merge this MR now, despite @polarathene's interventions, which I have tried to summarise and briefly analyse, below.

Summary of @polarathene 's technical points, and my responses

I have read these messages. It's not so easy to see what they're trying to achieve, but I think I have grasped the main points.

The bulk of the messages consists of detailed analysis of the bugs in our dependencies, which are causing a plain -Z minimal-versions to fail. There are some observations about the maintenance status of our dependencies. I don't think this is actionable; at least, not on the config-rs stable release branch. It might be actionable as MRs against some of our dependencies, or as a programme of improvement for our mainline.

Also, @polarathene doubts whether it is a good idea to run the tests in our MSRV CI check. I think running the tests in CI, even in the MSRV check, is important. But I also think this is not something we should be changing on the stable branch at this stage. Committing a Cargo.lock is a simple intervention which alllows us to continue to run those tests; which has been recognised by Rust upstream as a good approach; and which has no impact on our users. A proposal to not run the tests during MSRV checks should be made on the mainline, in its own MR, and shouldn't form part of fixing the CI on the release branch.

I'm not sure whether these interventions are intended to be blocking. But I am requesting that you proceed as if they aren't.

Personal, nontechnical, response to @polarathene

Thanks for your enthusiasm, but I am finding your contributions here very frustrating.

It is quite unclear whether you intend to block this MR to fix the CI on the stable branch? Because that is the effect your messages are having. Even your "tl;dr" is 11 paragraphs and needs a "tl;dr"!

In practical terms, the maintainers are naturally going to be reluctant to proceed unless they can see whether there is consensus on my proposal, and they feel they understand the underlying issues. But that requires reading and comprehend everything you've written. But the rate at which you are producing text seems to exceed the rate at which the maintainers are able to digest it.

The maintainers must also figure out whether they think you are objecting, and, if so, whether your objections are sound. But it is very unclear from your messges whether you intend to object. That important framing is lacking.

There are probably improvements that could be made to the approach I have taken here. But I don't think your messages offer a viable alternative. We need the CI fixed on the stable branch. Further improvements could be made in followup MRs. Therefore, with some reluctance, I am inviting @matthiasbeyer to disregard your comments.

Your messages would have been much less problematic if they had started with:

tl;dr: Please merge this MR

Summary

I have done some investigation of the problems with our dependencies and our MSRV. The full report of my findings is below. You don't need to read it, but I wanted to write it down for posterity and in case we might want to try to improve things further.

I've put it in this MR since I wasn't sure I wanted to file a separate ticket and I'm not sure ithe information is actionable.

If indeed that was your intent.

polarathene commented 7 months ago

There are probably improvements that could be made to the approach I have taken here. But I don't think your messages offer a viable alternative. We need the CI fixed on the stable branch. Further improvements could be made in followup MRs. I am inviting @matthiasbeyer to disregard your comments.

While my 2nd message opens early with:

cargo +nightly update -Z direct-minimal-versions -Z msrv-policy
cargo update --package memchr --precise 2.5.0
cargo update --package ordered-multimap --precise 0.4.0

Your messages would have been much less problematic if they had started with:

I prefaced the two messages with a disclaimer that:

The maintainers must also figure out whether they think you are objecting, and, if so, whether your objections are sound. But it is very unclear from your messages whether you intend to object. That important framing is lacking.


@matthiasbeyer Please would you merge this MR now, despite @polarathene's interventions, which I have tried to summarise and briefly analyse, below.

While I apologize for the misunderstanding / confusion my messages caused you, I find this a tad disrespectful to request (I was notified around 2am).

Since your applying pressure, I am responding early with the above (before I was ready for my follow-up comment later on today). Likewise with the following suggestion (verified to work).

Here are the minimal changes that will work:

git clone https://github.com/mehcode/config-rs -b release-0.13.x --depth 1 .

# Requires `rust-version = "1.56.0" in Cargo.toml, and `warp = "=0.3.5"`:
cargo +nightly update -Z msrv-policy

# 3 pins for MSRV 1.56 compatibility (_due to lack of support for `-Z msrv-policy`_):
cargo update --package memchr --precise 2.5.0
cargo update --package ordered-multimap --precise 0.4.0
cargo update --package tokio-util --precise 0.7.8

# These both pass successfully:
cargo +1.56.1 check --tests
cargo +1.56.1 clippy --locked --offline --all-targets --all-features -- -D warnings

Ignore the earlier -Z direct-minimal-versions suggestion as Cargo.toml presently misrepresents many direct deps supported min version for the MSRV. I have resolved what those are if they'd be of interest.

polarathene commented 7 months ago
  • Your documented approach (via commit message) is 9 deps being updated with -Z minimal-versions.

  • One of which is relying on a version ref (fragile), tokio-util@0.7.9.

For reference, this already fails within 2 weeks since PR was opened:

$ cargo +nightly update -Z minimal-versions -p tokio-util@0.7.9

error: package ID specification `tokio-util@0.7.9` did not match any packages
Did you mean one of these?

  tokio-util@0.6.10
  tokio-util@0.7.10

# Fragile:
$ cargo +nightly update -Z minimal-versions -p tokio-util@0.7.10

Also:

# Still broken:
$ cargo +1.56.1 check --tests
error: package `scoped-tls v1.0.1` cannot be built because it requires rustc 1.59 or newer, while the currently active rustc version is 1.56.1

$ cargo +1.56.1 clippy --locked --offline --all-targets --all-features -- -D warnings
error: package `chrono v0.4.31` cannot be built because it requires rustc 1.57.0 or newer, while the currently active rustc version is 1.56.1

cargo +nightly update -Z minimal-versions -p chrono will fail without using --precise or updating the min version in Cargo.toml since the actual min compatible version for MSRV tests to pass is 0.4.23, and 0.4.26 is the last release supporting MSRV of 1.56.

EDIT: Oh, not once was I corrected about the MSRV being 1.59. The README on the branch and prior discussion in this PR I asked about the MSRV multiple times and came to the conclusion of it being 1.56.1 since that's what cargo-msrv deduced and what the README documents.

So if anything, not only is my alternative lockfile generation simpler, it allows to continue supporting the original MSRV of 1.56.1 instead of the pattern of bumping the MSRV for CI to pass.

ijackson commented 7 months ago

I'm going to try to limit myself to comments which will hopefully allow us to move forward.

@polarathene Thanks for the alternative MR #495.

Thanks also for the investigations. I agree that it's likely that the actual MSRV is 1.56 rather than 1.59. I didn't set out, with this MR, to lower the MSRV of the 0.13.x branch. But that seems like it would be a good thing to do.

I do think that we should continue to run tests and examples with our MSRV in CI. I think we must do that with a fixed Cargo.lock, since otherwise our CI is likely to (once again) break due to changes in our dependency stack.

It is not so easy to produce a working Cargo.lock. I note that #495 does not produce a Cargo.lock capable of running the tests and examples. I used the runes in https://github.com/mehcode/config-rs/pull/495#issuecomment-1793634940, with that branch, and it didn't work for me. (And, CI on that MR is failing, but that's partly due to that branch not having a Cargo.lock at all.)

My approach to producing a working Cargo.lock was indeed not very principled. My own commit message was not intended as a recipe for future use. It was intended as an explanation of what I had done, for traceability and documentation.

I am very open to an alternative method of generating a working Cargo.lock. In whatever way we produce it, the method that was used generating it ought to be written down somewhere. (And as I say I think it needs to be committed to the tree and used for our MSRV CI.)

If we can make a stable and reproducible way of generating a working Cargo.lock that would be ideal; we could even commit the generation script. That's what we do in the Arti project. But my attempts to do that for config-rs were not successful.

The concern about dependabot could perhaps be resolved by committing the Cargo.lock under a different name. In derive-adhoc I have a Cargo.lock.minimal. We would have to change the CI to cp Cargo.lock.minimal Cargo.lock in the MSRV tests.

polarathene commented 7 months ago

It is not so easy to produce a working Cargo.lock. I note that #495 does not produce a Cargo.lock capable of running the tests and examples.

I used the runes in #495 (comment), with that branch, and it didn't work for me.

How did you test? I had verified with:

cargo +1.56.1 check --tests
cargo +1.56.1 clippy --locked --offline --all-targets --all-features -- -D warnings

It's possible that something may have changed upstream since I last tested which may have introduced another resolver issue, I will verify it again soon.

If there is a different command that fails, please let me know and I'll sort that out. If I'm unable to reproduce the failure I would be very interested in knowing what is different with our environments (I used the official Rust Docker image, cloned the release branch and generated Cargo.lock with commands I shared on the updated Cargo.toml).


The concern about dependabot could perhaps be resolved by committing the Cargo.lock under a different name.

Yes, I have seen Cargo.lock.msrv is quite common.

I haven't had time yet to continue looking into that concern, I should be able today. I've not configured dependabot before but assume there's some explicit option to alleviate the implicit detection concern 👍


I do think that we should continue to run tests and examples with our MSRV in CI. I think we must do that with a fixed Cargo.lock, since otherwise our CI is likely to (once again) break due to changes in our dependency stack.

I am all good with that 👍

If we can make a stable and reproducible way of generating a working Cargo.lock that would be ideal; we could even commit the generation script.

Documenting the generation is probably sufficient for now 👍


How would you like to proceed?

I could commit Cargo.lock similar to what you've done here on my PR if you're ok with that approach? It may help with verifying the lock works on your end and the CI should then pass.

EDIT: Just noticed https://github.com/mehcode/config-rs/pull/496 reading that now 😅

ijackson commented 7 months ago

How would you like to proceed?

EDIT: Just noticed #496 reading that now 😅

Right. Thanks for the thumbs-up there.

I could commit Cargo.lock similar to what you've done here on my PR if you're ok with that approach? It may help with verifying the lock works on your end and the CI should then pass.

I suggest we merge #496 and then you consider rebasing #495 on top of it. If #495's Cargo.toml is compatible its CI will pass, then.

Otherwise maybe it will be somehow possible to make a Cargo.lock that does work with #495. AFAICT from reading #495, the process of making a working Cargo.lock may be easier with those dependency updates. Like I say in #496, if we can come up with a better way of generating a working Cargo.lock then I'm all in favour. I just think it should be committed. If we merge #496, committed as Cargo.lock.msrv.

polarathene commented 7 months ago

How did you test? I had verified with:

I didn't get a response to this, I have verified again.

Here are the exact steps:

# Reproduction environment:
docker run --rm -it --workdir /tmp/reproduction rust bash

# Prepare:
# Clone PR branch: https://github.com/mehcode/config-rs/pull/495
git clone https://github.com/mehcode/config-rs -b 'chore/0.13.x-min-versions' --depth 1 .
rustup toolchain install nightly 1.56.1
rustup component add --toolchain 1.56.1 clippy

# Create lockfile and pin deps with bad msrv-policy support:
cargo +nightly update -Z msrv-policy
cargo update --package memchr --precise 2.5.0
cargo update --package ordered-multimap --precise 0.4.0
cargo update --package tokio-util --precise 0.7.8

# Verify tests pass:
cargo +1.56.1 check --tests
cargo +1.56.1 clippy --locked --offline --all-targets --all-features -- -D warnings

I suggest we merge #496 and then you consider rebasing #495 on top of it. If #495's Cargo.toml is compatible its CI will pass, then.

I am fine with this 👍

EDIT: Done, #495 has been updated and awaiting review: https://github.com/mehcode/config-rs/pull/495#issuecomment-1798007587

ijackson commented 7 months ago

Closing since #492 was merged.

ijackson commented 7 months ago

I mean #496