Repository navigation
Expand file tree
/
Copy pathPtyTestProcess.ts
More file actions
85 lines (80 loc) · 2.79 KB
/
Copy pathPtyTestProcess.ts
File metadata and controls
85 lines (80 loc) · 2.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
import { spawn, spawnSync } from 'node:child_process';
import { once } from 'node:events';
export const canRunPtyTests: boolean =
process.platform === 'linux' &&
spawnSync('python3', ['-c', 'import pty'], { stdio: 'ignore' }).status === 0;
interface IPtyResult {
readonly exitCode: number;
readonly output: string;
readonly restored: boolean;
}
const PYTHON_DRIVER: string = `
import errno,fcntl,json,os,pty,select,signal,struct,subprocess,sys,termios,time
master,slave=pty.openpty()
fcntl.ioctl(slave,termios.TIOCSWINSZ,struct.pack('HHHH',24,123,0,0))
def attach_terminal():
os.setsid()
fcntl.ioctl(1,termios.TIOCSCTTY,0)
child=subprocess.Popen(sys.argv[2:]+['-q','tty'],
stdin=slave if sys.argv[1]=='tty' else subprocess.DEVNULL,stdout=slave,stderr=slave,
close_fds=True,preexec_fn=attach_terminal)
output=b''
signaled=False
try:
deadline=time.monotonic()+15
while time.monotonic()<deadline:
if select.select([master],[],[],0.1)[0]:
try:
chunk=os.read(master,65536)
except OSError as error:
if error.errno!=errno.EIO: raise
break
if not chunk: break
output+=chunk
if not signaled and b'raw-ready' in output:
with open('tty.pid') as pid_file:
os.kill(int(pid_file.read()),signal.SIGINT)
signaled=True
elif child.poll() is not None:
break
if child.poll() is None:
raise RuntimeError('PTY child did not finish within its deadline')
state=termios.tcgetattr(slave)[3]
print(json.dumps({'exitCode':child.wait(),'output':output.decode('utf8'),
'restored':bool(state & termios.ICANON and state & termios.ECHO)}))
finally:
if child.poll() is None:
os.killpg(child.pid,signal.SIGKILL)
child.wait()
os.close(master)
os.close(slave)
`;
export async function invokeRushxPtyAsync(
entry: string,
cwd: string,
environment: NodeJS.ProcessEnv,
stdinIsTTY: boolean
): Promise<IPtyResult> {
const child = spawn(
'python3',
['-c', PYTHON_DRIVER, stdinIsTTY ? 'tty' : 'pipe', process.execPath, entry],
{
cwd,
env: environment,
stdio: ['ignore', 'pipe', 'pipe']
}
);
let stdout: string = '';
let stderr: string = '';
child.stdout.on('data', (bytes: Buffer) => {
stdout += bytes.toString();
});
child.stderr.on('data', (bytes: Buffer) => {
stderr += bytes.toString();
});
const [code] = await once(child, 'close');
if (code !== 0) throw new Error(`PTY driver failed (${code}): ${stderr}`);
return JSON.parse(stdout);
}