diff options
5 files changed, 110 insertions, 19 deletions
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 12e402f2edd..f0b8c0713a3 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 @@ -37,6 +37,7 @@ import org.sonar.scanner.fs.InputModuleHierarchy; import org.sonar.scanner.protocol.output.ScannerReport; import org.sonar.scanner.protocol.output.ScannerReport.Metadata.BranchType; import org.sonar.scanner.protocol.output.ScannerReportWriter; +import org.sonar.scanner.repository.ReferenceBranchSupplier; import org.sonar.scanner.rule.QProfile; import org.sonar.scanner.rule.QualityProfiles; import org.sonar.scanner.scan.branch.BranchConfiguration; @@ -57,10 +58,11 @@ public class MetadataPublisher implements ReportPublisherStep { private final ScmRevision scmRevision; private final InputComponentStore componentStore; private final ScmConfiguration scmConfiguration; + private final ReferenceBranchSupplier referenceBranchSupplier; public MetadataPublisher(ProjectInfo projectInfo, InputModuleHierarchy moduleHierarchy, QualityProfiles qProfiles, CpdSettings cpdSettings, ScannerPluginRepository pluginRepository, BranchConfiguration branchConfiguration, - ScmRevision scmRevision, InputComponentStore componentStore, ScmConfiguration scmConfiguration) { + ScmRevision scmRevision, InputComponentStore componentStore, ScmConfiguration scmConfiguration, ReferenceBranchSupplier referenceBranchSupplier) { this.projectInfo = projectInfo; this.moduleHierarchy = moduleHierarchy; this.qProfiles = qProfiles; @@ -70,6 +72,7 @@ public class MetadataPublisher implements ReportPublisherStep { this.scmRevision = scmRevision; this.componentStore = componentStore; this.scmConfiguration = scmConfiguration; + this.referenceBranchSupplier = referenceBranchSupplier; } @Override @@ -88,6 +91,11 @@ public class MetadataPublisher implements ReportPublisherStep { addBranchInformation(builder); } + String newCodeReferenceBranch = referenceBranchSupplier.getFromProperties(); + if (newCodeReferenceBranch != null) { + builder.setNewCodeReferenceBranch(newCodeReferenceBranch); + } + addScmInformation(builder); addNotAnalyzedFileCountsByLanguage(builder); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ReferenceBranchSupplier.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ReferenceBranchSupplier.java index f5ca8b165c2..b700fa59ada 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ReferenceBranchSupplier.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/repository/ReferenceBranchSupplier.java @@ -19,8 +19,10 @@ */ package org.sonar.scanner.repository; +import java.util.Optional; import javax.annotation.CheckForNull; import org.sonar.api.batch.fs.internal.DefaultInputProject; +import org.sonar.api.config.Configuration; import org.sonar.api.utils.log.Logger; import org.sonar.api.utils.log.Loggers; import org.sonar.api.utils.log.Profiler; @@ -28,16 +30,22 @@ import org.sonar.scanner.scan.branch.BranchConfiguration; import org.sonar.scanner.scan.branch.ProjectBranches; import org.sonarqube.ws.NewCodePeriods; +import static java.lang.String.format; + public class ReferenceBranchSupplier { private static final Logger LOG = Loggers.get(ReferenceBranchSupplier.class); private static final String LOG_MSG_WS = "Load New Code definition"; + private static final String NEW_CODE_PARAM_KEY = "sonar.newCode.referenceBranch"; + private final Configuration configuration; private final NewCodePeriodLoader newCodePeriodLoader; private final BranchConfiguration branchConfiguration; private final DefaultInputProject project; private final ProjectBranches branches; - public ReferenceBranchSupplier(NewCodePeriodLoader newCodePeriodLoader, BranchConfiguration branchConfiguration, DefaultInputProject project, ProjectBranches branches) { + public ReferenceBranchSupplier(Configuration configuration, NewCodePeriodLoader newCodePeriodLoader, BranchConfiguration branchConfiguration, DefaultInputProject project, + ProjectBranches branches) { + this.configuration = configuration; this.newCodePeriodLoader = newCodePeriodLoader; this.branchConfiguration = branchConfiguration; this.project = project; @@ -51,8 +59,12 @@ public class ReferenceBranchSupplier { return null; } + return Optional.ofNullable(getFromProperties()).orElseGet(this::loadWs); + } + + private String loadWs() { + String branchName = getBranchName(); Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG_WS); - String branchName = branchConfiguration.branchName() != null ? branchConfiguration.branchName() : branches.defaultBranchName(); NewCodePeriods.ShowWSResponse newCode = newCodePeriodLoader.load(project.key(), branchName); profiler.stopInfo(); if (newCode.getType() != NewCodePeriods.NewCodePeriodType.REFERENCE_BRANCH) { @@ -67,4 +79,26 @@ public class ReferenceBranchSupplier { return referenceBranchName; } + + @CheckForNull + public String getFromProperties() { + // branches will be empty in CE + if (branchConfiguration.isPullRequest() || branches.isEmpty()) { + return null; + } + + Optional<String> value = configuration.get(NEW_CODE_PARAM_KEY); + if (value.isPresent()) { + String referenceBranchName = value.get(); + if (getBranchName().equals(referenceBranchName)) { + throw new IllegalStateException(format("Reference branch set with '%s' points to the current branch '%s'", NEW_CODE_PARAM_KEY, referenceBranchName)); + } + return referenceBranchName; + } + return null; + } + + private String getBranchName() { + return branchConfiguration.branchName() != null ? branchConfiguration.branchName() : branches.defaultBranchName(); + } } 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 5d8e1967ede..f10faf46a4b 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 @@ -28,7 +28,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.time.Instant; import java.util.Collections; import java.util.Date; import java.util.Optional; @@ -116,7 +115,7 @@ public class MetadataPublisherTest { scmConfiguration = mock(ScmConfiguration.class); when(scmConfiguration.provider()).thenReturn(scmProvider); underTest = new MetadataPublisher(projectInfo, inputModuleHierarchy, qProfiles, cpdSettings, - pluginRepository, branches, scmRevision, componentStore, scmConfiguration); + pluginRepository, branches, scmRevision, componentStore, scmConfiguration, referenceBranchSupplier); } @Test @@ -128,12 +127,13 @@ public class MetadataPublisherTest { "php", new ScannerPlugin("php", 45678L, null))); File outputDir = temp.newFolder(); ScannerReportWriter writer = new ScannerReportWriter(outputDir); - + when(referenceBranchSupplier.getFromProperties()).thenReturn("newCodeReference"); underTest.publish(writer); ScannerReportReader reader = new ScannerReportReader(outputDir); ScannerReport.Metadata metadata = reader.readMetadata(); assertThat(metadata.getAnalysisDate()).isEqualTo(1234567L); + assertThat(metadata.getNewCodeReferenceBranch()).isEqualTo("newCodeReference"); assertThat(metadata.getProjectKey()).isEqualTo("root"); assertThat(metadata.getModulesProjectRelativePathByKeyMap()).containsOnly(entry("module", "modulePath"), entry("root", "")); assertThat(metadata.getProjectVersion()).isEmpty(); @@ -241,6 +241,20 @@ public class MetadataPublisherTest { } @Test + public void dont_write_new_code_reference_if_not_specified_in_properties() throws IOException { + when(referenceBranchSupplier.get()).thenReturn("ref"); + when(referenceBranchSupplier.getFromProperties()).thenReturn(null); + + File outputDir = temp.newFolder(); + underTest.publish(new ScannerReportWriter(outputDir)); + + ScannerReportReader reader = new ScannerReportReader(outputDir); + ScannerReport.Metadata metadata = reader.readMetadata(); + + assertThat(metadata.getNewCodeReferenceBranch()).isEmpty(); + } + + @Test public void write_project_basedir() throws Exception { String path = "some/dir"; Path relativePathFromScmRoot = Paths.get(path); diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/ReferenceBranchSupplierTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/ReferenceBranchSupplierTest.java index ac79dd82557..3b51bd63d61 100644 --- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/ReferenceBranchSupplierTest.java +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/repository/ReferenceBranchSupplierTest.java @@ -21,20 +21,18 @@ package org.sonar.scanner.repository; import java.nio.file.Path; import java.nio.file.Paths; -import java.time.Instant; +import java.util.Optional; import org.junit.Before; import org.junit.Test; import org.sonar.api.batch.fs.internal.DefaultInputProject; -import org.sonar.api.batch.scm.ScmProvider; -import org.sonar.api.notifications.AnalysisWarnings; +import org.sonar.api.config.Configuration; import org.sonar.scanner.scan.branch.BranchConfiguration; import org.sonar.scanner.scan.branch.BranchType; import org.sonar.scanner.scan.branch.ProjectBranches; -import org.sonar.scanner.scm.ScmConfiguration; import org.sonarqube.ws.NewCodePeriods; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.anyString; +import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -49,19 +47,21 @@ public class ReferenceBranchSupplierTest { private final NewCodePeriodLoader newCodePeriodLoader = mock(NewCodePeriodLoader.class); private final BranchConfiguration branchConfiguration = mock(BranchConfiguration.class); + private final Configuration configuration = mock(Configuration.class); private final DefaultInputProject project = mock(DefaultInputProject.class); private final ProjectBranches projectBranches = mock(ProjectBranches.class); - private final ReferenceBranchSupplier referenceBranchSupplier = new ReferenceBranchSupplier(newCodePeriodLoader, branchConfiguration, project, projectBranches); + private final ReferenceBranchSupplier referenceBranchSupplier = new ReferenceBranchSupplier(configuration, newCodePeriodLoader, branchConfiguration, project, projectBranches); @Before public void setUp() { when(projectBranches.isEmpty()).thenReturn(false); when(project.key()).thenReturn(PROJECT_KEY); when(project.getBaseDir()).thenReturn(BASE_DIR); + when(configuration.get("sonar.newCode.referenceBranch")).thenReturn(Optional.empty()); } @Test - public void returns_reference_branch_when_set() { + public void get_returns_reference_branch_when_set() { when(branchConfiguration.branchType()).thenReturn(BranchType.BRANCH); when(branchConfiguration.branchName()).thenReturn(BRANCH_KEY); when(newCodePeriodLoader.load(PROJECT_KEY, BRANCH_KEY)).thenReturn(createResponse(NewCodePeriods.NewCodePeriodType.REFERENCE_BRANCH, "master")); @@ -70,7 +70,40 @@ public class ReferenceBranchSupplierTest { } @Test - public void uses_default_branch_if_no_branch_specified() { + public void get_uses_scanner_property_with_higher_priority() { + when(branchConfiguration.branchType()).thenReturn(BranchType.BRANCH); + when(branchConfiguration.branchName()).thenReturn(BRANCH_KEY); + when(newCodePeriodLoader.load(PROJECT_KEY, BRANCH_KEY)).thenReturn(createResponse(NewCodePeriods.NewCodePeriodType.REFERENCE_BRANCH, "master")); + + when(configuration.get("sonar.newCode.referenceBranch")).thenReturn(Optional.of("master2")); + + assertThat(referenceBranchSupplier.get()).isEqualTo("master2"); + } + + @Test + public void getFromProperties_uses_scanner_property() { + when(branchConfiguration.branchType()).thenReturn(BranchType.BRANCH); + when(branchConfiguration.branchName()).thenReturn(BRANCH_KEY); + when(configuration.get("sonar.newCode.referenceBranch")).thenReturn(Optional.of("master2")); + assertThat(referenceBranchSupplier.getFromProperties()).isEqualTo("master2"); + } + + @Test + public void getFromProperties_returns_null_if_no_property() { + assertThat(referenceBranchSupplier.getFromProperties()).isNull(); + } + + @Test + public void getFromProperties_throws_ISE_if_reference_is_the_same_as_branch() { + when(branchConfiguration.branchType()).thenReturn(BranchType.BRANCH); + when(branchConfiguration.branchName()).thenReturn(BRANCH_KEY); + + when(configuration.get("sonar.newCode.referenceBranch")).thenReturn(Optional.of(BRANCH_KEY)); + assertThatThrownBy(referenceBranchSupplier::getFromProperties).isInstanceOf(IllegalStateException.class); + } + + @Test + public void get_uses_default_branch_if_no_branch_specified() { when(branchConfiguration.branchType()).thenReturn(BranchType.BRANCH); when(branchConfiguration.branchName()).thenReturn(null); when(projectBranches.defaultBranchName()).thenReturn("default"); @@ -80,7 +113,7 @@ public class ReferenceBranchSupplierTest { } @Test - public void returns_null_if_no_branches() { + public void get_returns_null_if_no_branches() { when(projectBranches.isEmpty()).thenReturn(true); assertThat(referenceBranchSupplier.get()).isNull(); @@ -92,7 +125,7 @@ public class ReferenceBranchSupplierTest { } @Test - public void returns_null_if_reference_branch_is_the_branch_being_analyzed() { + public void get_returns_null_if_reference_branch_is_the_branch_being_analyzed() { when(branchConfiguration.branchType()).thenReturn(BranchType.BRANCH); when(branchConfiguration.branchName()).thenReturn(BRANCH_KEY); when(newCodePeriodLoader.load(PROJECT_KEY, BRANCH_KEY)).thenReturn(createResponse(NewCodePeriods.NewCodePeriodType.REFERENCE_BRANCH, BRANCH_KEY)); @@ -100,14 +133,14 @@ public class ReferenceBranchSupplierTest { assertThat(referenceBranchSupplier.get()).isNull(); verify(branchConfiguration, times(2)).branchName(); - verify(branchConfiguration).isPullRequest(); + verify(branchConfiguration, times(2)).isPullRequest(); verify(newCodePeriodLoader).load(PROJECT_KEY, BRANCH_KEY); verifyNoMoreInteractions(branchConfiguration); } @Test - public void returns_null_if_pull_request() { + public void get_returns_null_if_pull_request() { when(branchConfiguration.isPullRequest()).thenReturn(true); assertThat(referenceBranchSupplier.get()).isNull(); @@ -118,7 +151,7 @@ public class ReferenceBranchSupplierTest { } @Test - public void returns_null_if_new_code_period_is_not_ref() { + public void get_returns_null_if_new_code_period_is_not_ref() { when(branchConfiguration.isPullRequest()).thenReturn(true); when(branchConfiguration.branchName()).thenReturn(BRANCH_KEY); when(newCodePeriodLoader.load(PROJECT_KEY, BRANCH_KEY)).thenReturn(createResponse(NewCodePeriods.NewCodePeriodType.NUMBER_OF_DAYS, "2")); diff --git a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto index a6fc6398869..dda0893f219 100644 --- a/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto +++ b/sonar-scanner-protocol/src/main/protobuf/scanner_report.proto @@ -60,6 +60,8 @@ message Metadata { map<string, int32> not_analyzed_files_by_language = 20; + string new_code_reference_branch = 21; + message QProfile { string key = 1; string name = 2; |