diff --git a/crates/ruff/src/lib.rs b/crates/ruff/src/lib.rs index 882240b3b69c5..c7c32a223587b 100644 --- a/crates/ruff/src/lib.rs +++ b/crates/ruff/src/lib.rs @@ -112,7 +112,7 @@ fn is_stdin(files: &[PathBuf], stdin_filename: Option<&Path>) -> bool { file == Path::new("-") } -/// Returns the default set of files if none are provided, otherwise returns `None`. +/// Returns the default set of files if none are provided, otherwise returns provided files. fn resolve_default_files(files: Vec, is_stdin: bool) -> Vec { if files.is_empty() { if is_stdin { diff --git a/crates/ruff/src/resolve.rs b/crates/ruff/src/resolve.rs index 4fbea4dad6eb6..b68d2c61fd7ac 100644 --- a/crates/ruff/src/resolve.rs +++ b/crates/ruff/src/resolve.rs @@ -1,6 +1,6 @@ use std::path::Path; -use anyhow::Result; +use anyhow::{bail, Result}; use log::debug; use path_absolutize::path_dedot; @@ -21,10 +21,14 @@ pub fn resolve( config_arguments: &ConfigArguments, stdin_filename: Option<&Path>, ) -> Result { + let Ok(cwd) = std::env::current_dir() else { + bail!("Working directory does not exist") + }; + // First priority: if we're running in isolated mode, use the default settings. if config_arguments.isolated { let config = config_arguments.transform(Configuration::default()); - let settings = config.into_settings(&path_dedot::CWD)?; + let settings = config.into_settings(&cwd)?; debug!("Isolated mode, not reading any pyproject.toml"); return Ok(PyprojectConfig::new( PyprojectDiscoveryStrategy::Fixed, @@ -58,11 +62,7 @@ pub fn resolve( // that directory. (With `Strategy::Hierarchical`, we'll end up finding // the "closest" `pyproject.toml` file for every Python file later on, // so these act as the "default" settings.) - if let Some(pyproject) = pyproject::find_settings_toml( - stdin_filename - .as_ref() - .unwrap_or(&path_dedot::CWD.as_path()), - )? { + if let Some(pyproject) = pyproject::find_settings_toml(stdin_filename.unwrap_or(&cwd))? { debug!( "Using configuration file (via parent) at: {}", pyproject.display() @@ -130,17 +130,13 @@ pub fn resolve( // `pyproject::find_settings_toml`.) // However, there may be a `pyproject.toml` with a `requires-python` // specified, and that is what we look for in this step. - let fallback = find_fallback_target_version( - stdin_filename - .as_ref() - .unwrap_or(&path_dedot::CWD.as_path()), - ); + let fallback = find_fallback_target_version(stdin_filename.unwrap_or(&cwd)); if let Some(version) = fallback { debug!("Derived `target-version` from found `requires-python`: {version:?}"); } config.target_version = fallback.map(ast::PythonVersion::from); } - let settings = config.into_settings(&path_dedot::CWD)?; + let settings = config.into_settings(&cwd)?; Ok(PyprojectConfig::new( PyprojectDiscoveryStrategy::Hierarchical, settings, diff --git a/crates/ruff/tests/direxist_guard.rs b/crates/ruff/tests/direxist_guard.rs new file mode 100644 index 0000000000000..3812d9ba0336f --- /dev/null +++ b/crates/ruff/tests/direxist_guard.rs @@ -0,0 +1,30 @@ +//! Test to verify Ruff's behavior when run from deleted directory. +//! It has to be isolated in a separate module. +//! Tests in the same module become flaky under `cargo test`s parallel execution +//! due to in-test working directory manipulation. + +#![cfg(target_family = "unix")] + +use std::env::set_current_dir; +use std::process::Command; + +use insta_cmd::{assert_cmd_snapshot, get_cargo_bin}; +const BIN_NAME: &str = "ruff"; + +#[test] +fn check_in_deleted_directory_errors() { + let temp_dir = tempfile::tempdir().unwrap(); + let temp_path = temp_dir.path().to_path_buf(); + set_current_dir(&temp_path).unwrap(); + drop(temp_dir); + + assert_cmd_snapshot!(Command::new(get_cargo_bin(BIN_NAME)).arg("check"), @r###" + success: false + exit_code: 2 + ----- stdout ----- + + ----- stderr ----- + ruff failed + Cause: Working directory does not exist + "###); +}