From 3ba4b0b98ff1e5bb79711fecc742e55f6baa2b1f Mon Sep 17 00:00:00 2001 From: Wesley Rosenblum Date: Fri, 31 Jul 2026 14:08:26 -0700 Subject: [PATCH 1/3] chore: Decouple MSRV from the dev toolchain `rust-toolchain` pinned the MSRV, and CI recovered the MSRV by parsing `rustup show active-toolchain`. That coupling forced every contributor's toolchain to the MSRV, which is now old enough that current tooling refuses to work against it: rust-analyzer requires 1.94.0 or newer and misresolves std macros on older sysroots. Point `rust-toolchain` at stable and derive the MSRV in CI from the `rust-version` field that every crate already declares. The MSRV itself is unchanged at 1.92, and the test matrix still builds and tests against it explicitly. MSRV enforcement continues locally through clippy's `incompatible_msrv` lint, which is warn by default, denied in CI via `-D warnings`, and already configured by the `msrv` key in the `.clippy.toml` files. That covers use of library APIs stabilized after the MSRV, but not newer language features, so the MSRV matrix job remains the authority. Because nothing pins the MSRV in a single place anymore, the definitions step now fails if the crates stop agreeing on a `rust-version` rather than silently testing whichever version the parsed crate happened to declare. The nightly pin in tools/xdp/ebpf is left alone; it is a build requirement for the eBPF programs rather than an MSRV. --- .github/workflows/ci.yml | 13 ++++++++++++- dc/wireshark/rust-toolchain | 10 +++++++++- rust-toolchain | 10 +++++++++- tools/xdp/rust-toolchain | 10 +++++++++- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4176ce3ff1..34ed66befb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,11 +68,22 @@ jobs: id: definitions run: | rustup show active-toolchain - export MSRV=$(rustup show active-toolchain | awk -F'-' '{print $1}') + # The MSRV is declared by the `rust-version` field of each crate. It is + # deliberately not derived from `rust-toolchain`, which tracks a recent + # stable release so that tooling such as rust-analyzer keeps working. + export MSRV=$(grep -m1 '^rust-version' quic/s2n-quic/Cargo.toml | sed -E 's/^rust-version *= *"(.+)"/\1/') if [ -z "$MSRV" ]; then echo "Error: MSRV did not parse correctly" exit 1 fi + # Nothing forces the crates to agree on an MSRV now that it is no + # longer pinned in one place, so fail loudly if they drift apart + # rather than silently testing the wrong version. + if [ $(git ls-files '*Cargo.toml' | xargs grep -h '^rust-version' | sort -u | wc -l) -ne 1 ]; then + echo "Error: crates do not agree on a single rust-version" + git ls-files '*Cargo.toml' | xargs grep -H '^rust-version' | sort -u + exit 1 + fi echo "msrv=$MSRV" echo "msrv=$MSRV" >> $GITHUB_OUTPUT export RAW_VERSIONS="stable beta $MSRV" diff --git a/dc/wireshark/rust-toolchain b/dc/wireshark/rust-toolchain index 32dc170f29..32877284e9 100644 --- a/dc/wireshark/rust-toolchain +++ b/dc/wireshark/rust-toolchain @@ -1,3 +1,11 @@ [toolchain] -channel = "1.92.0" +# The toolchain used for local development and for the CI jobs that don't pin a +# version themselves. This intentionally tracks recent stable rather than the +# MSRV; pinning it to the MSRV left contributors on a toolchain too old for +# current tooling, notably rust-analyzer. +# +# The MSRV is declared by the `rust-version` field of each crate and is enforced +# by the CI test matrix, which builds and tests against it explicitly, and by +# clippy's `incompatible_msrv` lint. +channel = "stable" components = [ "rustc", "clippy", "rustfmt" ] diff --git a/rust-toolchain b/rust-toolchain index 32dc170f29..32877284e9 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1,3 +1,11 @@ [toolchain] -channel = "1.92.0" +# The toolchain used for local development and for the CI jobs that don't pin a +# version themselves. This intentionally tracks recent stable rather than the +# MSRV; pinning it to the MSRV left contributors on a toolchain too old for +# current tooling, notably rust-analyzer. +# +# The MSRV is declared by the `rust-version` field of each crate and is enforced +# by the CI test matrix, which builds and tests against it explicitly, and by +# clippy's `incompatible_msrv` lint. +channel = "stable" components = [ "rustc", "clippy", "rustfmt" ] diff --git a/tools/xdp/rust-toolchain b/tools/xdp/rust-toolchain index 32dc170f29..32877284e9 100644 --- a/tools/xdp/rust-toolchain +++ b/tools/xdp/rust-toolchain @@ -1,3 +1,11 @@ [toolchain] -channel = "1.92.0" +# The toolchain used for local development and for the CI jobs that don't pin a +# version themselves. This intentionally tracks recent stable rather than the +# MSRV; pinning it to the MSRV left contributors on a toolchain too old for +# current tooling, notably rust-analyzer. +# +# The MSRV is declared by the `rust-version` field of each crate and is enforced +# by the CI test matrix, which builds and tests against it explicitly, and by +# clippy's `incompatible_msrv` lint. +channel = "stable" components = [ "rustc", "clippy", "rustfmt" ] From 95f24dead55ad38d3fc526e2cdfd97cb8e2cc78b Mon Sep 17 00:00:00 2001 From: Wesley Rosenblum Date: Fri, 31 Jul 2026 15:49:02 -0700 Subject: [PATCH 2/3] fix: Allow a crate to require more than the MSRV The previous check required every crate to declare an identical `rust-version`, which would reject a pattern this repository has relied on before: the xdp and wireshark workspaces both carried a higher minimum than the repository MSRV while their dependencies outpaced it, and they are the crates most likely to need that again. Check the actual invariant instead. The MSRV is the floor s2n-quic promises, so no crate may declare lower than it, while a crate requiring a higher version is fine. --- .github/workflows/ci.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 34ed66befb..d91b4131a6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -76,11 +76,15 @@ jobs: echo "Error: MSRV did not parse correctly" exit 1 fi - # Nothing forces the crates to agree on an MSRV now that it is no - # longer pinned in one place, so fail loudly if they drift apart - # rather than silently testing the wrong version. - if [ $(git ls-files '*Cargo.toml' | xargs grep -h '^rust-version' | sort -u | wc -l) -ne 1 ]; then - echo "Error: crates do not agree on a single rust-version" + # The MSRV above is the floor s2n-quic promises to its users, so no + # crate may declare a lower `rust-version` than it. A crate requiring a + # higher one is allowed: the xdp and wireshark workspaces have needed + # that in the past when their dependencies outpaced the repository + # MSRV. Nothing pins these together anymore, so check it explicitly + # rather than silently testing a version some crate does not support. + export LOWEST=$(git ls-files '*Cargo.toml' | xargs grep -h '^rust-version' | sed -E 's/^rust-version *= *"(.+)"/\1/' | sort -V | head -1) + if [ "$LOWEST" != "$MSRV" ]; then + echo "Error: a crate declares rust-version $LOWEST, below the $MSRV MSRV of s2n-quic" git ls-files '*Cargo.toml' | xargs grep -H '^rust-version' | sort -u exit 1 fi From c037583481fe160bc666948780aafbf70b565dc3 Mon Sep 17 00:00:00 2001 From: Wesley Rosenblum Date: Fri, 31 Jul 2026 16:05:27 -0700 Subject: [PATCH 3/3] chore: Remove the rust-toolchain files With the MSRV no longer expressed by these files, they served no purpose. No CI job depended on them: every job that compiles Rust installs and overrides its own toolchain, and the ones that don't (env, compliance, copyright, kani, ci-status-report) never invoke rustc. Two places already worked around the files instead of relying on them: the cargo-deny job deleted the root file before running, and .dockerignore excludes it from the build context. Drop the now-failing `rm rust-toolchain` step from the cargo-deny job, since `rm` errors on a missing file, and document the development toolchain in CONTRIBUTING to replace the components the files installed. tools/xdp/ebpf/rust-toolchain.toml stays. That pin is a build requirement rather than an MSRV statement: the bpf targets are tier 3 with no prebuilt `rust-std`, so `core` has to be compiled from source with `-Zbuild-std`, which is still nightly only. --- .github/workflows/ci.yml | 5 +++-- .github/workflows/dependencies.yml | 3 --- CONTRIBUTING.md | 12 ++++++++++++ dc/wireshark/rust-toolchain | 11 ----------- rust-toolchain | 11 ----------- tools/xdp/rust-toolchain | 11 ----------- 6 files changed, 15 insertions(+), 38 deletions(-) delete mode 100644 dc/wireshark/rust-toolchain delete mode 100644 rust-toolchain delete mode 100644 tools/xdp/rust-toolchain diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d91b4131a6..791c05f272 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,8 +69,9 @@ jobs: run: | rustup show active-toolchain # The MSRV is declared by the `rust-version` field of each crate. It is - # deliberately not derived from `rust-toolchain`, which tracks a recent - # stable release so that tooling such as rust-analyzer keeps working. + # deliberately not tied to the toolchain anyone happens to be building + # with; jobs that need a specific toolchain install and override it + # themselves. export MSRV=$(grep -m1 '^rust-version' quic/s2n-quic/Cargo.toml | sed -E 's/^rust-version *= *"(.+)"/\1/') if [ -z "$MSRV" ]; then echo "Error: MSRV did not parse correctly" diff --git a/.github/workflows/dependencies.yml b/.github/workflows/dependencies.yml index 889fb4c049..04dbef3373 100644 --- a/.github/workflows/dependencies.yml +++ b/.github/workflows/dependencies.yml @@ -58,9 +58,6 @@ jobs: with: submodules: true - - name: "Remove rust-toolchain" - run: rm rust-toolchain - - uses: EmbarkStudios/cargo-deny-action@v2 with: arguments: --all-features --config .github/config/cargo-deny.toml diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 165afee2e2..71d5f5390f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,6 +40,18 @@ GitHub provides additional document on [forking a repository](https://help.githu [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). +## Development toolchain + +`s2n-quic` is developed against a recent stable Rust toolchain, along with the `clippy` and `rustfmt` components: + +``` +rustup toolchain install stable --component clippy,rustfmt +``` + +This is separate from the minimum supported Rust version. The MSRV is declared by the `rust-version` field of each crate and is verified in CI, so there is no need to develop against it directly. Clippy is configured with the MSRV through the `msrv` key in `.clippy.toml`, which reports uses of standard library APIs that are newer than the MSRV allows. + +Building the eBPF programs under `tools/xdp/ebpf` requires the nightly toolchain pinned by `tools/xdp/ebpf/rust-toolchain.toml`, which `cargo xtask` selects automatically. + ## Finding contributions to work on Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. diff --git a/dc/wireshark/rust-toolchain b/dc/wireshark/rust-toolchain deleted file mode 100644 index 32877284e9..0000000000 --- a/dc/wireshark/rust-toolchain +++ /dev/null @@ -1,11 +0,0 @@ -[toolchain] -# The toolchain used for local development and for the CI jobs that don't pin a -# version themselves. This intentionally tracks recent stable rather than the -# MSRV; pinning it to the MSRV left contributors on a toolchain too old for -# current tooling, notably rust-analyzer. -# -# The MSRV is declared by the `rust-version` field of each crate and is enforced -# by the CI test matrix, which builds and tests against it explicitly, and by -# clippy's `incompatible_msrv` lint. -channel = "stable" -components = [ "rustc", "clippy", "rustfmt" ] diff --git a/rust-toolchain b/rust-toolchain deleted file mode 100644 index 32877284e9..0000000000 --- a/rust-toolchain +++ /dev/null @@ -1,11 +0,0 @@ -[toolchain] -# The toolchain used for local development and for the CI jobs that don't pin a -# version themselves. This intentionally tracks recent stable rather than the -# MSRV; pinning it to the MSRV left contributors on a toolchain too old for -# current tooling, notably rust-analyzer. -# -# The MSRV is declared by the `rust-version` field of each crate and is enforced -# by the CI test matrix, which builds and tests against it explicitly, and by -# clippy's `incompatible_msrv` lint. -channel = "stable" -components = [ "rustc", "clippy", "rustfmt" ] diff --git a/tools/xdp/rust-toolchain b/tools/xdp/rust-toolchain deleted file mode 100644 index 32877284e9..0000000000 --- a/tools/xdp/rust-toolchain +++ /dev/null @@ -1,11 +0,0 @@ -[toolchain] -# The toolchain used for local development and for the CI jobs that don't pin a -# version themselves. This intentionally tracks recent stable rather than the -# MSRV; pinning it to the MSRV left contributors on a toolchain too old for -# current tooling, notably rust-analyzer. -# -# The MSRV is declared by the `rust-version` field of each crate and is enforced -# by the CI test matrix, which builds and tests against it explicitly, and by -# clippy's `incompatible_msrv` lint. -channel = "stable" -components = [ "rustc", "clippy", "rustfmt" ]