From ae6607d5dcd6b2c36f6808f1ed66811892436ecd Mon Sep 17 00:00:00 2001 From: liam Date: Sun, 26 Oct 2025 21:53:32 -0400 Subject: [PATCH] Deterministically order `--find-links` distributions (#16446) Made to address this comment: https://github.com/astral-sh/uv/pull/16103#discussion_r2437498249 This PR sorts the distributions collected by `FlatIndexClient::read_from_directory` (used for `--find-links`) so results are ordered deterministically by filename and index. --- Cargo.lock | 1 + crates/uv-client/Cargo.toml | 1 + crates/uv-client/src/flat_index.rs | 57 ++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 1744e2f7c..9875bf522 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5668,6 +5668,7 @@ dependencies = [ "serde", "serde_json", "sys-info", + "tempfile", "thiserror 2.0.17", "tl", "tokio", diff --git a/crates/uv-client/Cargo.toml b/crates/uv-client/Cargo.toml index 1a0128147..eb1967f22 100644 --- a/crates/uv-client/Cargo.toml +++ b/crates/uv-client/Cargo.toml @@ -74,3 +74,4 @@ hyper-util = { workspace = true } insta = { workspace = true } tokio = { workspace = true } wiremock = { workspace = true } +tempfile = { workspace = true } diff --git a/crates/uv-client/src/flat_index.rs b/crates/uv-client/src/flat_index.rs index 53ff94632..872947697 100644 --- a/crates/uv-client/src/flat_index.rs +++ b/crates/uv-client/src/flat_index.rs @@ -321,6 +321,63 @@ impl<'a> FlatIndexClient<'a> { index: flat_index.clone(), }); } + + dists.sort_by(|a, b| { + a.filename + .cmp(&b.filename) + .then_with(|| a.index.cmp(&b.index)) + }); + Ok(FlatIndexEntries::from_entries(dists)) } } + +#[cfg(test)] +mod tests { + use super::*; + use fs_err::File; + use std::io::Write; + use tempfile::tempdir; + + #[test] + fn read_from_directory_sorts_distributions() { + let dir = tempdir().unwrap(); + + let filenames = [ + "beta-2.0.0-py3-none-any.whl", + "alpha-1.0.0.tar.gz", + "alpha-1.0.0-py3-none-any.whl", + ]; + + for name in &filenames { + let mut file = File::create(dir.path().join(name)).unwrap(); + file.write_all(b"").unwrap(); + } + + let entries = FlatIndexClient::read_from_directory( + dir.path(), + &IndexUrl::parse(&dir.path().to_string_lossy(), None).unwrap(), + ) + .unwrap(); + + let actual = entries + .entries + .iter() + .map(|entry| entry.filename.to_string()) + .collect::>(); + + let mut expected = filenames + .iter() + .map(|name| DistFilename::try_from_normalized_filename(name).unwrap()) + .collect::>(); + + expected.sort(); + + let expected = expected + .into_iter() + .map(|filename| filename.to_string()) + .collect::>(); + + assert_eq!(actual, expected); + } +}