*/
package org.sonar.ce.task.projectanalysis.step;
+import com.google.common.collect.ImmutableSet;
import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
import org.sonar.ce.task.projectanalysis.analysis.AnalysisMetadataHolder;
import org.sonar.ce.task.projectanalysis.batch.BatchReportReader;
import org.sonar.ce.task.step.ComputationStep;
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_DETECTEDCI;
import static org.sonar.core.config.CorePropertyDefinitions.SONAR_ANALYSIS_DETECTEDSCM;
/**
public class PersistAnalysisPropertiesStep implements ComputationStep {
private static final String SONAR_PULL_REQUEST = "sonar.pullrequest.";
+ private static final Set<String> ANALYSIS_PROPERTIES_TO_PERSIST = ImmutableSet.of(SONAR_ANALYSIS_DETECTEDSCM, SONAR_ANALYSIS_DETECTEDCI);
private final DbClient dbClient;
private final AnalysisMetadataHolder analysisMetadataHolder;
it.forEachRemaining(
contextProperty -> {
String propertyKey = contextProperty.getKey();
- if (propertyKey.startsWith(SONAR_ANALYSIS) || propertyKey.startsWith(SONAR_PULL_REQUEST) || SONAR_ANALYSIS_DETECTEDSCM.equals(propertyKey)) {
+ if (propertyKey.startsWith(SONAR_ANALYSIS) || propertyKey.startsWith(SONAR_PULL_REQUEST) ||
+ ANALYSIS_PROPERTIES_TO_PERSIST.contains(propertyKey)) {
analysisPropertyDtos.add(new AnalysisPropertyDto()
.setUuid(uuidFactory.create())
.setKey(propertyKey)
private final Map<String, Long> nclocByLanguage;
private final List<String> externalAuthenticationProviders;
private final Map<String, Long> projectCountByScm;
+ private final Map<String, Long> projectCountByCi;
private final EditionProvider.Edition edition;
private final String licenseType;
private final Long installationDate;
customSecurityConfigs = builder.customSecurityConfigs == null ? emptyList() : builder.customSecurityConfigs;
externalAuthenticationProviders = builder.externalAuthenticationProviders;
projectCountByScm = builder.projectCountByScm;
+ projectCountByCi = builder.projectCountByCi;
}
public String getServerId() {
return projectCountByScm;
}
+ public Map<String, Long> getProjectCountByCi() {
+ return projectCountByCi;
+ }
+
static Builder builder() {
return new Builder();
}
private List<String> customSecurityConfigs;
private List<String> externalAuthenticationProviders;
private Map<String, Long> projectCountByScm;
-
+ private Map<String, Long> projectCountByCi;
private Builder() {
// enforce static factory method
return this;
}
+ Builder setProjectCountByCi(Map<String, Long> projectCountByCi) {
+ this.projectCountByCi = projectCountByCi;
+ return this;
+ }
+
Builder setServerId(String serverId) {
this.serverId = serverId;
return this;
requireNonNull(usingBranches);
requireNonNull(externalAuthenticationProviders);
requireNonNull(projectCountByScm);
+ requireNonNull(projectCountByCi);
return new TelemetryData(this);
}
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();
+ addScmInfo(json, statistics);
+ addCiInfo(json, statistics);
json.prop("sonarlintWeeklyUsers", statistics.sonarlintWeeklyUsers());
+
if (statistics.getInstallationDate() != null) {
json.prop("installationDate", statistics.getInstallationDate());
}
json.prop("docker", statistics.isInDocker());
json.endObject();
}
+
+ private static void addScmInfo(JsonWriter json, TelemetryData statistics) {
+ json.name("projectCountByScm");
+ json.beginArray();
+ statistics.getProjectCountByScm().forEach((scm, count) -> {
+ json.beginObject();
+ json.prop("scm", scm);
+ json.prop(COUNT, count);
+ json.endObject();
+ });
+ json.endArray();
+ }
+
+ private static void addCiInfo(JsonWriter json, TelemetryData statistics) {
+ json.name("projectCountByCI");
+ json.beginArray();
+ statistics.getProjectCountByCi().forEach((ci, count) -> {
+ json.beginObject();
+ json.prop("ci", ci);
+ json.prop(COUNT, count);
+ json.endObject();
+ });
+ json.endArray();
+ }
}
.setExternalAuthenticationProviders(asList("github", "gitlab"))
.setProjectCountByScm(Collections.emptyMap())
.setSonarlintWeeklyUsers(10)
+ .setProjectCountByCi(Collections.emptyMap())
.setDatabase(new TelemetryData.Database("H2", "11"))
.setUsingBranches(true);
+ "]}");
}
+ @Test
+ public void write_project_count_by_ci() {
+ TelemetryData data = SOME_TELEMETRY_DATA
+ .setProjectCountByCi(ImmutableMap.of("Bitbucket Pipelines", 5L, "Github Actions", 4L, "Jenkins", 3L, "undetected", 2L))
+ .build();
+
+ String json = writeTelemetryData(data);
+
+ assertJson(json).isSimilarTo("{" +
+ " \"projectCountByCI\": ["
+ + "{ \"ci\":\"Bitbucket Pipelines\", \"count\":5},"
+ + "{ \"ci\":\"Github Actions\", \"count\":4},"
+ + "{ \"ci\":\"Jenkins\", \"count\":3},"
+ + "{ \"ci\":\"undetected\", \"count\":2},"
+ + "]}");
+ }
+
@Test
public void write_project_stats_by_language() {
int projectCount = random.nextInt(8909);
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);
data.setSonarlintWeeklyUsers(dbClient.userDao().countSonarlintWeeklyUsers(dbSession));
+ addScmInformationToTelemetry(dbSession, data);
+ addCiInformationToTelemetry(dbSession, data);
}
setSecurityCustomConfigIfPresent(data);
});
}
+ private void addScmInformationToTelemetry(DbSession dbSession, TelemetryData.Builder data) {
+ Map<String, Long> projectCountPerScmDetected = dbClient.analysisPropertiesDao()
+ .selectProjectCountPerAnalysisPropertyValueInLastAnalysis(dbSession, CorePropertyDefinitions.SONAR_ANALYSIS_DETECTEDSCM)
+ .stream()
+ .collect(Collectors.toMap(ProjectCountPerAnalysisPropertyValue::getPropertyValue, ProjectCountPerAnalysisPropertyValue::getCount));
+ data.setProjectCountByScm(projectCountPerScmDetected);
+ }
+
+ private void addCiInformationToTelemetry(DbSession dbSession, TelemetryData.Builder data) {
+ Map<String, Long> projectCountPerCiDetected = dbClient.analysisPropertiesDao()
+ .selectProjectCountPerAnalysisPropertyValueInLastAnalysis(dbSession, CorePropertyDefinitions.SONAR_ANALYSIS_DETECTEDCI)
+ .stream()
+ .collect(Collectors.toMap(ProjectCountPerAnalysisPropertyValue::getPropertyValue, ProjectCountPerAnalysisPropertyValue::getCount));
+ data.setProjectCountByCi(projectCountPerCiDetected);
+ }
+
private Map<String, Long> countAlmUsage(DbSession dbSession) {
return dbClient.almSettingDao().selectAll(dbSession).stream()
.collect(Collectors.groupingBy(almSettingDto -> {
+ "\"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\":[],\"projectCountByScm\":[],\"sonarlintWeeklyUsers\":0,\"installationDate\":0,"
- + "\"installationVersion\":\"\",\"docker\":false}}");
+ + "\"nclocByLanguage\":[],\"almIntegrationCount\":[],\"externalAuthProviders\":[],\"projectCountByScm\":[],\"projectCountByCI\":[],\"sonarlintWeeklyUsers\":0,"
+ + "\"installationDate\":0,\"installationVersion\":\"\",\"docker\":false}}");
}
private static NodeInfo createNodeInfo(String name) {
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\":[],\"projectCountByScm\":[],"
- + "\"sonarlintWeeklyUsers\":0,\"installationDate\":0,\"installationVersion\":\"\",\"docker\":false}}");
+ + "\"projectCountByCI\":[],\"sonarlintWeeklyUsers\":0,\"installationDate\":0,\"installationVersion\":\"\",\"docker\":false}}");
}
private void logInAsSystemAdministrator() {
.setAlmIntegrationCountByAlm(Collections.emptyMap())
.setExternalAuthenticationProviders(singletonList("github"))
.setProjectCountByScm(Collections.emptyMap())
+ .setProjectCountByCi(Collections.emptyMap())
.setProjectMeasuresStatistics(ProjectMeasuresStatistics.builder()
.setProjectCount(12)
.setProjectCountByLanguage(Collections.emptyMap())
public static final String SONAR_ANALYSIS = "sonar.analysis.";
public static final String SONAR_ANALYSIS_DETECTEDSCM = "sonar.analysis.detectedscm";
+ public static final String SONAR_ANALYSIS_DETECTEDCI = "sonar.analysis.detectedci";
private static final String CATEGORY_ORGANIZATIONS = "organizations";
*/
public interface CiConfiguration {
+ /**
+ * Name of the CI environment
+ */
+ String getCiName();
+
/**
* The revision that triggered the analysis. It should
* be the revision as seen by end-user, but not the necessarily
import static org.apache.commons.lang.StringUtils.defaultIfBlank;
public class CiConfigurationImpl implements CiConfiguration {
+ private final String ciName;
@Nullable
private final String scmRevision;
- public CiConfigurationImpl(@Nullable String scmRevision) {
+ public CiConfigurationImpl(@Nullable String scmRevision, String ciName) {
this.scmRevision = defaultIfBlank(scmRevision, null);
+ this.ciName = ciName;
}
@Override
public Optional<String> getScmRevision() {
return Optional.ofNullable(scmRevision);
}
+
+ @Override
+ public String getCiName() {
+ return ciName;
+ }
}
return new EmptyCiConfiguration();
}
- private static class EmptyCiConfiguration implements CiConfiguration {
+ static class EmptyCiConfiguration implements CiConfiguration {
@Override
public Optional<String> getScmRevision() {
return Optional.empty();
}
+
+ @Override
+ public String getCiName() {
+ return "undetected";
+ }
}
}
/**
* Support of https://www.appveyor.com
- *
+ * <p>
* Environment variables: https://www.appveyor.com/docs/environment-variables/
*/
public class AppVeyor implements CiVendor {
@Override
public CiConfiguration loadConfiguration() {
String revision = system.envVariable("APPVEYOR_REPO_COMMIT");
- return new CiConfigurationImpl(revision);
+ return new CiConfigurationImpl(revision, getName());
}
}
--- /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.scanner.ci.vendors;
+
+import org.sonar.api.utils.System2;
+import org.sonar.scanner.ci.CiConfiguration;
+import org.sonar.scanner.ci.CiConfigurationImpl;
+import org.sonar.scanner.ci.CiVendor;
+
+public class AwsCodeBuild implements CiVendor {
+ private final System2 system;
+
+ public AwsCodeBuild(System2 system) {
+ this.system = system;
+ }
+
+ @Override
+ public String getName() {
+ return "AwsCodeBuild";
+ }
+
+
+ @Override
+ public boolean isDetected() {
+ return environmentVariableIsPresent("CODEBUILD_BUILD_ID") &&
+ environmentVariableIsPresent("CODEBUILD_START_TIME");
+ }
+
+ @Override
+ public CiConfiguration loadConfiguration() {
+ return new CiConfigurationImpl(null, getName());
+ }
+
+ private boolean environmentVariableIsPresent(String key) {
+ return system.envVariable(key) != null;
+ }
+}
/**
* Support of https://azure.microsoft.com/en-us/services/devops/
- *
+ * <p>
* Environment variables: https://docs.microsoft.com/en-us/azure/devops/pipelines/build/variables
*/
public class AzureDevops implements CiVendor {
if (isBlank(revision)) {
revision = system.envVariable("BUILD_SOURCEVERSION");
}
- return new CiConfigurationImpl(revision);
+ return new CiConfigurationImpl(revision, getName());
}
}
--- /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.scanner.ci.vendors;
+
+import org.sonar.api.utils.System2;
+import org.sonar.scanner.ci.CiConfiguration;
+import org.sonar.scanner.ci.CiConfigurationImpl;
+import org.sonar.scanner.ci.CiVendor;
+
+public class Bamboo implements CiVendor {
+ private final System2 system;
+
+ public Bamboo(System2 system) {
+ this.system = system;
+ }
+
+ @Override
+ public String getName() {
+ return "Bamboo";
+ }
+
+
+ @Override
+ public boolean isDetected() {
+ return system.envVariable("bamboo_buildNumber") != null;
+ }
+
+ @Override
+ public CiConfiguration loadConfiguration() {
+ String revision = system.envVariable("bamboo_planRepository_revision");
+ return new CiConfigurationImpl(revision, getName());
+ }
+}
@Override
public CiConfiguration loadConfiguration() {
String revision = system.envVariable("BITBUCKET_COMMIT");
- return new CiConfigurationImpl(revision);
+ return new CiConfigurationImpl(revision, getName());
}
}
--- /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.scanner.ci.vendors;
+
+import java.util.Optional;
+import org.sonar.api.utils.System2;
+import org.sonar.scanner.ci.CiConfiguration;
+import org.sonar.scanner.ci.CiConfigurationImpl;
+import org.sonar.scanner.ci.CiVendor;
+
+public class Bitrise implements CiVendor {
+
+ private final System2 system2;
+
+ public Bitrise(System2 system2) {
+ this.system2 = system2;
+ }
+
+ @Override
+ public String getName() {
+ return "Bitrise";
+ }
+
+ @Override
+ public boolean isDetected() {
+ return environmentVariableIsTrue("CI") && environmentVariableIsTrue("BITRISE_IO");
+ }
+
+ @Override
+ public CiConfiguration loadConfiguration() {
+ String revision = system2.envVariable("BITRISE_GIT_COMMIT");
+ return new CiConfigurationImpl(revision, getName());
+ }
+
+ private boolean environmentVariableIsTrue(String key) {
+ return Optional.ofNullable(system2.envVariable(key))
+ .map(Boolean::parseBoolean)
+ .orElse(false);
+ }
+}
/**
* Support of https://buildkite.com
- *
+ * <p>
* Environment variables: https://buildkite.com/docs/pipelines/environment-variables
*/
public class Buildkite implements CiVendor {
@Override
public CiConfiguration loadConfiguration() {
String revision = system.envVariable("BUILDKITE_COMMIT");
- return new CiConfigurationImpl(revision);
+ return new CiConfigurationImpl(revision, getName());
}
}
/**
* Support of https://circleci.com
- *
+ * <p>
* Environment variables: https://circleci.com/docs/2.0/env-vars/#built-in-environment-variables
*/
public class CircleCi implements CiVendor {
@Override
public CiConfiguration loadConfiguration() {
String revision = system.envVariable("CIRCLE_SHA1");
- return new CiConfigurationImpl(revision);
+ return new CiConfigurationImpl(revision, getName());
}
}
/**
* Support https://cirrus-ci.org/
- *
+ * <p>
* Environment variables are documented at https://cirrus-ci.org/guide/writing-tasks/#environment-variables
*/
public class CirrusCi implements CiVendor {
if (isEmpty(revision)) {
Loggers.get(getClass()).warn("Missing environment variable " + PROPERTY_COMMIT);
}
- return new CiConfigurationImpl(revision);
+ return new CiConfigurationImpl(revision, getName());
}
}
/**
* Support https://drone.io
- *
+ * <p>
* Environment variables are documented at https://docs.drone.io/reference/environ/
*/
public class DroneCi implements CiVendor {
@Override
public CiConfiguration loadConfiguration() {
String revision = system.envVariable("DRONE_COMMIT_SHA");
- return new CiConfigurationImpl(revision);
+ return new CiConfigurationImpl(revision, getName());
}
}
/**
* Support of https://github.com/features/actions
- *
+ * <p>
* Environment variables: https://developer.github.com/actions/creating-github-actions/accessing-the-runtime-environment/#environment-variables
*/
public class GithubActions implements CiVendor {
if (isEmpty(revision)) {
Loggers.get(getClass()).warn("Missing environment variable " + PROPERTY_COMMIT);
}
- return new CiConfigurationImpl(revision);
+ return new CiConfigurationImpl(revision, getName());
}
}
@Override
public CiConfiguration loadConfiguration() {
String revision = system.envVariable("CI_COMMIT_SHA");
- return new CiConfigurationImpl(revision);
+ return new CiConfigurationImpl(revision, getName());
}
}
// https://wiki.jenkins-ci.org/display/JENKINS/Building+a+software+project
String revision = system.envVariable("ghprbActualCommit");
if (StringUtils.isNotBlank(revision)) {
- return new CiConfigurationImpl(revision);
+ return new CiConfigurationImpl(revision, getName());
}
revision = system.envVariable("GIT_COMMIT");
if (StringUtils.isNotBlank(system.envVariable("CHANGE_ID"))) {
String jenkinsGitPrSha1 = getJenkinsGitPrSha1();
if (StringUtils.isNotBlank(jenkinsGitPrSha1)) {
- return new CiConfigurationImpl(jenkinsGitPrSha1);
+ return new CiConfigurationImpl(jenkinsGitPrSha1, getName());
}
}
- return new CiConfigurationImpl(revision);
+ return new CiConfigurationImpl(revision, getName());
}
revision = system.envVariable("SVN_COMMIT");
- return new CiConfigurationImpl(revision);
+ return new CiConfigurationImpl(revision, getName());
}
private String getJenkinsGitPrSha1() {
@Override
public CiConfiguration loadConfiguration() {
String revision = system.envVariable("SEMAPHORE_GIT_SHA");
- return new CiConfigurationImpl(revision);
+ return new CiConfigurationImpl(revision, getName());
}
}
/**
* Support of https://travis-ci.com
- *
+ * <p>
* Environment variables: https://docs.travis-ci.com/user/environment-variables/
*/
public class TravisCi implements CiVendor {
revision = system.envVariable("TRAVIS_PULL_REQUEST_SHA");
}
- return new CiConfigurationImpl(revision);
+ return new CiConfigurationImpl(revision, getName());
}
}
package org.sonar.scanner.report;
import java.util.AbstractMap;
+import java.util.ArrayList;
+import java.util.List;
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.ci.CiConfiguration;
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;
+import static org.sonar.core.config.CorePropertyDefinitions.SONAR_ANALYSIS_DETECTEDCI;
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;
+ private final CiConfiguration ciConfiguration;
- public ContextPropertiesPublisher(ContextPropertiesCache cache, DefaultConfiguration config, ScmConfiguration scmConfiguration) {
+ public ContextPropertiesPublisher(ContextPropertiesCache cache, DefaultConfiguration config, ScmConfiguration scmConfiguration,
+ CiConfiguration ciConfiguration) {
this.cache = cache;
this.config = config;
this.scmConfiguration = scmConfiguration;
+ this.ciConfiguration = ciConfiguration;
}
@Override
public void publish(ScannerReportWriter writer) {
- MapEntryToContextPropertyFunction transformer = new MapEntryToContextPropertyFunction();
-
- // properties defined programmatically by plugins
- Stream<ScannerReport.ContextProperty> fromCache = Stream.concat(cache.getAll().entrySet().stream(), Stream.of(constructScmInfo())).map(transformer);
-
+ List<Map.Entry<String, String>> properties = new ArrayList<>(cache.getAll().entrySet());
+ properties.add(constructScmInfo());
+ properties.add(constructCiInfo());
// properties that are automatically included to report so that
// they can be included to webhook payloads
- Stream<ScannerReport.ContextProperty> fromSettings = config.getProperties().entrySet().stream()
+ properties.addAll(config.getProperties().entrySet()
+ .stream()
.filter(e -> e.getKey().startsWith(CorePropertyDefinitions.SONAR_ANALYSIS))
- .map(transformer);
+ .collect(Collectors.toList()));
- writer.writeContextProperties(Stream.concat(fromCache, fromSettings).collect(Collectors.toList()));
+ writer.writeContextProperties(properties
+ .stream()
+ .map(e -> ScannerReport.ContextProperty.newBuilder()
+ .setKey(e.getKey())
+ .setValue(e.getValue())
+ .build())
+ .collect(Collectors.toList()));
}
private Map.Entry<String, String> constructScmInfo() {
}
}
- private static final class MapEntryToContextPropertyFunction implements Function<Map.Entry<String, String>, ScannerReport.ContextProperty> {
- private final ScannerReport.ContextProperty.Builder builder = ScannerReport.ContextProperty.newBuilder();
-
- @Override
- public ScannerReport.ContextProperty apply(@Nonnull Map.Entry<String, String> input) {
- return builder.clear().setKey(input.getKey()).setValue(input.getValue()).build();
- }
+ private Map.Entry<String, String> constructCiInfo() {
+ return new AbstractMap.SimpleEntry<>(SONAR_ANALYSIS_DETECTEDCI, ciConfiguration.getCiName());
}
}
import org.sonar.scanner.bootstrap.PostJobExtensionDictionnary;
import org.sonar.scanner.ci.CiConfigurationProvider;
import org.sonar.scanner.ci.vendors.AppVeyor;
+import org.sonar.scanner.ci.vendors.AwsCodeBuild;
import org.sonar.scanner.ci.vendors.AzureDevops;
+import org.sonar.scanner.ci.vendors.Bamboo;
import org.sonar.scanner.ci.vendors.BitbucketPipelines;
+import org.sonar.scanner.ci.vendors.Bitrise;
import org.sonar.scanner.ci.vendors.Buildkite;
import org.sonar.scanner.ci.vendors.CircleCi;
import org.sonar.scanner.ci.vendors.CirrusCi;
// CI
new CiConfigurationProvider(),
AppVeyor.class,
+ AwsCodeBuild.class,
AzureDevops.class,
+ Bamboo.class,
BitbucketPipelines.class,
+ Bitrise.class,
Buildkite.class,
CircleCi.class,
CirrusCi.class,
@Test
public void getScmRevision() {
- assertThat(new CiConfigurationImpl(null).getScmRevision()).isEmpty();
- assertThat(new CiConfigurationImpl("").getScmRevision()).isEmpty();
- assertThat(new CiConfigurationImpl(" ").getScmRevision()).isEmpty();
- assertThat(new CiConfigurationImpl("a7bdf2d").getScmRevision()).hasValue("a7bdf2d");
+ assertThat(new CiConfigurationImpl(null, "test").getScmRevision()).isEmpty();
+ assertThat(new CiConfigurationImpl("", "test").getScmRevision()).isEmpty();
+ assertThat(new CiConfigurationImpl(" ", "test").getScmRevision()).isEmpty();
+ assertThat(new CiConfigurationImpl("a7bdf2d", "test").getScmRevision()).hasValue("a7bdf2d");
+ }
+
+ @Test
+ public void getNam_for_undetected_ci() {
+ assertThat(new CiConfigurationProvider.EmptyCiConfiguration().getCiName()).isEqualTo("undetected");
+ }
+
+ @Test
+ public void getName_for_detected_ci() {
+ assertThat(new CiConfigurationImpl(null, "test").getCiName()).isEqualTo("test");
}
}
public class CiConfigurationProviderTest {
- private MapSettings cli = new MapSettings();
- private CiConfigurationProvider underTest = new CiConfigurationProvider();
+ private final MapSettings cli = new MapSettings();
+ private final CiConfigurationProvider underTest = new CiConfigurationProvider();
@Test
public void empty_configuration_if_no_ci_vendors() {
@Test
public void empty_configuration_if_no_ci_detected() {
- CiConfiguration ciConfiguration = underTest.provide(cli.asConfig(), new CiVendor[] {new DisabledCiVendor("vendor1"), new DisabledCiVendor("vendor2")});
+ CiConfiguration ciConfiguration = underTest.provide(cli.asConfig(), new CiVendor[]{new DisabledCiVendor("vendor1"), new DisabledCiVendor("vendor2")});
assertThat(ciConfiguration.getScmRevision()).isEmpty();
}
@Test
public void configuration_defined_by_ci_vendor() {
- CiConfiguration ciConfiguration = underTest.provide(cli.asConfig(), new CiVendor[] {new DisabledCiVendor("vendor1"), new EnabledCiVendor("vendor2")});
+ CiConfiguration ciConfiguration = underTest.provide(cli.asConfig(), new CiVendor[]{new DisabledCiVendor("vendor1"), new EnabledCiVendor("vendor2")});
assertThat(ciConfiguration.getScmRevision()).hasValue(EnabledCiVendor.SHA);
}
@Test
public void fail_if_multiple_ci_vendor_are_detected() {
- Throwable thrown = catchThrowable(() -> underTest.provide(cli.asConfig(), new CiVendor[] {new EnabledCiVendor("vendor1"), new EnabledCiVendor("vendor2")}));
+ Throwable thrown = catchThrowable(() -> underTest.provide(cli.asConfig(), new CiVendor[]{new EnabledCiVendor("vendor1"), new EnabledCiVendor("vendor2")}));
assertThat(thrown)
.isInstanceOf(MessageException.class)
@Test
public void empty_configuration_if_auto_configuration_is_disabled() {
cli.setProperty("sonar.ci.autoconfig.disabled", true);
- CiConfiguration ciConfiguration = underTest.provide(cli.asConfig(), new CiVendor[] {new EnabledCiVendor("vendor1")});
+ CiConfiguration ciConfiguration = underTest.provide(cli.asConfig(), new CiVendor[]{new EnabledCiVendor("vendor1")});
assertThat(ciConfiguration.getScmRevision()).isEmpty();
}
@Override
public CiConfiguration loadConfiguration() {
- return new CiConfigurationImpl(SHA);
+ return new CiConfigurationImpl(SHA, name);
}
}
}
--- /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.scanner.ci.vendors;
+
+import javax.annotation.Nullable;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.scanner.ci.CiVendor;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class AwsCodeBuildTest {
+
+ private final System2 system = mock(System2.class);
+ private final CiVendor underTest = new AwsCodeBuild(system);
+
+ @Test
+ public void getName() {
+ assertThat(underTest.getName()).isEqualTo("AwsCodeBuild");
+ }
+
+ @Test
+ public void isDetected() {
+ assertThat(underTest.isDetected()).isFalse();
+
+ setEnvVariable("CODEBUILD_BUILD_ID", "51");
+ assertThat(underTest.isDetected()).isFalse();
+
+ setEnvVariable("CODEBUILD_BUILD_ID", "52");
+ setEnvVariable("CODEBUILD_START_TIME", "some-time");
+ assertThat(underTest.isDetected()).isTrue();
+ }
+
+ @Test
+ public void loadConfiguration() {
+ setEnvVariable("CODEBUILD_BUILD_ID", "51");
+ setEnvVariable("CODEBUILD_START_TIME", "some-time");
+
+ assertThat(underTest.loadConfiguration().getScmRevision()).isEmpty();
+ }
+
+ private void setEnvVariable(String key, @Nullable String value) {
+ when(system.envVariable(key)).thenReturn(value);
+ }
+}
--- /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.scanner.ci.vendors;
+
+import javax.annotation.Nullable;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.scanner.ci.CiVendor;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class BambooTest {
+
+ private final System2 system = mock(System2.class);
+ private final CiVendor underTest = new Bamboo(system);
+
+ @Test
+ public void getName() {
+ assertThat(underTest.getName()).isEqualTo("Bamboo");
+ }
+
+ @Test
+ public void isDetected() {
+ assertThat(underTest.isDetected()).isFalse();
+
+ setEnvVariable("bamboo_buildNumber", "41");
+ assertThat(underTest.isDetected()).isTrue();
+ }
+
+ @Test
+ public void loadConfiguration() {
+ setEnvVariable("bamboo_buildNumber", "41");
+ setEnvVariable("bamboo_planRepository_revision", "42109719-93c9-4d47-9d7c-b3b0b87b6985");
+
+ assertThat(underTest.loadConfiguration().getScmRevision()).hasValue("42109719-93c9-4d47-9d7c-b3b0b87b6985");
+ }
+
+ private void setEnvVariable(String key, @Nullable String value) {
+ when(system.envVariable(key)).thenReturn(value);
+ }
+}
--- /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.scanner.ci.vendors;
+
+import javax.annotation.Nullable;
+import org.junit.Test;
+import org.sonar.api.utils.System2;
+import org.sonar.scanner.ci.CiVendor;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class BitriseTest {
+
+ private final System2 system = mock(System2.class);
+ private final CiVendor underTest = new Bitrise(system);
+
+ @Test
+ public void getName() {
+ assertThat(underTest.getName()).isEqualTo("Bitrise");
+ }
+
+ @Test
+ public void isDetected() {
+
+ assertThat(underTest.isDetected()).isFalse();
+
+ setEnvVariable("CI", "true");
+ assertThat(underTest.isDetected()).isFalse();
+
+ setEnvVariable("CI", "true");
+ setEnvVariable("BITRISE_IO", "false");
+ assertThat(underTest.isDetected()).isFalse();
+
+ setEnvVariable("CI", "true");
+ setEnvVariable("BITRISE_IO", "true");
+ assertThat(underTest.isDetected()).isTrue();
+ }
+
+ @Test
+ public void loadConfiguration() {
+ setEnvVariable("CI", "true");
+ setEnvVariable("BITRISE_IO", "true");
+ setEnvVariable("BITRISE_GIT_COMMIT", "abd12fc");
+
+ assertThat(underTest.loadConfiguration().getScmRevision()).hasValue("abd12fc");
+ }
+
+ private void setEnvVariable(String key, @Nullable String value) {
+ when(system.envVariable(key)).thenReturn(value);
+ }
+}
import java.util.List;
import java.util.Map;
import org.junit.Before;
-import org.junit.Rule;
import org.junit.Test;
-import org.junit.rules.ExpectedException;
+import org.sonar.scanner.ci.CiConfiguration;
import org.sonar.scanner.config.DefaultConfiguration;
import org.sonar.scanner.protocol.output.ScannerReport;
import org.sonar.scanner.protocol.output.ScannerReportWriter;
import static org.mockito.Mockito.when;
public class ContextPropertiesPublisherTest {
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
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);
+ private final CiConfiguration ciConfiguration = mock(CiConfiguration.class);
+ private final ContextPropertiesPublisher underTest = new ContextPropertiesPublisher(cache, config, scmConfiguration, ciConfiguration);
@Before
public void prepareMock() {
when(config.getProperties()).thenReturn(props);
+ when(ciConfiguration.getCiName()).thenReturn("undetected");
}
@Test
List<ScannerReport.ContextProperty> expected = Arrays.asList(
newContextProperty("foo1", "bar1"),
newContextProperty("foo2", "bar2"),
- newContextProperty("sonar.analysis.detectedscm", "undetected"));
+ newContextProperty("sonar.analysis.detectedscm", "undetected"),
+ newContextProperty("sonar.analysis.detectedci", "undetected"));
expectWritten(expected);
}
List<ScannerReport.ContextProperty> expected = Arrays.asList(
newContextProperty("sonar.analysis.revision", "ab45b3"),
newContextProperty("sonar.analysis.build.number", "B123"),
- newContextProperty("sonar.analysis.detectedscm", "undetected"));
+ newContextProperty("sonar.analysis.detectedscm", "undetected"),
+ newContextProperty("sonar.analysis.detectedci", "undetected"));
expectWritten(expected);
}