builder: enforce target, source sync, and timeout contracts

This commit is contained in:
2026-11-07 12:32:45 +00:00
parent a27d579264
commit 2f18f0fa4c
4 changed files with 182 additions and 23 deletions
+34 -11
View File
@@ -59,6 +59,25 @@ impl Builder {
.contains(&action),
"unsupported Cargo command"
);
let compiles = ["build", "check", "clippy", "test", "run"].contains(&action);
let mut args = request.argv.iter().take_while(|arg| arg.as_str() != "--");
while let Some(arg) = args.next() {
ensure!(
arg != "--target-dir" && !arg.starts_with("--target-dir="),
"the builder controls the target directory"
);
let target = if arg == "--target" {
args.next().map(String::as_str)
} else {
arg.strip_prefix("--target=")
};
if compiles && let Some(target) = target {
ensure!(
target == "wasm32-wasip2",
"the build target is always wasm32-wasip2"
);
}
}
let seconds = request.timeout_secs.unwrap_or(1800);
ensure!(seconds > 0, "timeout must be positive");
let deadline = Instant::now()
@@ -81,7 +100,7 @@ impl Builder {
if source.exists() {
std::fs::remove_dir_all(&source)?;
}
files::copy_out(self.root.as_ref(), &workspace, &source).await?;
let before = files::copy_out(self.root.as_ref(), &workspace, &source).await?;
for path in [&target, &registry, &cache, &target.join("tmp")] {
std::fs::create_dir_all(path)?;
}
@@ -147,7 +166,7 @@ impl Builder {
startup.arg(tools.join("build.nu"));
let (startup_code, _) = run(startup, tx, deadline).await?;
if startup_code != 0 {
files::copy_back(self.root.as_ref(), &source, &workspace, false).await?;
files::copy_back(self.root.as_ref(), &source, &workspace, Some(&before)).await?;
self.evict(20 * 1024 * 1024 * 1024)?;
tx.send(Event::Exit {
code: startup_code,
@@ -238,7 +257,7 @@ impl Builder {
}
let split = args.iter().position(|a| a == "--").unwrap_or(args.len());
cmd.args(&args[..split]);
if ["build", "check", "clippy", "test", "run"].contains(&action) {
if compiles {
cmd.args([
"--target",
"wasm32-wasip2",
@@ -257,7 +276,7 @@ impl Builder {
self.root.as_ref(),
&artifacts,
&format!("{world_target}/wasm32-wasip2"),
true,
None,
)
.await?;
}
@@ -284,7 +303,7 @@ impl Builder {
executables.dedup();
}
}
files::copy_back(self.root.as_ref(), &source, &workspace, false).await?;
files::copy_back(self.root.as_ref(), &source, &workspace, Some(&before)).await?;
self.evict(20 * 1024 * 1024 * 1024)?;
tx.send(Event::Exit { code, executables }).await?;
Ok(())
@@ -410,14 +429,18 @@ async fn run(
)?;
Ok::<_, anyhow::Error>((status.code().unwrap_or(1), out))
};
let result = tokio::select! {
result = collect => result,
let (result, timed_out) = tokio::select! {
result = collect => (result, false),
_ = tx.closed() => anyhow::bail!("caller exited"),
_ = tokio::time::sleep_until(deadline) => {
tx.send(Event::Stderr { bytes: b"cargo: command timed out\n".to_vec() }).await?;
Ok((124, Vec::new()))
}
_ = tokio::time::sleep_until(deadline) => (Ok((124, Vec::new())), true),
};
// Kill the command before a full output channel can delay its timeout message.
drop(group);
if timed_out {
tx.send(Event::Stderr {
bytes: b"cargo: command timed out\n".to_vec(),
})
.await?;
}
result
}