diff --git a/src/pack.rs b/src/pack.rs index ca881e3..77df651 100644 --- a/src/pack.rs +++ b/src/pack.rs @@ -49,6 +49,32 @@ use anyhow::anyhow; static DEFAULT_REQWEST_TIMEOUT_SEC: Duration = Duration::from_secs(5 * 60); +/// Contents of the `CACHEDIR.TAG` file written to the cache directory. +/// +/// See for the specification. The signature on +/// the first line marks the directory as a cache so that backup and archiving +/// tools can skip it. +const CACHEDIR_TAG_CONTENT: &str = "Signature: 8a477f597d28d172789f06886806bc55\n\ +# This file is a cache directory tag created by pixi-pack.\n\ +# For information about cache directory tags see https://bford.info/cachedir/\n"; + +/// Write a `CACHEDIR.TAG` file to the given cache directory if it does not +/// already exist, marking it as a cache directory. +async fn write_cachedir_tag(cache_dir: &Path) -> Result<()> { + create_dir_all(cache_dir) + .await + .map_err(|e| anyhow!("could not create cache directory: {}", e))?; + + let tag_path = cache_dir.join("CACHEDIR.TAG"); + if tag_path.exists() { + return Ok(()); + } + + fs::write(&tag_path, CACHEDIR_TAG_CONTENT) + .await + .map_err(|e| anyhow!("could not write CACHEDIR.TAG file: {}", e)) +} + #[derive(Debug, Clone, Copy, PartialEq)] pub enum OutputMode { Archive, @@ -112,6 +138,12 @@ pub async fn pack(options: PackOptions) -> Result<()> { let client = reqwest_client_from_options(&options) .map_err(|e| anyhow!("could not create reqwest client from auth storage: {e}"))?; + // Mark the cache directory (if any) with a CACHEDIR.TAG file so that backup + // and archiving tools can skip it. + if let Some(cache_dir) = options.cache_dir.as_deref() { + write_cachedir_tag(cache_dir).await?; + } + let env = lockfile.environment(&options.environment).ok_or(anyhow!( "environment not found in lockfile: {}", options.environment diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 52409f4..a801d47 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -797,6 +797,19 @@ async fn test_package_caching( // Both output files should exist and be valid assert!(options.pack_options.output_file.exists()); assert!(output_file2.exists()); + + // A CACHEDIR.TAG file should have been created in the cache directory, + // marking it as a cache directory for backup and archiving tools. + let cachedir_tag = cache_dir.join("CACHEDIR.TAG"); + assert!( + cachedir_tag.is_file(), + "CACHEDIR.TAG should be created in the cache directory" + ); + let tag_contents = fs::read_to_string(&cachedir_tag).unwrap(); + assert!( + tag_contents.starts_with("Signature: 8a477f597d28d172789f06886806bc55"), + "CACHEDIR.TAG should start with the standard cache directory tag signature" + ); } #[rstest]