Files
azuredatastudio/extensions/sql-database-projects/src/tools/netcoreTool.ts
Udeesha Gautam 6928904a26 Small fixes for output window and test (#11533)
* Small fixes for output window and test

* removing some parts of recent tests that might cause issues
2020-07-27 13:28:18 -07:00

161 lines
6.8 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 vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
import * as os from 'os';
import * as cp from 'promisify-child-process';
import * as nls from 'vscode-nls';
import { isNullOrUndefined } from 'util';
import * as utils from '../common/utils';
const localize = nls.loadMessageBundle();
export const DBProjectConfigurationKey: string = 'sqlDatabaseProjects';
export const NetCoreInstallLocationKey: string = 'netCoreSDKLocation';
export const NextCoreNonWindowsDefaultPath = '/usr/local/share';
export const NetCoreInstallationConfirmation: string = localize('sqlDatabaseProjects.NetCoreInstallationConfirmation', "The .NET Core SDK cannnot be located. Project build will not work. Please install .NET Core SDK version 3.1 or update the .Net Core SDK location in settings if already installed.");
export const UpdateNetCoreLocation: string = localize('sqlDatabaseProjects.UpdateNetCoreLocation', "Update .Net Core location");
export const InstallNetCore: string = localize('sqlDatabaseProjects.InstallNetCore', "Install .Net Core SDK");
const projectsOutputChannel = localize('sqlDatabaseProjects.outputChannel', "Database Projects");
const dotnet = os.platform() === 'win32' ? 'dotnet.exe' : 'dotnet';
export interface DotNetCommandOptions {
workingDirectory?: string;
additionalEnvironmentVariables?: NodeJS.ProcessEnv;
commandTitle?: string;
argument?: string;
}
export class NetCoreTool {
private static _outputChannel: vscode.OutputChannel = vscode.window.createOutputChannel(projectsOutputChannel);
public async findOrInstallNetCore(): Promise<boolean> {
if (!this.isNetCoreInstallationPresent) {
await this.showInstallDialog();
return false;
}
return true;
}
private async showInstallDialog(): Promise<void> {
let result = await vscode.window.showInformationMessage(NetCoreInstallationConfirmation, UpdateNetCoreLocation, InstallNetCore);
if (result === UpdateNetCoreLocation) {
//open settings
await vscode.commands.executeCommand('workbench.action.openGlobalSettings');
}
else if (result === InstallNetCore) {
//open install link
const dotnetcoreURL = 'https://dotnet.microsoft.com/download/dotnet-core/3.1';
await vscode.env.openExternal(vscode.Uri.parse(dotnetcoreURL));
}
}
private get isNetCoreInstallationPresent(): Boolean {
return (!isNullOrUndefined(this.netcoreInstallLocation) && fs.existsSync(this.netcoreInstallLocation));
}
public get netcoreInstallLocation(): string {
return vscode.workspace.getConfiguration(DBProjectConfigurationKey)[NetCoreInstallLocationKey] ||
this.defaultLocalInstallLocationByDistribution;
}
private get defaultLocalInstallLocationByDistribution(): string | undefined {
const osPlatform: string = os.platform();
return (osPlatform === 'win32') ? this.defaultWindowsLocation :
(osPlatform === 'darwin' || osPlatform === 'linux') ? this.defaultnonWindowsLocation :
undefined;
}
private get defaultnonWindowsLocation(): string | undefined {
const defaultNonWindowsInstallLocation = NextCoreNonWindowsDefaultPath; //default folder for net core sdk
return this.getDotnetPathIfPresent(defaultNonWindowsInstallLocation) ||
this.getDotnetPathIfPresent(os.homedir()) ||
undefined;
}
private get defaultWindowsLocation(): string | undefined {
return this.getDotnetPathIfPresent(process.env['ProgramW6432']) ||
this.getDotnetPathIfPresent(process.env['ProgramFiles(x86)']) ||
this.getDotnetPathIfPresent(process.env['ProgramFiles']);
}
private getDotnetPathIfPresent(folderPath: string | undefined): string | undefined {
if (!isNullOrUndefined(folderPath) && fs.existsSync(path.join(folderPath, 'dotnet'))) {
return path.join(folderPath, 'dotnet');
}
return undefined;
}
public async runDotnetCommand(options: DotNetCommandOptions): Promise<string> {
if (options && options.commandTitle !== undefined && options.commandTitle !== null) {
NetCoreTool._outputChannel.appendLine(`\t[ ${options.commandTitle} ]`);
}
if (!this.findOrInstallNetCore()) {
throw new Error(NetCoreInstallationConfirmation);
}
const dotnetPath = utils.getQuotedPath(path.join(this.netcoreInstallLocation, dotnet));
const command = dotnetPath + ' ' + options.argument;
try {
return await this.runStreamedCommand(command, NetCoreTool._outputChannel, options);
} catch (error) {
NetCoreTool._outputChannel.append(localize('sqlDatabaseProject.RunCommand.ErroredOut', "\t>>> {0} … errored out: {1}", command, utils.getErrorMessage(error))); //errors are localized in our code where emitted, other errors are pass through from external components that are not easily localized
throw error;
}
}
// spawns the dotnet command with arguments and redirects the error and output to ADS output channel
public async runStreamedCommand(command: string, outputChannel: vscode.OutputChannel, options?: DotNetCommandOptions): Promise<string> {
const stdoutData: string[] = [];
outputChannel.appendLine(` > ${command}`);
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
};
const child = cp.spawn(command, [], spawnOptions);
outputChannel.show();
// Add listeners to print stdout and stderr and exit code
child.on('exit', (code: number | null, signal: string | null) => {
if (code !== null) {
outputChannel.appendLine(localize('sqlDatabaseProjects.RunStreamedCommand.ExitedWithCode', " >>> {0} … exited with code: {1}", command, code));
} else {
outputChannel.appendLine(localize('sqlDatabaseProjects.RunStreamedCommand.ExitedWithSignal', " >>> {0} … exited with signal: {1}", command, signal));
}
});
child.stdout!.on('data', (data: string | Buffer) => {
stdoutData.push(data.toString());
this.outputDataChunk(data, outputChannel, localize('sqlDatabaseProjects.RunCommand.stdout', " stdout: "));
});
child.stderr!.on('data', (data: string | Buffer) => {
this.outputDataChunk(data, outputChannel, localize('sqlDatabaseProjects.RunCommand.stderr', " stderr: "));
});
await child;
return stdoutData.join('');
}
private outputDataChunk(data: string | Buffer, outputChannel: vscode.OutputChannel, header: string): void {
data.toString().split(/\r?\n/)
.forEach(line => {
outputChannel.appendLine(header + line);
});
}
}