diff --git a/CHANGELOG.md b/CHANGELOG.md index 13084e2..8170529 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). cargo-r ### Fixed +- Require the conventional `target` directory name before accepting generic `CACHEDIR.TAG` evidence. Custom Cargo output directories remain supported through Cargo configuration or `.rustc_info.json`. + - The scheduler service never pruned its own state, so plans, reports, and run logs grew unbounded — on a large workspace a plan snapshot reaches hundreds of megabytes, so a 5-minute trigger wrote gigabytes per hour. The service now prunes at startup (clearing any existing backlog) and after every cycle; terminal `apply` prunes the reports directory it writes to. ### Changed diff --git a/src/scanner/targets.rs b/src/scanner/targets.rs index a80d7a3..b277ea3 100644 --- a/src/scanner/targets.rs +++ b/src/scanner/targets.rs @@ -124,7 +124,9 @@ pub(crate) fn classify_target_candidate_with_overrides( )); } - if path.join(CACHEDIR_TAG).is_file() { + if path.file_name().is_some_and(|name| name == TARGET_DIR_NAME) + && path.join(CACHEDIR_TAG).is_file() + { return Ok(TargetCandidate::candidate( path.to_path_buf(), TargetCandidateKind::CargoTargetDir, diff --git a/tests/scanner_foundation.rs b/tests/scanner_foundation.rs index 50d77a3..05dbfef 100644 --- a/tests/scanner_foundation.rs +++ b/tests/scanner_foundation.rs @@ -483,3 +483,19 @@ impl Drop for TestTemp { let _ = fs::remove_dir_all(&self.path); } } + +#[test] +fn generic_cache_marker_does_not_make_a_non_target_directory_a_cargo_target() +-> Result<(), Box> { + let temp = TestTemp::new("non_cargo_cache")?; + let cache = temp.path().join("python-cache"); + fs::create_dir(&cache)?; + fs::write( + cache.join("CACHEDIR.TAG"), + "Signature: 8a477f597d28d172789f06886806bc55\n", + )?; + let candidate = classify_target_candidate(&cache, None, None, &ScannerOptions::default())?; + assert!(candidate.evidence.is_none()); + assert!(candidate.skip_reason.is_some()); + Ok(()) +} diff --git a/tests/scanner_recursive.rs b/tests/scanner_recursive.rs index e6077d8..3862292 100644 --- a/tests/scanner_recursive.rs +++ b/tests/scanner_recursive.rs @@ -329,3 +329,26 @@ fn test_lock() -> Result, Box> { .lock() .map_err(|_| std::io::Error::other("env lock poisoned").into()) } + +#[test] +fn recursive_scan_preserves_non_cargo_cache_contents() -> Result<(), Box> { + let _test_guard = test_lock()?; + let temp = TestTemp::new("recursive_non_cargo_cache")?; + let cache = temp.path().join("python-cache"); + let valuable = cache.join("release/deps/valuable.txt"); + fs::create_dir_all(valuable.parent().expect("parent"))?; + fs::write( + cache.join("CACHEDIR.TAG"), + "Signature: 8a477f597d28d172789f06886806bc55\n", + )?; + fs::write(&valuable, "user data")?; + let plan = cargo_reclaim::build_plan_from_roots( + [temp.path()], + cargo_reclaim::PolicyKind::Balanced, + &ScannerOptions::default(), + &cargo_reclaim::InventoryOptions::default(), + )?; + assert_eq!(plan.totals.delete_candidate_count, 0); + assert_eq!(fs::read_to_string(valuable)?, "user data"); + Ok(()) +}