Remove build and folder includes when converting to SDK-style project (#18889)

* remove Build and Folder Includes

* add tests

* cleanup

* rollback if there was an error during the conversion
This commit is contained in:
Kim Santiago
2022-04-01 18:04:18 -07:00
committed by GitHub
parent a795e64bed
commit d6dab3dd18
7 changed files with 296 additions and 35 deletions

View File

@@ -617,43 +617,90 @@ export class Project implements ISqlProject {
await this.createCleanFileNode(beforeBuildNode);
}
public async convertProjectToSdkStyle(): Promise<void> {
public async convertProjectToSdkStyle(): Promise<boolean> {
// 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;
return false;
}
// 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);
try {
// remove Build includes and folder includes
const beforeFiles = this.files.filter(f => f.type === EntryType.File);
const beforeFolders = this.files.filter(f => f.type === EntryType.Folder);
if (projectAttributeVal === constants.NetCoreTargets || projectAttributeVal === constants.SqlDbTargets || projectAttributeVal === constants.MsBuildtargets) {
importsToRemove.push(importTarget);
// remove Build includes
for (const file of beforeFiles) {
// only remove build includes in the same folder as the project
if (!file.relativePath.includes('..')) {
await this.exclude(file);
}
}
// remove Folder includes
for (const folder of beforeFolders) {
await this.exclude(folder);
}
// remove "Properties" folder if it's there. This isn't tracked in the project's folders here because ADS doesn't support it.
// It's a reserved folder only used for the UI in SSDT
try {
await this.removeFolderFromProjFile('Properties');
} catch { }
// 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
await this.serializeToProjFile(this.projFileXmlDoc!);
await this.readProjFile();
// Make sure the same files are included as before and there aren't extra files included by the default **/*.sql glob
for (const file of this.files.filter(f => f.type === EntryType.File)) {
if (!beforeFiles.find(f => f.pathForSqlProj() === file.pathForSqlProj())) {
await this.exclude(file);
}
}
// add back any folders that were previously specified in the sqlproj, but aren't included by the **/*.sql glob because they're empty
const folders = this.files.filter(f => f.type === EntryType.Folder);
for (const folder of beforeFolders) {
if (!folders.find(f => f.relativePath === folder.relativePath)) {
await this.addFolderItem(folder.relativePath);
}
}
} catch (e) {
console.error(e);
// if there was an uncaught error during conversion, rollback project update
await fs.copyFile(this._projectFilePath + '_backup', this._projectFilePath);
await this.readProjFile();
return false;
}
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();
return true;
}
private async createCleanFileNode(parentNode: Element): Promise<void> {