mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-15 09:35:37 -05:00
92 lines
3.2 KiB
TypeScript
92 lines
3.2 KiB
TypeScript
/*---------------------------------------------------------------------------------------------
|
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
|
*--------------------------------------------------------------------------------------------*/
|
|
|
|
import * as cp from 'promisify-child-process';
|
|
import * as vscode from 'vscode';
|
|
import * as nls from 'vscode-nls';
|
|
const localize = nls.loadMessageBundle();
|
|
|
|
export interface ShellCommandOptions {
|
|
workingDirectory?: string;
|
|
additionalEnvironmentVariables?: NodeJS.ProcessEnv;
|
|
commandTitle?: string;
|
|
argument?: string;
|
|
}
|
|
|
|
export class ShellExecutionHelper {
|
|
constructor(protected _outputChannel: vscode.OutputChannel) {
|
|
}
|
|
|
|
/**
|
|
* spawns the shell command with arguments and redirects the error and output to ADS output channel
|
|
*/
|
|
public async runStreamedCommand(command: string, options?: ShellCommandOptions, sensitiveData: string[] = [], timeout: number = 5 * 60 * 1000): Promise<string> {
|
|
const stdoutData: string[] = [];
|
|
|
|
let cmdOutputMessage = command;
|
|
sensitiveData.forEach(element => {
|
|
cmdOutputMessage = cmdOutputMessage.replace(element, '***');
|
|
});
|
|
|
|
this._outputChannel.appendLine(` > ${cmdOutputMessage}`);
|
|
|
|
const spawnOptions = {
|
|
cwd: options && options.workingDirectory,
|
|
env: Object.assign({}, process.env, options && options.additionalEnvironmentVariables),
|
|
encoding: 'utf8',
|
|
maxBuffer: 10 * 1024 * 1024, // 10 Mb of output can be captured.
|
|
shell: true,
|
|
detached: false,
|
|
windowsHide: true,
|
|
timeout: timeout
|
|
};
|
|
|
|
try {
|
|
const child = cp.spawn(command, [], spawnOptions);
|
|
this._outputChannel.show();
|
|
|
|
// Add listeners to print stdout and stderr and exit code
|
|
void child.on('exit', (code: number | null, signal: string | null) => {
|
|
if (code !== null) {
|
|
this._outputChannel.appendLine(localize('sqlDatabaseProjects.RunStreamedCommand.ExitedWithCode', " >>> {0} … exited with code: {1}", cmdOutputMessage, code));
|
|
} else {
|
|
this._outputChannel.appendLine(localize('sqlDatabaseProjects.RunStreamedCommand.ExitedWithSignal', " >>> {0} … exited with signal: {1}", cmdOutputMessage, signal));
|
|
}
|
|
});
|
|
|
|
child.stdout!.on('data', (data: string | Buffer) => {
|
|
stdoutData.push(data.toString());
|
|
ShellExecutionHelper.outputDataChunk(this._outputChannel, data, localize('sqlDatabaseProjects.RunCommand.stdout', " stdout: "));
|
|
});
|
|
|
|
child.stderr!.on('data', (data: string | Buffer) => {
|
|
ShellExecutionHelper.outputDataChunk(this._outputChannel, data, localize('sqlDatabaseProjects.RunCommand.stderr', " stderr: "));
|
|
});
|
|
|
|
await child;
|
|
|
|
return stdoutData.join('');
|
|
}
|
|
catch (err) {
|
|
// removing sensitive data from the exception
|
|
sensitiveData.forEach(element => {
|
|
err.cmd = err.cmd?.replace(element, '***');
|
|
err.message = err.message?.replace(element, '***');
|
|
});
|
|
|
|
throw err;
|
|
}
|
|
}
|
|
|
|
private static outputDataChunk(outputChannel: vscode.OutputChannel, data: string | Buffer, header: string): void {
|
|
data.toString().split(/\r?\n/)
|
|
.forEach(line => {
|
|
if (outputChannel) {
|
|
outputChannel.appendLine(header + line);
|
|
}
|
|
});
|
|
}
|
|
}
|