diff --git a/crates/turborepo-lib/src/lib.rs b/crates/turborepo-lib/src/lib.rs index aa028106a399b..629d880152125 100644 --- a/crates/turborepo-lib/src/lib.rs +++ b/crates/turborepo-lib/src/lib.rs @@ -1,5 +1,6 @@ #![feature(assert_matches)] #![feature(box_patterns)] +#![feature(byte_slice_trim_ascii)] #![feature(error_generic_member_access)] #![feature(hash_extract_if)] #![feature(option_get_or_insert_default)] diff --git a/crates/turborepo-lib/src/process/child.rs b/crates/turborepo-lib/src/process/child.rs index 26442c0f6af91..8f8e74934600d 100644 --- a/crates/turborepo-lib/src/process/child.rs +++ b/crates/turborepo-lib/src/process/child.rs @@ -161,6 +161,26 @@ impl ChildHandle { let controller = pair.master; let receiver = pair.slave; + #[cfg(unix)] + { + use nix::sys::termios; + if let Some((file_desc, mut termios)) = controller + .as_raw_fd() + .and_then(|fd| Some(fd).zip(termios::tcgetattr(fd).ok())) + { + // We unset ECHOCTL to disable rendering of the closing of stdin + // as ^D + termios.local_flags &= !nix::sys::termios::LocalFlags::ECHOCTL; + if let Err(e) = nix::sys::termios::tcsetattr( + file_desc, + nix::sys::termios::SetArg::TCSANOW, + &termios, + ) { + debug!("unable to unset ECHOCTL: {e}"); + } + } + } + let child = receiver .spawn_command(command) .map_err(|err| match err.downcast() { @@ -762,6 +782,7 @@ mod test { const STARTUP_DELAY: Duration = Duration::from_millis(500); // We skip testing PTY usage on Windows const TEST_PTY: bool = !cfg!(windows); + const EOT: char = '\u{4}'; fn find_script_dir() -> AbsoluteSystemPathBuf { let cwd = AbsoluteSystemPathBuf::cwd().unwrap(); @@ -845,8 +866,10 @@ mod test { }; let output_str = String::from_utf8(output).expect("Failed to parse stdout"); + let trimmed_output = output_str.trim(); + let trimmed_output = trimmed_output.strip_prefix(EOT).unwrap_or(trimmed_output); - assert!(output_str.contains("hello world"), "got: {}", output_str); + assert_eq!(trimmed_output, "hello world"); } child.wait().await; @@ -884,8 +907,10 @@ mod test { }; let output_str = String::from_utf8(output).expect("Failed to parse stdout"); + let trimmed_out = output_str.trim(); + let trimmed_out = trimmed_out.strip_prefix(EOT).unwrap_or(trimmed_out); - assert!(output_str.contains(input), "got: {}", output_str); + assert!(trimmed_out.contains(input), "got: {}", trimmed_out); child.wait().await; @@ -1031,8 +1056,10 @@ mod test { let exit = child.wait_with_piped_outputs(&mut out).await.unwrap(); let out = String::from_utf8(out).unwrap(); + let trimmed_out = out.trim(); + let trimmed_out = trimmed_out.strip_prefix(EOT).unwrap_or(trimmed_out); - assert!(out.contains("hello world"), "got: {}", out); + assert_eq!(trimmed_out, "hello world"); assert_matches!(exit, Some(ChildExit::Finished(Some(0)))); } @@ -1073,11 +1100,9 @@ mod test { let exit = child.wait_with_piped_outputs(&mut out).await.unwrap(); let expected = &[0, 159, 146, 150]; - assert!( - out.windows(4).any(|actual| actual == expected), - "got: {:?}", - out - ); + let trimmed_out = out.trim_ascii(); + let trimmed_out = trimmed_out.strip_prefix(&[4]).unwrap_or(trimmed_out); + assert_eq!(trimmed_out, expected); assert_matches!(exit, Some(ChildExit::Finished(Some(0)))); }