Make global justfile filename case-insensitive (#2802)
Some checks failed
CI / test (macos-latest) (push) Has been cancelled
CI / test (windows-latest) (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / msrv (push) Has been cancelled
CI / pages (push) Has been cancelled
CI / test (ubuntu-latest) (push) Has been cancelled

This commit is contained in:
Casey Rodarmor 2025-07-01 13:17:42 -07:00 committed by GitHub
parent 4359f7432b
commit 743e700d8b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 53 additions and 22 deletions

View file

@ -73,6 +73,7 @@ unexpected_cfgs = { level = "warn", check-cfg = ['cfg(fuzzing)'] }
all = { level = "deny", priority = -1 } all = { level = "deny", priority = -1 }
arbitrary-source-item-ordering = "deny" arbitrary-source-item-ordering = "deny"
enum_glob_use = "allow" enum_glob_use = "allow"
ignore_without_reason = "allow"
needless_pass_by_value = "allow" needless_pass_by_value = "allow"
pedantic = { level = "deny", priority = -1 } pedantic = { level = "deny", priority = -1 }
similar_names = "allow" similar_names = "allow"

View file

@ -11,23 +11,18 @@ pub(crate) struct Search {
} }
impl Search { impl Search {
fn global_justfile_paths() -> Vec<PathBuf> { fn global_justfile_paths() -> Vec<(PathBuf, &'static str)> {
let mut paths = Vec::new(); let mut paths = Vec::new();
if let Some(config_dir) = dirs::config_dir() { if let Some(config_dir) = dirs::config_dir() {
paths.push(config_dir.join("just").join(DEFAULT_JUSTFILE_NAME)); paths.push((config_dir.join("just"), DEFAULT_JUSTFILE_NAME));
} }
if let Some(home_dir) = dirs::home_dir() { if let Some(home_dir) = dirs::home_dir() {
paths.push( paths.push((home_dir.join(".config").join("just"), DEFAULT_JUSTFILE_NAME));
home_dir
.join(".config")
.join("just")
.join(DEFAULT_JUSTFILE_NAME),
);
for justfile_name in JUSTFILE_NAMES { for justfile_name in JUSTFILE_NAMES {
paths.push(home_dir.join(justfile_name)); paths.push((home_dir.clone(), justfile_name));
} }
} }
@ -51,11 +46,7 @@ impl Search {
}) })
} }
SearchConfig::GlobalJustfile => Ok(Self { SearchConfig::GlobalJustfile => Ok(Self {
justfile: Self::global_justfile_paths() justfile: Self::find_global_justfile()?,
.iter()
.find(|path| path.exists())
.cloned()
.ok_or(SearchError::GlobalJustfileNotFound)?,
working_directory: Self::project_root(invocation_directory)?, working_directory: Self::project_root(invocation_directory)?,
}), }),
SearchConfig::WithJustfile { justfile } => { SearchConfig::WithJustfile { justfile } => {
@ -76,6 +67,26 @@ impl Search {
} }
} }
fn find_global_justfile() -> SearchResult<PathBuf> {
for (directory, filename) in Self::global_justfile_paths() {
if let Ok(read_dir) = fs::read_dir(&directory) {
for entry in read_dir {
let entry = entry.map_err(|io_error| SearchError::Io {
io_error,
directory: directory.clone(),
})?;
if let Some(candidate) = entry.file_name().to_str() {
if candidate.eq_ignore_ascii_case(filename) {
return Ok(entry.path());
}
}
}
}
}
Err(SearchError::GlobalJustfileNotFound)
}
/// Find justfile starting from parent directory of current justfile /// Find justfile starting from parent directory of current justfile
pub(crate) fn search_parent_directory(&self) -> SearchResult<Self> { pub(crate) fn search_parent_directory(&self) -> SearchResult<Self> {
let parent = self let parent = self
@ -150,6 +161,7 @@ impl Search {
io_error, io_error,
directory: directory.to_owned(), directory: directory.to_owned(),
})?; })?;
for entry in entries { for entry in entries {
let entry = entry.map_err(|io_error| SearchError::Io { let entry = entry.map_err(|io_error| SearchError::Io {
io_error, io_error,

View file

@ -63,3 +63,20 @@ fn unix() {
.stdout("bar\n") .stdout("bar\n")
.run(); .run();
} }
#[test]
#[cfg(all(unix, not(target_os = "macos")))]
fn case_insensitive() {
let tempdir = tempdir();
let path = tempdir.path().to_owned();
Test::with_tempdir(tempdir)
.no_justfile()
.test_round_trip(false)
.write("just/JUSTFILE", "@default:\n echo foo")
.env("XDG_CONFIG_HOME", path.to_str().unwrap())
.args(["--global-justfile"])
.stdout("foo\n")
.run();
}

View file

@ -42,7 +42,7 @@ fn interrupt_test(arguments: &[&str], justfile: &str) {
} }
#[test] #[test]
#[ignore = "Interrupt tests are flaky."] #[ignore]
fn interrupt_shebang() { fn interrupt_shebang() {
interrupt_test( interrupt_test(
&[], &[],
@ -55,7 +55,7 @@ fn interrupt_shebang() {
} }
#[test] #[test]
#[ignore = "Interrupt tests are flaky."] #[ignore]
fn interrupt_line() { fn interrupt_line() {
interrupt_test( interrupt_test(
&[], &[],
@ -67,7 +67,7 @@ fn interrupt_line() {
} }
#[test] #[test]
#[ignore = "Interrupt tests are flaky."] #[ignore]
fn interrupt_backtick() { fn interrupt_backtick() {
interrupt_test( interrupt_test(
&[], &[],
@ -81,15 +81,16 @@ fn interrupt_backtick() {
} }
#[test] #[test]
#[ignore = "Interrupt tests are flaky."] #[ignore]
fn interrupt_command() { fn interrupt_command() {
interrupt_test(&["--command", "sleep", "1"], ""); interrupt_test(&["--command", "sleep", "1"], "");
} }
// This test is ignored because it is sensitive to the process signal mask.
// Programs like `watchexec` and `cargo-watch` change the signal mask to ignore
// `SIGHUP`, which causes this test to fail.
#[test] #[test]
#[ignore = "This test is sensitive to the process signal mask. Programs like \ #[ignore]
`watchexec` and `cargo-watch` change the signal mask to ignore \
`SIGHUP`, which causes this test to fail."]
fn forwarding() { fn forwarding() {
let just = executable_path("just"); let just = executable_path("just");
@ -155,7 +156,7 @@ fn forwarding() {
} }
#[test] #[test]
#[ignore = "Includes a 500ms wait is often flakey."] #[ignore]
#[cfg(any( #[cfg(any(
target_os = "dragonfly", target_os = "dragonfly",
target_os = "freebsd", target_os = "freebsd",