fix(build): stop a stale or missing keepsake viewer from shipping silently
Three ways the compiled-in viewer could be wrong, none of which anything would have reported. Found by mutation-testing the guard added below — it failed when it should have passed, and the reason was the second bullet. * `include_dir!` registers NO rebuild dependency. Run `npm run build` in frontend/export-viewer, then `cargo build`, and cargo sees no source change and reuses the cached binary — carrying the PREVIOUS index.html. The file on disk and the file in the binary disagree, git is clean, every check passes, and Memories.zip ships a stale viewer. Confirmed empirically: after replacing the artifact the compiled-in copy did not change until a source file was touched. A build.rs now declares `rerun-if-changed` for `static/export-viewer` AND `migrations` — sqlx::migrate!() embeds its directory the same way, and there the stale snapshot is worse still: the binary boots against a database that already ran a newer migration and crash-loops with VersionMissing. * `emptyOutDir: true` deleted the committed artifact BEFORE generating. That was safe while the build could not fail; it no longer is, because `inlineThemeFonts` now calls `this.error` on a keepsake that is not self-contained. A failed build left the directory empty — and include_dir! over an empty directory compiles fine, while `write_viewer_with_data` iterates zero files and returns Ok. The result is a valid archive with every photo and no viewer. The output is one overwritten file, so nothing accumulates without the wipe. * Nothing asserted the viewer was there at all. Now asserted at the point of use (bail rather than write a viewer-less keepsake) and in a test that checks presence, plausible size, and that no `url(/...)` survived inlining — the three ways it can be present but useless. The Dockerfile copies build.rs with the sources rather than with Cargo.toml, so the dependency-cache layer stays byte-identical and the dummy build does not run it.
This commit is contained in:
25
backend/build.rs
Normal file
25
backend/build.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
//! Tell cargo which non-Rust inputs are baked into the binary.
|
||||
//!
|
||||
//! `include_dir!` and `sqlx::migrate!()` both embed directory contents at COMPILE time, and neither
|
||||
//! registers a rebuild dependency on its own. Cargo therefore reuses a cached binary when only
|
||||
//! those directories changed — the source files are untouched, so as far as cargo is concerned
|
||||
//! nothing happened.
|
||||
//!
|
||||
//! For the keepsake viewer that is a silent, shippable defect: run `npm run build` in
|
||||
//! `frontend/export-viewer`, then `cargo build`, and the resulting binary still carries the
|
||||
//! PREVIOUS `static/export-viewer/index.html`. The artifact on disk and the artifact in the binary
|
||||
//! disagree, `git status` is clean, and every check passes — while `Memories.zip` ships a stale
|
||||
//! viewer. Confirmed empirically: after replacing the file, the compiled-in copy did not change
|
||||
//! until a source file was touched.
|
||||
//!
|
||||
//! Production is mostly insulated because images are built from a clean context (no cache to
|
||||
//! reuse), but every incremental build — i.e. all local development and any test run that follows
|
||||
//! a viewer rebuild — hits it, and that includes the test that asserts the viewer is present.
|
||||
fn main() {
|
||||
// The compiled-in keepsake viewer (services/export.rs: `include_dir!`).
|
||||
println!("cargo:rerun-if-changed=static/export-viewer");
|
||||
// The embedded migration set (db.rs: `sqlx::migrate!()`). Same mechanism, and the failure is
|
||||
// worse: a binary built from a stale snapshot boots against a database that has already run a
|
||||
// newer migration and crash-loops with VersionMissing.
|
||||
println!("cargo:rerun-if-changed=migrations");
|
||||
}
|
||||
Reference in New Issue
Block a user