Add CACHEDIR and .gitignore tags to cache directories (#526)

## Summary

Even if this will typically be in the user's application folder (rather
than a local directory), it's still a good practice.

Closes https://github.com/astral-sh/puffin/issues/280.
This commit is contained in:
Charlie Marsh 2023-12-01 19:37:51 -05:00 committed by GitHub
parent 9806901a16
commit ee2fca3a48
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 49 additions and 18 deletions

View file

@ -16,6 +16,7 @@ workspace = true
[dependencies]
pypi-types = { path = "../pypi-types" }
cachedir = { workspace = true }
clap = { workspace = true, features = ["derive"], optional = true }
directories = { workspace = true }
fs-err = { workspace = true }

View file

@ -5,7 +5,6 @@ use std::path::PathBuf;
use clap::Parser;
use directories::ProjectDirs;
use fs_err as fs;
use crate::Cache;
@ -31,18 +30,14 @@ impl TryFrom<CacheArgs> for Cache {
///
/// Returns an absolute cache dir.
fn try_from(value: CacheArgs) -> Result<Self, Self::Error> {
let project_dirs = ProjectDirs::from("", "", "puffin");
if value.no_cache {
Ok(Cache::temp()?)
Cache::temp()
} else if let Some(cache_dir) = value.cache_dir {
fs::create_dir_all(&cache_dir)?;
Ok(Cache::from_path(fs::canonicalize(cache_dir)?))
} else if let Some(project_dirs) = project_dirs {
Ok(Cache::from_path(project_dirs.cache_dir().to_path_buf()))
Cache::from_path(cache_dir)
} else if let Some(project_dirs) = ProjectDirs::from("", "", "puffin") {
Cache::from_path(project_dirs.cache_dir())
} else {
let cache_dir = ".puffin_cache";
fs::create_dir_all(cache_dir)?;
Ok(Cache::from_path(fs::canonicalize(cache_dir)?))
Cache::from_path(".puffin_cache")
}
}
}

View file

@ -1,8 +1,10 @@
use std::fmt::{Display, Formatter};
use std::io;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use fs_err as fs;
use tempfile::{tempdir, TempDir};
pub use canonical_url::{CanonicalUrl, RepositoryUrl};
@ -47,22 +49,23 @@ pub struct Cache {
impl Cache {
/// A persistent cache directory at `root`.
pub fn from_path(root: impl Into<PathBuf>) -> Self {
Self {
root: root.into(),
pub fn from_path(root: impl Into<PathBuf>) -> Result<Self, io::Error> {
Ok(Self {
root: Self::init(root)?,
_temp_dir_drop: None,
}
})
}
/// Create a temporary cache directory.
pub fn temp() -> Result<Self, io::Error> {
let temp_dir = tempdir()?;
Ok(Self {
root: temp_dir.path().to_path_buf(),
root: Self::init(temp_dir.path())?,
_temp_dir_drop: Some(Arc::new(temp_dir)),
})
}
/// Return the root of the cache.
pub fn root(&self) -> &Path {
&self.root
}
@ -72,6 +75,7 @@ impl Cache {
self.root.join(cache_bucket.to_str())
}
/// Compute an entry in the cache.
pub fn entry(
&self,
cache_bucket: CacheBucket,
@ -83,10 +87,30 @@ impl Cache {
file,
}
}
/// Initialize a directory for use as a cache.
fn init(root: impl Into<PathBuf>) -> Result<PathBuf, io::Error> {
let root = root.into();
// Create the cache directory, if it doesn't exist.
fs::create_dir_all(&root)?;
// Add the CACHEDIR.TAG.
cachedir::ensure_tag(&root)?;
// Add the .gitignore.
let gitignore_path = root.join(".gitignore");
if !gitignore_path.exists() {
let mut file = fs::File::create(gitignore_path)?;
file.write_all(b"*")?;
}
fs::canonicalize(root)
}
}
/// The different kinds of data in the cache are stored in different bucket, which in our case
/// are subfolders.
/// are subdirectories of the cache root.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
pub enum CacheBucket {
/// Wheels (excluding built wheels), their metadata and cache policy.

View file

@ -185,13 +185,13 @@ pub(crate) async fn sync_requirements(
.await
.context("Failed to download distributions")?;
let download_s = if wheels.len() == 1 { "" } else { "s" };
let s = if wheels.len() == 1 { "" } else { "s" };
writeln!(
printer,
"{}",
format!(
"Downloaded {} in {}",
format!("{} package{}", wheels.len(), download_s).bold(),
format!("{} package{}", wheels.len(), s).bold(),
elapsed(start.elapsed())
)
.dimmed()