fix excluding pre/post/none deploy scripts in sdk style projects (#18117)

This commit is contained in:
Kim Santiago
2022-01-25 11:49:52 -08:00
committed by GitHub
parent 84c7afa6da
commit f0dd31c457
2 changed files with 83 additions and 3 deletions

View File

@@ -238,6 +238,10 @@ export class Project implements ISqlProject {
this.preDeployScripts.forEach(f => filesSet.delete(f.relativePath));
this.postDeployScripts.forEach(f => filesSet.delete(f.relativePath));
this.noneDeployScripts.forEach(f => filesSet.delete(f.relativePath));
// remove any none remove scripts (these would be pre/post/none deploy scripts that were excluded)
const noneRemoveScripts = this.readNoneRemoveScripts();
noneRemoveScripts.forEach(f => filesSet.delete(f.relativePath));
}
// create a FileProjectEntry for each file
@@ -379,7 +383,10 @@ export class Project implements ISqlProject {
try {
const noneItems = itemGroup.getElementsByTagName(constants.None);
for (let n = 0; n < noneItems.length; n++) {
noneDeployScripts.push(this.createFileProjectEntry(noneItems[n].getAttribute(constants.Include)!, EntryType.File));
const includeAttribute = noneItems[n].getAttribute(constants.Include);
if (includeAttribute) {
noneDeployScripts.push(this.createFileProjectEntry(includeAttribute, EntryType.File));
}
}
} catch (e) {
void window.showErrorMessage(constants.errorReadingProject(constants.NoneElements, this.projectFilePath));
@@ -390,6 +397,30 @@ export class Project implements ISqlProject {
return noneDeployScripts;
}
/**
* @returns all the files specified as <None Remove="file.sql" /> in the sqlproj
*/
private readNoneRemoveScripts(): FileProjectEntry[] {
const noneRemoveScripts: FileProjectEntry[] = [];
for (let ig = 0; ig < this.projFileXmlDoc!.documentElement.getElementsByTagName(constants.ItemGroup).length; ig++) {
const itemGroup = this.projFileXmlDoc!.documentElement.getElementsByTagName(constants.ItemGroup)[ig];
// find all none remove scripts to specified in the sqlproj
try {
const noneItems = itemGroup.getElementsByTagName(constants.None);
for (let n = 0; n < noneItems.length; n++) {
noneRemoveScripts.push(this.createFileProjectEntry(noneItems[n].getAttribute(constants.Remove)!, EntryType.File));
}
} catch (e) {
void window.showErrorMessage(constants.errorReadingProject(constants.NoneElements, this.projectFilePath));
console.error(utils.getErrorMessage(e));
}
}
return noneRemoveScripts;
}
private readDatabaseReferences(): IDatabaseReferenceProjectEntry[] {
const databaseReferenceEntries: IDatabaseReferenceProjectEntry[] = [];
@@ -969,6 +1000,8 @@ export class Project implements ISqlProject {
const noneNodes = this.projFileXmlDoc!.documentElement.getElementsByTagName(constants.None);
const nodes = [fileNodes, preDeployNodes, postDeployNodes, noneNodes];
const isBuildElement = this.files.find(f => f.relativePath === path);
let deleted = false;
// remove the <Build Include="..."> entry if there is one
@@ -992,13 +1025,17 @@ export class Project implements ISqlProject {
if (deleted) {
await this.serializeToProjFile(this.projFileXmlDoc!);
}
this._preDeployScripts = this.readPreDeployScripts();
this._postDeployScripts = this.readPostDeployScripts();
this._noneDeployScripts = this.readNoneDeployScripts();
const currentFiles = await this.readFilesInProject();
// only add a node to exclude the file if it's still included by a glob
// only add a Remove node to exclude the file if it's still included by a glob
if (currentFiles.find(f => f.relativePath === utils.convertSlashesForSqlProj(path))) {
const removeFileNode = this.projFileXmlDoc!.createElement(constants.Build);
const removeFileNode = isBuildElement ? this.projFileXmlDoc!.createElement(constants.Build) : this.projFileXmlDoc!.createElement(constants.None);
removeFileNode.setAttribute(constants.Remove, utils.convertSlashesForSqlProj(path));
this.findOrCreateItemGroup(constants.Build).appendChild(removeFileNode);
return;
}
return;
@@ -1503,6 +1540,10 @@ export class Project implements ISqlProject {
* @returns Project entry for the last folder in the path, if path is under the project folder; otherwise `undefined`.
*/
private async ensureFolderItems(relativeFolderPath: string): Promise<FileProjectEntry | undefined> {
if (!relativeFolderPath) {
return;
}
const absoluteFolderPath = path.join(this.projectFolderPath, relativeFolderPath);
const normalizedProjectFolderPath = path.normalize(this.projectFolderPath);

View File

@@ -988,6 +988,45 @@ describe('Project: sdk style project content operations', function (): void {
should(project.files.filter(f => f.relativePath === 'file1.sql').length).equal(0);
});
it('Should exclude pre/post/none deploy scripts correctly', async function (): Promise<void> {
const folderPath = await testUtils.generateTestFolderPath();
projFilePath = await testUtils.createTestSqlProjFile(baselines.newSdkStyleProjectSdkNodeBaseline, folderPath);
const project: Project = await Project.openProject(projFilePath);
await project.addScriptItem('Script.PreDeployment1.sql', 'fake contents', templates.preDeployScript);
await project.addScriptItem('Script.PreDeployment2.sql', 'fake contents', templates.preDeployScript);
await project.addScriptItem('Script.PostDeployment1.sql', 'fake contents', templates.postDeployScript);
// verify they were added to the sqlproj
let projFileText = (await fs.readFile(projFilePath)).toString();
should(projFileText.includes('<PreDeploy Include="Script.PreDeployment1.sql" />')).equal(true);
should(projFileText.includes('<None Include="Script.PreDeployment2.sql" />')).equal(true);
should(projFileText.includes('<PostDeploy Include="Script.PostDeployment1.sql" />')).equal(true);
should(project.preDeployScripts.length).equal(1, 'Script.PreDeployment1.sql should have been added');
should(project.noneDeployScripts.length).equal(1, 'Script.PreDeployment2.sql should have been added');
should(project.preDeployScripts.length).equal(1, 'Script.PostDeployment1.sql should have been added');
should(project.files.length).equal(0, 'There should not be any files');
// exclude the pre/post/none deploy script
await project.exclude(project.preDeployScripts.find(f => f.relativePath === 'Script.PreDeployment1.sql')!);
await project.exclude(project.noneDeployScripts.find(f => f.relativePath === 'Script.PreDeployment2.sql')!);
await project.exclude(project.postDeployScripts.find(f => f.relativePath === 'Script.PostDeployment1.sql')!);
// verify they are excluded in the sqlproj
projFileText = (await fs.readFile(projFilePath)).toString();
should(projFileText.includes('<PreDeploy Include="Script.PreDeployment1.sql" />')).equal(false);
should(projFileText.includes('<None Include="Script.PreDeployment2.sql" />')).equal(false);
should(projFileText.includes('<PostDeploy Include="Script.PostDeployment1.sql" />')).equal(false);
should(projFileText.includes('<None Remove="Script.PreDeployment1.sql" />')).equal(true);
should(projFileText.includes('<None Remove="Script.PreDeployment2.sql" />')).equal(true);
should(projFileText.includes('<None Remove="Script.PostDeployment1.sql" />')).equal(true);
should(project.preDeployScripts.length).equal(0, 'Script.PreDeployment1.sql should have been removed');
should(project.noneDeployScripts.length).equal(0, 'Script.PreDeployment2.sql should have been removed');
should(project.postDeployScripts.length).equal(0, 'Script.PostDeployment1.sql should have been removed');
should(project.files.length).equal(0, 'There should not be any files after the excludes');
});
it('Should handle excluding files included by glob patterns', async function (): Promise<void> {
const testFolderPath = await testUtils.generateTestFolderPath();
const mainProjectPath = path.join(testFolderPath, 'project');