fix(maestro): tie maestro-web recompile to web/dist + harden bundle build

Watch the embedded web/dist output (not just frontend inputs) so a
rebuilt bundle is actually re-embedded; assert pnpm build produced
web/dist/index.html; strengthen the embed test to require a vite-built
hashed asset reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-28 22:02:11 +00:00
co-authored by Claude Opus 4.8
parent 092a04857f
commit c355f9bd2e
2 changed files with 33 additions and 0 deletions
+25
View File
@@ -27,6 +27,16 @@ fn main() {
println!("cargo:rerun-if-changed={}", web_dir.join(rel).display());
}
// Also watch the embedded OUTPUT directory. The input watches above decide
// whether THIS script re-runs and regenerates `web/dist`; but `include_dir!`
// in `static_bundle.rs` reads `web/dist`, and cargo has no other declared
// dependency on those output files. Without this watch, an incremental build
// could regenerate `web/dist` yet keep the OLD bundle embedded. Tying the
// crate's recompilation to the regenerated output makes the fresh assets
// actually get re-embedded.
let dist_dir = web_dir.join("dist");
println!("cargo:rerun-if-changed={}", dist_dir.display());
require_tool("pnpm");
require_tool("node");
@@ -40,6 +50,21 @@ fn main() {
Command::new("pnpm").arg("build").current_dir(&web_dir),
"pnpm build",
);
// Sanity-check that pnpm build actually produced the bundle `include_dir!`
// expects. A green build that embeds an empty/placeholder dir is worse than
// a loud failure here.
let index_html = dist_dir.join("index.html");
let produced = std::fs::metadata(&index_html)
.map(|m| m.is_file() && m.len() > 0)
.unwrap_or(false);
if !produced {
panic!(
"`pnpm build` did not produce the expected bundle at {} \
(missing or empty) — check the vite `outDir`.",
index_html.display()
);
}
}
fn require_tool(tool: &str) {
@@ -18,4 +18,12 @@ async fn embedded_index_is_real_spa_not_placeholder() {
!html.contains("maestro-web is running"),
"served index is the placeholder, not the built bundle"
);
// Stronger signal than `id="app"` (which appears in both source and built
// HTML): the vite build rewrites the entry to a hashed `/assets/index-<hash>`
// bundle, whereas the un-built source references `/src/main.ts`. This catches
// both an empty/placeholder embed and the staleness case.
assert!(
html.contains("/assets/index-"),
"served index does not reference a vite-built hashed asset; got:\n{html}"
);
}