diff --git a/crates/uv-cache/src/lib.rs b/crates/uv-cache/src/lib.rs index 4049d3f4b4f9b..202aab7cdbd9b 100644 --- a/crates/uv-cache/src/lib.rs +++ b/crates/uv-cache/src/lib.rs @@ -792,17 +792,19 @@ impl CacheBucket { fn to_str(self) -> &'static str { match self { // Note that when bumping this, you'll also need to bump it - // in crates/uv/tests/cache_prune.rs. + // in `crates/uv/tests/it/cache_prune.rs`. Self::SourceDistributions => "sdists-v7", Self::FlatIndex => "flat-index-v2", Self::Git => "git-v0", Self::Interpreter => "interpreter-v4", // Note that when bumping this, you'll also need to bump it - // in crates/uv/tests/cache_clean.rs. + // in `crates/uv/tests/it/cache_clean.rs`. Self::Simple => "simple-v15", // Note that when bumping this, you'll also need to bump it - // in crates/uv/tests/cache_prune.rs. - Self::Wheels => "wheels-v3", + // in `crates/uv/tests/it/cache_prune.rs`. + Self::Wheels => "wheels-v4", + // Note that when bumping this, you'll also need to bump it + // in `crates/uv-distribution/src/archive.rs`. Self::Archive => "archive-v0", Self::Builds => "builds-v0", Self::Environments => "environments-v1", diff --git a/crates/uv-distribution/src/archive.rs b/crates/uv-distribution/src/archive.rs index cde4b220497fb..591699de75c5d 100644 --- a/crates/uv-distribution/src/archive.rs +++ b/crates/uv-distribution/src/archive.rs @@ -2,6 +2,11 @@ use uv_cache::{ArchiveId, Cache}; use uv_distribution_types::Hashed; use uv_pypi_types::HashDigest; +/// The version of the archive bucket. +/// +/// Must be kept in-sync with the version in [`uv_cache::CacheBucket::to_str`]. +const ARCHIVE_VERSION: u8 = 0; + /// An archive (unzipped wheel) that exists in the local cache. #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub struct Archive { @@ -9,17 +14,23 @@ pub struct Archive { pub id: ArchiveId, /// The computed hashes of the archive. pub hashes: Vec, + /// The version of the archive bucket. + pub version: u8, } impl Archive { /// Create a new [`Archive`] with the given ID and hashes. pub(crate) fn new(id: ArchiveId, hashes: Vec) -> Self { - Self { id, hashes } + Self { + id, + hashes, + version: ARCHIVE_VERSION, + } } /// Returns `true` if the archive exists in the cache. pub(crate) fn exists(&self, cache: &Cache) -> bool { - cache.archive(&self.id).exists() + self.version == ARCHIVE_VERSION && cache.archive(&self.id).exists() } } diff --git a/crates/uv-distribution/src/index/cached_wheel.rs b/crates/uv-distribution/src/index/cached_wheel.rs index c8186ce6f3211..fbe4186825901 100644 --- a/crates/uv-distribution/src/index/cached_wheel.rs +++ b/crates/uv-distribution/src/index/cached_wheel.rs @@ -127,7 +127,14 @@ impl CachedWheel { // Read the pointer. let pointer = HttpArchivePointer::read_from(path).ok()??; let cache_info = pointer.to_cache_info(); - let Archive { id, hashes } = pointer.into_archive(); + let archive = pointer.into_archive(); + + // Ignore stale pointers. + if !archive.exists(cache) { + return None; + } + + let Archive { id, hashes, .. } = archive; let entry = cache.entry(CacheBucket::Archive, "", id); @@ -151,7 +158,14 @@ impl CachedWheel { // Read the pointer. let pointer = LocalArchivePointer::read_from(path).ok()??; let cache_info = pointer.to_cache_info(); - let Archive { id, hashes } = pointer.into_archive(); + let archive = pointer.into_archive(); + + // Ignore stale pointers. + if !archive.exists(cache) { + return None; + } + + let Archive { id, hashes, .. } = archive; // Convert to a cached wheel. let entry = cache.entry(CacheBucket::Archive, "", id); diff --git a/crates/uv/tests/it/cache_prune.rs b/crates/uv/tests/it/cache_prune.rs index 4cf8642eca523..9dc75294a108e 100644 --- a/crates/uv/tests/it/cache_prune.rs +++ b/crates/uv/tests/it/cache_prune.rs @@ -158,7 +158,7 @@ fn prune_stale_symlink() -> Result<()> { .success(); // Remove the wheels directory, causing the symlink to become stale. - let wheels = context.cache_dir.child("wheels-v3"); + let wheels = context.cache_dir.child("wheels-v4"); fs_err::remove_dir_all(wheels)?; let filters: Vec<_> = context