From a723dc8852f761b65b2f4cd25e958bf656278074 Mon Sep 17 00:00:00 2001 From: Ogulcan Celik Date: Fri, 17 Jul 2026 16:03:47 +0300 Subject: fix(windows): prevent remaining subprocess console flashes refs #1468 --- src/cli/plugin.rs | 4 ++-- src/integration/version.rs | 2 +- src/platform/mod.rs | 4 +++- src/platform/windows.rs | 20 ++++++++++++++++++-- src/sound.rs | 6 ++++-- src/worktree.rs | 10 +++++----- 6 files changed, 33 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/cli/plugin.rs b/src/cli/plugin.rs index 68cf0282..32447d1b 100644 --- a/src/cli/plugin.rs +++ b/src/cli/plugin.rs @@ -849,7 +849,7 @@ fn git_checkout( } fn run_git(cwd: Option<&Path>, args: [&str; N]) -> std::io::Result<()> { - let mut command = Command::new("git"); + let mut command = crate::noninteractive_process::command("git"); command.args(args); if let Some(cwd) = cwd { command.current_dir(cwd); @@ -865,7 +865,7 @@ fn run_git(cwd: Option<&Path>, args: [&str; N]) -> std::io::Resu } fn git_output(cwd: &Path, args: [&str; N]) -> std::io::Result { - let output = Command::new("git") + let output = crate::noninteractive_process::command("git") .args(args) .current_dir(cwd) .stdin(Stdio::null()) diff --git a/src/integration/version.rs b/src/integration/version.rs index c9c2af8d..648b86ae 100644 --- a/src/integration/version.rs +++ b/src/integration/version.rs @@ -47,7 +47,7 @@ pub(crate) fn enforce_agent_version( requirement: &AgentVersionRequirement, ) -> io::Result> { let probe = format!("{} {}", requirement.binary, requirement.args.join(" ")); - let output = match std::process::Command::new(requirement.binary) + let output = match crate::noninteractive_process::command(requirement.binary) .args(requirement.args) .output() { diff --git a/src/platform/mod.rs b/src/platform/mod.rs index 42b9e128..82a6704d 100644 --- a/src/platform/mod.rs +++ b/src/platform/mod.rs @@ -26,7 +26,9 @@ pub enum Signal { } pub(crate) fn detached_custom_command_process(command: &str) -> std::process::Command { - detached_custom_command_process_platform(command) + let mut process = detached_custom_command_process_platform(command); + configure_background_command(&mut process); + process } pub(crate) fn pane_custom_command_pty_builder(command: &str) -> portable_pty::CommandBuilder { diff --git a/src/platform/windows.rs b/src/platform/windows.rs index 46c75141..5b0d647f 100644 --- a/src/platform/windows.rs +++ b/src/platform/windows.rs @@ -671,7 +671,7 @@ mod tests { const CONSOLE_TEST_CHILD_ENV: &str = "HERDR_TEST_CONSOLE_CHILD_MODE"; #[test] - fn background_and_server_daemon_commands_do_not_have_consoles() { + fn windows_background_and_server_daemon_commands_do_not_have_consoles() { if let Some(mode) = std::env::var_os(CONSOLE_TEST_CHILD_ENV) { assert!( unsafe { GetConsoleWindow() }.is_null(), @@ -696,7 +696,7 @@ mod tests { for (mode, configure) in configurations { let mut child = Command::new(&test_exe); child - .arg("background_and_server_daemon_commands_do_not_have_consoles") + .arg("windows_background_and_server_daemon_commands_do_not_have_consoles") .env(CONSOLE_TEST_CHILD_ENV, mode) .stdin(Stdio::null()) .stdout(Stdio::null()) @@ -710,6 +710,22 @@ mod tests { ); } + let command = format!( + r#""{}" windows_background_and_server_daemon_commands_do_not_have_consoles"#, + test_exe.display() + ); + let status = crate::platform::detached_custom_command_process(&command) + .env(CONSOLE_TEST_CHILD_ENV, "detached custom command descendant") + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status() + .expect("spawn detached custom command test child"); + assert!( + status.success(), + "detached custom command descendant opened or inherited a console" + ); + if allocated_console { unsafe { FreeConsole(); diff --git a/src/sound.rs b/src/sound.rs index ef45ccc7..7925069c 100644 --- a/src/sound.rs +++ b/src/sound.rs @@ -6,7 +6,9 @@ use std::io::Write; use std::path::{Path, PathBuf}; -use std::process::{Command, Output}; +#[cfg(not(windows))] +use std::process::Command; +use std::process::Output; use std::sync::atomic::{AtomicU64, Ordering}; use tracing::warn; @@ -142,7 +144,7 @@ if (-not $script:done) { throw 'sound playback timed out' } #[cfg(windows)] fn run_windows_player(path: &Path) -> Result { - Command::new("powershell.exe") + crate::noninteractive_process::command("powershell.exe") .args([ "-NoLogo", "-NoProfile", diff --git a/src/worktree.rs b/src/worktree.rs index 33b03d56..0bb091d7 100644 --- a/src/worktree.rs +++ b/src/worktree.rs @@ -199,7 +199,7 @@ pub(crate) fn worktree_dirty_remove_message(path: &Path) -> String { #[cfg(any(windows, test))] pub(crate) fn checkout_has_dirty_files(path: &Path) -> Result { let path_arg = path.display().to_string(); - let output = std::process::Command::new("git") + let output = crate::noninteractive_process::command("git") .args([ "-C", &path_arg, @@ -265,7 +265,7 @@ pub(crate) fn build_worktree_add_existing_branch_command( } pub(crate) fn local_branch_exists(repo_root: &Path, branch: &str) -> Result { - let output = std::process::Command::new("git") + let output = crate::noninteractive_process::command("git") .arg("-C") .arg(repo_root) .args(["show-ref", "--verify", "--quiet"]) @@ -306,7 +306,7 @@ pub(crate) fn run_worktree_add_command( } pub(crate) fn run_worktree_command(command: &WorktreeCommand) -> Result<(), String> { - let output = std::process::Command::new(&command.program) + let output = crate::noninteractive_process::command(&command.program) .args(&command.args) .output() .map_err(|err| err.to_string())?; @@ -375,7 +375,7 @@ fn leftover_worktree_checkout_matches_repo(repo_root: &Path, path: &Path) -> boo } fn git_common_worktrees_dir(repo_root: &Path) -> Option { - let output = std::process::Command::new("git") + let output = crate::noninteractive_process::command("git") .arg("-C") .arg(repo_root) .args(["rev-parse", "--git-common-dir"]) @@ -471,7 +471,7 @@ pub(crate) fn parse_worktree_list_porcelain(output: &str) -> Vec Result, String> { - let output = std::process::Command::new("git") + let output = crate::noninteractive_process::command("git") .arg("-C") .arg(repo_root) .args(["worktree", "list", "--porcelain"]) -- cgit v1.2.3