chore(core): fix command info (#30720)

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

"Printing" the command info before output causes `run-script` to hang
and not run. In addition, the screen was cleared.

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

The command in run-script runs without issues. The screen is not
cleared.

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
This commit is contained in:
Jason Jean 2025-04-14 17:31:14 -04:00
parent 9a60dec0de
commit 016e5fda2a
2 changed files with 21 additions and 27 deletions

View File

@ -64,6 +64,7 @@ async function ptyProcess(
env: Record<string, string>
) {
const terminal = createPseudoTerminal();
await terminal.init();
return new Promise<void>((res, rej) => {
const cp = terminal.runCommand(command, { cwd, jsEnv: env });

View File

@ -1,10 +1,11 @@
use anyhow::anyhow;
use crossbeam_channel::{bounded, unbounded, Receiver};
use crossbeam_channel::{bounded, unbounded, Receiver, Sender};
use crossterm::{
terminal,
terminal::{disable_raw_mode, enable_raw_mode},
tty::IsTty,
};
use itertools::Itertools;
use napi::bindgen_prelude::*;
use portable_pty::{CommandBuilder, NativePtySystem, PtyPair, PtySize, PtySystem};
use std::io::stdout;
@ -27,7 +28,8 @@ use crate::native::pseudo_terminal::child_process::ChildProcess;
pub struct PseudoTerminal {
pub pty_pair: PtyPair,
pub message_rx: Receiver<String>,
pub stdout_tx: Sender<String>,
pub stdout_rx: Receiver<String>,
pub printing_rx: Receiver<()>,
pub quiet: Arc<AtomicBool>,
pub running: Arc<AtomicBool>,
@ -84,7 +86,7 @@ impl PseudoTerminal {
}
let mut reader = pty_pair.master.try_clone_reader()?;
let (message_tx, message_rx) = unbounded();
let (stdout_tx, stdout_rx) = unbounded();
let (printing_tx, printing_rx) = unbounded();
// Output -> stdout handling
let quiet_clone = quiet.clone();
@ -92,25 +94,21 @@ impl PseudoTerminal {
let parser = Arc::new(RwLock::new(Parser::new(h, w, 10000)));
let parser_clone = parser.clone();
let stdout_tx_clone = stdout_tx.clone();
std::thread::spawn(move || {
let mut stdout = std::io::stdout();
let mut buf = [0; 8 * 1024];
let mut first: bool = true;
'read_loop: loop {
if let Ok(len) = reader.read(&mut buf) {
if len == 0 {
break;
}
message_tx
stdout_tx_clone
.send(String::from_utf8_lossy(&buf[0..len]).to_string())
.ok();
let quiet = quiet_clone.load(Ordering::Relaxed);
trace!("Quiet: {}", quiet);
let contains_clear = buf[..len]
.windows(4)
.any(|window| window == [0x1B, 0x5B, 0x32, 0x4A]);
debug!("Contains clear: {}", contains_clear);
debug!("Read {} bytes", len);
if let Ok(mut parser) = parser_clone.write() {
let prev = parser.screen().clone();
@ -118,12 +116,7 @@ impl PseudoTerminal {
parser.process(&buf[..len]);
debug!("{}", parser.get_raw_output().len());
let write_buf = if first {
parser.screen().contents_formatted()
} else {
parser.screen().contents_diff(&prev)
};
first = false;
let write_buf = parser.screen().contents_diff(&prev);
if !quiet {
let mut logged_interrupted_error = false;
while let Err(e) = stdout.write_all(&write_buf) {
@ -162,7 +155,8 @@ impl PseudoTerminal {
running,
parser,
pty_pair,
message_rx,
stdout_rx,
stdout_tx,
printing_rx,
is_within_nx_tui,
})
@ -204,17 +198,16 @@ impl PseudoTerminal {
}
let (exit_to_process_tx, exit_to_process_rx) = bounded(1);
let command_info = format!("> {}\n\n\r", command);
self.stdout_tx.send(command_info.clone()).ok();
self.parser
.write()
.expect("Failed to acquire parser write lock")
.process(command_info.as_bytes());
trace!("Running {}", command);
// TODO(@FrozenPandaz): This access is too naive, we need to handle the writer lock properly (e.g. multiple invocations of run_command sequentially)
// Prepend the command to the output
self.writer
.lock()
.unwrap()
// Sadly ANSI escape codes don't seem to work properly when writing directly to the writer...
.write_all(format!("> {}\n\n", command).as_bytes())
.unwrap();
let mut child = pair.slave.spawn_command(cmd)?;
self.running.store(true, Ordering::SeqCst);
@ -270,7 +263,7 @@ impl PseudoTerminal {
self.parser.clone(),
self.writer.clone(),
process_killer,
self.message_rx.clone(),
self.stdout_rx.clone(),
exit_to_process_rx,
))
}