diff --git a/Cargo.lock b/Cargo.lock index 54a4a04c6..0a0d7dfda 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -391,6 +391,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" +[[package]] +name = "cachedir" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4703f3937077db8fa35bee3c8789343c1aec2585f0146f09d658d4ccc0e8d873" +dependencies = [ + "tempfile", +] + [[package]] name = "camino" version = "1.1.6" @@ -2273,6 +2282,7 @@ dependencies = [ name = "puffin-cache" version = "0.0.1" dependencies = [ + "cachedir", "clap", "directories", "fs-err", diff --git a/Cargo.toml b/Cargo.toml index 1daffae48..bcf1d446e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ async_http_range_reader = { git = "https://github.com/baszalmstra/async_http_ran async_zip = { version = "0.0.15", features = ["tokio", "deflate"] } bitflags = { version = "2.4.1" } bytesize = { version = "1.3.0" } +cachedir = { version = "0.3.0" } camino = { version = "1.1.6", features = ["serde1"] } cargo-util = { version = "0.2.6" } chrono = { version = "0.4.31" } diff --git a/crates/puffin-cache/Cargo.toml b/crates/puffin-cache/Cargo.toml index 42fb06379..ba141714e 100644 --- a/crates/puffin-cache/Cargo.toml +++ b/crates/puffin-cache/Cargo.toml @@ -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 } diff --git a/crates/puffin-cache/src/cli.rs b/crates/puffin-cache/src/cli.rs index c2c76864e..0ffcea38c 100644 --- a/crates/puffin-cache/src/cli.rs +++ b/crates/puffin-cache/src/cli.rs @@ -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 for Cache { /// /// Returns an absolute cache dir. fn try_from(value: CacheArgs) -> Result { - 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") } } } diff --git a/crates/puffin-cache/src/lib.rs b/crates/puffin-cache/src/lib.rs index 750b28752..9550d7097 100644 --- a/crates/puffin-cache/src/lib.rs +++ b/crates/puffin-cache/src/lib.rs @@ -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) -> Self { - Self { - root: root.into(), + pub fn from_path(root: impl Into) -> Result { + Ok(Self { + root: Self::init(root)?, _temp_dir_drop: None, - } + }) } /// Create a temporary cache directory. pub fn temp() -> Result { 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) -> Result { + 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. diff --git a/crates/puffin-cli/src/commands/pip_sync.rs b/crates/puffin-cli/src/commands/pip_sync.rs index 0d0141c6a..bcd9c8b8c 100644 --- a/crates/puffin-cli/src/commands/pip_sync.rs +++ b/crates/puffin-cli/src/commands/pip_sync.rs @@ -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()