Skip to content

Commit

Permalink
feat(core): forward stdin to commands started via rust (#21195)
Browse files Browse the repository at this point in the history
Co-authored-by: Jonathan Cammisuli <jon@cammisuli.ca>
  • Loading branch information
AgentEnder and Cammisuli authored Jan 22, 2024
1 parent 33e1391 commit cb5eeb7
Show file tree
Hide file tree
Showing 9 changed files with 81 additions and 33 deletions.
2 changes: 1 addition & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
58 changes: 47 additions & 11 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/nx/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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']
Expand Down
2 changes: 0 additions & 2 deletions packages/nx/src/command-line/affected/affected.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 0 additions & 2 deletions packages/nx/src/command-line/release/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
2 changes: 0 additions & 2 deletions packages/nx/src/command-line/run-many/run-many.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
2 changes: 0 additions & 2 deletions packages/nx/src/command-line/run/run-one.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
25 changes: 22 additions & 3 deletions packages/nx/src/native/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
})?;
Expand All @@ -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];
Expand Down Expand Up @@ -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();
});

Expand Down
19 changes: 10 additions & 9 deletions packages/nx/src/native/tests/command.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
});
});
Expand Down

1 comment on commit cb5eeb7

@vercel
Copy link

@vercel vercel bot commented on cb5eeb7 Jan 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nx-dev – ./

nx-dev-nrwl.vercel.app
nx.dev
nx-dev-git-master-nrwl.vercel.app
nx-five.vercel.app

Please sign in to comment.