From 22a2ff4f1277dad2f32542c876310459ed734e64 Mon Sep 17 00:00:00 2001 From: Cole Miller Date: Mon, 16 Jun 2025 16:59:49 -0400 Subject: [PATCH] Bail and signal error when the cwd of a resolved task doesn't exist (#32777) Closes #32688 Release Notes: - Fixed tasks (including build tasks for debug configurations) silently using `/` as a working directory when the specified `cwd` didn't exist. --- Cargo.lock | 1 + crates/debugger_ui/src/session/running.rs | 11 +++++++++- crates/task/Cargo.toml | 1 + crates/task/src/task_template.rs | 24 +++++++++++++++++++++- crates/terminal_view/src/terminal_panel.rs | 19 ++++++++++++++++- crates/workspace/src/workspace.rs | 2 +- 6 files changed, 54 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e6a0d087fa..7006a99065 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15835,6 +15835,7 @@ dependencies = [ "serde_json_lenient", "sha2", "shellexpand 2.1.2", + "smol", "util", "workspace-hack", "zed_actions", diff --git a/crates/debugger_ui/src/session/running.rs b/crates/debugger_ui/src/session/running.rs index beb6d848ff..f3a348da46 100644 --- a/crates/debugger_ui/src/session/running.rs +++ b/crates/debugger_ui/src/session/running.rs @@ -850,9 +850,18 @@ impl RunningState { (task, None) } }; - let Some(task) = task_template.resolve_task("debug-build-task", &task_context) else { + let Some(task) = task_template.resolve_task_and_check_cwd("debug-build-task", &task_context, cx.background_executor().clone()) else { anyhow::bail!("Could not resolve task variables within a debug scenario"); }; + let task = match task.await { + Ok(task) => task, + Err(e) => { + workspace.update(cx, |workspace, cx| { + workspace.show_error(&e, cx); + }).ok(); + return Err(e) + } + }; let locator_name = if let Some(locator_name) = locator_name { debug_assert!(!config_is_valid); diff --git a/crates/task/Cargo.toml b/crates/task/Cargo.toml index f79b39616f..aad013c8f0 100644 --- a/crates/task/Cargo.toml +++ b/crates/task/Cargo.toml @@ -29,6 +29,7 @@ serde_json.workspace = true serde_json_lenient.workspace = true sha2.workspace = true shellexpand.workspace = true +smol.workspace = true util.workspace = true workspace-hack.workspace = true zed_actions.workspace = true diff --git a/crates/task/src/task_template.rs b/crates/task/src/task_template.rs index 02310bb1b0..b0d7649596 100644 --- a/crates/task/src/task_template.rs +++ b/crates/task/src/task_template.rs @@ -1,5 +1,6 @@ -use anyhow::{Context as _, bail}; +use anyhow::{Context as _, Result, anyhow, bail}; use collections::{HashMap, HashSet}; +use gpui::{BackgroundExecutor, Task}; use schemars::{JsonSchema, r#gen::SchemaSettings}; use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; @@ -270,6 +271,27 @@ impl TaskTemplate { }, }) } + + pub fn resolve_task_and_check_cwd( + &self, + id_base: &str, + task_context: &TaskContext, + executor: BackgroundExecutor, + ) -> Option>> { + let resolved_task = self.resolve_task(id_base, task_context)?; + let task = executor.spawn(async move { + if let Some(cwd) = resolved_task.resolved.cwd.as_deref() { + match smol::fs::metadata(cwd).await { + Ok(metadata) if metadata.is_dir() => Ok(resolved_task), + Ok(_) => Err(anyhow!("cwd for resolved task is not a directory: {cwd:?}")), + Err(e) => Err(e).context(format!("reading cwd of resolved task: {cwd:?}")), + } + } else { + Ok(resolved_task) + } + }); + Some(task) + } } const MAX_DISPLAY_VARIABLE_LENGTH: usize = 15; diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index dc9313a38f..fd5ec476c6 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -1461,10 +1461,27 @@ impl workspace::TerminalProvider for TerminalProvider { &self, task: SpawnInTerminal, window: &mut Window, - cx: &mut App, + cx: &mut Context, ) -> Task>> { let terminal_panel = self.0.clone(); + let workspace = cx.weak_entity(); window.spawn(cx, async move |cx| { + if let Some(cwd) = task.cwd.as_deref() { + let result = match smol::fs::metadata(cwd).await { + Ok(metadata) if metadata.is_dir() => Ok(()), + Ok(_) => Err(anyhow!("cwd for resolved task is not a directory: {cwd:?}")), + Err(e) => Err(e).context(format!("reading cwd of resolved task: {cwd:?}")), + }; + if let Err(e) = result { + workspace + .update(cx, |workspace, cx| { + workspace.show_error(&e, cx); + }) + .ok(); + return None; + } + } + let terminal = terminal_panel .update_in(cx, |terminal_panel, window, cx| { terminal_panel.spawn_task(&task, window, cx) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 3b90968251..6756782e5f 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -135,7 +135,7 @@ pub trait TerminalProvider { &self, task: SpawnInTerminal, window: &mut Window, - cx: &mut App, + cx: &mut Context, ) -> Task>>; }