diff options
author | Duarte Meneses <duarte.meneses@sonarsource.com> | 2018-11-26 11:53:51 -0600 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2019-01-16 09:43:02 +0100 |
commit | 056023aba07e95f5e9bb1d5d2c4e611955458986 (patch) | |
tree | bfc5466d4328502b01c20d87759132057e709320 | |
parent | f686185f25130fe4d84ae3b9307b784895414d5a (diff) | |
download | sonarqube-056023aba07e95f5e9bb1d5d2c4e611955458986.tar.gz sonarqube-056023aba07e95f5e9bb1d5d2c4e611955458986.zip |
SONAR-11463 Remove path from scanner report and add module directories
15 files changed, 283 insertions, 108 deletions
diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ComponentUuidFactory.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ComponentUuidFactory.java index b87e740492a..3a23ea8cbe1 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ComponentUuidFactory.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ComponentUuidFactory.java @@ -19,9 +19,11 @@ */ package org.sonar.ce.task.projectanalysis.component; +import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Supplier; import java.util.stream.Collectors; import org.apache.commons.lang.StringUtils; import org.sonar.api.resources.Qualifiers; @@ -36,11 +38,11 @@ import org.sonar.db.component.KeyWithUuidDto; public class ComponentUuidFactory { private final Map<String, String> uuidsByKey = new HashMap<>(); - public ComponentUuidFactory(DbClient dbClient, DbSession dbSession, String rootKey) { - Map<String, String> modulePathsByUuid = loadModulePathsByUuid(dbClient, dbSession, rootKey); + public ComponentUuidFactory(DbClient dbClient, DbSession dbSession, String rootKey, Supplier<Map<String, String>> reportModulesPath) { + Map<String, String> modulePathsByUuid = loadModulePathsByUuid(dbClient, dbSession, rootKey, reportModulesPath); if (modulePathsByUuid.isEmpty()) { - // only contains root project + // only contains root project or we don't have relative paths for other modules anyway List<KeyWithUuidDto> keys = dbClient.componentDao().selectUuidsByKeyFromProjectKey(dbSession, rootKey); keys.forEach(dto -> uuidsByKey.put(dto.key(), dto.uuid())); } else { @@ -57,33 +59,24 @@ public class ComponentUuidFactory { return dbClient.componentDao().selectComponentsWithModuleUuidFromProjectKey(dbSession, rootKey); } - private static Map<String, String> loadModulePathsByUuid(DbClient dbClient, DbSession dbSession, String rootKey) { + private static Map<String, String> loadModulePathsByUuid(DbClient dbClient, DbSession dbSession, String rootKey, Supplier<Map<String, String>> reportModulesPath) { List<ComponentDto> moduleDtos = dbClient.componentDao() .selectEnabledModulesFromProjectKey(dbSession, rootKey, false).stream() .filter(c -> Qualifiers.MODULE.equals(c.qualifier())) .collect(Collectors.toList()); - Map<String, ComponentDto> dtoByUuid = moduleDtos.stream() - .collect(Collectors.toMap(ComponentDto::uuid, dto -> dto)); + if (moduleDtos.isEmpty()) { + return Collections.emptyMap(); + } + Map<String, String> pathByModuleKey = reportModulesPath.get(); Map<String, String> modulePathByUuid = new HashMap<>(); - for (ComponentDto dto : moduleDtos) { - String modulePath = null; - ComponentDto currentDto = dto; - while (currentDto != null && currentDto.moduleUuid() != null) { - String path = currentDto.path(); - if (modulePath == null) { - modulePath = path; - } else { - modulePath = path + "/" + modulePath; - } - currentDto = dtoByUuid.get(currentDto.moduleUuid()); + String relativePath = pathByModuleKey.get(dto.getKey()); + if (relativePath != null) { + modulePathByUuid.put(dto.uuid(), relativePath); } - - modulePathByUuid.put(dto.uuid(), modulePath); } - return modulePathByUuid; } diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ReportModulesPath.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ReportModulesPath.java new file mode 100644 index 00000000000..57c8e8c4806 --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/component/ReportModulesPath.java @@ -0,0 +1,67 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.ce.task.projectanalysis.component; + +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.function.Supplier; +import org.sonar.ce.task.projectanalysis.batch.BatchReportReader; +import org.sonar.scanner.protocol.output.ScannerReport; + +public class ReportModulesPath implements Supplier<Map<String, String>> { + private final BatchReportReader reader; + + public ReportModulesPath(BatchReportReader reader) { + this.reader = reader; + } + + public Map<String, String> get() { + ScannerReport.Metadata metadata = reader.readMetadata(); + Map<String, String> modulesProjectRelativePathByKey = metadata.getModulesProjectRelativePathByKeyMap(); + if (modulesProjectRelativePathByKey.isEmpty()) { + return collectModulesPathFromHierarchy(metadata); + } + return modulesProjectRelativePathByKey; + } + + /** + * This should only be needed if we receive a report of the previous version without the path per module explicitly set + * (due to blue/green deployment) + * Can be removed in any future version + */ + private Map<String, String> collectModulesPathFromHierarchy(ScannerReport.Metadata metadata) { + ScannerReport.Component root = reader.readComponent(metadata.getRootComponentRef()); + Map<String, String> modulesPathByKey = new LinkedHashMap<>(); + LinkedList<Integer> queue = new LinkedList<>(); + queue.addAll(root.getChildRefList()); + + while (!queue.isEmpty()) { + ScannerReport.Component component = reader.readComponent(queue.removeFirst()); + if (component.getType() == ScannerReport.Component.ComponentType.MODULE) { + queue.addAll(component.getChildRefList()); + modulesPathByKey.put(component.getKey(), component.getProjectRelativePath()); + } + } + + return modulesPathByKey; + } + +} diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java index 7f661f1cde3..3d67ddb385f 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/container/ProjectAnalysisTaskContainerPopulator.java @@ -35,6 +35,7 @@ import org.sonar.ce.task.projectanalysis.component.ConfigurationRepositoryImpl; import org.sonar.ce.task.projectanalysis.component.DbIdsRepositoryImpl; import org.sonar.ce.task.projectanalysis.component.DisabledComponentsHolderImpl; import org.sonar.ce.task.projectanalysis.component.MergeBranchComponentUuids; +import org.sonar.ce.task.projectanalysis.component.ReportModulesPath; import org.sonar.ce.task.projectanalysis.component.ShortBranchComponentsWithIssues; import org.sonar.ce.task.projectanalysis.component.TreeRootHolderImpl; import org.sonar.ce.task.projectanalysis.dbmigration.DbMigrationModule; @@ -179,7 +180,7 @@ public final class ProjectAnalysisTaskContainerPopulator implements ContainerPop new ComputationTempFolderProvider(), DbMigrationModule.class, - + ReportModulesPath.class, MetricModule.class, // holders diff --git a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStep.java b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStep.java index 342e5cdbc81..b1511df95ab 100644 --- a/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStep.java +++ b/server/sonar-ce-task-projectanalysis/src/main/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStep.java @@ -31,6 +31,7 @@ import org.sonar.ce.task.projectanalysis.component.ComponentTreeBuilder; import org.sonar.ce.task.projectanalysis.component.ComponentUuidFactory; import org.sonar.ce.task.projectanalysis.component.DefaultBranchImpl; import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolder; +import org.sonar.ce.task.projectanalysis.component.ReportModulesPath; import org.sonar.ce.task.projectanalysis.issue.IssueRelocationToRoot; import org.sonar.ce.task.step.ComputationStep; import org.sonar.db.DbClient; @@ -49,14 +50,16 @@ public class BuildComponentTreeStep implements ComputationStep { private final MutableTreeRootHolder treeRootHolder; private final MutableAnalysisMetadataHolder analysisMetadataHolder; private final IssueRelocationToRoot issueRelocationToRoot; + private final ReportModulesPath reportModulesPath; - public BuildComponentTreeStep(DbClient dbClient, BatchReportReader reportReader, - MutableTreeRootHolder treeRootHolder, MutableAnalysisMetadataHolder analysisMetadataHolder, IssueRelocationToRoot issueRelocationToRoot) { + public BuildComponentTreeStep(DbClient dbClient, BatchReportReader reportReader, MutableTreeRootHolder treeRootHolder, + MutableAnalysisMetadataHolder analysisMetadataHolder, IssueRelocationToRoot issueRelocationToRoot, ReportModulesPath reportModulesPath) { this.dbClient = dbClient; this.reportReader = reportReader; this.treeRootHolder = treeRootHolder; this.analysisMetadataHolder = analysisMetadataHolder; this.issueRelocationToRoot = issueRelocationToRoot; + this.reportModulesPath = reportModulesPath; } @Override @@ -70,12 +73,13 @@ public class BuildComponentTreeStep implements ComputationStep { ScannerReport.Component reportProject = reportReader.readComponent(analysisMetadataHolder.getRootComponentRef()); ComponentKeyGenerator keyGenerator = loadKeyGenerator(); ComponentKeyGenerator publicKeyGenerator = loadPublicKeyGenerator(); + ScannerReport.Metadata metadata = reportReader.readMetadata(); // root key of branch, not necessarily of project String rootKey = keyGenerator.generateKey(reportProject, null); // loads the UUIDs from database. If they don't exist, then generate new ones - ComponentUuidFactory componentUuidFactory = new ComponentUuidFactory(dbClient, dbSession, rootKey); + ComponentUuidFactory componentUuidFactory = new ComponentUuidFactory(dbClient, dbSession, rootKey, reportModulesPath); String rootUuid = componentUuidFactory.getOrCreateForKey(rootKey); SnapshotDto baseAnalysis = loadBaseAnalysis(dbSession, rootUuid); @@ -86,7 +90,7 @@ public class BuildComponentTreeStep implements ComputationStep { analysisMetadataHolder.getProject(), analysisMetadataHolder.getBranch(), baseAnalysis, issueRelocationToRoot); - String relativePathFromScmRoot = reportReader.readMetadata().getRelativePathFromScmRoot(); + String relativePathFromScmRoot = metadata.getRelativePathFromScmRoot(); Component reportTreeRoot = builder.buildProject(reportProject, relativePathFromScmRoot); diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilderTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilderTest.java index 9f4920dac21..00d22565e21 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilderTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentTreeBuilderTest.java @@ -416,7 +416,7 @@ public class ComponentTreeBuilderTest { scannerComponentProvider.add(newBuilder() .setRef(2) .setType(DIRECTORY) - .setPath("/") + .setProjectRelativePath("/") .addChildRef(3)); scannerComponentProvider.add(newBuilder() .setRef(3) diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentUuidFactoryTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentUuidFactoryTest.java index 90ad37a15cb..3d84801d17a 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentUuidFactoryTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ComponentUuidFactoryTest.java @@ -19,6 +19,9 @@ */ package org.sonar.ce.task.projectanalysis.component; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import org.junit.Rule; import org.junit.Test; import org.sonar.api.utils.System2; @@ -27,19 +30,23 @@ import org.sonar.db.component.ComponentDto; import org.sonar.db.component.ComponentTesting; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class ComponentUuidFactoryTest { @Rule public DbTester db = DbTester.create(System2.INSTANCE); + private ReportModulesPath reportModulesPath = mock(ReportModulesPath.class); + @Test public void load_uuids_from_existing_components_in_db() { ComponentDto project = db.components().insertPrivateProject(); ComponentDto module = db.components().insertComponent(ComponentTesting - .newModuleDto(project).setPath("module1")); - - ComponentUuidFactory underTest = new ComponentUuidFactory(db.getDbClient(), db.getSession(), project.getDbKey()); + .newModuleDto(project)); + when(reportModulesPath.get()).thenReturn(Collections.singletonMap(module.getKey(), "module1_path")); + ComponentUuidFactory underTest = new ComponentUuidFactory(db.getDbClient(), db.getSession(), project.getDbKey(), reportModulesPath); assertThat(underTest.getOrCreateForKey(project.getDbKey())).isEqualTo(project.uuid()); assertThat(underTest.getOrCreateForKey(module.getDbKey())).isNotEqualTo(module.uuid()); @@ -49,11 +56,9 @@ public class ComponentUuidFactoryTest { public void migrate_project_with_modules() { ComponentDto project = db.components().insertPrivateProject(dto -> dto.setDbKey("project")); ComponentDto module1 = db.components().insertComponent(ComponentTesting.newModuleDto(project) - .setDbKey("project:module1") - .setPath("module1_path")); + .setDbKey("project:module1")); ComponentDto module2 = db.components().insertComponent(ComponentTesting.newModuleDto(module1) - .setDbKey("project:module1:module2") - .setPath("module2_path")); + .setDbKey("project:module1:module2")); ComponentDto file1 = db.components().insertComponent(ComponentTesting.newFileDto(project) .setDbKey("project:file1") .setPath("file1_path")); @@ -62,8 +67,11 @@ public class ComponentUuidFactoryTest { .setPath("file2_path")); assertThat(file2.moduleUuidPath()).isEqualTo("." + project.uuid() + "." + module1.uuid() + "." + module2.uuid() + "."); - - ComponentUuidFactory underTest = new ComponentUuidFactory(db.getDbClient(), db.getSession(), project.getDbKey()); + Map<String, String> modulesRelativePaths = new HashMap<>(); + modulesRelativePaths.put("project:module1", "module1_path"); + modulesRelativePaths.put("project:module1:module2", "module1_path/module2_path"); + when(reportModulesPath.get()).thenReturn(modulesRelativePaths); + ComponentUuidFactory underTest = new ComponentUuidFactory(db.getDbClient(), db.getSession(), project.getDbKey(), reportModulesPath); // migrated files assertThat(underTest.getOrCreateForKey("project:file1_path")).isEqualTo(file1.uuid()); @@ -84,51 +92,23 @@ public class ComponentUuidFactoryTest { ComponentDto project = db.components().insertPrivateProject(dto -> dto.setDbKey("project")); ComponentDto module1 = db.components().insertComponent(ComponentTesting.newModuleDto(project) .setDbKey("project:module1") - .setEnabled(false) - .setPath("module1_path")); + .setEnabled(false)); ComponentDto file1 = db.components().insertComponent(ComponentTesting.newFileDto(module1) .setDbKey("project:file1") .setEnabled(false) .setPath("file1_path")); + when(reportModulesPath.get()).thenReturn(Collections.singletonMap("project:module1", "module1_path")); - ComponentUuidFactory underTest = new ComponentUuidFactory(db.getDbClient(), db.getSession(), project.getDbKey()); + ComponentUuidFactory underTest = new ComponentUuidFactory(db.getDbClient(), db.getSession(), project.getDbKey(), reportModulesPath); // migrated files assertThat(underTest.getOrCreateForKey("project:module1_path/file1_path")).isEqualTo(file1.uuid()); } @Test - public void migrate_project_having_modules_without_paths() { - ComponentDto project = db.components().insertPrivateProject(dto -> dto.setDbKey("project")); - ComponentDto module = db.components().insertComponent(ComponentTesting.newModuleDto(project) - .setDbKey("project:module") - .setPath(null)); - ComponentDto file = db.components().insertComponent(ComponentTesting.newFileDto(module) - .setDbKey("project:module:file") - .setPath("file_path")); - - assertThat(file.moduleUuidPath()).isEqualTo("." + project.uuid() + "." + module.uuid() + "."); - - ComponentUuidFactory underTest = new ComponentUuidFactory(db.getDbClient(), db.getSession(), project.getDbKey()); - - // file will have this key since the module has a null path - assertThat(underTest.getOrCreateForKey("project:file_path")).isEqualTo(file.uuid()); - - // migrated module - // TODO!! - //assertThat(underTest.getOrCreateForKey("project:module")).isEqualTo(module.uuid()); - - // project remains the same - //assertThat(underTest.getOrCreateForKey(project.getDbKey())).isEqualTo(project.uuid()); - - // old keys with modules don't exist - assertThat(underTest.getOrCreateForKey(module.getDbKey())).isNotEqualTo(module.uuid()); - assertThat(underTest.getOrCreateForKey(file.getDbKey())).isNotEqualTo(file.uuid()); - } - - @Test public void generate_uuid_if_it_does_not_exist_in_db() { - ComponentUuidFactory underTest = new ComponentUuidFactory(db.getDbClient(), db.getSession(), "theProjectKey"); + when(reportModulesPath.get()).thenReturn(Collections.emptyMap()); + ComponentUuidFactory underTest = new ComponentUuidFactory(db.getDbClient(), db.getSession(), "theProjectKey", reportModulesPath); String generatedKey = underTest.getOrCreateForKey("foo"); assertThat(generatedKey).isNotEmpty(); diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ReportModulesPathTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ReportModulesPathTest.java new file mode 100644 index 00000000000..760c98d931e --- /dev/null +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/component/ReportModulesPathTest.java @@ -0,0 +1,89 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.ce.task.projectanalysis.component; + +import java.util.Arrays; +import java.util.Map; +import javax.annotation.Nullable; +import org.junit.Test; +import org.sonar.ce.task.projectanalysis.batch.BatchReportReader; +import org.sonar.scanner.protocol.output.ScannerReport; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.entry; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +public class ReportModulesPathTest { + private BatchReportReader reader = mock(BatchReportReader.class); + private ReportModulesPath reportModulesPath = new ReportModulesPath(reader); + private ScannerReport.Component root = addComponent(1, "project", ScannerReport.Component.ComponentType.PROJECT, null, 2); + + @Test + public void should_not_read_hierarchy_if_metadata_available() { + when(reader.readMetadata()).thenReturn(ScannerReport.Metadata.newBuilder() + .putModulesProjectRelativePathByKey("module1", "path1") + .setRootComponentRef(1) + .build()); + Map<String, String> pathByModuleKey = reportModulesPath.get(); + + assertThat(pathByModuleKey).containsExactly(entry("module1", "path1")); + verify(reader).readMetadata(); + verifyNoMoreInteractions(reader); + } + + @Test + public void should_read_hierarchy_if_metadata_not_available() { + when(reader.readMetadata()).thenReturn(ScannerReport.Metadata.newBuilder().setRootComponentRef(1).build()); + addComponent(2, "project:module1", ScannerReport.Component.ComponentType.MODULE, "path1", 3); + addComponent(3, "project:module1:module2", ScannerReport.Component.ComponentType.MODULE, "path1/path2", 4); + addComponent(4, "project:module1:module2:dir", ScannerReport.Component.ComponentType.DIRECTORY, "path1/path2/dir"); + + Map<String, String> pathByModuleKey = reportModulesPath.get(); + + assertThat(pathByModuleKey).containsOnly( + entry("project:module1", "path1"), + entry("project:module1:module2", "path1/path2")); + verify(reader).readMetadata(); + verify(reader).readComponent(1); + verify(reader).readComponent(2); + verify(reader).readComponent(3); + verify(reader).readComponent(4); + + verifyNoMoreInteractions(reader); + } + + private ScannerReport.Component addComponent(int ref, String key, ScannerReport.Component.ComponentType type, @Nullable String path, Integer... children) { + ScannerReport.Component.Builder builder = ScannerReport.Component.newBuilder() + .setRef(ref) + .setKey(key) + .addAllChildRef(Arrays.asList(children)) + .setType(type); + + if (path != null) { + builder.setProjectRelativePath(path); + } + ScannerReport.Component component = builder.build(); + when(reader.readComponent(ref)).thenReturn(component); + return component; + } +} diff --git a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStepTest.java b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStepTest.java index ba95929db6a..5d7b05fc7c5 100644 --- a/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStepTest.java +++ b/server/sonar-ce-task-projectanalysis/src/test/java/org/sonar/ce/task/projectanalysis/step/BuildComponentTreeStepTest.java @@ -35,6 +35,7 @@ import org.sonar.ce.task.projectanalysis.batch.BatchReportReaderRule; import org.sonar.ce.task.projectanalysis.component.Component; import org.sonar.ce.task.projectanalysis.component.DefaultBranchImpl; import org.sonar.ce.task.projectanalysis.component.MutableTreeRootHolderRule; +import org.sonar.ce.task.projectanalysis.component.ReportModulesPath; import org.sonar.ce.task.projectanalysis.issue.IssueRelocationToRoot; import org.sonar.ce.task.step.TestComputationStepContext; import org.sonar.db.DbClient; @@ -102,11 +103,12 @@ public class BuildComponentTreeStepTest { public MutableTreeRootHolderRule treeRootHolder = new MutableTreeRootHolderRule(); @Rule public MutableAnalysisMetadataHolderRule analysisMetadataHolder = new MutableAnalysisMetadataHolderRule(); - + private ReportModulesPath reportModulesPath = new ReportModulesPath(reportReader); private IssueRelocationToRoot issueRelocationToRoot = mock(IssueRelocationToRoot.class); private DbClient dbClient = dbTester.getDbClient(); - private BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, issueRelocationToRoot); + private BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, + issueRelocationToRoot, reportModulesPath); @Test(expected = NullPointerException.class) public void fails_if_root_component_does_not_exist_in_reportReader() { @@ -202,7 +204,8 @@ public class BuildComponentTreeStepTest { reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, DIR_REF_1)); reportReader.putComponent(componentWithPath(DIR_REF_1, DIRECTORY, "module/" + REPORT_DIR_PATH_1, FILE_1_REF)); reportReader.putComponent(componentWithPath(FILE_1_REF, FILE, "module/" + REPORT_FILE_PATH_1)); - + reportReader.setMetadata(ScannerReport.Metadata.newBuilder().putModulesProjectRelativePathByKey(REPORT_MODULE_KEY, + "module").build()); underTest.execute(new TestComputationStepContext()); verifyComponentByRef(ROOT_REF, REPORT_PROJECT_KEY, "ABCD"); @@ -221,7 +224,8 @@ public class BuildComponentTreeStepTest { .setAnalysisDate(ANALYSIS_DATE) .setProject(Project.from(newPrivateProjectDto(newOrganizationDto()).setDbKey(REPORT_PROJECT_KEY))) .setBranch(branch); - BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, issueRelocationToRoot); + BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, + issueRelocationToRoot, reportModulesPath); reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, MODULE_REF)); reportReader.putComponent(component(MODULE_REF, MODULE, REPORT_MODULE_KEY, DIR_REF_1)); reportReader.putComponent(componentWithPath(DIR_REF_1, DIRECTORY, REPORT_DIR_PATH_1, FILE_1_REF)); @@ -248,7 +252,8 @@ public class BuildComponentTreeStepTest { .setAnalysisDate(ANALYSIS_DATE) .setProject(Project.from(newPrivateProjectDto(newOrganizationDto()).setDbKey(REPORT_PROJECT_KEY))) .setBranch(branch); - BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, issueRelocationToRoot); + BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, + issueRelocationToRoot, reportModulesPath); reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, MODULE_REF, LEAFLESS_MODULE_REF)); reportReader.putComponent(component(MODULE_REF, MODULE, REPORT_MODULE_KEY, DIR_REF_1)); reportReader.putComponent(componentWithPath(DIR_REF_1, DIRECTORY, REPORT_DIR_PATH_1, FILE_1_REF)); @@ -288,7 +293,8 @@ public class BuildComponentTreeStepTest { .setAnalysisDate(ANALYSIS_DATE) .setProject(Project.from(newPrivateProjectDto(newOrganizationDto()).setDbKey(REPORT_PROJECT_KEY))) .setBranch(branch); - BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, issueRelocationToRoot); + BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, + issueRelocationToRoot, reportModulesPath); reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, MODULE_REF, LEAFLESS_MODULE_REF)); reportReader.putComponent(component(MODULE_REF, MODULE, REPORT_MODULE_KEY, DIR_REF_1)); reportReader.putComponent(componentWithPath(DIR_REF_1, DIRECTORY, REPORT_DIR_PATH_1, FILE_1_REF)); @@ -335,7 +341,8 @@ public class BuildComponentTreeStepTest { .setAnalysisDate(ANALYSIS_DATE) .setProject(Project.from(projectDto)) .setBranch(branch); - BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, issueRelocationToRoot); + BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, + issueRelocationToRoot, reportModulesPath); reportReader.putComponent(component(ROOT_REF, PROJECT, branchDto.getKey())); underTest.execute(new TestComputationStepContext()); @@ -350,7 +357,8 @@ public class BuildComponentTreeStepTest { .setAnalysisDate(ANALYSIS_DATE) .setProject(Project.from(newPrivateProjectDto(newOrganizationDto()).setDbKey(REPORT_PROJECT_KEY))) .setBranch(branch); - BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, issueRelocationToRoot); + BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, + issueRelocationToRoot, reportModulesPath); reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, MODULE_REF)); reportReader.putComponent(component(MODULE_REF, MODULE, REPORT_MODULE_KEY, DIR_REF_1)); reportReader.putComponent(componentWithPath(DIR_REF_1, DIRECTORY, REPORT_DIR_PATH_1, FILE_1_REF)); @@ -370,7 +378,8 @@ public class BuildComponentTreeStepTest { .setAnalysisDate(ANALYSIS_DATE) .setProject(Project.from(newPrivateProjectDto(newOrganizationDto()).setDbKey(REPORT_PROJECT_KEY))) .setBranch(new DefaultBranchImpl("origin/feature")); - BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, issueRelocationToRoot); + BuildComponentTreeStep underTest = new BuildComponentTreeStep(dbClient, reportReader, treeRootHolder, analysisMetadataHolder, + issueRelocationToRoot, reportModulesPath); reportReader.putComponent(component(ROOT_REF, PROJECT, REPORT_PROJECT_KEY, MODULE_REF)); reportReader.putComponent(component(MODULE_REF, MODULE, REPORT_MODULE_KEY, DIR_REF_1)); reportReader.putComponent(componentWithPath(DIR_REF_1, DIRECTORY, REPORT_DIR_PATH_1, FILE_1_REF)); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java index 26ca46ace2c..2b36b3b5d28 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/report/MetadataPublisher.java @@ -21,11 +21,13 @@ package org.sonar.scanner.report; import java.io.File; import java.nio.file.Path; +import java.util.LinkedList; import java.util.Map.Entry; import java.util.Optional; import java.util.regex.Pattern; import javax.annotation.Nullable; import org.sonar.api.batch.fs.internal.AbstractProjectOrModule; +import org.sonar.api.batch.fs.internal.DefaultInputModule; import org.sonar.api.batch.fs.internal.InputModuleHierarchy; import org.sonar.api.batch.scm.ScmProvider; import org.sonar.api.utils.log.Logger; @@ -59,8 +61,8 @@ public class MetadataPublisher implements ReportPublisherStep { private final ScmConfiguration scmConfiguration; public MetadataPublisher(ProjectAnalysisInfo projectAnalysisInfo, InputModuleHierarchy moduleHierarchy, ScanProperties properties, - QualityProfiles qProfiles, CpdSettings cpdSettings, ScannerPluginRepository pluginRepository, BranchConfiguration branchConfiguration, - @Nullable ScmConfiguration scmConfiguration) { + QualityProfiles qProfiles, CpdSettings cpdSettings, ScannerPluginRepository pluginRepository, BranchConfiguration branchConfiguration, + @Nullable ScmConfiguration scmConfiguration) { this.projectAnalysisInfo = projectAnalysisInfo; this.moduleHierarchy = moduleHierarchy; this.properties = properties; @@ -72,7 +74,7 @@ public class MetadataPublisher implements ReportPublisherStep { } public MetadataPublisher(ProjectAnalysisInfo projectAnalysisInfo, InputModuleHierarchy moduleHierarchy, ScanProperties properties, - QualityProfiles qProfiles, CpdSettings cpdSettings, ScannerPluginRepository pluginRepository, BranchConfiguration branchConfiguration) { + QualityProfiles qProfiles, CpdSettings cpdSettings, ScannerPluginRepository pluginRepository, BranchConfiguration branchConfiguration) { this(projectAnalysisInfo, moduleHierarchy, properties, qProfiles, cpdSettings, pluginRepository, branchConfiguration, null); } @@ -99,20 +101,37 @@ public class MetadataPublisher implements ReportPublisherStep { } for (QProfile qp : qProfiles.findAll()) { - builder.getMutableQprofilesPerLanguage().put(qp.getLanguage(), ScannerReport.Metadata.QProfile.newBuilder() + builder.putQprofilesPerLanguage(qp.getLanguage(), ScannerReport.Metadata.QProfile.newBuilder() .setKey(qp.getKey()) .setLanguage(qp.getLanguage()) .setName(qp.getName()) .setRulesUpdatedAt(qp.getRulesUpdatedAt().getTime()).build()); } for (Entry<String, ScannerPlugin> pluginEntry : pluginRepository.getPluginsByKey().entrySet()) { - builder.getMutablePluginsByKey().put(pluginEntry.getKey(), ScannerReport.Metadata.Plugin.newBuilder() + builder.putPluginsByKey(pluginEntry.getKey(), ScannerReport.Metadata.Plugin.newBuilder() .setKey(pluginEntry.getKey()) .setUpdatedAt(pluginEntry.getValue().getUpdatedAt()).build()); } + + addModulesRelativePaths(builder); + writer.writeMetadata(builder.build()); } + private void addModulesRelativePaths(ScannerReport.Metadata.Builder builder) { + LinkedList<DefaultInputModule> queue = new LinkedList<>(); + queue.add(moduleHierarchy.root()); + + while (!queue.isEmpty()) { + DefaultInputModule module = queue.removeFirst(); + queue.addAll(moduleHierarchy.children(module)); + String relativePath = moduleHierarchy.relativePath(module); + if (relativePath != null) { + builder.putModulesProjectRelativePathByKey(module.key(), relativePath); + } + } + } + private void addScmInformation(ScannerReport.Metadata.Builder builder) { ScmProvider scmProvider = scmConfiguration.provider(); if (scmProvider != null) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultInputModuleHierarchy.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultInputModuleHierarchy.java index 8c37407023a..3c9995269be 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultInputModuleHierarchy.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/DefaultInputModuleHierarchy.java @@ -84,11 +84,10 @@ public class DefaultInputModuleHierarchy implements InputModuleHierarchy { public String relativePath(DefaultInputModule module) { AbstractProjectOrModule parent = parent(module); if (parent == null) { - return null; + return ""; } - DefaultInputModule inputModule = (DefaultInputModule) module; Path parentBaseDir = parent.getBaseDir(); - Path moduleBaseDir = inputModule.getBaseDir(); + Path moduleBaseDir = module.getBaseDir(); return PathResolver.relativize(parentBaseDir, moduleBaseDir).orElse(null); } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MetadataPublisherTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MetadataPublisherTest.java index 5eda8a2114d..744cabca3ed 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MetadataPublisherTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/report/MetadataPublisherTest.java @@ -22,8 +22,10 @@ package org.sonar.scanner.report; import com.google.common.collect.ImmutableMap; import java.io.File; import java.io.IOException; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.util.Collections; import java.util.Date; import java.util.Optional; import org.junit.Before; @@ -81,14 +83,27 @@ public class MetadataPublisherTest { when(scmProvider.relativePathFromScmRoot(any(Path.class))).thenReturn(Paths.get("dummy/path")); when(scmProvider.revisionId(any(Path.class))).thenReturn("dummy-sha1"); - createPublisher(ProjectDefinition.create().setKey("foo")); + createPublisher(ProjectDefinition.create()); when(pluginRepository.getPluginsByKey()).thenReturn(emptyMap()); } private void createPublisher(ProjectDefinition def) throws IOException { - rootModule = new DefaultInputModule(def.setBaseDir(temp.newFolder()).setWorkDir(temp.newFolder()), TestInputFileBuilder.nextBatchId()); + Path rootBaseDir = temp.newFolder().toPath(); + Path moduleBaseDir = rootBaseDir.resolve("moduleDir"); + Files.createDirectory(moduleBaseDir); + rootModule = new DefaultInputModule(def + .setBaseDir(rootBaseDir.toFile()) + .setKey("root") + .setWorkDir(temp.newFolder()), TestInputFileBuilder.nextBatchId()); inputModuleHierarchy = mock(InputModuleHierarchy.class); when(inputModuleHierarchy.root()).thenReturn(rootModule); + DefaultInputModule child = new DefaultInputModule(ProjectDefinition.create() + .setKey("module") + .setBaseDir(moduleBaseDir.toFile()) + .setWorkDir(temp.newFolder()), TestInputFileBuilder.nextBatchId()); + when(inputModuleHierarchy.children(rootModule)).thenReturn(Collections.singletonList(child)); + when(inputModuleHierarchy.relativePath(child)).thenReturn("modulePath"); + when(inputModuleHierarchy.relativePath(rootModule)).thenReturn(""); branches = mock(BranchConfiguration.class); scmConfiguration = mock(ScmConfiguration.class); when(scmConfiguration.provider()).thenReturn(scmProvider); @@ -111,18 +126,18 @@ public class MetadataPublisherTest { ScannerReportReader reader = new ScannerReportReader(outputDir); ScannerReport.Metadata metadata = reader.readMetadata(); assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L); - assertThat(metadata.getProjectKey()).isEqualTo("foo"); - assertThat(metadata.getProjectKey()).isEqualTo("foo"); - assertThat(metadata.getQprofilesPerLanguage()).containsOnly(entry("java", org.sonar.scanner.protocol.output.ScannerReport.Metadata.QProfile.newBuilder() + assertThat(metadata.getProjectKey()).isEqualTo("root"); + assertThat(metadata.getModulesProjectRelativePathByKeyMap()).containsOnly(entry("module", "modulePath"), entry("root", "")); + assertThat(metadata.getQprofilesPerLanguageMap()).containsOnly(entry("java", org.sonar.scanner.protocol.output.ScannerReport.Metadata.QProfile.newBuilder() .setKey("q1") .setName("Q1") .setLanguage("java") .setRulesUpdatedAt(date.getTime()) .build())); assertThat(metadata.getPluginsByKey()).containsOnly(entry("java", org.sonar.scanner.protocol.output.ScannerReport.Metadata.Plugin.newBuilder() - .setKey("java") - .setUpdatedAt(12345) - .build()), + .setKey("java") + .setUpdatedAt(12345) + .build()), entry("php", org.sonar.scanner.protocol.output.ScannerReport.Metadata.Plugin.newBuilder() .setKey("php") .setUpdatedAt(45678) @@ -146,7 +161,7 @@ public class MetadataPublisherTest { ScannerReportReader reader = new ScannerReportReader(outputDir); ScannerReport.Metadata metadata = reader.readMetadata(); assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L); - assertThat(metadata.getProjectKey()).isEqualTo("foo"); + assertThat(metadata.getProjectKey()).isEqualTo("root"); assertThat(metadata.getDeprecatedBranch()).isEqualTo("myBranch"); assertThat(metadata.getCrossProjectDuplicationActivated()).isFalse(); } @@ -162,7 +177,7 @@ public class MetadataPublisherTest { ScannerReportReader reader = new ScannerReportReader(outputDir); ScannerReport.Metadata metadata = reader.readMetadata(); - assertThat(properties.organizationKey()).isEqualTo(Optional.of("SonarSource")); + assertThat(metadata.getOrganizationKey()).isEqualTo("SonarSource"); } @Test diff --git a/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/viewer/ScannerReportViewerApp.java b/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/viewer/ScannerReportViewerApp.java index dca9835a5b1..0b2f132e1c3 100644 --- a/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/viewer/ScannerReportViewerApp.java +++ b/sonar-scanner-protocol/src/main/java/org/sonar/scanner/protocol/viewer/ScannerReportViewerApp.java @@ -229,7 +229,7 @@ public class ScannerReportViewerApp { return component.getName(); case DIRECTORY: case FILE: - return component.getPath(); + return component.getProjectRelativePath(); default: throw new IllegalArgumentException("Unknow component type: " + component.getType()); } diff --git a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto index f81097b5a92..57f86f9a73a 100644 --- a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto +++ b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto @@ -47,6 +47,7 @@ message Metadata { string scm_revision_id = 13; string pull_request_key = 14; + map<string, string> modules_project_relative_path_by_key = 15; message QProfile { string key = 1; @@ -101,8 +102,6 @@ message ComponentLink { message Component { int32 ref = 1; - // Path relative to module base directory - string path = 2 [deprecated=true]; string name = 3; ComponentType type = 4; bool is_test = 5; diff --git a/sonar-scanner-protocol/src/test/java/org/sonar/scanner/protocol/output/ScannerReportReaderTest.java b/sonar-scanner-protocol/src/test/java/org/sonar/scanner/protocol/output/ScannerReportReaderTest.java index 33b14b37ac1..68fe5aa7188 100644 --- a/sonar-scanner-protocol/src/test/java/org/sonar/scanner/protocol/output/ScannerReportReaderTest.java +++ b/sonar-scanner-protocol/src/test/java/org/sonar/scanner/protocol/output/ScannerReportReaderTest.java @@ -82,10 +82,10 @@ public class ScannerReportReaderTest { ScannerReportWriter writer = new ScannerReportWriter(dir); ScannerReport.Component.Builder component = ScannerReport.Component.newBuilder() .setRef(1) - .setPath("src/main/java/Foo.java"); + .setProjectRelativePath("src/main/java/Foo.java"); writer.writeComponent(component.build()); - assertThat(underTest.readComponent(1).getPath()).isEqualTo("src/main/java/Foo.java"); + assertThat(underTest.readComponent(1).getProjectRelativePath()).isEqualTo("src/main/java/Foo.java"); } @Test(expected = IllegalStateException.class) diff --git a/sonar-scanner-protocol/src/test/java/org/sonar/scanner/protocol/output/ScannerReportWriterTest.java b/sonar-scanner-protocol/src/test/java/org/sonar/scanner/protocol/output/ScannerReportWriterTest.java index 2b6d3e402f1..ec33c74d38e 100644 --- a/sonar-scanner-protocol/src/test/java/org/sonar/scanner/protocol/output/ScannerReportWriterTest.java +++ b/sonar-scanner-protocol/src/test/java/org/sonar/scanner/protocol/output/ScannerReportWriterTest.java @@ -80,7 +80,7 @@ public class ScannerReportWriterTest { ScannerReport.Component.Builder component = ScannerReport.Component.newBuilder() .setRef(1) .setLanguage("java") - .setPath("src/Foo.java") + .setProjectRelativePath("src/Foo.java") .setType(ComponentType.FILE) .setIsTest(false) .addChildRef(5) @@ -142,13 +142,13 @@ public class ScannerReportWriterTest { // write data ScannerReport.AdHocRule rule = ScannerReport.AdHocRule.newBuilder() - .setEngineId("eslint") - .setRuleId("123") - .setName("Foo") - .setDescription("Description") - .setSeverity(Constants.Severity.BLOCKER) - .setType(ScannerReport.IssueType.BUG) - .build(); + .setEngineId("eslint") + .setRuleId("123") + .setName("Foo") + .setDescription("Description") + .setSeverity(Constants.Severity.BLOCKER) + .setType(ScannerReport.IssueType.BUG) + .build(); underTest.appendAdHocRule(rule); File file = underTest.getFileStructure().adHocRules(); |