mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-14 17:22:20 -05:00
Hook up sql proj publish (#16444)
This commit is contained in:
@@ -14,8 +14,6 @@ export const dataSourcesFileName = 'datasources.json';
|
||||
export const sqlprojExtension = '.sqlproj';
|
||||
export const sqlFileExtension = '.sql';
|
||||
export const schemaCompareExtensionId = 'microsoft.schema-compare';
|
||||
export const sqlDatabaseProjectExtensionId = 'microsoft.sql-database-projects';
|
||||
export const mssqlExtensionId = 'microsoft.mssql';
|
||||
export const master = 'master';
|
||||
export const masterDacpac = 'master.dacpac';
|
||||
export const msdb = 'msdb';
|
||||
@@ -215,7 +213,7 @@ export function invalidInput(input: string) { return localize('invalidInput', "I
|
||||
export function invalidProjectPropertyValue(propertyName: string) { return localize('invalidPropertyValue', "Invalid value specified for the property '{0}' in .sqlproj file", propertyName); }
|
||||
export function unableToCreatePublishConnection(input: string) { return localize('unableToCreatePublishConnection', "Unable to construct connection: {0}", input); }
|
||||
export function circularProjectReference(project1: string, project2: string) { return localize('cicularProjectReference', "Circular reference from project {0} to project {1}", project1, project2); }
|
||||
export function mssqlNotFound(mssqlConfigDir: string) { return localize('mssqlNotFound', "Could not get mssql extension's install location at {0}", mssqlConfigDir); }
|
||||
export function sqlToolsServiceNotFound(sqlToolsServiceDir: string) { return localize('mssqlNotFound', "Could not get SQL Tools Service install location at {0}", sqlToolsServiceDir); }
|
||||
export function projBuildFailed(errorMessage: string) { return localize('projBuildFailed', "Build failed. Check output pane for more details. {0}", errorMessage); }
|
||||
export function unexpectedProjectContext(uri: string) { return localize('unexpectedProjectContext', "Unable to establish project context. Command invoked from unexpected location: {0}", uri); }
|
||||
export function unableToPerformAction(action: string, uri: string) { return localize('unableToPerformAction', "Unable to locate '{0}' target: '{1}'", action, uri); }
|
||||
|
||||
@@ -13,6 +13,7 @@ import * as dataworkspace from 'dataworkspace';
|
||||
import * as mssql from '../../../mssql';
|
||||
import * as vscodeMssql from 'vscode-mssql';
|
||||
import { promises as fs } from 'fs';
|
||||
import { Project } from '../models/project';
|
||||
|
||||
/**
|
||||
* Consolidates on the error message string
|
||||
@@ -253,6 +254,7 @@ export function getDataWorkspaceExtensionApi(): dataworkspace.IExtension {
|
||||
}
|
||||
|
||||
export type IDacFxService = mssql.IDacFxService | vscodeMssql.IDacFxService;
|
||||
export type ISchemaCompareService = mssql.ISchemaCompareService | vscodeMssql.ISchemaCompareService;
|
||||
|
||||
export async function getDacFxService(): Promise<IDacFxService> {
|
||||
if (getAzdataApi()) {
|
||||
@@ -265,18 +267,40 @@ export async function getDacFxService(): Promise<IDacFxService> {
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSchemaCompareService(): Promise<ISchemaCompareService> {
|
||||
if (getAzdataApi()) {
|
||||
const ext = vscode.extensions.getExtension(mssql.extension.name) as vscode.Extension<mssql.IExtension>;
|
||||
const api = await ext.activate();
|
||||
return api.schemaCompare;
|
||||
} else {
|
||||
const api = await getVscodeMssqlApi();
|
||||
return api.schemaCompare;
|
||||
}
|
||||
}
|
||||
|
||||
export async function getVscodeMssqlApi(): Promise<vscodeMssql.IExtension> {
|
||||
const ext = vscode.extensions.getExtension(vscodeMssql.extension.name) as vscode.Extension<vscodeMssql.IExtension>;
|
||||
return ext.activate();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns the default deployment options from DacFx
|
||||
* Returns the default deployment options from DacFx, filtered to appropriate options for the given project.
|
||||
*/
|
||||
export async function GetDefaultDeploymentOptions(): Promise<mssql.DeploymentOptions> {
|
||||
const service = (vscode.extensions.getExtension(mssql.extension.name)!.exports as mssql.IExtension).schemaCompare;
|
||||
const result = await service.schemaCompareGetDefaultOptions();
|
||||
export async function getDefaultPublishDeploymentOptions(project: Project): Promise<mssql.DeploymentOptions | vscodeMssql.DeploymentOptions> {
|
||||
const schemaCompareService = await getSchemaCompareService();
|
||||
const result = await schemaCompareService.schemaCompareGetDefaultOptions();
|
||||
const deploymentOptions = result.defaultDeploymentOptions;
|
||||
// re-include database-scoped credentials
|
||||
if (getAzdataApi()) {
|
||||
deploymentOptions.excludeObjectTypes = (deploymentOptions as mssql.DeploymentOptions).excludeObjectTypes.filter(x => x !== mssql.SchemaObjectType.DatabaseScopedCredentials);
|
||||
} else {
|
||||
deploymentOptions.excludeObjectTypes = (deploymentOptions as vscodeMssql.DeploymentOptions).excludeObjectTypes.filter(x => x !== vscodeMssql.SchemaObjectType.DatabaseScopedCredentials);
|
||||
}
|
||||
|
||||
// this option needs to be true for same database references validation to work
|
||||
if (project.databaseReferences.length > 0) {
|
||||
deploymentOptions.includeCompositeObjects = true;
|
||||
}
|
||||
return result.defaultDeploymentOptions;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,19 @@ import { SqlTargetPlatform } from 'sqldbproj';
|
||||
|
||||
const maxTableLength = 10;
|
||||
|
||||
/**
|
||||
* This is a duplicate of the TaskExecutionMode from azdata.d.ts/vscode-mssql.d.ts, which is needed
|
||||
* for using when running in VS Code since we don't have an actual implementation of the enum at runtime
|
||||
* (unlike azdata which is injected by the extension host). Even specifying it as a const enum in the
|
||||
* typings file currently doesn't work as the TypeScript compiler doesn't currently inline const enum
|
||||
* values imported as "import type" https://github.com/microsoft/TypeScript/issues/40344
|
||||
*/
|
||||
export enum TaskExecutionMode {
|
||||
execute = 0,
|
||||
script = 1,
|
||||
executeAndScript = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Controller for managing lifecycle of projects
|
||||
*/
|
||||
@@ -252,7 +265,7 @@ export class ProjectsController {
|
||||
|
||||
return publishDatabaseDialog;
|
||||
} else {
|
||||
launchPublishDatabaseQuickpick(project);
|
||||
launchPublishDatabaseQuickpick(project, this);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -268,9 +281,7 @@ export class ProjectsController {
|
||||
const telemetryProps: Record<string, string> = {};
|
||||
const telemetryMeasures: Record<string, number> = {};
|
||||
const buildStartTime = new Date().getTime();
|
||||
|
||||
const dacpacPath = await this.buildProject(project);
|
||||
|
||||
const buildEndTime = new Date().getTime();
|
||||
telemetryMeasures.buildDuration = buildEndTime - buildStartTime;
|
||||
telemetryProps.buildSucceeded = (dacpacPath !== '').toString();
|
||||
@@ -287,7 +298,6 @@ export class ProjectsController {
|
||||
// copy dacpac to temp location before publishing
|
||||
const tempPath = path.join(os.tmpdir(), `${path.parse(dacpacPath).name}_${new Date().getTime()}${constants.sqlprojExtension}`);
|
||||
await fs.copyFile(dacpacPath, tempPath);
|
||||
|
||||
const dacFxService = await utils.getDacFxService();
|
||||
|
||||
let result: mssql.DacFxResult;
|
||||
@@ -308,19 +318,19 @@ export class ProjectsController {
|
||||
if (publish) {
|
||||
telemetryProps.publishAction = 'deploy';
|
||||
if (azdataApi) {
|
||||
result = await (dacFxService as mssql.IDacFxService).deployDacpac(tempPath, settings.databaseName, true, settings.connectionUri, azdataApi.TaskExecutionMode.execute, settings.sqlCmdVariables, settings.deploymentOptions);
|
||||
result = await (dacFxService as mssql.IDacFxService).deployDacpac(tempPath, settings.databaseName, true, settings.connectionUri, azdataApi.TaskExecutionMode.execute, settings.sqlCmdVariables, settings.deploymentOptions as mssql.DeploymentOptions);
|
||||
} else {
|
||||
// TODO@chgagnon Fix typing
|
||||
result = await (dacFxService as mssqlVscode.IDacFxService).deployDacpac(tempPath, settings.databaseName, true, settings.connectionUri, <mssqlVscode.TaskExecutionMode><any>azdataApi!.TaskExecutionMode.execute, settings.sqlCmdVariables, <mssqlVscode.DeploymentOptions><any>settings.deploymentOptions);
|
||||
// Have to cast to unknown first to get around compiler error since the mssqlVscode doesn't exist as an actual module at runtime
|
||||
result = await (dacFxService as mssqlVscode.IDacFxService).deployDacpac(tempPath, settings.databaseName, true, settings.connectionUri, TaskExecutionMode.execute as unknown as mssqlVscode.TaskExecutionMode, settings.sqlCmdVariables, settings.deploymentOptions as mssqlVscode.DeploymentOptions);
|
||||
}
|
||||
|
||||
} else {
|
||||
telemetryProps.publishAction = 'generateScript';
|
||||
if (azdataApi) {
|
||||
result = await (dacFxService as mssql.IDacFxService).generateDeployScript(tempPath, settings.databaseName, settings.connectionUri, azdataApi.TaskExecutionMode.script, settings.sqlCmdVariables, settings.deploymentOptions);
|
||||
result = await (dacFxService as mssql.IDacFxService).generateDeployScript(tempPath, settings.databaseName, settings.connectionUri, azdataApi.TaskExecutionMode.script, settings.sqlCmdVariables, settings.deploymentOptions as mssql.DeploymentOptions);
|
||||
} else {
|
||||
// TODO@chgagnon Fix typing
|
||||
result = await (dacFxService as mssqlVscode.IDacFxService).generateDeployScript(tempPath, settings.databaseName, settings.connectionUri, <any>undefined/*mssqlVscode.TaskExecutionMode.script*/, settings.sqlCmdVariables, <mssqlVscode.DeploymentOptions><any>settings.deploymentOptions);
|
||||
// Have to cast to unknown first to get around compiler error since the mssqlVscode doesn't exist as an actual module at runtime
|
||||
result = await (dacFxService as mssqlVscode.IDacFxService).generateDeployScript(tempPath, settings.databaseName, settings.connectionUri, TaskExecutionMode.script as unknown as mssqlVscode.TaskExecutionMode, settings.sqlCmdVariables, settings.deploymentOptions as mssqlVscode.DeploymentOptions);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -337,10 +347,8 @@ export class ProjectsController {
|
||||
const currentPublishIndex = this.publishInfo.findIndex(d => d.startDate === currentPublishTimeInfo);
|
||||
this.publishInfo[currentPublishIndex].status = Status.failed;
|
||||
this.publishInfo[currentPublishIndex].timeToCompleteAction = utils.timeConversion(timeToFailurePublish);
|
||||
|
||||
throw err;
|
||||
}
|
||||
|
||||
const actionEndTime = new Date().getTime();
|
||||
const timeToPublish = actionEndTime - actionStartTime;
|
||||
telemetryProps.actionDuration = timeToPublish.toString();
|
||||
|
||||
@@ -11,7 +11,7 @@ import * as utils from '../common/utils';
|
||||
import { Project } from '../models/project';
|
||||
import { SqlConnectionDataSource } from '../models/dataSources/sqlConnectionStringSource';
|
||||
import { IDeploySettings } from '../models/IDeploySettings';
|
||||
import { DeploymentOptions, SchemaObjectType } from '../../../mssql/src/mssql';
|
||||
import { DeploymentOptions } from '../../../mssql/src/mssql';
|
||||
import { IconPathHelper } from '../common/iconHelper';
|
||||
import { cssStyles } from '../common/uiConstants';
|
||||
import { getConnectionName } from './utils';
|
||||
@@ -221,15 +221,8 @@ export class PublishDatabaseDialog {
|
||||
// eventually, database options will be configurable in this dialog
|
||||
// but for now, just send the default DacFx deployment options if no options were loaded from a publish profile
|
||||
if (!this.deploymentOptions) {
|
||||
this.deploymentOptions = await utils.GetDefaultDeploymentOptions();
|
||||
|
||||
// re-include database-scoped credentials
|
||||
this.deploymentOptions.excludeObjectTypes = this.deploymentOptions.excludeObjectTypes.filter(x => x !== SchemaObjectType.DatabaseScopedCredentials);
|
||||
|
||||
// this option needs to be true for same database references validation to work
|
||||
if (this.project.databaseReferences.length > 0) {
|
||||
this.deploymentOptions.includeCompositeObjects = true;
|
||||
}
|
||||
// We only use the dialog in ADS context currently so safe to cast to the mssql DeploymentOptions here
|
||||
this.deploymentOptions = await utils.getDefaultPublishDeploymentOptions(this.project) as DeploymentOptions;
|
||||
}
|
||||
|
||||
return this.deploymentOptions;
|
||||
|
||||
@@ -8,13 +8,15 @@ import * as constants from '../common/constants';
|
||||
import { Project } from '../models/project';
|
||||
import { PublishProfile, readPublishProfile } from '../models/publishProfile/publishProfile';
|
||||
import { promptForPublishProfile } from './publishDatabaseDialog';
|
||||
import { getVscodeMssqlApi } from '../common/utils';
|
||||
import { getDefaultPublishDeploymentOptions, getVscodeMssqlApi } from '../common/utils';
|
||||
import { IConnectionInfo } from 'vscode-mssql';
|
||||
import { ProjectsController } from '../controllers/projectController';
|
||||
import { IDeploySettings } from '../models/IDeploySettings';
|
||||
|
||||
/**
|
||||
* Create flow for Publishing a database using only VS Code-native APIs such as QuickPick
|
||||
*/
|
||||
export async function launchPublishDatabaseQuickpick(project: Project): Promise<void> {
|
||||
export async function launchPublishDatabaseQuickpick(project: Project, projectController: ProjectsController): Promise<void> {
|
||||
|
||||
// 1. Select publish settings file (optional)
|
||||
// Create custom quickpick so we can control stuff like displaying the loading indicator
|
||||
@@ -192,12 +194,13 @@ export async function launchPublishDatabaseQuickpick(project: Project): Promise<
|
||||
|
||||
// TODO@chgagnon: Get deployment options
|
||||
// 6. Generate script/publish
|
||||
// let settings: IDeploySettings | IGenerateScriptSettings = {
|
||||
// databaseName: databaseName,
|
||||
// serverName: connectionProfile!.server,
|
||||
// connectionUri: '', // TODO@chgagnon: Get from connection profile
|
||||
// sqlCmdVariables: sqlCmdVariables,
|
||||
// deploymentOptions: undefined, // await this.getDeploymentOptions(),
|
||||
// profileUsed: !!publishProfile
|
||||
// };
|
||||
let settings: IDeploySettings = {
|
||||
databaseName: databaseName,
|
||||
serverName: connectionProfile!.server,
|
||||
connectionUri: connectionUri,
|
||||
sqlCmdVariables: sqlCmdVariables,
|
||||
deploymentOptions: await getDefaultPublishDeploymentOptions(project),
|
||||
profileUsed: !!publishProfile
|
||||
};
|
||||
await projectController.publishOrScriptProject(project, settings, action === constants.publish);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { DeploymentOptions } from '../../../mssql/src/mssql';
|
||||
import { DeploymentOptions as mssqlDeploymentOptions } from '../../../mssql/src/mssql';
|
||||
import { DeploymentOptions as vscodeMssqlDeploymentOptions } from 'vscode-mssql';
|
||||
|
||||
export type DeploymentOptions = mssqlDeploymentOptions | vscodeMssqlDeploymentOptions;
|
||||
|
||||
export interface IDeploySettings {
|
||||
databaseName: string;
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
import * as vscode from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as os from 'os';
|
||||
import * as constants from './../common/constants';
|
||||
import { promises as fs } from 'fs';
|
||||
import * as utils from '../common/utils';
|
||||
import { mssqlNotFound } from '../common/constants';
|
||||
import { sqlToolsServiceNotFound } from '../common/constants';
|
||||
import * as mssql from '../../../mssql/src/mssql';
|
||||
import * as vscodeMssql from 'vscode-mssql';
|
||||
import * as sqldbproj from 'sqldbproj';
|
||||
|
||||
const buildDirectory = 'BuildDirectory';
|
||||
const buildFiles: string[] = [
|
||||
@@ -33,7 +35,7 @@ export class BuildHelper {
|
||||
private initialized: boolean = false;
|
||||
|
||||
constructor() {
|
||||
this.extensionDir = vscode.extensions.getExtension(constants.sqlDatabaseProjectExtensionId)?.extensionPath ?? '';
|
||||
this.extensionDir = vscode.extensions.getExtension(sqldbproj.extension.name)?.extensionPath ?? '';
|
||||
this.extensionBuildDir = path.join(this.extensionDir, buildDirectory);
|
||||
}
|
||||
|
||||
@@ -59,18 +61,35 @@ export class BuildHelper {
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
// get mssql sqltoolsservice path
|
||||
/**
|
||||
* Gets the path to the SQL Tools Service installation
|
||||
* @returns
|
||||
*/
|
||||
private async getBuildDirPathFromMssqlTools(): Promise<string> {
|
||||
const mssqlConfigDir = vscode.extensions.getExtension(constants.mssqlExtensionId)?.extensionPath ?? '';
|
||||
let mssqlConfigDir = '';
|
||||
if (utils.getAzdataApi()) {
|
||||
mssqlConfigDir = vscode.extensions.getExtension(mssql.extension.name)?.extensionPath ?? '';
|
||||
} else {
|
||||
// VS Code MSSQL extension has its tools service config in a slightly different spot
|
||||
mssqlConfigDir = path.join(vscode.extensions.getExtension(vscodeMssql.extension.name)?.extensionPath ?? '', 'out', 'src');
|
||||
}
|
||||
|
||||
if (await utils.exists(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());
|
||||
let installDir = '';
|
||||
if (utils.getAzdataApi()) {
|
||||
installDir = config.installDirectory?.replace('{#version#}', config.version).replace('{#platform#}', this.getPlatform());
|
||||
} else {
|
||||
// VS Code MSSQL extension has its config.json
|
||||
installDir = config.service?.installDir?.replace('{#version#}', config.version).replace('{#platform#}', this.getPlatform());
|
||||
}
|
||||
|
||||
if (installDir) {
|
||||
return path.join(mssqlConfigDir, installDir);
|
||||
}
|
||||
}
|
||||
throw new Error(mssqlNotFound(mssqlConfigDir));
|
||||
throw new Error(sqlToolsServiceNotFound(mssqlConfigDir));
|
||||
}
|
||||
|
||||
private getPlatform(): string {
|
||||
|
||||
Reference in New Issue
Block a user