From 957cab4650d9c10c2bb939be551a0d93254b5d30 Mon Sep 17 00:00:00 2001 From: David Wiesen Date: Sat, 21 Mar 2026 10:38:09 -0700 Subject: [PATCH] Handle PowerShell -NonInteractive apply_patch wrappers --- codex-rs/apply-patch/src/invocation.rs | 39 ++++++++++++++++++++---- codex-rs/shell-command/src/powershell.rs | 17 ++++++++++- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/codex-rs/apply-patch/src/invocation.rs b/codex-rs/apply-patch/src/invocation.rs index 7623aef80d..bf6f1caf33 100644 --- a/codex-rs/apply-patch/src/invocation.rs +++ b/codex-rs/apply-patch/src/invocation.rs @@ -68,7 +68,11 @@ fn classify_shell(shell: &str, flag: &str) -> Option { fn can_skip_flag(shell: &str, flag: &str) -> bool { classify_shell_name(shell).is_some_and(|name| { - matches!(name.as_str(), "pwsh" | "powershell") && flag.eq_ignore_ascii_case("-noprofile") + matches!(name.as_str(), "pwsh" | "powershell") + && matches!( + flag.to_ascii_lowercase().as_str(), + "-noprofile" | "-nologo" | "-noninteractive" + ) }) } @@ -78,11 +82,16 @@ fn parse_shell_script(argv: &[String]) -> Option<(ApplyPatchShell, &str)> { let script = script.as_str(); (shell_type, script) }), - [shell, skip_flag, flag, script] if can_skip_flag(shell, skip_flag) => { - classify_shell(shell, flag).map(|shell_type| { - let script = script.as_str(); - (shell_type, script) - }) + [shell, flags @ .., script] if !flags.is_empty() => { + let (flag, skip_flags) = flags.split_last()?; + if skip_flags.iter().all(|skip_flag| can_skip_flag(shell, skip_flag)) { + classify_shell(shell, flag).map(|shell_type| { + let script = script.as_str(); + (shell_type, script) + }) + } else { + None + } } _ => None, } @@ -400,6 +409,17 @@ mod tests { strs_to_strings(&["powershell.exe", "-NoProfile", "-Command", script]) } + fn args_powershell_noninteractive(script: &str) -> Vec { + strs_to_strings(&[ + "powershell.exe", + "-NoLogo", + "-NoProfile", + "-NonInteractive", + "-Command", + script, + ]) + } + fn args_pwsh(script: &str) -> Vec { strs_to_strings(&["pwsh", "-NoProfile", "-Command", script]) } @@ -572,6 +592,13 @@ PATCH"#, let script = heredoc_script(""); assert_match_args(args_powershell_no_profile(&script), None); } + + #[test] + fn test_powershell_heredoc_noninteractive() { + let script = heredoc_script(""); + assert_match_args(args_powershell_noninteractive(&script), None); + } + #[test] fn test_pwsh_heredoc() { let script = heredoc_script(""); diff --git a/codex-rs/shell-command/src/powershell.rs b/codex-rs/shell-command/src/powershell.rs index 4d3b553333..063441b790 100644 --- a/codex-rs/shell-command/src/powershell.rs +++ b/codex-rs/shell-command/src/powershell.rs @@ -5,7 +5,8 @@ use codex_utils_absolute_path::AbsolutePathBuf; use crate::shell_detect::ShellType; use crate::shell_detect::detect_shell_type; -const POWERSHELL_FLAGS: &[&str] = &["-nologo", "-noprofile", "-command", "-c"]; +const POWERSHELL_FLAGS: &[&str] = + &["-nologo", "-noprofile", "-noninteractive", "-command", "-c"]; /// Prefixed command for powershell shell calls to force UTF-8 console output. pub const UTF8_OUTPUT_PREFIX: &str = "[Console]::OutputEncoding=[System.Text.Encoding]::UTF8;\n"; @@ -201,4 +202,18 @@ mod tests { let (_shell, script) = extract_powershell_command(&cmd).expect("extract"); assert_eq!(script, "Get-ChildItem | Select-String foo"); } + + #[test] + fn extracts_with_noninteractive_wrapper() { + let cmd = vec![ + "powershell.exe".to_string(), + "-NoLogo".to_string(), + "-NoProfile".to_string(), + "-NonInteractive".to_string(), + "-Command".to_string(), + "Write-Host hi".to_string(), + ]; + let (_shell, script) = extract_powershell_command(&cmd).expect("extract"); + assert_eq!(script, "Write-Host hi"); + } }