import org.sonar.scanner.protocol.output.ScannerReport;
import static org.sonar.core.config.CorePropertyDefinitions.SONAR_ANALYSIS;
+import static org.sonar.core.config.CorePropertyDefinitions.SONAR_ANALYSIS_DETECTEDSCM;
/**
* Persist analysis properties
- * Only properties starting with "sonar.analysis" or "sonar.pullrequest" will be persisted in database
*/
public class PersistAnalysisPropertiesStep implements ComputationStep {
private final UuidFactory uuidFactory;
public PersistAnalysisPropertiesStep(DbClient dbClient, AnalysisMetadataHolder analysisMetadataHolder,
- BatchReportReader reportReader, UuidFactory uuidFactory) {
+ BatchReportReader reportReader, UuidFactory uuidFactory) {
this.dbClient = dbClient;
this.analysisMetadataHolder = analysisMetadataHolder;
this.reportReader = reportReader;
it.forEachRemaining(
contextProperty -> {
String propertyKey = contextProperty.getKey();
- if (propertyKey.startsWith(SONAR_ANALYSIS) || propertyKey.startsWith(SONAR_PULL_REQUEST)) {
+ if (propertyKey.startsWith(SONAR_ANALYSIS) || propertyKey.startsWith(SONAR_PULL_REQUEST) || SONAR_ANALYSIS_DETECTEDSCM.equals(propertyKey)) {
analysisPropertyDtos.add(new AnalysisPropertyDto()
.setUuid(uuidFactory.create())
.setKey(propertyKey)
import org.sonar.db.component.ComponentWithModuleUuidDto;
import org.sonar.db.component.FilePathWithHashDto;
import org.sonar.db.component.KeyWithUuidDto;
+import org.sonar.db.component.ProjectCountPerAnalysisPropertyValue;
import org.sonar.db.component.ProjectLinkMapper;
import org.sonar.db.component.ResourceDto;
import org.sonar.db.component.ScrapAnalysisPropertyDto;
confBuilder.loadAlias("PrIssue", PrIssueDto.class);
confBuilder.loadAlias("ProjectQgateAssociation", ProjectQgateAssociationDto.class);
confBuilder.loadAlias("Project", ProjectDto.class);
+ confBuilder.loadAlias("ProjectCountPerAnalysisPropertyValue", ProjectCountPerAnalysisPropertyValue.class);
confBuilder.loadAlias("ProjectMapping", ProjectMappingDto.class);
confBuilder.loadAlias("PurgeableAnalysis", PurgeableAnalysisDto.class);
confBuilder.loadAlias("QualityGateCondition", QualityGateConditionDto.class);
}
}
+ public List<ProjectCountPerAnalysisPropertyValue> selectProjectCountPerAnalysisPropertyValueInLastAnalysis(DbSession session, String analysisPropertyKey) {
+ return getMapper(session).selectProjectCountPerAnalysisPropertyValueInLastAnalysis(analysisPropertyKey);
+ }
+
private static boolean mustBeStoredInClob(String value) {
return value.length() > VARCHAR_MAXSIZE;
}
void insertAsClob(@Param("analysisPropertyDto") AnalysisPropertyDto analysisPropertyDto, @Param("createdAt") long createdAt);
void insertAsText(@Param("analysisPropertyDto") AnalysisPropertyDto analysisPropertyDto, @Param("createdAt") long createdAt);
+
+ List<ProjectCountPerAnalysisPropertyValue> selectProjectCountPerAnalysisPropertyValueInLastAnalysis(@Param("analysisPropertyKey") String analysisPropertyKey);
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2021 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.db.component;
+
+public class ProjectCountPerAnalysisPropertyValue {
+ private String propertyValue;
+ private Long count;
+
+ public ProjectCountPerAnalysisPropertyValue() {
+ //nothing to do here
+ }
+
+ public String getPropertyValue() {
+ return propertyValue;
+ }
+
+ public void setPropertyValue(String propertyValue) {
+ this.propertyValue = propertyValue;
+ }
+
+ public Long getCount() {
+ return count;
+ }
+
+ public void setCount(Long count) {
+ this.count = count;
+ }
+}
analysis_uuid = #{analysisUuid}
</select>
+ <select id="selectProjectCountPerAnalysisPropertyValueInLastAnalysis" parameterType="string" resultType="ProjectCountPerAnalysisPropertyValue">
+ select
+ ap.text_value as "propertyValue",
+ count(ap.text_value) as "count"
+ from components cp
+ inner join snapshots s on s.component_uuid = cp.uuid
+ inner join analysis_properties ap on ap.analysis_uuid = s.uuid
+ where
+ s.islast = ${_true} and ap.kee = #{analysisPropertyKey, jdbcType=VARCHAR}
+ group by ap.text_value
+ </select>
+
<insert id="insertAsEmpty" parameterType="map" useGeneratedKeys="false">
INSERT INTO analysis_properties (
uuid,
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
-import org.sonar.api.utils.System2;
import org.sonar.api.impl.utils.TestSystem2;
+import org.sonar.api.utils.System2;
import org.sonar.db.DbSession;
import org.sonar.db.DbTester;
+import org.sonar.db.project.ProjectDto;
import static org.apache.commons.lang.RandomStringUtils.randomAlphanumeric;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.AssertionsForClassTypes.tuple;
public class AnalysisPropertiesDaoTest {
private static final long NOW = 1_000L;
assertThat(result).containsExactlyInAnyOrder(propertyDtos.toArray(new AnalysisPropertyDto[0]));
}
+ @Test
+ public void selectProjectCountPerAnalysisPropertyValueInLastAnalysis_should_return_correct_values() {
+ final String analysisPropertyKey = "key";
+ for (int i = 0; i < 7; i++) {
+ final int index = i;
+ ProjectDto project = dbTester.components().insertPrivateProjectDto();
+ dbTester.components().insertSnapshot(project, s -> s.setLast(true).setUuid("uuid" + index));
+ }
+ underTest.insert(dbSession, new AnalysisPropertyDto().setKey(analysisPropertyKey).setValue("git").setAnalysisUuid("uuid0").setUuid("0"));
+ underTest.insert(dbSession, new AnalysisPropertyDto().setKey(analysisPropertyKey).setValue("svn").setAnalysisUuid("uuid1").setUuid("1"));
+ underTest.insert(dbSession, new AnalysisPropertyDto().setKey(analysisPropertyKey).setValue("undetected").setAnalysisUuid("uuid2").setUuid("2"));
+ underTest.insert(dbSession, new AnalysisPropertyDto().setKey(analysisPropertyKey).setValue("undetected").setAnalysisUuid("uuid3").setUuid("3"));
+ underTest.insert(dbSession, new AnalysisPropertyDto().setKey(analysisPropertyKey).setValue("git").setAnalysisUuid("uuid4").setUuid("4"));
+ underTest.insert(dbSession, new AnalysisPropertyDto().setKey(analysisPropertyKey).setValue("git").setAnalysisUuid("uuid5").setUuid("5"));
+
+ List<ProjectCountPerAnalysisPropertyValue> result = underTest.selectProjectCountPerAnalysisPropertyValueInLastAnalysis(dbSession, analysisPropertyKey);
+
+ assertThat(result)
+ .extracting(ProjectCountPerAnalysisPropertyValue::getPropertyValue, ProjectCountPerAnalysisPropertyValue::getCount)
+ .containsExactlyInAnyOrder(
+ tuple("git", 3L),
+ tuple("svn", 1L),
+ tuple("undetected", 2L)
+ );
+ }
+
private AnalysisPropertyDto insertAnalysisPropertyDto(int valueLength) {
AnalysisPropertyDto analysisPropertyDto = newAnalysisPropertyDto(valueLength, randomAlphanumeric(40));
underTest.insert(dbSession, analysisPropertyDto);
.setKey(randomAlphanumeric(512))
.setUuid(randomAlphanumeric(40))
.setValue(randomAlphanumeric(valueLength))
- .setCreatedAt( 1_000L);
+ .setCreatedAt(1_000L);
}
private void compareFirstValueWith(AnalysisPropertyDto analysisPropertyDto) {
private final Map<String, Long> almIntegrationCountByAlm;
private final Map<String, Long> nclocByLanguage;
private final List<String> externalAuthenticationProviders;
+ private final Map<String, Long> projectCountByScm;
private final EditionProvider.Edition edition;
private final String licenseType;
private final Long installationDate;
hasUnanalyzedCpp = builder.hasUnanalyzedCpp;
customSecurityConfigs = builder.customSecurityConfigs == null ? emptyList() : builder.customSecurityConfigs;
externalAuthenticationProviders = builder.externalAuthenticationProviders;
+ projectCountByScm = builder.projectCountByScm;
}
public String getServerId() {
return externalAuthenticationProviders;
}
+ public Map<String, Long> getProjectCountByScm() {
+ return projectCountByScm;
+ }
+
static Builder builder() {
return new Builder();
}
private Boolean hasUnanalyzedCpp;
private List<String> customSecurityConfigs;
private List<String> externalAuthenticationProviders;
+ private Map<String, Long> projectCountByScm;
+
private Builder() {
// enforce static factory method
return this;
}
+ Builder setProjectCountByScm(Map<String, Long> projectCountByScm) {
+ this.projectCountByScm = projectCountByScm;
+ return this;
+ }
+
Builder setServerId(String serverId) {
this.serverId = serverId;
return this;
requireNonNull(database);
requireNonNull(usingBranches);
requireNonNull(externalAuthenticationProviders);
+ requireNonNull(projectCountByScm);
return new TelemetryData(this);
}
public class TelemetryDataJsonWriter {
+ public static final String COUNT = "count";
+
public void writeTelemetryData(JsonWriter json, TelemetryData statistics) {
json.beginObject();
json.prop("id", statistics.getServerId());
statistics.getProjectCountByLanguage().forEach((language, count) -> {
json.beginObject();
json.prop("language", language);
- json.prop("count", count);
+ json.prop(COUNT, count);
json.endObject();
});
json.endArray();
statistics.getAlmIntegrationCountByAlm().forEach((alm, count) -> {
json.beginObject();
json.prop("alm", alm);
- json.prop("count", count);
+ json.prop(COUNT, count);
json.endObject();
});
json.endArray();
statistics.getExternalAuthenticationProviders().forEach(json::value);
json.endArray();
+ json.name("projectCountByScm");
+ json.beginArray();
+ statistics.getProjectCountByScm().forEach((scm, count) -> {
+ json.beginObject();
+ json.prop("scm", scm);
+ json.prop(COUNT, count);
+ json.endObject();
+ });
+ json.endArray();
+
if (statistics.getInstallationDate() != null) {
json.prop("installationDate", statistics.getInstallationDate());
}
import org.sonar.core.util.stream.MoreCollectors;
import org.sonar.server.measure.index.ProjectMeasuresStatistics;
+import static java.util.Arrays.asList;
import static java.util.stream.Collectors.joining;
import static org.apache.commons.lang.RandomStringUtils.randomAlphabetic;
import static org.assertj.core.api.Assertions.assertThat;
.setNclocByLanguage(Collections.emptyMap())
.build())
.setNcloc(42L)
- .setExternalAuthenticationProviders(Arrays.asList("github", "gitlab"))
+ .setExternalAuthenticationProviders(asList("github", "gitlab"))
+ .setProjectCountByScm(Collections.emptyMap())
.setDatabase(new TelemetryData.Database("H2", "11"))
.setUsingBranches(true);
"}");
}
+ @Test
+ public void write_project_count_by_scm() {
+ TelemetryData data = SOME_TELEMETRY_DATA
+ .setProjectCountByScm(ImmutableMap.of("git", 5L, "svn", 4L, "cvs", 3L, "undetected", 2L))
+ .build();
+
+ String json = writeTelemetryData(data);
+
+ assertJson(json).isSimilarTo("{" +
+ " \"projectCountByScm\": ["
+ + "{ \"scm\":\"git\", \"count\":5},"
+ + "{ \"scm\":\"svn\", \"count\":4},"
+ + "{ \"scm\":\"cvs\", \"count\":3},"
+ + "{ \"scm\":\"undetected\", \"count\":2},"
+ + "]}");
+ }
+
@Test
public void write_project_stats_by_language() {
int projectCount = random.nextInt(8909);
@DataProvider
public static Object[][] allEditions() {
return Arrays.stream(EditionProvider.Edition.values())
- .map(t -> new Object[] {t})
+ .map(t -> new Object[]{t})
.toArray(Object[][]::new);
}
import org.sonar.api.config.Configuration;
import org.sonar.api.platform.Server;
import org.sonar.api.server.ServerSide;
+import org.sonar.core.config.CorePropertyDefinitions;
import org.sonar.core.platform.PlatformEditionProvider;
import org.sonar.core.platform.PluginInfo;
import org.sonar.core.platform.PluginRepository;
import org.sonar.db.DbSession;
import org.sonar.db.alm.setting.ALM;
import org.sonar.db.alm.setting.AlmSettingDto;
+import org.sonar.db.component.ProjectCountPerAnalysisPropertyValue;
import org.sonar.db.measure.SumNclocDbQuery;
import org.sonar.server.es.SearchOptions;
import org.sonar.server.measure.index.ProjectMeasuresIndex;
private final LicenseReader licenseReader;
public TelemetryDataLoaderImpl(Server server, DbClient dbClient, PluginRepository pluginRepository, UserIndex userIndex, ProjectMeasuresIndex projectMeasuresIndex,
- PlatformEditionProvider editionProvider, InternalProperties internalProperties, Configuration configuration, DockerSupport dockerSupport) {
+ PlatformEditionProvider editionProvider, InternalProperties internalProperties, Configuration configuration, DockerSupport dockerSupport) {
this(server, dbClient, pluginRepository, userIndex, projectMeasuresIndex, editionProvider, internalProperties, configuration, dockerSupport, null);
}
public TelemetryDataLoaderImpl(Server server, DbClient dbClient, PluginRepository pluginRepository, UserIndex userIndex, ProjectMeasuresIndex projectMeasuresIndex,
- PlatformEditionProvider editionProvider, InternalProperties internalProperties, Configuration configuration,
- DockerSupport dockerSupport, @Nullable LicenseReader licenseReader) {
+ PlatformEditionProvider editionProvider, InternalProperties internalProperties, Configuration configuration,
+ DockerSupport dockerSupport, @Nullable LicenseReader licenseReader) {
this.server = server;
this.dbClient = dbClient;
this.pluginRepository = pluginRepository;
data.setAlmIntegrationCountByAlm(countAlmUsage(dbSession));
data.setExternalAuthenticationProviders(dbClient.userDao().selectExternalIdentityProviders(dbSession));
-
+ Map<String, Long> projectCountPerScmDetected = dbClient.analysisPropertiesDao()
+ .selectProjectCountPerAnalysisPropertyValueInLastAnalysis(dbSession, CorePropertyDefinitions.SONAR_ANALYSIS_DETECTEDSCM)
+ .stream()
+ .collect(Collectors.toMap(ProjectCountPerAnalysisPropertyValue::getPropertyValue, ProjectCountPerAnalysisPropertyValue::getCount));
+ data.setProjectCountByScm(projectCountPerScmDetected);
}
setSecurityCustomConfigIfPresent(data);
import static org.mockito.Mockito.when;
public class ClusterSystemInfoWriterTest {
- private GlobalInfoLoader globalInfoLoader = mock(GlobalInfoLoader.class);
- private AppNodesInfoLoader appNodesInfoLoader = mock(AppNodesInfoLoader.class);
- private SearchNodesInfoLoader searchNodesInfoLoader = mock(SearchNodesInfoLoader.class);
- private HealthChecker healthChecker = mock(HealthChecker.class);
- private TelemetryDataLoader telemetry = mock(TelemetryDataLoader.class, Mockito.RETURNS_MOCKS);
- private TelemetryDataJsonWriter dataJsonWriter = new TelemetryDataJsonWriter();
- private ClusterSystemInfoWriter underTest = new ClusterSystemInfoWriter(globalInfoLoader, appNodesInfoLoader,
+ private final GlobalInfoLoader globalInfoLoader = mock(GlobalInfoLoader.class);
+ private final AppNodesInfoLoader appNodesInfoLoader = mock(AppNodesInfoLoader.class);
+ private final SearchNodesInfoLoader searchNodesInfoLoader = mock(SearchNodesInfoLoader.class);
+ private final HealthChecker healthChecker = mock(HealthChecker.class);
+ private final TelemetryDataLoader telemetry = mock(TelemetryDataLoader.class, Mockito.RETURNS_MOCKS);
+ private final TelemetryDataJsonWriter dataJsonWriter = new TelemetryDataJsonWriter();
+ private final ClusterSystemInfoWriter underTest = new ClusterSystemInfoWriter(globalInfoLoader, appNodesInfoLoader,
searchNodesInfoLoader, healthChecker, telemetry, dataJsonWriter);
@Before
underTest.write(jsonWriter);
jsonWriter.endObject();
- assertThat(writer.toString()).isEqualTo("{\"Health\":\"GREEN\","
+ assertThat(writer).hasToString("{\"Health\":\"GREEN\","
+ "\"Health Causes\":[],\"\":{\"name\":\"globalInfo\"},"
+ "\"Application Nodes\":[{\"Name\":\"appNodes\",\"\":{\"name\":\"appNodes\"}}],"
+ "\"Search Nodes\":[{\"Name\":\"searchNodes\",\"\":{\"name\":\"searchNodes\"}}],"
+ "\"Statistics\":{\"id\":\"\",\"version\":\"\",\"database\":{\"name\":\"\",\"version\":\"\"},\"plugins\":[],"
+ "\"userCount\":0,\"projectCount\":0,\"usingBranches\":false,\"ncloc\":0,\"projectCountByLanguage\":[]," +
- "\"nclocByLanguage\":[],\"almIntegrationCount\":[],\"externalAuthProviders\":[],\"installationDate\":0,\"installationVersion\":\"\",\"docker\":false}}");
+ "\"nclocByLanguage\":[],\"almIntegrationCount\":[],\"externalAuthProviders\":[],\"projectCountByScm\":[],\"installationDate\":0,\"installationVersion\":\"\",\"docker\":false}}");
}
private static NodeInfo createNodeInfo(String name) {
@Rule
public ExpectedException expectedException = ExpectedException.none();
- private SystemInfoSection section1 = mock(SystemInfoSection.class);
- private SystemInfoSection section2 = mock(SystemInfoSection.class);
- private CeHttpClient ceHttpClient = mock(CeHttpClientImpl.class, Mockito.RETURNS_MOCKS);
- private TestStandaloneHealthChecker healthChecker = new TestStandaloneHealthChecker();
- private TelemetryDataLoader telemetry = mock(TelemetryDataLoader.class, Mockito.RETURNS_MOCKS);
- private TelemetryDataJsonWriter dataJsonWriter = new TelemetryDataJsonWriter();
-
- private StandaloneSystemInfoWriter underTest = new StandaloneSystemInfoWriter(telemetry, ceHttpClient, healthChecker, dataJsonWriter, section1, section2);
+ private final SystemInfoSection section1 = mock(SystemInfoSection.class);
+ private final SystemInfoSection section2 = mock(SystemInfoSection.class);
+ private final CeHttpClient ceHttpClient = mock(CeHttpClientImpl.class, Mockito.RETURNS_MOCKS);
+ private final TestStandaloneHealthChecker healthChecker = new TestStandaloneHealthChecker();
+ private final TelemetryDataLoader telemetry = mock(TelemetryDataLoader.class, Mockito.RETURNS_MOCKS);
+ private final TelemetryDataJsonWriter dataJsonWriter = new TelemetryDataJsonWriter();
+ private final StandaloneSystemInfoWriter underTest = new StandaloneSystemInfoWriter(telemetry, ceHttpClient, healthChecker, dataJsonWriter, section1, section2);
@Test
public void write_json() {
underTest.write(jsonWriter);
jsonWriter.endObject();
// response does not contain empty "Section Three"
- assertThat(writer.toString()).isEqualTo("{\"Health\":\"GREEN\",\"Health Causes\":[],\"Section One\":{\"foo\":\"bar\"},\"Section Two\":{\"one\":1,\"two\":2}," +
+ assertThat(writer).hasToString("{\"Health\":\"GREEN\",\"Health Causes\":[],\"Section One\":{\"foo\":\"bar\"},\"Section Two\":{\"one\":1,\"two\":2}," +
"\"Statistics\":{\"id\":\"\",\"version\":\"\",\"database\":{\"name\":\"\",\"version\":\"\"},\"plugins\":[],\"userCount\":0,\"projectCount\":0,\"usingBranches\":false," +
- "\"ncloc\":0,\"projectCountByLanguage\":[],\"nclocByLanguage\":[],\"almIntegrationCount\":[],\"externalAuthProviders\":[],\"installationDate\":0," +
- "\"installationVersion\":\"\",\"docker\":false}}");
+ "\"ncloc\":0,\"projectCountByLanguage\":[],\"nclocByLanguage\":[],\"almIntegrationCount\":[],\"externalAuthProviders\":[],\"projectCountByScm\":[],\"installationDate\":0,\"installationVersion\":\"\",\"docker\":false}}");
}
private void logInAsSystemAdministrator() {
import org.sonar.server.util.GlobalLockManager;
import org.sonar.server.util.GlobalLockManagerImpl;
+import static java.util.Collections.singletonList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
.setVersion("bar")
.setPlugins(Collections.emptyMap())
.setAlmIntegrationCountByAlm(Collections.emptyMap())
+ .setExternalAuthenticationProviders(singletonList("github"))
+ .setProjectCountByScm(Collections.emptyMap())
.setProjectMeasuresStatistics(ProjectMeasuresStatistics.builder()
.setProjectCount(12)
.setProjectCountByLanguage(Collections.emptyMap())
.setUsingBranches(true)
.build();
- private TelemetryClient client = mock(TelemetryClient.class);
- private InternalProperties internalProperties = spy(new MapInternalProperties());
+ private final TelemetryClient client = mock(TelemetryClient.class);
+ private final InternalProperties internalProperties = spy(new MapInternalProperties());
private final GlobalLockManager lockManager = mock(GlobalLockManagerImpl.class);
- private TestSystem2 system2 = new TestSystem2().setNow(System.currentTimeMillis());
- private MapSettings settings = new MapSettings();
+ private final TestSystem2 system2 = new TestSystem2().setNow(System.currentTimeMillis());
+ private final MapSettings settings = new MapSettings();
private final TelemetryDataLoader dataLoader = mock(TelemetryDataLoader.class);
private final TelemetryDataJsonWriter dataJsonWriter = mock(TelemetryDataJsonWriter.class);
- private TelemetryDaemon underTest = new TelemetryDaemon(dataLoader, dataJsonWriter, client, settings.asConfig(), internalProperties, lockManager, system2);
+ private final TelemetryDaemon underTest = new TelemetryDaemon(dataLoader, dataJsonWriter, client, settings.asConfig(), internalProperties, lockManager, system2);
@After
public void tearDown() {
internalProperties.write("telemetry.lastPing", String.valueOf(sevenDaysAgo));
- verify(client, timeout(2_000)).upload(anyString());
+ verify(client, timeout(2_000)).upload(anyString());
verify(dataJsonWriter).writeTelemetryData(any(JsonWriter.class), same(SOME_TELEMETRY_DATA));
}
public class CorePropertyDefinitions {
public static final String SONAR_ANALYSIS = "sonar.analysis.";
+ public static final String SONAR_ANALYSIS_DETECTEDSCM = "sonar.analysis.detectedscm";
private static final String CATEGORY_ORGANIZATIONS = "organizations";
*/
package org.sonar.scanner.report;
+import java.util.AbstractMap;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nonnull;
+import org.sonar.api.batch.scm.ScmProvider;
import org.sonar.core.config.CorePropertyDefinitions;
import org.sonar.scanner.config.DefaultConfiguration;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.scanner.protocol.output.ScannerReportWriter;
import org.sonar.scanner.repository.ContextPropertiesCache;
+import org.sonar.scanner.scm.ScmConfiguration;
-public class ContextPropertiesPublisher implements ReportPublisherStep {
+import static org.sonar.core.config.CorePropertyDefinitions.SONAR_ANALYSIS_DETECTEDSCM;
+public class ContextPropertiesPublisher implements ReportPublisherStep {
private final ContextPropertiesCache cache;
private final DefaultConfiguration config;
+ private final ScmConfiguration scmConfiguration;
- public ContextPropertiesPublisher(ContextPropertiesCache cache, DefaultConfiguration config) {
+ public ContextPropertiesPublisher(ContextPropertiesCache cache, DefaultConfiguration config, ScmConfiguration scmConfiguration) {
this.cache = cache;
this.config = config;
+ this.scmConfiguration = scmConfiguration;
}
@Override
MapEntryToContextPropertyFunction transformer = new MapEntryToContextPropertyFunction();
// properties defined programmatically by plugins
- Stream<ScannerReport.ContextProperty> fromCache = cache.getAll().entrySet().stream().map(transformer);
+ Stream<ScannerReport.ContextProperty> fromCache = Stream.concat(cache.getAll().entrySet().stream(), Stream.of(constructScmInfo())).map(transformer);
// properties that are automatically included to report so that
// they can be included to webhook payloads
writer.writeContextProperties(Stream.concat(fromCache, fromSettings).collect(Collectors.toList()));
}
+ private Map.Entry<String, String> constructScmInfo() {
+ ScmProvider scmProvider = scmConfiguration.provider();
+ if (scmProvider != null) {
+ return new AbstractMap.SimpleEntry<>(SONAR_ANALYSIS_DETECTEDSCM, scmProvider.key());
+ } else {
+ return new AbstractMap.SimpleEntry<>(SONAR_ANALYSIS_DETECTEDSCM, "undetected");
+ }
+ }
+
private static final class MapEntryToContextPropertyFunction implements Function<Map.Entry<String, String>, ScannerReport.ContextProperty> {
private final ScannerReport.ContextProperty.Builder builder = ScannerReport.ContextProperty.newBuilder();
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.scanner.protocol.output.ScannerReportWriter;
import org.sonar.scanner.repository.ContextPropertiesCache;
+import org.sonar.scanner.scm.ScmConfiguration;
-import static java.util.Collections.emptyList;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
@Rule
public ExpectedException expectedException = ExpectedException.none();
- private ScannerReportWriter writer = mock(ScannerReportWriter.class);
- private ContextPropertiesCache cache = new ContextPropertiesCache();
- private DefaultConfiguration config = mock(DefaultConfiguration.class);
- private Map<String, String> props = new HashMap<>();
- private ContextPropertiesPublisher underTest = new ContextPropertiesPublisher(cache, config);
+ private final ScannerReportWriter writer = mock(ScannerReportWriter.class);
+ private final ContextPropertiesCache cache = new ContextPropertiesCache();
+ private final DefaultConfiguration config = mock(DefaultConfiguration.class);
+ private final Map<String, String> props = new HashMap<>();
+ private final ScmConfiguration scmConfiguration = mock(ScmConfiguration.class);
+ private final ContextPropertiesPublisher underTest = new ContextPropertiesPublisher(cache, config, scmConfiguration);
@Before
public void prepareMock() {
List<ScannerReport.ContextProperty> expected = Arrays.asList(
newContextProperty("foo1", "bar1"),
- newContextProperty("foo2", "bar2"));
+ newContextProperty("foo2", "bar2"),
+ newContextProperty("sonar.analysis.detectedscm", "undetected"));
expectWritten(expected);
}
- @Test
- public void publish_writes_no_properties_to_report() {
- underTest.publish(writer);
-
- expectWritten(emptyList());
- }
-
@Test
public void publish_settings_prefixed_with_sonar_analysis_for_webhooks() {
props.put("foo", "should not be exported");
List<ScannerReport.ContextProperty> expected = Arrays.asList(
newContextProperty("sonar.analysis.revision", "ab45b3"),
- newContextProperty("sonar.analysis.build.number", "B123"));
+ newContextProperty("sonar.analysis.build.number", "B123"),
+ newContextProperty("sonar.analysis.detectedscm", "undetected"));
expectWritten(expected);
}