From 38ade1742c46b2840ad23411b3105766bd32338c Mon Sep 17 00:00:00 2001 From: Andrew Moore Date: Sun, 9 Aug 2026 16:37:41 -0700 Subject: [PATCH] Merge nucleic/tidy-thistle-egret-5vjm into dev --- NUCLEIC_FORK.md | 1 + brush-builtins/src/dot.rs | 28 +++++++++++++++---- .../tests/cases/compat/builtins/dot.yaml | 10 +++++++ 3 files changed, 33 insertions(+), 6 deletions(-) diff --git a/NUCLEIC_FORK.md b/NUCLEIC_FORK.md index 56acdc8..5660200 100644 --- a/NUCLEIC_FORK.md +++ b/NUCLEIC_FORK.md @@ -15,6 +15,7 @@ Every fork change is marked with a `// nash:` comment. Current patches: | `brush-core/src/expansion.rs` | corpus divergence D1: added `ExpansionPiece::LiteralText` — literal unquoted text is never field-split (bash splits only expansion results) but keeps glob characters active. Parameter-expansion substitutions (`${v:-word}` etc.) convert back to splittable at the expansion boundary; `ExpanderOptions.field_split_literal_text` lets data-string callers opt in. Upstream candidate. | | `brush-core/src/completion.rs` | `compgen -W` word list opts into `field_split_literal_text` (the -W string is data) | | `brush-shell/tests/cases/compat/ifs.yaml` | removed `known_failure` from 3 IFS tests the D1 fix repairs | +| `brush-builtins/src/dot.rs` + `brush-shell/tests/cases/compat/builtins/dot.yaml` | the `.` / `source` builtin searches `PATH` for slashless operands when `sourcepath` is enabled, as Bash/POSIX require; fixes `git-subtree` sourcing `git-sh-setup` | | `brush-core/src/gate.rs` (new) + `lib.rs` | the `Gate` trait (docs/NASH.md §4.2): process-global, verdict-shaped hook; allow-all default so an unconfigured shell behaves exactly like upstream | | `brush-core/src/commands.rs` | `SimpleCommand::execute` split into gate wrapper + `execute_inner`: `on_exec` before dispatch (Deny short-circuits), completion correlated through all three spawn-result variants | | `brush-core/src/processes.rs` | `ChildProcess.gate_token` + `on_exit` reporting from `wait()`/`poll()` (128+signal for signal deaths) | diff --git a/brush-builtins/src/dot.rs b/brush-builtins/src/dot.rs index 7bf5a4b..a53e796 100644 --- a/brush-builtins/src/dot.rs +++ b/brush-builtins/src/dot.rs @@ -1,4 +1,4 @@ -use std::path::Path; +use std::path::{Path, PathBuf}; use brush_core::builtins; use clap::Parser; @@ -22,13 +22,29 @@ impl builtins::Command for DotCommand { context: brush_core::ExecutionContext<'_, SE>, ) -> Result { // TODO(dot): Handle trap inheritance. + // nash: POSIX requires `.` to search PATH when its operand has no slash. + // Git's `git-subtree` relies on this with `. git-sh-setup`; treating the + // operand as cwd-relative leaves `require_work_tree` undefined. + let script_path = Path::new(&self.script_path); + let resolved_path = if context.shell.options().source_builtin_searches_path + && !self.script_path.chars().any(std::path::is_separator) + { + context + .shell + .env_str("PATH") + .and_then(|path| { + std::env::split_paths(path.as_ref()) + .map(|dir| dir.join(script_path)) + .find(|candidate| candidate.is_file()) + }) + .unwrap_or_else(|| PathBuf::from(script_path)) + } else { + PathBuf::from(script_path) + }; + context .shell - .source_script( - Path::new(&self.script_path), - self.script_args.iter(), - &context.params, - ) + .source_script(&resolved_path, self.script_args.iter(), &context.params) .await } } diff --git a/brush-shell/tests/cases/compat/builtins/dot.yaml b/brush-shell/tests/cases/compat/builtins/dot.yaml index a583e04..b606608 100644 --- a/brush-shell/tests/cases/compat/builtins/dot.yaml +++ b/brush-shell/tests/cases/compat/builtins/dot.yaml @@ -11,6 +11,16 @@ cases: . script.sh echo "var: ${var}" + - name: "Dot searches PATH for git setup scripts" + test_files: + - path: "git-core/git-sh-setup" + contents: | + require_work_tree() { echo "sourced git setup"; } + stdin: | + PATH="$PWD/git-core:$PATH" + . git-sh-setup + require_work_tree + - name: "Basic source usage" test_files: - path: "script.sh"