mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-14 01:25:37 -05:00
add initial SDK-style project migration (#18827)
* add initial SDK-style project migration * addressing comments
This commit is contained in:
@@ -22,6 +22,7 @@ export const msdbDacpac = 'msdb.dacpac';
|
||||
export const MicrosoftDatatoolsSchemaSqlSql = 'Microsoft.Data.Tools.Schema.Sql.Sql';
|
||||
export const databaseSchemaProvider = 'DatabaseSchemaProvider';
|
||||
export const sqlProjectSdk = 'Microsoft.Build.Sql';
|
||||
export const sqlProjectSdkVersion = '0.1.3-preview';
|
||||
|
||||
// Project Provider
|
||||
export const emptySqlDatabaseProjectTypeId = 'EmptySqlDbProj';
|
||||
@@ -100,6 +101,7 @@ export function deleteConfirmationContents(toDelete: string) { return localize('
|
||||
export function deleteReferenceConfirmation(toDelete: string) { return localize('deleteReferenceConfirmation', "Are you sure you want to delete the reference to {0}?", toDelete); }
|
||||
export function selectTargetPlatform(currentTargetPlatform: string) { return localize('selectTargetPlatform', "Current target platform: {0}. Select new target platform", currentTargetPlatform); }
|
||||
export function currentTargetPlatform(projectName: string, currentTargetPlatform: string) { return localize('currentTargetPlatform', "Target platform of the project {0} is now {1}", projectName, currentTargetPlatform); }
|
||||
export function projectUpdatedToSdkStyle(projectName: string) { return localize('projectUpdatedToSdkStyle', "The project {0} has been updated to be an SDK-style project. Click 'Learn More' for details on the Microsoft.Build.Sql SDK and ways to simplify the project file.", projectName); }
|
||||
|
||||
// Publish dialog strings
|
||||
|
||||
|
||||
@@ -862,6 +862,24 @@ export class ProjectsController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a legacy style project to an SDK-style project
|
||||
* @param context a treeItem in a project's hierarchy, to be used to obtain a Project
|
||||
*/
|
||||
public async convertToSdkStyleProject(context: dataworkspace.WorkspaceTreeItem): Promise<void> {
|
||||
const project = this.getProjectFromContext(context);
|
||||
|
||||
await project.convertProjectToSdkStyle();
|
||||
void this.reloadProject(context);
|
||||
|
||||
// show message that project file can be simplified
|
||||
const result = await vscode.window.showInformationMessage(constants.projectUpdatedToSdkStyle(project.projectFileName), constants.learnMore);
|
||||
|
||||
if (result === constants.learnMore) {
|
||||
void vscode.env.openExternal(vscode.Uri.parse(constants.sdkLearnMoreUrl!));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a database reference to the project
|
||||
* @param context a treeItem in a project's hierarchy, to be used to obtain a Project
|
||||
|
||||
@@ -617,6 +617,45 @@ export class Project implements ISqlProject {
|
||||
await this.createCleanFileNode(beforeBuildNode);
|
||||
}
|
||||
|
||||
public async convertProjectToSdkStyle(): Promise<void> {
|
||||
// don't do anything if the project is already SDK style or it's an SSDT project that hasn't been updated to build in ADS
|
||||
if (this.isSdkStyleProject || !this._importedTargets.includes(constants.NetCoreTargets)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// make backup copy of project
|
||||
await fs.copyFile(this._projectFilePath, this._projectFilePath + '_backup');
|
||||
|
||||
// remove SSDT and ADS SqlTasks imports
|
||||
const importsToRemove = [];
|
||||
for (let i = 0; i < this.projFileXmlDoc!.documentElement.getElementsByTagName(constants.Import).length; i++) {
|
||||
const importTarget = this.projFileXmlDoc!.documentElement.getElementsByTagName(constants.Import)[i];
|
||||
const projectAttributeVal = importTarget.getAttribute(constants.Project);
|
||||
|
||||
if (projectAttributeVal === constants.NetCoreTargets || projectAttributeVal === constants.SqlDbTargets || projectAttributeVal === constants.MsBuildtargets) {
|
||||
importsToRemove.push(importTarget);
|
||||
}
|
||||
}
|
||||
|
||||
const parent = importsToRemove[0]?.parentNode;
|
||||
importsToRemove.forEach(i => { parent?.removeChild(i); });
|
||||
|
||||
// add SDK node
|
||||
const sdkNode = this.projFileXmlDoc!.createElement(constants.Sdk);
|
||||
sdkNode.setAttribute(constants.Name, constants.sqlProjectSdk);
|
||||
sdkNode.setAttribute(constants.Version, constants.sqlProjectSdkVersion);
|
||||
|
||||
const projectNode = this.projFileXmlDoc!.documentElement;
|
||||
projectNode.insertBefore(sdkNode, projectNode.firstChild);
|
||||
|
||||
// TODO: also update system dacpac path, but might as well wait for them to get included in the SDK since the path will probably change again
|
||||
|
||||
// TODO: remove Build includes and folder includes. Make sure the same files and folders are being included and there aren't extra files included by the default **/*.sql glob
|
||||
|
||||
await this.serializeToProjFile(this.projFileXmlDoc!);
|
||||
await this.readProjFile();
|
||||
}
|
||||
|
||||
private async createCleanFileNode(parentNode: Element): Promise<void> {
|
||||
const deleteFileNode = this.projFileXmlDoc!.createElement(constants.Delete);
|
||||
deleteFileNode.setAttribute(constants.Files, constants.ProjJsonToClean);
|
||||
|
||||
@@ -1577,7 +1577,7 @@ describe('Project: round trip updates', function (): void {
|
||||
await testUpdateInRoundTrip(baselines.SSDTUpdatedProjectBaseline, baselines.SSDTUpdatedProjectAfterSystemDbUpdateBaseline);
|
||||
});
|
||||
|
||||
it('Should update SSDT project to work in ADS handling pre-exsiting targets', async function (): Promise<void> {
|
||||
it('Should update SSDT project to work in ADS handling pre-existing targets', async function (): Promise<void> {
|
||||
await testUpdateInRoundTrip(baselines.SSDTProjectBaselineWithBeforeBuildTarget, baselines.SSDTProjectBaselineWithBeforeBuildTargetAfterUpdate);
|
||||
});
|
||||
|
||||
@@ -1646,3 +1646,55 @@ async function testUpdateInRoundTrip(fileBeforeupdate: string, fileAfterUpdate:
|
||||
should(stub.calledOnce).be.true('showWarningMessage should have been called exactly once');
|
||||
sinon.restore();
|
||||
}
|
||||
|
||||
describe('Project: legacy to SDK-style updates', function (): void {
|
||||
before(async function (): Promise<void> {
|
||||
await baselines.loadBaselines();
|
||||
});
|
||||
|
||||
beforeEach(function (): void {
|
||||
sinon.restore();
|
||||
});
|
||||
|
||||
it('Should update legacy style project to SDK-style', async function (): Promise<void> {
|
||||
const projFilePath = await testUtils.createTestSqlProjFile(baselines.openProjectFileBaseline);
|
||||
const project = await Project.openProject(projFilePath);
|
||||
|
||||
should(project.importedTargets.length).equal(3, 'SSDT and ADS imports should be in the project');
|
||||
should(project.isSdkStyleProject).equal(false);
|
||||
await project.convertProjectToSdkStyle();
|
||||
|
||||
should(await exists(projFilePath + '_backup')).equal(true, 'Backup file should have been generated before the project was updated');
|
||||
should(project.importedTargets.length).equal(0, 'SSDT and ADS imports should have been removed');
|
||||
should(project.isSdkStyleProject).equal(true);
|
||||
});
|
||||
|
||||
it('Should not update project and no backup file should be created when project is already SDK-style', async function (): Promise<void> {
|
||||
// setup test files
|
||||
const folderPath = await testUtils.generateTestFolderPath();
|
||||
const sqlProjPath = await testUtils.createTestSqlProjFile(baselines.openSdkStyleSqlProjectBaseline, folderPath);
|
||||
|
||||
const project = await Project.openProject(Uri.file(sqlProjPath).fsPath);
|
||||
should(project.isSdkStyleProject).equal(true);
|
||||
await project.convertProjectToSdkStyle();
|
||||
|
||||
should(await exists(sqlProjPath + '_backup')).equal(false, 'No backup file should have been created');
|
||||
should(project.isSdkStyleProject).equal(true);
|
||||
});
|
||||
|
||||
it('Should not update project and no backup file should be created when it is an SSDT project that has not been updated to work in ADS', async function (): Promise<void> {
|
||||
sinon.stub(window, 'showWarningMessage').returns(<any>Promise.resolve(constants.noString));
|
||||
// setup test files
|
||||
const folderPath = await testUtils.generateTestFolderPath();
|
||||
const sqlProjPath = await testUtils.createTestSqlProjFile(baselines.SSDTProjectFileBaseline, folderPath);
|
||||
|
||||
const project = await Project.openProject(Uri.file(sqlProjPath).fsPath);
|
||||
should(project.isSdkStyleProject).equal(false);
|
||||
should(project.importedTargets.length).equal(2, 'Project should have 2 SSDT imports');
|
||||
await project.convertProjectToSdkStyle();
|
||||
|
||||
should(await exists(sqlProjPath + '_backup')).equal(false, 'No backup file should have been created');
|
||||
should(project.importedTargets.length).equal(2, 'Project imports should not have been changed');
|
||||
should(project.isSdkStyleProject).equal(false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user