diff --git a/crates/vite_task/src/execute.rs b/crates/vite_task/src/execute.rs index df1c763033..fda97b0fc9 100644 --- a/crates/vite_task/src/execute.rs +++ b/crates/vite_task/src/execute.rs @@ -8,6 +8,7 @@ use std::{ time::{Duration, Instant}, }; +use anyhow; use bincode::{Decode, Encode}; use fspy::{AccessMode, Spy, TrackedChild}; use futures_util::future::{try_join3, try_join4}; @@ -16,7 +17,7 @@ use sha2::{Digest, Sha256}; use supports_color::{Stream, on}; use tokio::io::{AsyncRead, AsyncReadExt as _, AsyncWrite, AsyncWriteExt as _}; use vite_glob::GlobPatternSet; -use vite_path::{AbsolutePath, RelativePathBuf}; +use vite_path::{AbsolutePath, AbsolutePathBuf, RelativePathBuf}; use vite_str::Str; use wax::Glob; @@ -163,6 +164,7 @@ const DEFAULT_PASSTHROUGH_ENVS: &[&str] = &[ "SHELL", "PWD", "PATH", + "INIT_CWD", // CI/CD environments "CI", // Node.js specific @@ -300,6 +302,13 @@ impl TaskEnvs { // This prevents nested auto-install execution all_envs.insert("VITE_TASK_EXECUTION_ENV".into(), Arc::::from(OsStr::new("1"))); + // Add INIT_CWD to hold the original working directory where vite was invoked + if let Ok(init_cwd_guard) = INIT_CWD.lock() { + if let Some(init_cwd) = init_cwd_guard.as_ref() { + all_envs.insert("INIT_CWD".into(), Arc::::from(init_cwd.as_path().as_os_str())); + } + } + // Add node_modules/.bin to PATH let env_path = all_envs.entry("PATH".into()).or_insert_with(|| Arc::::from(OsStr::new(""))); @@ -320,6 +329,19 @@ impl TaskEnvs { pub static CURRENT_EXECUTION_ID: LazyLock> = LazyLock::new(|| std::env::var("VITE_TASK_EXECUTION_ID").ok()); +pub static INIT_CWD: LazyLock>> = + LazyLock::new(|| Mutex::new(None)); + +/// Set the initial working directory for INIT_CWD environment variable +pub fn set_init_cwd(cwd: AbsolutePathBuf) -> Result<(), Error> { + if let Ok(mut init_cwd_guard) = INIT_CWD.lock() { + *init_cwd_guard = Some(cwd); + Ok(()) + } else { + Err(Error::AnyhowError(anyhow::anyhow!("Failed to acquire INIT_CWD lock"))) + } +} + pub static EXECUTION_SUMMARY_DIR: LazyLock = LazyLock::new(|| { std::env::var("VITE_TASK_EXECUTION_DIR") .map(PathBuf::from) @@ -874,4 +896,51 @@ mod tests { std::env::remove_var("app1_name"); } } + + #[test] + fn test_init_cwd_environment_variable() { + use crate::config::{TaskCommand, TaskConfig}; + + // Create a simple task config for testing + let task_config = TaskConfig { + command: TaskCommand::ShellScript("echo hello".into()), + cwd: RelativePathBuf::default(), + cacheable: true, + inputs: HashSet::new(), + envs: HashSet::new(), + pass_through_envs: HashSet::new(), + }; + + let resolved_task_config = + ResolvedTaskConfig { config_dir: RelativePathBuf::default(), config: task_config }; + + let base_dir = if cfg!(windows) { + AbsolutePath::new("C:\\workspace").unwrap() + } else { + AbsolutePath::new("/workspace").unwrap() + }; + + // Test without INIT_CWD set + let result_without_init = TaskEnvs::resolve(base_dir, &resolved_task_config).unwrap(); + assert!(!result_without_init.all_envs.contains_key("INIT_CWD")); + + // Set INIT_CWD + let test_init_cwd = if cfg!(windows) { + AbsolutePathBuf::new("C:\\original\\path".into()).unwrap() + } else { + AbsolutePathBuf::new("/original/path".into()).unwrap() + }; + set_init_cwd(test_init_cwd.clone()).unwrap(); + + // Test with INIT_CWD set + let result_with_init = TaskEnvs::resolve(base_dir, &resolved_task_config).unwrap(); + assert!(result_with_init.all_envs.contains_key("INIT_CWD")); + let init_cwd_value = result_with_init.all_envs.get("INIT_CWD").unwrap(); + assert_eq!(init_cwd_value.as_ref(), test_init_cwd.as_path().as_os_str()); + + // Clean up - reset INIT_CWD to None + if let Ok(mut init_cwd_guard) = INIT_CWD.lock() { + *init_cwd_guard = None; + } + } } diff --git a/crates/vite_task/src/lib.rs b/crates/vite_task/src/lib.rs index e8529a9891..8f11831f15 100644 --- a/crates/vite_task/src/lib.rs +++ b/crates/vite_task/src/lib.rs @@ -31,7 +31,7 @@ use vite_str::Str; pub use crate::config::Workspace; use crate::{ cache::TaskCache, - execute::{CURRENT_EXECUTION_ID, EXECUTION_SUMMARY_DIR}, + execute::{CURRENT_EXECUTION_ID, EXECUTION_SUMMARY_DIR, set_init_cwd}, fmt::FmtConfig, lint::LintConfig, schedule::{ExecutionPlan, ExecutionStatus, ExecutionSummary}, @@ -285,6 +285,9 @@ pub async fn main< >, >, ) -> Result { + // Set the initial working directory for INIT_CWD environment variable + set_init_cwd(cwd.clone())?; + // Auto-install dependencies if needed, but skip for install command itself, or if `VITE_DISABLE_AUTO_INSTALL=1` is set. if !matches!(args.commands, Commands::Install { .. }) && std::env::var_os("VITE_DISABLE_AUTO_INSTALL") != Some("1".into())