feat(core): forward stdin to commands started via rust (#21195)
Co-authored-by: Jonathan Cammisuli <jon@cammisuli.ca>
This commit is contained in:
parent
33e13910b1
commit
cb5eeb7475
@ -16,7 +16,7 @@ packages/jest/src/schematics/**/files/**/*.json
|
||||
packages/nx/src/plugins/js/lock-file/__fixtures__/**/*.*
|
||||
packages/**/schematics/**/files/**/*.html
|
||||
packages/**/generators/**/files/**/*.html
|
||||
packages/nx/src/native/
|
||||
packages/nx/src/native/**/*.rs
|
||||
nx-dev/nx-dev/.next/
|
||||
nx-dev/nx-dev/public/documentation
|
||||
graph/client/src/assets/environment.js
|
||||
|
||||
58
Cargo.lock
generated
58
Cargo.lock
generated
@ -346,6 +346,31 @@ dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm"
|
||||
version = "0.27.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"crossterm_winapi",
|
||||
"libc",
|
||||
"mio",
|
||||
"parking_lot",
|
||||
"signal-hook",
|
||||
"signal-hook-mio",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossterm_winapi"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.2.0"
|
||||
@ -1503,6 +1528,7 @@ dependencies = [
|
||||
"assert_fs",
|
||||
"colored",
|
||||
"crossbeam-channel",
|
||||
"crossterm",
|
||||
"dashmap",
|
||||
"dunce",
|
||||
"fs_extra",
|
||||
@ -1526,7 +1552,6 @@ dependencies = [
|
||||
"swc_ecma_dep_graph",
|
||||
"swc_ecma_parser",
|
||||
"swc_ecma_visit",
|
||||
"term_size",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@ -2080,6 +2105,27 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-mio"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"mio",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.1"
|
||||
@ -2383,16 +2429,6 @@ dependencies = [
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "term_size"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termios"
|
||||
version = "0.2.2"
|
||||
|
||||
@ -42,7 +42,7 @@ swc_common = "0.31.16"
|
||||
swc_ecma_parser = { version = "0.137.1", features = ["typescript"] }
|
||||
swc_ecma_visit = "0.93.0"
|
||||
swc_ecma_ast = "0.107.0"
|
||||
term_size = "0.3.2"
|
||||
crossterm = "0.27.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ['cdylib']
|
||||
|
||||
@ -114,8 +114,6 @@ export async function affected(
|
||||
extraTargetDependencies,
|
||||
{ excludeTaskDependencies: false, loadDotEnvFiles: true }
|
||||
);
|
||||
// fix for https://github.com/nrwl/nx/issues/1666
|
||||
if (process.stdin['unref']) (process.stdin as any).unref();
|
||||
process.exit(status);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -177,8 +177,6 @@ async function runPublishOnProjects(
|
||||
);
|
||||
|
||||
if (status !== 0) {
|
||||
// fix for https://github.com/nrwl/nx/issues/1666
|
||||
if (process.stdin['unref']) (process.stdin as any).unref();
|
||||
process.exit(status);
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,8 +75,6 @@ export async function runMany(
|
||||
extraTargetDependencies,
|
||||
extraOptions
|
||||
);
|
||||
// fix for https://github.com/nrwl/nx/issues/1666
|
||||
if (process.stdin['unref']) (process.stdin as any).unref();
|
||||
process.exit(status);
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,8 +89,6 @@ export async function runOne(
|
||||
extraTargetDependencies,
|
||||
extraOptions
|
||||
);
|
||||
// fix for https://github.com/nrwl/nx/issues/1666
|
||||
if (process.stdin['unref']) (process.stdin as any).unref();
|
||||
process.exit(status);
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,8 @@ use std::{
|
||||
|
||||
use anyhow::anyhow;
|
||||
use crossbeam_channel::{bounded, unbounded, Receiver};
|
||||
use crossterm::terminal::{self, disable_raw_mode, enable_raw_mode};
|
||||
use crossterm::tty::IsTty;
|
||||
use napi::threadsafe_function::ErrorStrategy::Fatal;
|
||||
use napi::threadsafe_function::ThreadsafeFunction;
|
||||
use napi::threadsafe_function::ThreadsafeFunctionCallMode::NonBlocking;
|
||||
@ -126,10 +128,10 @@ pub fn run_command(
|
||||
|
||||
let pty_system = NativePtySystem::default();
|
||||
|
||||
let (w, h) = term_size::dimensions().unwrap_or((80, 24));
|
||||
let (w, h) = terminal::size().unwrap_or((80, 24));
|
||||
let pair = pty_system.openpty(PtySize {
|
||||
rows: h as u16,
|
||||
cols: w as u16,
|
||||
rows: h,
|
||||
cols: w,
|
||||
pixel_width: 0,
|
||||
pixel_height: 0,
|
||||
})?;
|
||||
@ -148,6 +150,8 @@ pub fn run_command(
|
||||
|
||||
let reader = pair.master.try_clone_reader()?;
|
||||
let mut stdout = std::io::stdout();
|
||||
|
||||
// Output -> stdout handling
|
||||
std::thread::spawn(move || {
|
||||
let mut reader = BufReader::new(reader);
|
||||
let mut buffer = [0; 8 * 1024];
|
||||
@ -182,10 +186,25 @@ pub fn run_command(
|
||||
|
||||
let process_killer = child.clone_killer();
|
||||
let (exit_tx, exit_rx) = bounded(1);
|
||||
|
||||
let mut writer = pair.master.take_writer()?;
|
||||
|
||||
// Stdin -> pty stdin
|
||||
if std::io::stdout().is_tty() {
|
||||
std::thread::spawn(move || {
|
||||
enable_raw_mode().expect("Failed to enter raw terminal mode");
|
||||
let mut stdin = std::io::stdin();
|
||||
#[allow(clippy::redundant_pattern_matching)]
|
||||
// ignore errors that come from copying the stream
|
||||
if let Ok(_) = std::io::copy(&mut stdin, &mut writer) {}
|
||||
});
|
||||
}
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let exit = child.wait().unwrap();
|
||||
// make sure that master is only dropped after we wait on the child. Otherwise windows does not like it
|
||||
drop(pair.master);
|
||||
disable_raw_mode().expect("Failed to restore non-raw terminal");
|
||||
exit_tx.send(exit.exit_code()).ok();
|
||||
});
|
||||
|
||||
|
||||
@ -10,39 +10,40 @@ describe('runCommand', () => {
|
||||
});
|
||||
it('should kill a running command', () => {
|
||||
const childProcess = new PseudoTtyProcess(
|
||||
runCommand(
|
||||
'sleep 3 && echo "hello world" > file.txt',
|
||||
process.cwd()
|
||||
)
|
||||
runCommand('sleep 3 && echo "hello world" > file.txt', process.cwd())
|
||||
);
|
||||
childProcess.onExit((exit_code) => {
|
||||
expect(exit_code).not.toEqual(0);
|
||||
});
|
||||
childProcess.kill();
|
||||
expect(childProcess.isAlive).toEqual(false);
|
||||
|
||||
}, 1000);
|
||||
|
||||
it('should subscribe to output', (done) => {
|
||||
const childProcess = runCommand('echo "hello world"', process.cwd());
|
||||
|
||||
childProcess.onOutput((output) => {
|
||||
expect(output.trim()).toEqual('hello world');
|
||||
let output = '';
|
||||
childProcess.onOutput((chunk) => {
|
||||
output += chunk;
|
||||
});
|
||||
|
||||
childProcess.onExit(() => {
|
||||
expect(output.trim()).toEqual('hello world');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be tty', (done) => {
|
||||
const childProcess = runCommand('node -p "process.stdout.isTTY"');
|
||||
let output = '';
|
||||
childProcess.onOutput((out) => {
|
||||
let output = JSON.stringify(out.trim());
|
||||
output += out.trim();
|
||||
// check to make sure that we have ansi sequence characters only available in tty terminals
|
||||
expect(output).toMatchInlineSnapshot(`""\\u001b[33mtrue\\u001b[39m""`);
|
||||
});
|
||||
childProcess.onExit((_) => {
|
||||
expect(JSON.stringify(output)).toMatchInlineSnapshot(
|
||||
`""\\u001b[33mtrue\\u001b[39m""`
|
||||
);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user