mirror of
https://github.com/openai/codex.git
synced 2026-10-20 17:38:45 +00:00
## Why Background helpers launched from a detached Windows process can allocate console windows. Job Object launches and containment fallbacks need to preserve console suppression as well. ## What changed - Add `codex_utils_process::background_command` to set `CREATE_NO_WINDOW` on Windows, and use it across background subprocess launches, including Git, plugins, authentication, diagnostics, and sandbox helpers. - Centralize console suppression in Job Object launches. Keep `CREATE_NO_WINDOW` when retrying without containment, and clear `CREATE_SUSPENDED` so the fallback child can run. - Preserve console inheritance for interactive shell launches and interactive AWS authentication refreshes. ## Testing Add Windows regression tests covering console-free launches from detached parents through standard and Tokio commands, stdout/stderr capture, successful containment, Job Object creation and assignment failures, and sandbox setup refresh helpers. GitOrigin-RevId: b2c238fa7d4f9fbd868e5adf7d4be9958ff01e6c
55 lines
2.2 KiB
Rust
55 lines
2.2 KiB
Rust
//! Verifies detached background children stay console-free through std and Tokio launches.
|
|
|
|
use super::background_command;
|
|
use pretty_assertions::assert_eq;
|
|
use std::os::windows::process::CommandExt;
|
|
use std::process::Command;
|
|
|
|
#[tokio::test]
|
|
async fn background_commands_have_no_console_under_detached_parent() {
|
|
const TEST_NAME: &str = "tests::background_commands_have_no_console_under_detached_parent";
|
|
const PHASE: &str = "CODEX_TEST_BACKGROUND_COMMAND_PHASE";
|
|
let executable = std::env::current_exe().expect("test executable");
|
|
match std::env::var(PHASE).as_deref() {
|
|
Ok("probe") => {
|
|
#[link(name = "kernel32")]
|
|
unsafe extern "system" {
|
|
#[link_name = "GetConsoleWindow"]
|
|
fn get_console_window() -> isize;
|
|
}
|
|
assert_eq!(unsafe { get_console_window() }, 0);
|
|
println!("background stdout");
|
|
eprintln!("background stderr");
|
|
}
|
|
Ok("detached") => {
|
|
for asynchronous in [false, true] {
|
|
let mut command = background_command(&executable);
|
|
command
|
|
.args(["--exact", TEST_NAME, "--nocapture"])
|
|
.env(PHASE, "probe");
|
|
let output = if asynchronous {
|
|
tokio::process::Command::from(command).output().await
|
|
} else {
|
|
command.output()
|
|
}
|
|
.expect("run background command");
|
|
assert!(output.status.success(), "probe failed: {output:?}");
|
|
assert!(String::from_utf8_lossy(&output.stdout).contains("background stdout"));
|
|
assert!(String::from_utf8_lossy(&output.stderr).contains("background stderr"));
|
|
}
|
|
}
|
|
_ => {
|
|
let output = Command::new(executable)
|
|
.args(["--exact", TEST_NAME, "--nocapture"])
|
|
.env(PHASE, "detached")
|
|
.creation_flags(/*flags*/ 0x0000_0008) // DETACHED_PROCESS
|
|
.output()
|
|
.expect("spawn detached parent");
|
|
assert!(
|
|
output.status.success(),
|
|
"detached parent failed: {output:?}"
|
|
);
|
|
}
|
|
}
|
|
}
|