crates/monetize-admin/build/console_fresh.rs
//! **[console-stale]**: the console bundle (`monetize-ui-web/dist`) must be
//! newer than every source it was built from. Shared by `build.rs` (which
//! refuses the build) and `tests/console_stale.rs` (which proves it).
//!
//! MEASURED 2026-10-12: the ONE box shipped a `dist/` built 10-11 07:08, before
//! the usernameless-passkey fix; `build.rs` embedded whatever `dist/` held.
use std::path::{Path, PathBuf};
use std::time::SystemTime;
/// The sources a console bundle is built from, under `monetize-ui-web/`.
pub const SOURCES: [&str; 6] = ["src", "styles", "assets", "index.html", "Cargo.toml", "Trunk.toml"];
/// Every file under `p` (a file is itself).
pub fn files(p: &Path, out: &mut Vec<PathBuf>) {
if p.is_dir() {
if let Ok(rd) = std::fs::read_dir(p) {
for e in rd.flatten() {
files(&e.path(), out);
}
}
} else if p.is_file() {
out.push(p.to_path_buf());
}
}
fn modified(p: &Path) -> Option<SystemTime> {
std::fs::metadata(p).ok()?.modified().ok()
}
/// `Ok(())` when the bundle in `dist` is newer than every source under
/// `web` (the `monetize-ui-web` crate); the refusal, by name, otherwise.
pub fn check(dist: &Path, web: &Path) -> Result<(), String> {
let mut built = Vec::new();
files(dist, &mut built);
let Some(oldest) = built.iter().filter_map(|f| modified(f)).min() else { return Ok(()) };
let mut sources = Vec::new();
for s in SOURCES {
files(&web.join(s), &mut sources);
}
let newer: Vec<&PathBuf> = sources.iter().filter(|f| modified(f).is_some_and(|m| m > oldest)).collect();
match newer.first() {
None => Ok(()),
Some(first) => Err(format!(
"REFUSED [console-stale] the console bundle {} is older than its sources ({} changed after it was built, e.g. {}): rebuild it (`trunk build --release` in {}) before building monetize-admin",
dist.display(),
newer.len(),
first.display(),
web.display()
)),
}
}