fix(dev-infra): spawned child processes messing with tty output (#41948)
Currently we have a common utility method for running commands in a child process. This method pipes all stdout and stderr, but sets the `stdin` to `inherited`. This seemed to work as expected in terms of allowing interactive commands being executed, but it messes with the TTY in Windows (and potentially other platforms) so that colors and prompts no longer work properly. See attached screenshot. We fix this by not inheriting the stdin by default; but exposing a dedicated method for interactive commands. This results in more readable and obvious code too, so it's worth making this change regardless of the TTY issues. PR Close #41948
This commit is contained in:
parent
fb38175a40
commit
21c2a06811
|
@ -5212,6 +5212,21 @@ const ReleaseBuildCommandModule = {
|
|||
* Use of this source code is governed by an MIT-style license that can be
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
/**
|
||||
* Spawns a given command with the specified arguments inside an interactive shell. All process
|
||||
* stdin, stdout and stderr output is printed to the current console.
|
||||
*
|
||||
* @returns a Promise resolving on success, and rejecting on command failure with the status code.
|
||||
*/
|
||||
function spawnInteractiveCommand(command, args, options) {
|
||||
if (options === void 0) { options = {}; }
|
||||
return new Promise(function (resolve, reject) {
|
||||
var commandText = command + " " + args.join(' ');
|
||||
debug("Executing command: " + commandText);
|
||||
var childProcess = child_process.spawn(command, args, tslib.__assign(tslib.__assign({}, options), { shell: true, stdio: 'inherit' }));
|
||||
childProcess.on('exit', function (status) { return status === 0 ? resolve() : reject(status); });
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Spawns a given command with the specified arguments inside a shell. All process stdout
|
||||
* output is captured and returned as resolution on completion. Depending on the chosen
|
||||
|
@ -5226,7 +5241,7 @@ function spawnWithDebugOutput(command, args, options) {
|
|||
var commandText = command + " " + args.join(' ');
|
||||
var outputMode = options.mode;
|
||||
debug("Executing command: " + commandText);
|
||||
var childProcess = child_process.spawn(command, args, tslib.__assign(tslib.__assign({}, options), { shell: true, stdio: ['inherit', 'pipe', 'pipe'] }));
|
||||
var childProcess = child_process.spawn(command, args, tslib.__assign(tslib.__assign({}, options), { shell: true, stdio: 'pipe' }));
|
||||
var logOutput = '';
|
||||
var stdout = '';
|
||||
var stderr = '';
|
||||
|
@ -5324,7 +5339,7 @@ function npmIsLoggedIn(registryUrl) {
|
|||
}
|
||||
/**
|
||||
* Log into NPM at a provided registry.
|
||||
* @throws With the process log output if the login fails.
|
||||
* @throws With the `npm login` status code if the login failed.
|
||||
*/
|
||||
function npmLogin(registryUrl) {
|
||||
return tslib.__awaiter(this, void 0, void 0, function* () {
|
||||
|
@ -5335,7 +5350,9 @@ function npmLogin(registryUrl) {
|
|||
if (registryUrl !== undefined) {
|
||||
args.splice(1, 0, '--registry', registryUrl);
|
||||
}
|
||||
yield spawnWithDebugOutput('npm', args);
|
||||
// The login command prompts for username, password and other profile information. Hence
|
||||
// the process needs to be interactive (i.e. respecting current TTYs stdin).
|
||||
yield spawnInteractiveCommand('npm', args);
|
||||
});
|
||||
}
|
||||
/**
|
||||
|
|
|
@ -12,7 +12,6 @@ import {spawnWithDebugOutput} from '../../utils/child-process';
|
|||
import {GithubConfig} from '../../utils/config';
|
||||
import {debug, error, info, log, promptConfirm, red, yellow} from '../../utils/console';
|
||||
import {GitClient} from '../../utils/git/index';
|
||||
import {exec} from '../../utils/shelljs';
|
||||
import {ReleaseConfig} from '../config/index';
|
||||
import {ActiveReleaseTrains, fetchActiveReleaseTrains, nextBranchName} from '../versioning/active-release-trains';
|
||||
import {npmIsLoggedIn, npmLogin, npmLogout} from '../versioning/npm-publish';
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
*/
|
||||
|
||||
import * as semver from 'semver';
|
||||
import {spawnWithDebugOutput} from '../../utils/child-process';
|
||||
import {spawnInteractiveCommand, spawnWithDebugOutput} from '../../utils/child-process';
|
||||
|
||||
/**
|
||||
* Runs NPM publish within a specified package directory.
|
||||
|
@ -57,7 +57,7 @@ export async function npmIsLoggedIn(registryUrl: string|undefined): Promise<bool
|
|||
|
||||
/**
|
||||
* Log into NPM at a provided registry.
|
||||
* @throws With the process log output if the login fails.
|
||||
* @throws With the `npm login` status code if the login failed.
|
||||
*/
|
||||
export async function npmLogin(registryUrl: string|undefined) {
|
||||
const args = ['login', '--no-browser'];
|
||||
|
@ -67,7 +67,9 @@ export async function npmLogin(registryUrl: string|undefined) {
|
|||
if (registryUrl !== undefined) {
|
||||
args.splice(1, 0, '--registry', registryUrl);
|
||||
}
|
||||
await spawnWithDebugOutput('npm', args);
|
||||
// The login command prompts for username, password and other profile information. Hence
|
||||
// the process needs to be interactive (i.e. respecting current TTYs stdin).
|
||||
await spawnInteractiveCommand('npm', args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -23,6 +23,22 @@ export interface SpawnedProcessResult {
|
|||
stderr: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a given command with the specified arguments inside an interactive shell. All process
|
||||
* stdin, stdout and stderr output is printed to the current console.
|
||||
*
|
||||
* @returns a Promise resolving on success, and rejecting on command failure with the status code.
|
||||
*/
|
||||
export function spawnInteractiveCommand(
|
||||
command: string, args: string[], options: Omit<SpawnOptions, 'stdio'> = {}) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const commandText = `${command} ${args.join(' ')}`;
|
||||
debug(`Executing command: ${commandText}`);
|
||||
const childProcess = spawn(command, args, {...options, shell: true, stdio: 'inherit'});
|
||||
childProcess.on('exit', status => status === 0 ? resolve() : reject(status));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a given command with the specified arguments inside a shell. All process stdout
|
||||
* output is captured and returned as resolution on completion. Depending on the chosen
|
||||
|
@ -40,8 +56,7 @@ export function spawnWithDebugOutput(
|
|||
|
||||
debug(`Executing command: ${commandText}`);
|
||||
|
||||
const childProcess =
|
||||
spawn(command, args, {...options, shell: true, stdio: ['inherit', 'pipe', 'pipe']});
|
||||
const childProcess = spawn(command, args, {...options, shell: true, stdio: 'pipe'});
|
||||
let logOutput = '';
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
|
@ -57,6 +72,7 @@ export function spawnWithDebugOutput(
|
|||
process.stderr.write(message);
|
||||
}
|
||||
});
|
||||
|
||||
childProcess.stdout.on('data', message => {
|
||||
stdout += message;
|
||||
logOutput += message;
|
||||
|
|
Loading…
Reference in New Issue