Remove dangling archives in `uv cache clean ${package}` (#6915)

## Summary

Closes https://github.com/astral-sh/uv/issues/6909.
This commit is contained in:
Charlie Marsh 2024-09-01 13:27:13 -04:00 committed by GitHub
parent a5f1e1c765
commit 049c73d09e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 99 additions and 9 deletions

View File

@ -322,10 +322,62 @@ impl Cache {
///
/// Returns the number of entries removed from the cache.
pub fn remove(&self, name: &PackageName) -> Result<Removal, io::Error> {
// Collect the set of referenced archives.
let before = {
let mut references = FxHashSet::default();
for bucket in CacheBucket::iter() {
let bucket = self.bucket(bucket);
if bucket.is_dir() {
for entry in walkdir::WalkDir::new(bucket) {
let entry = entry?;
if entry.file_type().is_symlink() {
if let Ok(target) = fs_err::canonicalize(entry.path()) {
references.insert(target);
}
}
}
}
}
references
};
// Remove any entries for the package from the cache.
let mut summary = Removal::default();
for bucket in CacheBucket::iter() {
summary += bucket.remove(self, name)?;
}
// Collect the set of referenced archives after the removal.
let after = {
let mut references = FxHashSet::default();
for bucket in CacheBucket::iter() {
let bucket = self.bucket(bucket);
if bucket.is_dir() {
for entry in walkdir::WalkDir::new(bucket) {
let entry = entry?;
if entry.file_type().is_symlink() {
if let Ok(target) = fs_err::canonicalize(entry.path()) {
references.insert(target);
}
}
}
}
}
references
};
if before != after {
// Remove any archives that are no longer referenced.
for entry in fs::read_dir(self.bucket(CacheBucket::Archive))? {
let entry = entry?;
let path = fs_err::canonicalize(entry.path())?;
if !after.contains(&path) && before.contains(&path) {
debug!("Removing dangling cache entry: {}", path.display());
summary += rm_rf(path)?;
}
}
}
Ok(summary)
}

View File

@ -65,14 +65,27 @@ fn clean_package_pypi() -> Result<()> {
"Expected the `.rkyv` file to exist for `iniconfig`"
);
uv_snapshot!(context.filters(), context.clean().arg("--verbose").arg("iniconfig"), @r###"
let filters: Vec<_> = context
.filters()
.into_iter()
.chain([
// The cache entry does not have a stable key, so we filter it out
(
r"\[CACHE_DIR\](\\|\/)(.+)(\\|\/).*",
"[CACHE_DIR]/$2/[ENTRY]",
),
])
.collect();
uv_snapshot!(&filters, context.clean().arg("--verbose").arg("iniconfig"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
DEBUG uv [VERSION] ([COMMIT] DATE)
Removed 4 files for iniconfig ([SIZE])
DEBUG Removing dangling cache entry: [CACHE_DIR]/archive-v0/[ENTRY]
Removed 13 files for iniconfig ([SIZE])
"###);
// Assert that the `.rkyv` file is removed for `iniconfig`.
@ -81,6 +94,18 @@ fn clean_package_pypi() -> Result<()> {
"Expected the `.rkyv` file to be removed for `iniconfig`"
);
// Running `uv cache prune` should have no effect.
uv_snapshot!(&filters, context.prune().arg("--verbose"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
DEBUG uv [VERSION] ([COMMIT] DATE)
Pruning cache at: [CACHE_DIR]/
No unused entries found
"###);
Ok(())
}
@ -113,14 +138,27 @@ fn clean_package_index() -> Result<()> {
"Expected the `.rkyv` file to exist for `iniconfig`"
);
uv_snapshot!(context.filters(), context.clean().arg("--verbose").arg("iniconfig"), @r###"
let filters: Vec<_> = context
.filters()
.into_iter()
.chain([
// The cache entry does not have a stable key, so we filter it out
(
r"\[CACHE_DIR\](\\|\/)(.+)(\\|\/).*",
"[CACHE_DIR]/$2/[ENTRY]",
),
])
.collect();
uv_snapshot!(&filters, context.clean().arg("--verbose").arg("iniconfig"), @r###"
success: true
exit_code: 0
----- stdout -----
----- stderr -----
DEBUG uv [VERSION] ([COMMIT] DATE)
Removed 4 files for iniconfig ([SIZE])
DEBUG Removing dangling cache entry: [CACHE_DIR]/archive-v0/[ENTRY]
Removed 13 files for iniconfig ([SIZE])
"###);
// Assert that the `.rkyv` file is removed for `iniconfig`.

View File

@ -1396,7 +1396,7 @@ fn install_url_source_dist_cached() -> Result<()> {
----- stdout -----
----- stderr -----
Removed 13 files for source-distribution ([SIZE])
Removed 19 files for source-distribution ([SIZE])
"###
);
@ -1591,7 +1591,7 @@ fn install_registry_source_dist_cached() -> Result<()> {
----- stdout -----
----- stderr -----
Removed 14 files for source-distribution ([SIZE])
Removed 20 files for source-distribution ([SIZE])
"###
);
@ -1687,7 +1687,7 @@ fn install_path_source_dist_cached() -> Result<()> {
----- stdout -----
----- stderr -----
Removed 13 files for source-distribution ([SIZE])
Removed 19 files for source-distribution ([SIZE])
"###
);
@ -1788,7 +1788,7 @@ fn install_path_built_dist_cached() -> Result<()> {
----- stdout -----
----- stderr -----
Removed 2 files for tomli ([SIZE])
Removed 11 files for tomli ([SIZE])
"###
);
@ -1876,7 +1876,7 @@ fn install_url_built_dist_cached() -> Result<()> {
----- stdout -----
----- stderr -----
Removed 3 files for tqdm ([SIZE])
Removed 43 files for tqdm ([SIZE])
"###
);