diff --git a/crates/uv-extract/src/stream.rs b/crates/uv-extract/src/stream.rs index fdb834ed7d94..b7b6b21f9b05 100644 --- a/crates/uv-extract/src/stream.rs +++ b/crates/uv-extract/src/stream.rs @@ -92,7 +92,7 @@ pub async fn untar( ) -> Result<(), Error> { let decompressed_bytes = async_compression::tokio::bufread::GzipDecoder::new(reader); let mut archive = tokio_tar::ArchiveBuilder::new(decompressed_bytes) - .set_preserve_permissions(false) + .set_preserve_mtime(false) .build(); Ok(archive.unpack(target.as_ref()).await?) } diff --git a/crates/uv/tests/pip_sync.rs b/crates/uv/tests/pip_sync.rs index 53d980d40f0d..8c752c9518fd 100644 --- a/crates/uv/tests/pip_sync.rs +++ b/crates/uv/tests/pip_sync.rs @@ -2709,3 +2709,28 @@ fn conflicting_requirement() -> Result<()> { Ok(()) } + +/// Don't preserve the mtime from .tar.gz files, it may be the unix epoch (1970-01-01), while Python's zip +/// implementation can't handle files with an mtime older than 1980. +/// See also . +#[test] +fn tar_dont_preserve_mtime() -> Result<()> { + let context = TestContext::new("3.12"); + let requirements_txt = context.temp_dir.child("requirements.txt"); + requirements_txt.write_str("tomli @ https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz")?; + + uv_snapshot!(command(&context) + .arg("requirements.txt"), @r###" + success: true + exit_code: 0 + ----- stdout ----- + + ----- stderr ----- + Resolved 1 package in [TIME] + Downloaded 1 package in [TIME] + Installed 1 package in [TIME] + + tomli==2.0.1 (from https://files.pythonhosted.org/packages/c0/3f/d7af728f075fb08564c5949a9c95e44352e23dee646869fa104a3b2060a3/tomli-2.0.1.tar.gz) + "###); + + Ok(()) +}