use std::collections::BTreeMap; use std::path::Path; fn main() { let version = std::env::var("CARGO_PKG_VERSION").unwrap_or_else(|_| "unknown".to_string()); let git_hash = std::process::Command::new("git") .args(["rev-parse", "--short", "HEAD"]) .output() .ok() .and_then(|o| String::from_utf8(o.stdout).ok()) .map(|s| s.trim().to_string()) .unwrap_or_else(|| "unknown".to_string()); let timestamp = std::process::Command::new("date") .args(["-u", "+%Y-%m-%dT%H:%M:%SZ"]) .output() .ok() .and_then(|o| String::from_utf8(o.stdout).ok()) .map(|s| s.trim().to_string()) .unwrap_or_else(|| "unknown".to_string()); println!("cargo:rustc-env=BUILD_VERSION={}", version); println!("cargo:rustc-env=BUILD_GIT_HASH={}", git_hash); println!("cargo:rustc-env=BUILD_TIMESTAMP={}", timestamp); // ── Schema migration manifest ── // Scan release/migrate_*.sql, compute SHA256, embed as JSON string let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap_or_else(|_| ".".to_string()); let release_dir = Path::new(&manifest_dir).join("release"); let mut migrations = BTreeMap::new(); // sorted by filename if let Ok(entries) = std::fs::read_dir(&release_dir) { for entry in entries.flatten() { let path = entry.path(); let fname = path.file_name().and_then(|n| n.to_str()).unwrap_or(""); if fname.starts_with("migrate_") && fname.ends_with(".sql") { if let Ok(content) = std::fs::read(&path) { let hash = sha256_hex(&content); migrations.insert(fname.to_string(), hash); } } } } // Encode as comma-separated: name1:hash1,name2:hash2,... let manifest: String = migrations .iter() .map(|(name, hash)| format!("{}:{}", name, hash)) .collect::>() .join(","); println!("cargo:rustc-env=REQUIRED_MIGRATIONS={}", manifest); println!( "cargo:info=Embedded {} migration checksums", migrations.len() ); } fn sha256_hex(data: &[u8]) -> String { use std::io::Write; use std::process::{Command, Stdio}; if let Ok(mut child) = Command::new("shasum") .arg("-a") .arg("256") .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() { if let Some(mut stdin) = child.stdin.take() { let _ = stdin.write_all(data); } if let Ok(out) = child.wait_with_output() { if let Ok(s) = String::from_utf8(out.stdout) { if let Some(hash) = s.split(' ').next() { return hash.to_string(); } } } } "unknown".to_string() }