mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-17 02:51:36 -05:00
Feature/project build (#10332)
* initial build command execution * adding tests * Clean up test names * update SqltoolsService release in ADS for Build * Updating as per PR comments * updating yarn lock * Adding one more test for command run * Test fixes
This commit is contained in:
@@ -106,6 +106,7 @@ const indentationFilter = [
|
|||||||
'!extensions/sql-database-projects/resources/templates/*.xml',
|
'!extensions/sql-database-projects/resources/templates/*.xml',
|
||||||
'!extensions/sql-database-projects/src/test/baselines/*.xml',
|
'!extensions/sql-database-projects/src/test/baselines/*.xml',
|
||||||
'!extensions/sql-database-projects/src/test/baselines/*.json',
|
'!extensions/sql-database-projects/src/test/baselines/*.json',
|
||||||
|
'!extensions/sql-database-projects/src/test/baselines/*.sqlproj',
|
||||||
'!extensions/big-data-cluster/src/bigDataCluster/controller/apiGenerated.ts',
|
'!extensions/big-data-cluster/src/bigDataCluster/controller/apiGenerated.ts',
|
||||||
'!extensions/big-data-cluster/src/bigDataCluster/controller/clusterApiGenerated2.ts',
|
'!extensions/big-data-cluster/src/bigDataCluster/controller/clusterApiGenerated2.ts',
|
||||||
'!resources/linux/snap/electron-launch'
|
'!resources/linux/snap/electron-launch'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
"downloadUrl": "https://github.com/Microsoft/sqltoolsservice/releases/download/v{#version#}/microsoft.sqltools.servicelayer-{#fileName#}",
|
||||||
"version": "2.0.0-release.64",
|
"version": "2.0.0-release.65",
|
||||||
"downloadFileNames": {
|
"downloadFileNames": {
|
||||||
"Windows_86": "win-x86-netcoreapp2.2.zip",
|
"Windows_86": "win-x86-netcoreapp2.2.zip",
|
||||||
"Windows_64": "win-x64-netcoreapp2.2.zip",
|
"Windows_64": "win-x64-netcoreapp2.2.zip",
|
||||||
|
|||||||
3
extensions/sql-database-projects/.gitignore
vendored
3
extensions/sql-database-projects/.gitignore
vendored
@@ -1 +1,2 @@
|
|||||||
*.vsix
|
*.vsix
|
||||||
|
BuildDirectory/*.*
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
"preview": true,
|
"preview": true,
|
||||||
"engines": {
|
"engines": {
|
||||||
"vscode": "^1.30.1",
|
"vscode": "^1.30.1",
|
||||||
"azdata": ">=1.12.0"
|
"azdata": ">=1.18.0"
|
||||||
},
|
},
|
||||||
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt",
|
"license": "https://raw.githubusercontent.com/Microsoft/azuredatastudio/master/LICENSE.txt",
|
||||||
"icon": "images/extension.png",
|
"icon": "images/extension.png",
|
||||||
@@ -239,6 +239,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"promisify-child-process": "^3.1.1",
|
||||||
"vscode-languageclient": "^5.3.0-next.1",
|
"vscode-languageclient": "^5.3.0-next.1",
|
||||||
"vscode-nls": "^3.2.1",
|
"vscode-nls": "^3.2.1",
|
||||||
"xmldom": "^0.3.0"
|
"xmldom": "^0.3.0"
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ export const projectNameRequired = localize('projectNameRequired', "Name is requ
|
|||||||
export const projectLocationRequired = localize('projectLocationRequired', "Location is required to create a new database project.");
|
export const projectLocationRequired = localize('projectLocationRequired', "Location is required to create a new database project.");
|
||||||
export function projectAlreadyOpened(path: string) { return localize('projectAlreadyOpened', "Project '{0}' is already opened.", path); }
|
export function projectAlreadyOpened(path: string) { return localize('projectAlreadyOpened', "Project '{0}' is already opened.", path); }
|
||||||
export function projectAlreadyExists(name: string, path: string) { return localize('projectAlreadyExists', "A project named {0} already exists in {1}.", name, path); }
|
export function projectAlreadyExists(name: string, path: string) { return localize('projectAlreadyExists', "A project named {0} already exists in {1}.", name, path); }
|
||||||
|
export function mssqlNotFound(mssqlConfigDir: string) { return localize('mssqlNotFound', "Could not get mssql extension's install location at {0}", mssqlConfigDir); }
|
||||||
|
|
||||||
// Project script types
|
// Project script types
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,14 @@
|
|||||||
*--------------------------------------------------------------------------------------------*/
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
|
import * as os from 'os';
|
||||||
/**
|
/**
|
||||||
* Consolidates on the error message string
|
* Consolidates on the error message string
|
||||||
*/
|
*/
|
||||||
export function getErrorMessage(error: Error | string): string {
|
export function getErrorMessage(error: any): string {
|
||||||
return (error instanceof Error) ? error.message : error;
|
return (error instanceof Error)
|
||||||
|
? (typeof error.message === 'string' ? error.message : '')
|
||||||
|
: typeof error === 'string' ? error : `${JSON.stringify(error, undefined, '\t')}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -45,3 +48,29 @@ export function trimChars(input: string, chars: string): string {
|
|||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get quoted path to be used in any commandline argument
|
||||||
|
* @param filePath
|
||||||
|
*/
|
||||||
|
export function getSafePath(filePath: string): string {
|
||||||
|
return (os.platform() === 'win32') ?
|
||||||
|
getSafeWindowsPath(filePath) :
|
||||||
|
getSafeNonWindowsPath(filePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ensure that path with spaces are handles correctly
|
||||||
|
*/
|
||||||
|
export function getSafeWindowsPath(filePath: string): string {
|
||||||
|
filePath = filePath.split('\\').join('\\\\').split('"').join('');
|
||||||
|
return '"' + filePath + '"';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ensure that path with spaces are handles correctly
|
||||||
|
*/
|
||||||
|
export function getSafeNonWindowsPath(filePath: string): string {
|
||||||
|
filePath = filePath.split('\\').join('/').split('"').join('');
|
||||||
|
return '"' + filePath + '"';
|
||||||
|
}
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ export default class MainController implements Disposable {
|
|||||||
this.apiWrapper.registerCommand('sqlDatabaseProjects.close', (node: BaseProjectTreeItem) => { this.projectsController.closeProject(node); });
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.close', (node: BaseProjectTreeItem) => { this.projectsController.closeProject(node); });
|
||||||
this.apiWrapper.registerCommand('sqlDatabaseProjects.properties', async (node: BaseProjectTreeItem) => { await this.apiWrapper.showErrorMessage(`Properties not yet implemented: ${node.uri.path}`); }); // TODO
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.properties', async (node: BaseProjectTreeItem) => { await this.apiWrapper.showErrorMessage(`Properties not yet implemented: ${node.uri.path}`); }); // TODO
|
||||||
|
|
||||||
this.apiWrapper.registerCommand('sqlDatabaseProjects.build', async (node: BaseProjectTreeItem) => { await this.projectsController.build(node); });
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.build', async (node: BaseProjectTreeItem) => { await this.projectsController.buildProject(node); });
|
||||||
this.apiWrapper.registerCommand('sqlDatabaseProjects.deploy', (node: BaseProjectTreeItem) => { this.projectsController.deploy(node); });
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.deploy', async (node: BaseProjectTreeItem) => { await this.projectsController.deploy(node); });
|
||||||
this.apiWrapper.registerCommand('sqlDatabaseProjects.import', async (node: BaseProjectTreeItem) => { await this.projectsController.import(node); });
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.import', async (node: BaseProjectTreeItem) => { await this.projectsController.import(node); });
|
||||||
|
|
||||||
this.apiWrapper.registerCommand('sqlDatabaseProjects.newScript', async (node: BaseProjectTreeItem) => { await this.projectsController.addItemPromptFromNode(node, templates.script); });
|
this.apiWrapper.registerCommand('sqlDatabaseProjects.newScript', async (node: BaseProjectTreeItem) => { await this.projectsController.addItemPromptFromNode(node, templates.script); });
|
||||||
|
|||||||
@@ -19,20 +19,25 @@ import { BaseProjectTreeItem } from '../models/tree/baseTreeItem';
|
|||||||
import { ProjectRootTreeItem } from '../models/tree/projectTreeItem';
|
import { ProjectRootTreeItem } from '../models/tree/projectTreeItem';
|
||||||
import { FolderNode } from '../models/tree/fileFolderTreeItem';
|
import { FolderNode } from '../models/tree/fileFolderTreeItem';
|
||||||
import { DeployDatabaseDialog } from '../dialogs/deployDatabaseDialog';
|
import { DeployDatabaseDialog } from '../dialogs/deployDatabaseDialog';
|
||||||
|
import { NetCoreTool, DotNetCommandOptions } from '../tools/netcoreTool';
|
||||||
|
import { BuildHelper } from '../tools/buildHelper';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller for managing project lifecycle
|
* Controller for managing project lifecycle
|
||||||
*/
|
*/
|
||||||
export class ProjectsController {
|
export class ProjectsController {
|
||||||
private projectTreeViewProvider: SqlDatabaseProjectTreeViewProvider;
|
private projectTreeViewProvider: SqlDatabaseProjectTreeViewProvider;
|
||||||
|
private netCoreTool: NetCoreTool;
|
||||||
|
private buildHelper: BuildHelper;
|
||||||
|
|
||||||
projects: Project[] = [];
|
projects: Project[] = [];
|
||||||
|
|
||||||
constructor(private apiWrapper: ApiWrapper, projTreeViewProvider: SqlDatabaseProjectTreeViewProvider) {
|
constructor(private apiWrapper: ApiWrapper, projTreeViewProvider: SqlDatabaseProjectTreeViewProvider) {
|
||||||
this.projectTreeViewProvider = projTreeViewProvider;
|
this.projectTreeViewProvider = projTreeViewProvider;
|
||||||
|
this.netCoreTool = new NetCoreTool();
|
||||||
|
this.buildHelper = new BuildHelper();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public refreshProjectsTree() {
|
public refreshProjectsTree() {
|
||||||
this.projectTreeViewProvider.load(this.projects);
|
this.projectTreeViewProvider.load(this.projects);
|
||||||
}
|
}
|
||||||
@@ -114,9 +119,17 @@ export class ProjectsController {
|
|||||||
this.refreshProjectsTree();
|
this.refreshProjectsTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async build(treeNode: BaseProjectTreeItem) {
|
public async buildProject(treeNode: BaseProjectTreeItem): Promise<void> {
|
||||||
|
// Check mssql extension for project dlls (tracking issue #10273)
|
||||||
|
await this.buildHelper.createBuildDirFolder();
|
||||||
|
|
||||||
const project = this.getProjectContextFromTreeNode(treeNode);
|
const project = this.getProjectContextFromTreeNode(treeNode);
|
||||||
await this.apiWrapper.showErrorMessage(`Build not yet implemented: ${project.projectFilePath}`); // TODO
|
const options: DotNetCommandOptions = {
|
||||||
|
commandTitle: 'Build',
|
||||||
|
workingDirectory: project.projectFolderPath,
|
||||||
|
argument: this.buildHelper.constructBuildArguments(project.projectFilePath, this.buildHelper.extensionBuildDirPath)
|
||||||
|
};
|
||||||
|
await this.netCoreTool.runDotnetCommand(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public deploy(treeNode: BaseProjectTreeItem): void {
|
public deploy(treeNode: BaseProjectTreeItem): void {
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||||
|
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||||
|
*--------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
import * as should from 'should';
|
||||||
|
import * as os from 'os';
|
||||||
|
import * as vscode from 'vscode';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { BuildHelper } from '../tools/buildHelper';
|
||||||
|
|
||||||
|
describe('BuildHelper: Build Helper tests', function (): void {
|
||||||
|
|
||||||
|
it('Should get correct build arguments', async function (): Promise<void> {
|
||||||
|
// update settings and validate
|
||||||
|
const buildHelper = new BuildHelper();
|
||||||
|
const resultArg = buildHelper.constructBuildArguments('dummy\\project path\\more space in path', 'dummy\\dll path');
|
||||||
|
|
||||||
|
if (os.platform() === 'win32') {
|
||||||
|
should(resultArg).equal(' build "dummy\\\\project path\\\\more space in path" /p:NetCoreBuild=true /p:NETCoreTargetsPath="dummy\\\\dll path"');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
should(resultArg).equal(' build "dummy/project path/more space in path" /p:NetCoreBuild=true /p:NETCoreTargetsPath="dummy/dll path"');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should get correct build folder', async function (): Promise<void> {
|
||||||
|
const buildHelper = new BuildHelper();
|
||||||
|
await buildHelper.createBuildDirFolder();
|
||||||
|
|
||||||
|
// get expected path for build
|
||||||
|
let expectedPath = vscode.extensions.getExtension('Microsoft.sql-database-projects')?.extensionPath ?? 'EmptyPath';
|
||||||
|
expectedPath = path.join(expectedPath, 'BuildDirectory');
|
||||||
|
should(buildHelper.extensionBuildDirPath).equal(expectedPath);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -5,19 +5,23 @@
|
|||||||
|
|
||||||
import * as should from 'should';
|
import * as should from 'should';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
import { NetCoreTool, DBProjectConfigurationKey, NetCoreInstallLocationKey, NextCoreNonWindowsDefaultPath } from '../tools/netcoreTool';
|
import { NetCoreTool, DBProjectConfigurationKey, NetCoreInstallLocationKey, NextCoreNonWindowsDefaultPath } from '../tools/netcoreTool';
|
||||||
|
import { getSafePath } from '../common/utils';
|
||||||
import { isNullOrUndefined } from 'util';
|
import { isNullOrUndefined } from 'util';
|
||||||
|
import { generateTestFolderPath } from './testUtils';
|
||||||
|
|
||||||
describe('NetCoreTool: Net core install popup tests', function (): void {
|
describe('NetCoreTool: Net core tests', function (): void {
|
||||||
|
|
||||||
it('settings value should override default paths', async function (): Promise<void> {
|
it('Should override dotnet default value with settings', async function (): Promise<void> {
|
||||||
try {
|
try {
|
||||||
// update settings and validate
|
// update settings and validate
|
||||||
await vscode.workspace.getConfiguration(DBProjectConfigurationKey).update(NetCoreInstallLocationKey, 'test value path', true);
|
await vscode.workspace.getConfiguration(DBProjectConfigurationKey).update(NetCoreInstallLocationKey, 'test value path', true);
|
||||||
const netcoreTool = new NetCoreTool();
|
const netcoreTool = new NetCoreTool();
|
||||||
should(netcoreTool.netcoreInstallLocation).equal('test value path'); // the path in settings should be taken
|
should(netcoreTool.netcoreInstallLocation).equal('test value path'); // the path in settings should be taken
|
||||||
should(netcoreTool.isNetCoreInstallationPresent).equal(false); // dotnet can not be present at dummy path in settings
|
should(netcoreTool.findOrInstallNetCore()).equal(false); // dotnet can not be present at dummy path in settings
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
// clean again
|
// clean again
|
||||||
@@ -25,7 +29,7 @@ describe('NetCoreTool: Net core install popup tests', function (): void {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should find right default paths', async function (): Promise<void> {
|
it('Should find right dotnet default paths', async function (): Promise<void> {
|
||||||
const netcoreTool = new NetCoreTool();
|
const netcoreTool = new NetCoreTool();
|
||||||
netcoreTool.findOrInstallNetCore();
|
netcoreTool.findOrInstallNetCore();
|
||||||
|
|
||||||
@@ -41,8 +45,23 @@ describe('NetCoreTool: Net core install popup tests', function (): void {
|
|||||||
should(result).true('dotnet is either not present or in /usr/local/share by default');
|
should(result).true('dotnet is either not present or in /usr/local/share by default');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
export async function sleep(ms: number): Promise<{}> {
|
it('should run a command successfully', async function (): Promise<void> {
|
||||||
return new Promise(resolve => setTimeout(resolve, ms));
|
const netcoreTool = new NetCoreTool();
|
||||||
}
|
const dummyFile = path.join(await generateTestFolderPath(), 'dummy.dacpac');
|
||||||
|
const outputChannel = vscode.window.createOutputChannel('db project test');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await netcoreTool.runStreamedCommand('echo test > ' + getSafePath(dummyFile), outputChannel, undefined);
|
||||||
|
const text = await fs.promises.readFile(dummyFile);
|
||||||
|
should(text.toString().trim()).equal('test');
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
await fs.exists(dummyFile, async (existBool) => {
|
||||||
|
if (existBool) {
|
||||||
|
await fs.promises.unlink(dummyFile);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
93
extensions/sql-database-projects/src/tools/buildHelper.ts
Normal file
93
extensions/sql-database-projects/src/tools/buildHelper.ts
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
/*---------------------------------------------------------------------------------------------
|
||||||
|
* 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 os from 'os';
|
||||||
|
import { promises as fs, existsSync } from 'fs';
|
||||||
|
import * as utils from '../common/utils';
|
||||||
|
import { mssqlNotFound } from '../common/constants';
|
||||||
|
|
||||||
|
const buildDirectory = 'BuildDirectory';
|
||||||
|
const buildFiles: string[] = [
|
||||||
|
'Microsoft.Data.SqlClient.dll',
|
||||||
|
'Microsoft.Data.Tools.Schema.Sql.dll',
|
||||||
|
'Microsoft.Data.Tools.Schema.Tasks.Sql.dll',
|
||||||
|
'Microsoft.Data.Tools.Utilities.dll',
|
||||||
|
'Microsoft.SqlServer.Dac.dll',
|
||||||
|
'Microsoft.SqlServer.Dac.Extensions.dll',
|
||||||
|
'Microsoft.SqlServer.TransactSql.ScriptDom.dll',
|
||||||
|
'Microsoft.SqlServer.Types.dll',
|
||||||
|
'System.ComponentModel.Composition.dll',
|
||||||
|
'Microsoft.Data.Tools.Schema.SqlTasks.targets'
|
||||||
|
];
|
||||||
|
|
||||||
|
export class BuildHelper {
|
||||||
|
|
||||||
|
private extensionDir: string;
|
||||||
|
private extensionBuildDir: string;
|
||||||
|
private initialized: boolean = false;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.extensionDir = vscode.extensions.getExtension('Microsoft.sql-database-projects')?.extensionPath ?? '';
|
||||||
|
this.extensionBuildDir = path.join(this.extensionDir, buildDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create build dlls directory
|
||||||
|
// this should not be required. temporary solution for issue #10273
|
||||||
|
public async createBuildDirFolder(): Promise<void> {
|
||||||
|
|
||||||
|
if (this.initialized) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!existsSync(this.extensionBuildDir)) {
|
||||||
|
await fs.mkdir(this.extensionBuildDir);
|
||||||
|
}
|
||||||
|
|
||||||
|
const buildfilesPath = await this.getBuildDirPathFromMssqlTools();
|
||||||
|
|
||||||
|
buildFiles.forEach(async (fileName) => {
|
||||||
|
if (existsSync(path.join(buildfilesPath, fileName))) {
|
||||||
|
await fs.copyFile(path.join(buildfilesPath, fileName), path.join(this.extensionBuildDir, fileName));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get mssql sqltoolsservice path
|
||||||
|
private async getBuildDirPathFromMssqlTools(): Promise<string> {
|
||||||
|
const mssqlConfigDir = path.join(this.extensionDir, '..', 'mssql');
|
||||||
|
|
||||||
|
if (existsSync(path.join(mssqlConfigDir, 'config.json'))) {
|
||||||
|
const rawConfig = await fs.readFile(path.join(mssqlConfigDir, 'config.json'));
|
||||||
|
const config = JSON.parse(rawConfig.toString());
|
||||||
|
const installDir = config.installDirectory?.replace('{#version#}', config.version).replace('{#platform#}', this.getPlatform());
|
||||||
|
if (installDir) {
|
||||||
|
return path.join(mssqlConfigDir, installDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new Error(mssqlNotFound(mssqlConfigDir));
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPlatform(): string {
|
||||||
|
return os.platform() === 'win32' ? 'Windows' :
|
||||||
|
os.platform() === 'darwin' ? 'OSX' :
|
||||||
|
os.platform() === 'linux' ? 'Linux' :
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
||||||
|
public get extensionBuildDirPath(): string {
|
||||||
|
return this.extensionBuildDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructBuildArguments(projectPath: string, buildDirPath: string): string {
|
||||||
|
projectPath = utils.getSafePath(projectPath);
|
||||||
|
buildDirPath = utils.getSafePath(buildDirPath);
|
||||||
|
return ` build ${projectPath} /p:NetCoreBuild=true /p:NETCoreTargetsPath=${buildDirPath}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -7,8 +7,10 @@ import * as vscode from 'vscode';
|
|||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as os from 'os';
|
import * as os from 'os';
|
||||||
import { isNullOrUndefined } from 'util';
|
import * as cp from 'promisify-child-process';
|
||||||
import * as nls from 'vscode-nls';
|
import * as nls from 'vscode-nls';
|
||||||
|
import { isNullOrUndefined } from 'util';
|
||||||
|
import * as utils from '../common/utils';
|
||||||
const localize = nls.loadMessageBundle();
|
const localize = nls.loadMessageBundle();
|
||||||
|
|
||||||
export const DBProjectConfigurationKey: string = 'sqlDatabaseProjects';
|
export const DBProjectConfigurationKey: string = 'sqlDatabaseProjects';
|
||||||
@@ -18,12 +20,26 @@ export const NetCoreInstallationConfirmation: string = localize('sqlDatabaseProj
|
|||||||
export const UpdateNetCoreLocation: string = localize('sqlDatabaseProjects.UpdateNetCoreLocation', "Update .Net Core location");
|
export const UpdateNetCoreLocation: string = localize('sqlDatabaseProjects.UpdateNetCoreLocation', "Update .Net Core location");
|
||||||
export const InstallNetCore: string = localize('sqlDatabaseProjects.InstallNetCore', "Install .Net Core SDK");
|
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 {
|
export class NetCoreTool {
|
||||||
|
|
||||||
public findOrInstallNetCore(): void {
|
private _outputChannel: vscode.OutputChannel = vscode.window.createOutputChannel(projectsOutputChannel);
|
||||||
|
|
||||||
|
public findOrInstallNetCore(): boolean {
|
||||||
if (!this.isNetCoreInstallationPresent) {
|
if (!this.isNetCoreInstallationPresent) {
|
||||||
this.showInstallDialog();
|
this.showInstallDialog();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private showInstallDialog(): void {
|
private showInstallDialog(): void {
|
||||||
@@ -40,7 +56,7 @@ export class NetCoreTool {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public get isNetCoreInstallationPresent(): Boolean {
|
private get isNetCoreInstallationPresent(): Boolean {
|
||||||
return (!isNullOrUndefined(this.netcoreInstallLocation) && fs.existsSync(this.netcoreInstallLocation));
|
return (!isNullOrUndefined(this.netcoreInstallLocation) && fs.existsSync(this.netcoreInstallLocation));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,4 +91,71 @@ export class NetCoreTool {
|
|||||||
}
|
}
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async runDotnetCommand(options: DotNetCommandOptions): Promise<string> {
|
||||||
|
if (options && options.commandTitle !== undefined && options.commandTitle !== null) {
|
||||||
|
this._outputChannel.appendLine(`\t[ ${options.commandTitle} ]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.findOrInstallNetCore()) {
|
||||||
|
throw new Error(NetCoreInstallationConfirmation);
|
||||||
|
}
|
||||||
|
|
||||||
|
const dotnetPath = utils.getSafePath(path.join(this.netcoreInstallLocation, dotnet));
|
||||||
|
const command = dotnetPath + ' ' + options.argument;
|
||||||
|
|
||||||
|
try {
|
||||||
|
return await this.runStreamedCommand(command, this._outputChannel, options);
|
||||||
|
} catch (error) {
|
||||||
|
this._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 aruments 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);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,6 +160,13 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.4.tgz#68a35e6b0319bbc014465be43828300113f2f2e8"
|
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.4.tgz#68a35e6b0319bbc014465be43828300113f2f2e8"
|
||||||
integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==
|
integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==
|
||||||
|
|
||||||
|
"@babel/runtime@^7.1.5":
|
||||||
|
version "7.9.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.6.tgz#a9102eb5cadedf3f31d08a9ecf294af7827ea29f"
|
||||||
|
integrity sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@babel/template@^7.7.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6":
|
"@babel/template@^7.7.4", "@babel/template@^7.8.3", "@babel/template@^7.8.6":
|
||||||
version "7.8.6"
|
version "7.8.6"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b"
|
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b"
|
||||||
@@ -680,6 +687,18 @@ postinstall-build@^5.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/postinstall-build/-/postinstall-build-5.0.3.tgz#238692f712a481d8f5bc8960e94786036241efc7"
|
resolved "https://registry.yarnpkg.com/postinstall-build/-/postinstall-build-5.0.3.tgz#238692f712a481d8f5bc8960e94786036241efc7"
|
||||||
integrity sha512-vPvPe8TKgp4FLgY3+DfxCE5PIfoXBK2lyLfNCxsRbDsV6vS4oU5RG/IWxrblMn6heagbnMED3MemUQllQ2bQUg==
|
integrity sha512-vPvPe8TKgp4FLgY3+DfxCE5PIfoXBK2lyLfNCxsRbDsV6vS4oU5RG/IWxrblMn6heagbnMED3MemUQllQ2bQUg==
|
||||||
|
|
||||||
|
promisify-child-process@^3.1.1:
|
||||||
|
version "3.1.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/promisify-child-process/-/promisify-child-process-3.1.4.tgz#3321827f283c0be30de1354bec1c6c627f02d53c"
|
||||||
|
integrity sha512-tLifJs99E4oOXUz/dKQjRgdchfiepmYQzBVrcVX9BtUWi9aGJeGSf2KgXOWBW1JFsSYgLkl1Z9HRm8i0sf4cTg==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.1.5"
|
||||||
|
|
||||||
|
regenerator-runtime@^0.13.4:
|
||||||
|
version "0.13.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"
|
||||||
|
integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==
|
||||||
|
|
||||||
resolve@^1.3.2:
|
resolve@^1.3.2:
|
||||||
version "1.13.1"
|
version "1.13.1"
|
||||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.13.1.tgz#be0aa4c06acd53083505abb35f4d66932ab35d16"
|
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.13.1.tgz#be0aa4c06acd53083505abb35f4d66932ab35d16"
|
||||||
|
|||||||
Reference in New Issue
Block a user