ci: Make Windows tests ~27% faster by putting temp folder in dev drive (#6680)

## Summary

This PR makes `cargo test | windows` faster in CI.

### Before

![Windows tests take
5m44s](https://github.com/user-attachments/assets/8dd9c619-9b7b-4ebd-a027-56e7967b6d34)

### After

![Windows tests take
5m12s](https://github.com/user-attachments/assets/7702fdba-3034-4db8-b211-85207a1feffa)

## Also

This PR disables the `brotli` feature of `async-compression` since it's
not strictly needed, but this has little to do with the improvements
(it's still less code to build).

This PR introduces additional code in uv tool uninstall to ignore errors
(that only seem to happen on ReFS, ie. on Dev Drives) akin to "the thing
we're trying to delete cannot be deleted because it's already being
deleted".

If `raw_os_error` was stable we could do u32 matching instead of that
`.to_string().contains()` abomination.
This commit is contained in:
Amos Wenger 2024-08-27 22:25:05 +02:00 committed by GitHub
parent ffb0c304d1
commit 2c5cc62106
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 54 additions and 41 deletions

View file

@ -285,6 +285,7 @@ jobs:
env:
CARGO_HOME: ${{ env.DEV_DRIVE }}/.cargo
RUSTUP_HOME: ${{ env.DEV_DRIVE }}/.rustup
UV_INTERNAL__TEST_DIR: ${{ env.DEV_DRIVE }}/tmp-uv
run: |
cargo nextest run --no-default-features --features python,pypi --workspace --status-level skip --failure-output immediate-final --no-fail-fast -j 20 --final-status-level slow

37
Cargo.lock generated
View file

@ -43,21 +43,6 @@ dependencies = [
"memchr",
]
[[package]]
name = "alloc-no-stdlib"
version = "2.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3"
[[package]]
name = "alloc-stdlib"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece"
dependencies = [
"alloc-no-stdlib",
]
[[package]]
name = "android-tzdata"
version = "0.1.1"
@ -211,7 +196,6 @@ version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fec134f64e2bc57411226dfc4e52dec859ddfc7e711fc5e07b612584f000e4aa"
dependencies = [
"brotli",
"bzip2",
"flate2",
"futures-core",
@ -461,27 +445,6 @@ version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "510a90332002c1af3317ef6b712f0dab697f30bbe809b86965eac2923c0bca8e"
[[package]]
name = "brotli"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
"brotli-decompressor",
]
[[package]]
name = "brotli-decompressor"
version = "4.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362"
dependencies = [
"alloc-no-stdlib",
"alloc-stdlib",
]
[[package]]
name = "bstr"
version = "1.10.0"

View file

@ -119,7 +119,7 @@ quote = { version = "1.0.36" }
rayon = { version = "1.8.0" }
reflink-copy = { version = "0.1.15" }
regex = { version = "1.10.2" }
reqwest = { version = "0.12.3", default-features = false, features = ["json", "gzip", "brotli", "stream", "rustls-tls", "rustls-tls-native-roots"] }
reqwest = { version = "0.12.3", default-features = false, features = ["json", "gzip", "stream", "rustls-tls", "rustls-tls-native-roots"] }
reqwest-middleware = { git = "https://github.com/astral-sh/reqwest-middleware", rev = "5e3eaf254b5bd481c75d2710eed055f95b756913" }
reqwest-retry = { git = "https://github.com/astral-sh/reqwest-middleware", rev = "5e3eaf254b5bd481c75d2710eed055f95b756913" }
rkyv = { version = "0.7.43", features = ["strict", "validation"] }

View file

@ -135,7 +135,14 @@ impl Cache {
/// Create a temporary cache directory.
pub fn temp() -> Result<Self, io::Error> {
let temp_dir = tempfile::tempdir()?;
let temp_dir =
if let Ok(test_dir) = std::env::var("UV_INTERNAL__TEST_DIR").map(PathBuf::from) {
let uv_cache_dir = test_dir.join("uv-cache");
let _ = fs_err::create_dir_all(&uv_cache_dir);
tempfile::tempdir_in(uv_cache_dir)?
} else {
tempfile::tempdir()?
};
Ok(Self {
root: temp_dir.path().to_path_buf(),
refresh: Refresh::None(Timestamp::now()),

View file

@ -32,10 +32,14 @@ pub(crate) async fn uninstall(name: Option<PackageName>, printer: Printer) -> Re
// Clean up any empty directories.
if uv_fs::directories(installed_tools.root()).all(|path| uv_fs::is_temporary(&path)) {
fs_err::tokio::remove_dir_all(&installed_tools.root()).await?;
fs_err::tokio::remove_dir_all(&installed_tools.root())
.await
.ignore_currently_being_deleted()?;
if let Some(top_level) = installed_tools.root().parent() {
if uv_fs::directories(top_level).all(|path| uv_fs::is_temporary(&path)) {
fs_err::tokio::remove_dir_all(top_level).await?;
fs_err::tokio::remove_dir_all(top_level)
.await
.ignore_currently_being_deleted()?;
}
}
}
@ -43,6 +47,44 @@ pub(crate) async fn uninstall(name: Option<PackageName>, printer: Printer) -> Re
Ok(ExitStatus::Success)
}
trait IoErrorExt: std::error::Error + 'static {
#[inline]
fn is_in_process_of_being_deleted(&self) -> bool {
if cfg!(target_os = "windows") {
use std::error::Error;
let mut e: &dyn Error = &self;
loop {
if e.to_string().contains("The file cannot be opened because it is in the process of being deleted. (os error 303)") {
return true;
}
e = match e.source() {
Some(e) => e,
None => break,
}
}
}
false
}
}
impl IoErrorExt for std::io::Error {}
/// An extension trait to suppress "cannot open file because it's currently being deleted"
trait IgnoreCurrentlyBeingDeleted {
fn ignore_currently_being_deleted(self) -> Self;
}
impl IgnoreCurrentlyBeingDeleted for Result<(), std::io::Error> {
fn ignore_currently_being_deleted(self) -> Self {
match self {
Ok(()) => Ok(()),
Err(err) if err.is_in_process_of_being_deleted() => Ok(()),
Err(err) => Err(err),
}
}
}
/// Perform the uninstallation.
async fn do_uninstall(
installed_tools: &InstalledTools,