Fix: surface build errors in deploy log instead of swallowing them

When run_build() returned an Err (e.g. spawn failure because the
build script path doesn't resolve) the error was only written to
tracing, leaving the deploy log empty and the user with no clue.

- build_worker now appends the Rust error message to the deploy log
  before setting status=failed, so it appears in the UI.
- run_build logs CWD, resolved script path, exists=true/false, build
  dir, and env file path before attempting spawn, so there is always
  at least one diagnostic line in the log even if spawn itself fails.
- spawn() error is wrapped with the attempted path for clarity.

https://claude.ai/code/session_01FKCW3FDjNFj6jve4niMFXH
This commit is contained in:
Claude 2026-03-19 09:06:37 +00:00
parent d322cc3ce1
commit c3f300e8ad
No known key found for this signature in database

View file

@ -45,6 +45,9 @@ pub async fn build_worker(state: AppState) {
Some(id) => { Some(id) => {
if let Err(e) = run_build(&state, &id).await { if let Err(e) = run_build(&state, &id).await {
tracing::error!("Build {} failed: {}", id, e); tracing::error!("Build {} failed: {}", id, e);
// Surface the Rust-level error in the deploy log so it's visible in the UI.
let msg = format!("\n[hiy] FATAL: {}\n", e);
let _ = append_log(&state.db, &id, &msg).await;
let _ = set_status(&state.db, &id, "failed").await; let _ = set_status(&state.db, &id, "failed").await;
} }
} }
@ -97,6 +100,24 @@ async fn run_build(state: &AppState, deploy_id: &str) -> anyhow::Result<()> {
let build_dir = format!("{}/builds/{}", state.data_dir, app.id); let build_dir = format!("{}/builds/{}", state.data_dir, app.id);
// Log diagnostics before spawning so even a spawn failure leaves a breadcrumb.
let cwd = std::env::current_dir()
.map(|p| p.display().to_string())
.unwrap_or_else(|_| "<unknown>".into());
let script_exists = std::path::Path::new(&build_script).exists();
append_log(
&state.db,
deploy_id,
&format!(
"[hiy] CWD: {}\n\
[hiy] Build script: {} (exists={})\n\
[hiy] Build dir: {}\n\
[hiy] Env file: {}\n---\n",
cwd, build_script, script_exists, build_dir, env_file
),
)
.await?;
let mut child = Command::new("bash") let mut child = Command::new("bash")
.arg(&build_script) .arg(&build_script)
.env("APP_ID", &app.id) .env("APP_ID", &app.id)
@ -109,7 +130,8 @@ async fn run_build(state: &AppState, deploy_id: &str) -> anyhow::Result<()> {
.env("BUILD_DIR", &build_dir) .env("BUILD_DIR", &build_dir)
.stdout(std::process::Stdio::piped()) .stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped()) .stderr(std::process::Stdio::piped())
.spawn()?; .spawn()
.map_err(|e| anyhow::anyhow!("Failed to spawn '{}': {}", build_script, e))?;
let stdout = child.stdout.take().expect("piped stdout"); let stdout = child.stdout.take().expect("piped stdout");
let stderr = child.stderr.take().expect("piped stderr"); let stderr = child.stderr.take().expect("piped stderr");