Skip to content

Commit cda21c1

Browse files
authored
cli: fix attach does not always work (microsoft#181273)
* fix: don't sync debug.lastExtensionDevelopmentWorkspace * cli: fix attach does not always work Seems like reading stdin when it's open but never written to blocks the process. Fix that, both by checking IS_INTERACTIVE_CLI before reading stdin, and by not passing stdin to the tunnel subprocess. Fixes microsoft#179122
1 parent e78d4b7 commit cda21c1

File tree

3 files changed

+35
-30
lines changed

3 files changed

+35
-30
lines changed

cli/src/tunnels/singleton_client.rs

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -63,29 +63,31 @@ pub async fn start_singleton_client(args: SingletonClientArgs) -> bool {
6363
"An existing tunnel is running on this machine, connecting to it..."
6464
);
6565

66-
let stdin_handle = rpc.get_caller(msg_tx.clone());
67-
thread::spawn(move || {
68-
let mut input = String::new();
69-
loop {
70-
input.truncate(0);
71-
72-
match std::io::stdin().read_line(&mut input) {
73-
Err(_) | Ok(0) => return, // EOF or not a tty
74-
_ => {}
75-
};
76-
77-
match input.chars().next().map(|c| c.to_ascii_lowercase()) {
78-
Some('x') => {
79-
stdin_handle.notify(protocol::singleton::METHOD_SHUTDOWN, EmptyObject {});
80-
return;
66+
if *IS_INTERACTIVE_CLI {
67+
let stdin_handle = rpc.get_caller(msg_tx.clone());
68+
thread::spawn(move || {
69+
let mut input = String::new();
70+
loop {
71+
input.truncate(0);
72+
println!("reading line");
73+
match std::io::stdin().read_line(&mut input) {
74+
Err(_) | Ok(0) => return, // EOF or not a tty
75+
_ => {}
76+
};
77+
78+
match input.chars().next().map(|c| c.to_ascii_lowercase()) {
79+
Some('x') => {
80+
stdin_handle.notify(protocol::singleton::METHOD_SHUTDOWN, EmptyObject {});
81+
return;
82+
}
83+
Some('r') => {
84+
stdin_handle.notify(protocol::singleton::METHOD_RESTART, EmptyObject {});
85+
}
86+
Some(_) | None => {}
8187
}
82-
Some('r') => {
83-
stdin_handle.notify(protocol::singleton::METHOD_RESTART, EmptyObject {});
84-
}
85-
Some(_) | None => {}
8688
}
87-
}
88-
});
89+
});
90+
}
8991

9092
let caller = rpc.get_caller(msg_tx);
9193
let mut rpc = rpc.methods(SingletonServerContext {

src/vs/code/node/cli.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { ChildProcess, ChildProcessWithoutNullStreams, spawn, SpawnOptions } from 'child_process';
6+
import { ChildProcess, spawn, SpawnOptions, StdioOptions } from 'child_process';
77
import { chmodSync, existsSync, readFileSync, statSync, truncateSync, unlinkSync } from 'fs';
88
import { homedir, release, tmpdir } from 'os';
99
import type { ProfilingSession, Target } from 'v8-inspect-profiler';
@@ -56,20 +56,21 @@ export async function main(argv: string[]): Promise<any> {
5656
}
5757
const tunnelArgs = argv.slice(argv.indexOf('tunnel') + 1); // all arguments behind `tunnel`
5858
return new Promise((resolve, reject) => {
59-
let tunnelProcess: ChildProcessWithoutNullStreams;
59+
let tunnelProcess: ChildProcess;
60+
const stdio: StdioOptions = ['ignore', 'pipe', 'pipe'];
6061
if (process.env['VSCODE_DEV']) {
61-
tunnelProcess = spawn('cargo', ['run', '--', 'tunnel', ...tunnelArgs], { cwd: join(getAppRoot(), 'cli') });
62+
tunnelProcess = spawn('cargo', ['run', '--', 'tunnel', ...tunnelArgs], { cwd: join(getAppRoot(), 'cli'), stdio });
6263
} else {
6364
const appPath = process.platform === 'darwin'
6465
// ./Contents/MacOS/Electron => ./Contents/Resources/app/bin/code-tunnel-insiders
6566
? join(dirname(dirname(process.execPath)), 'Resources', 'app')
6667
: dirname(process.execPath);
6768
const tunnelCommand = join(appPath, 'bin', `${product.tunnelApplicationName}${isWindows ? '.exe' : ''}`);
68-
tunnelProcess = spawn(tunnelCommand, ['tunnel', ...tunnelArgs], { cwd: cwd() });
69+
tunnelProcess = spawn(tunnelCommand, ['tunnel', ...tunnelArgs], { cwd: cwd(), stdio });
6970
}
7071

71-
tunnelProcess.stdout.pipe(process.stdout);
72-
tunnelProcess.stderr.pipe(process.stderr);
72+
tunnelProcess.stdout!.pipe(process.stdout);
73+
tunnelProcess.stderr!.pipe(process.stderr);
7374
tunnelProcess.on('exit', resolve);
7475
tunnelProcess.on('error', reject);
7576
});

src/vs/platform/remoteTunnel/node/remoteTunnelService.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { INativeEnvironmentService } from 'vs/platform/environment/common/enviro
1010
import { Disposable } from 'vs/base/common/lifecycle';
1111
import { ILogger, ILoggerService, LogLevelToString } from 'vs/platform/log/common/log';
1212
import { dirname, join } from 'vs/base/common/path';
13-
import { ChildProcess, spawn } from 'child_process';
13+
import { ChildProcess, StdioOptions, spawn } from 'child_process';
1414
import { IProductService } from 'vs/platform/product/common/productService';
1515
import { isMacintosh, isWindows } from 'vs/base/common/platform';
1616
import { CancelablePromise, createCancelablePromise, Delayer } from 'vs/base/common/async';
@@ -322,6 +322,8 @@ export class RemoteTunnelService extends Disposable implements IRemoteTunnelServ
322322
resolve(-1);
323323
}
324324
let tunnelProcess: ChildProcess | undefined;
325+
const stdio: StdioOptions = ['ignore', 'pipe', 'pipe'];
326+
325327
token.onCancellationRequested(() => {
326328
if (tunnelProcess) {
327329
this._logger.info(`${logLabel} terminating(${tunnelProcess.pid})`);
@@ -331,12 +333,12 @@ export class RemoteTunnelService extends Disposable implements IRemoteTunnelServ
331333
if (!this.environmentService.isBuilt) {
332334
onOutput('Building tunnel CLI from sources and run', false);
333335
onOutput(`${logLabel} Spawning: cargo run -- tunnel ${commandArgs.join(' ')}`, false);
334-
tunnelProcess = spawn('cargo', ['run', '--', 'tunnel', ...commandArgs], { cwd: join(this.environmentService.appRoot, 'cli') });
336+
tunnelProcess = spawn('cargo', ['run', '--', 'tunnel', ...commandArgs], { cwd: join(this.environmentService.appRoot, 'cli'), stdio });
335337
} else {
336338
onOutput('Running tunnel CLI', false);
337339
const tunnelCommand = this.getTunnelCommandLocation();
338340
onOutput(`${logLabel} Spawning: ${tunnelCommand} tunnel ${commandArgs.join(' ')}`, false);
339-
tunnelProcess = spawn(tunnelCommand, ['tunnel', ...commandArgs], { cwd: homedir() });
341+
tunnelProcess = spawn(tunnelCommand, ['tunnel', ...commandArgs], { cwd: homedir(), stdio });
340342
}
341343

342344
tunnelProcess.stdout!.on('data', data => {

0 commit comments

Comments
 (0)