diff --git a/Cargo.lock b/Cargo.lock index aaa88132ef450..0b10ae00332b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2674,6 +2674,15 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi", +] + [[package]] name = "nu-ansi-term" version = "0.50.1" @@ -2781,12 +2790,31 @@ dependencies = [ "objc2-encode", ] +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.9.4", +] + [[package]] name = "objc2-encode" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" +[[package]] +name = "objc2-io-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +dependencies = [ + "libc", + "objc2-core-foundation", +] + [[package]] name = "object" version = "0.36.7" @@ -4433,13 +4461,17 @@ dependencies = [ ] [[package]] -name = "sys-info" -version = "0.9.1" +name = "sysinfo" +version = "0.37.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b3a0d0aba8bf96a0e1ddfdc352fc53b3df7f39318c71854910c3c4b024ae52c" +checksum = "16607d5caffd1c07ce073528f9ed972d88db15dd44023fa57142963be3feb11f" dependencies = [ - "cc", "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "windows 0.61.3", ] [[package]] @@ -5686,7 +5718,7 @@ dependencies = [ "rustls", "serde", "serde_json", - "sys-info", + "sysinfo", "tempfile", "thiserror 2.0.17", "tokio", @@ -6397,6 +6429,7 @@ dependencies = [ "dunce", "fs-err", "futures", + "heck", "indexmap", "indoc", "insta", @@ -6411,7 +6444,7 @@ dependencies = [ "schemars", "serde", "serde_json", - "sys-info", + "sysinfo", "target-lexicon", "temp-env", "tempfile", diff --git a/Cargo.toml b/Cargo.toml index c523ecec02574..8404e6e2a5c77 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -171,7 +171,7 @@ sha2 = { version = "0.10.8" } smallvec = { version = "1.13.2" } spdx = { version = "0.12.0" } syn = { version = "2.0.77" } -sys-info = { version = "0.9.1" } +sysinfo = { version = "0.37.2" } tar = { version = "0.4.43" } target-lexicon = { version = "0.13.0" } tempfile = { version = "3.14.0" } diff --git a/crates/uv-client/Cargo.toml b/crates/uv-client/Cargo.toml index ffeb09151536e..e0742ba969e79 100644 --- a/crates/uv-client/Cargo.toml +++ b/crates/uv-client/Cargo.toml @@ -59,7 +59,7 @@ rmp-serde = { workspace = true } rustc-hash = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } -sys-info = { workspace = true } +sysinfo = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true } tokio-util = { workspace = true } diff --git a/crates/uv-client/src/linehaul.rs b/crates/uv-client/src/linehaul.rs index 4eea4f7550502..79b17305b69a4 100644 --- a/crates/uv-client/src/linehaul.rs +++ b/crates/uv-client/src/linehaul.rs @@ -1,4 +1,5 @@ use std::env; +use std::io::Read; use serde::{Deserialize, Serialize}; use tracing::instrument; @@ -89,13 +90,13 @@ impl LineHaul { // Build Distro as Linehaul expects. let distro: Option = if cfg!(target_os = "linux") { // Gather distribution info from /etc/os-release. - sys_info::linux_os_release().ok().map(|info| Distro { + Some(Distro { // e.g., Jammy, Focal, etc. - id: info.version_codename, + id: Self::get_version_codename().ok().flatten(), // e.g., Ubuntu, Fedora, etc. - name: info.name, + name: sysinfo::System::name(), // e.g., 22.04, etc. - version: info.version_id, + version: sysinfo::System::os_version(), // e.g., glibc 2.38, musl 1.2 libc, }) @@ -144,4 +145,27 @@ impl LineHaul { ci: looks_like_ci, } } + + // https://docs.rs/sys-info/latest/src/sys_info/lib.rs.html#473-515 + fn get_version_codename() -> Result, std::io::Error> { + if !cfg!(target_os = "linux") { + return Ok(None); + } + + let mut s = String::new(); + fs_err::File::open("/etc/os-release")?.read_to_string(&mut s)?; + + for line in s.lines() { + let line = line.trim(); + if line.starts_with("VERSION_CODENAME=") { + let value = line + .strip_prefix("VERSION_CODENAME=") + .unwrap() + .trim_matches('"'); + return Ok(Some(value.to_string())); + } + } + + Ok(None) + } } diff --git a/crates/uv-client/tests/it/user_agent_version.rs b/crates/uv-client/tests/it/user_agent_version.rs index 90aecef380aaa..4c3d0751cacc7 100644 --- a/crates/uv-client/tests/it/user_agent_version.rs +++ b/crates/uv-client/tests/it/user_agent_version.rs @@ -2,6 +2,9 @@ use std::str::FromStr; use anyhow::Result; use insta::{assert_json_snapshot, assert_snapshot, with_settings}; +use std::io::Read; +use std::str::FromStr; +use tokio::net::TcpListener; use url::Url; use uv_cache::Cache; @@ -12,6 +15,28 @@ use uv_platform_tags::{Arch, Os, Platform}; use uv_redacted::DisplaySafeUrl; use uv_version::version; +// https://docs.rs/sys-info/latest/src/sys_info/lib.rs.html#473-515 +fn get_version_codename() -> Result, std::io::Error> { + if !cfg!(target_os = "linux") { + return Ok(None); + } + + let mut s = String::new(); + fs_err::File::open("/etc/os-release")?.read_to_string(&mut s)?; + + for line in s.lines() { + let line = line.trim(); + if line.starts_with("VERSION_CODENAME=") { + let value = line + .strip_prefix("VERSION_CODENAME=") + .unwrap() + .trim_matches('"'); + return Ok(Some(value.to_string())); + } + } + + Ok(None) +} use crate::http_util::start_http_user_agent_server; #[tokio::test] @@ -201,12 +226,13 @@ async fn test_user_agent_has_linehaul() -> Result<()> { let distro_info = linehaul .distro .expect("got no distro, but expected one in linehaul"); - // Gather distribution info from /etc/os-release. - let release_info = sys_info::linux_os_release() - .expect("got no os release info, but expected one in linux"); - assert_eq!(distro_info.id, release_info.version_codename); - assert_eq!(distro_info.name, release_info.name); - assert_eq!(distro_info.version, release_info.version_id); + // Gather distribution info using sysinfo and custom parsing + let codename = get_version_codename().ok().flatten(); + let name = sysinfo::System::name(); + let version = sysinfo::System::os_version(); + assert_eq!(distro_info.id, codename); + assert_eq!(distro_info.name, name); + assert_eq!(distro_info.version, version); } else if cfg!(target_os = "macos") { // We mock the macOS distro assert_json_snapshot!(&linehaul.distro, @r###" diff --git a/crates/uv-python/Cargo.toml b/crates/uv-python/Cargo.toml index 379328edf5d9f..a6d4889fa6ba4 100644 --- a/crates/uv-python/Cargo.toml +++ b/crates/uv-python/Cargo.toml @@ -57,7 +57,7 @@ same-file = { workspace = true } schemars = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } -sys-info = { workspace = true } +sysinfo = { workspace = true } target-lexicon = { workspace = true } tempfile = { workspace = true } thiserror = { workspace = true } @@ -66,6 +66,7 @@ tokio-util = { workspace = true, features = ["compat"] } tracing = { workspace = true } url = { workspace = true } which = { workspace = true } +heck = "0.5.0" [target.'cfg(target_os = "windows")'.dependencies] windows-registry = { workspace = true } diff --git a/crates/uv-python/src/interpreter.rs b/crates/uv-python/src/interpreter.rs index 6bde8ddadbc0d..1f4c3cdcf7ca4 100644 --- a/crates/uv-python/src/interpreter.rs +++ b/crates/uv-python/src/interpreter.rs @@ -9,9 +9,11 @@ use std::{env, io}; use configparser::ini::Ini; use fs_err as fs; +use heck::ToTitleCase; use owo_colors::OwoColorize; use same_file::is_same_file; use serde::{Deserialize, Serialize}; +use sysinfo::System; use thiserror::Error; use tracing::{debug, trace, warn}; @@ -1095,10 +1097,11 @@ impl InterpreterInfo { CacheBucket::Interpreter, // Shard interpreter metadata by host architecture, operating system, and version, to // invalidate the cache (e.g.) on OS upgrades. + // std::env::consts::OS (e.g., "linux", "macos", "windows") cache_digest(&( ARCH, - sys_info::os_type().unwrap_or_default(), - sys_info::os_release().unwrap_or_default(), + std::env::consts::OS.to_title_case(), + System::kernel_version().unwrap_or_default(), )), // We use the absolute path for the cache entry to avoid cache collisions for relative // paths. But we don't want to query the executable with symbolic links resolved because