Skip to main content

atomcode_core/setup/
seeds.rs

1//! Embedded seeds: rust-best-practices.md, /review command, etc.
2//! Packed at build time into setup-seeds.tar.zst, extracted at first run to
3//! ~/.atomcode/seeds-cache/<binary-version>/. Subsequent runs hit the cache.
4
5use anyhow::{Context, Result};
6use std::path::{Path, PathBuf};
7
8const SEEDS_TARZST: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/setup-seeds.tar.zst"));
9
10/// Returns the path to the extracted seeds directory. Extracts on first call.
11///
12/// Cache key uses a hash of the embedded tar.zst content (not just CARGO_PKG_VERSION)
13/// so that rebuilding with updated seeds (same version number) correctly invalidates
14/// the cache. This prevents stale seed files from being served after a rebuild.
15pub fn ensure_seeds_extracted(cache_root: &Path) -> Result<PathBuf> {
16    // Use content hash so same-version rebuilds with new seeds still invalidate.
17    use sha2::{Digest, Sha256};
18    let mut h = Sha256::new();
19    h.update(SEEDS_TARZST);
20    let content_hash = format!("{:x}", h.finalize());
21    let short_hash = &content_hash[..12]; // 12 hex chars = 48 bits, plenty for cache key
22
23    let cache_dir = cache_root
24        .join("seeds-cache")
25        .join(format!("{}-{}", env!("CARGO_PKG_VERSION"), short_hash));
26    let sentinel = cache_dir.join(".extracted");
27    if sentinel.exists() {
28        return Ok(cache_dir);
29    }
30    std::fs::create_dir_all(&cache_dir)
31        .with_context(|| format!("create_dir_all({})", cache_dir.display()))?;
32
33    let decoder = zstd::Decoder::new(SEEDS_TARZST)
34        .context("zstd decoder for embedded seeds")?;
35    let mut archive = tar::Archive::new(decoder);
36    archive
37        .unpack(&cache_dir)
38        .with_context(|| format!("unpack to {}", cache_dir.display()))?;
39
40    #[cfg(target_os = "macos")]
41    remove_quarantine_recursive(&cache_dir);
42
43    std::fs::write(&sentinel, b"").context("write sentinel")?;
44    Ok(cache_dir)
45}
46
47/// Remove com.apple.quarantine xattr from all extracted files.
48/// Some IDEs refuse to read quarantined files.
49#[cfg(target_os = "macos")]
50fn remove_quarantine_recursive(dir: &std::path::Path) {
51    use std::process::Command;
52    let _ = Command::new("xattr")
53        .args(["-rd", "com.apple.quarantine"])
54        .arg(dir)
55        .output();
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn first_call_extracts_and_creates_sentinel() {
64        let dir = tempfile::tempdir().unwrap();
65        let extracted = ensure_seeds_extracted(dir.path()).unwrap();
66        assert!(extracted.join(".extracted").exists());
67        // Placeholder README from build.rs should be present.
68        assert!(
69            extracted.join("skills").join("README.md").exists()
70                || extracted.join("README.md").exists()
71                || extracted.read_dir().unwrap().count() > 0
72        );
73    }
74
75    #[test]
76    fn second_call_is_idempotent() {
77        let dir = tempfile::tempdir().unwrap();
78        let first = ensure_seeds_extracted(dir.path()).unwrap();
79        let second = ensure_seeds_extracted(dir.path()).unwrap();
80        assert_eq!(first, second);
81    }
82
83}