From f24fd0a266ca0dc30b99ab32defba039eb145df4 Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Sun, 12 May 2019 15:12:42 +0200 Subject: [PATCH] SONAR-11950 autoconfig of SCM revision on Cirrus and Bitbucket --- .../org/sonar/scanner/ci/CiConfiguration.java | 39 ++++++ .../sonar/scanner/ci/CiConfigurationImpl.java | 40 ++++++ .../scanner/ci/CiConfigurationProvider.java | 66 ++++++++++ .../java/org/sonar/scanner/ci/CiVendor.java | 43 +++++++ .../org/sonar/scanner/ci/package-info.java | 23 ++++ .../ci/vendors/BitbucketPipelines.java | 54 ++++++++ .../sonar/scanner/ci/vendors/CirrusCi.java | 62 +++++++++ .../scanner/ci/vendors/package-info.java | 23 ++++ .../scanner/report/MetadataPublisher.java | 49 ++++---- .../scanner/scan/ProjectScanContainer.java | 10 ++ .../sonar/scanner/scan/ScanProperties.java | 4 - .../org/sonar/scanner/scm/ScmRevision.java | 37 ++++++ .../sonar/scanner/scm/ScmRevisionImpl.java | 77 ++++++++++++ .../scanner/ci/CiConfigurationImplTest.java | 35 ++++++ .../ci/CiConfigurationProviderTest.java | 118 ++++++++++++++++++ .../ci/vendors/BitbucketPipelinesTest.java | 73 +++++++++++ .../scanner/ci/vendors/CirrusCiTest.java | 86 +++++++++++++ .../scanner/report/MetadataPublisherTest.java | 53 +------- .../scanner/scm/ScmRevisionImplTest.java | 85 +++++++++++++ 19 files changed, 897 insertions(+), 80 deletions(-) create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/CiConfiguration.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/CiConfigurationImpl.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/CiConfigurationProvider.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/CiVendor.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/package-info.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/vendors/BitbucketPipelines.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/vendors/CirrusCi.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/vendors/package-info.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmRevision.java create mode 100644 sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmRevisionImpl.java create mode 100644 sonar-scanner-engine/src/test/java/org/sonar/scanner/ci/CiConfigurationImplTest.java create mode 100644 sonar-scanner-engine/src/test/java/org/sonar/scanner/ci/CiConfigurationProviderTest.java create mode 100644 sonar-scanner-engine/src/test/java/org/sonar/scanner/ci/vendors/BitbucketPipelinesTest.java create mode 100644 sonar-scanner-engine/src/test/java/org/sonar/scanner/ci/vendors/CirrusCiTest.java create mode 100644 sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmRevisionImplTest.java diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/CiConfiguration.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/CiConfiguration.java new file mode 100644 index 00000000000..fc1d3c62677 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/CiConfiguration.java @@ -0,0 +1,39 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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; + +import java.util.Optional; + +/** + * Configuration provided by CI environments like TravisCI or Jenkins. + * + * @see CiVendor + */ +public interface CiConfiguration { + + /** + * The revision that triggered the analysis. It should + * be the revision as seen by end-user, but not the necessarily + * the effective revision of the clone on disk (merge commit with + * base branch for instance). + */ + Optional getScmRevision(); + +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/CiConfigurationImpl.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/CiConfigurationImpl.java new file mode 100644 index 00000000000..aabd92bc30a --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/CiConfigurationImpl.java @@ -0,0 +1,40 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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; + +import java.util.Optional; +import javax.annotation.Nullable; + +import static org.apache.commons.lang.StringUtils.defaultIfBlank; + +public class CiConfigurationImpl implements CiConfiguration { + + @Nullable + private final String scmRevision; + + public CiConfigurationImpl(@Nullable String scmRevision) { + this.scmRevision = defaultIfBlank(scmRevision, null); + } + + @Override + public Optional getScmRevision() { + return Optional.ofNullable(scmRevision); + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/CiConfigurationProvider.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/CiConfigurationProvider.java new file mode 100644 index 00000000000..38032976b05 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/CiConfigurationProvider.java @@ -0,0 +1,66 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import org.picocontainer.injectors.ProviderAdapter; +import org.sonar.api.config.Configuration; +import org.sonar.api.utils.MessageException; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; + +public class CiConfigurationProvider extends ProviderAdapter { + + private static final Logger LOG = Loggers.get(CiConfigurationProvider.class); + private static final String PROP_DISABLED = "sonar.ci.autoconfig.disabled"; + + public CiConfiguration provide(Configuration configuration, CiVendor[] ciVendors) { + boolean disabled = configuration.getBoolean(PROP_DISABLED).orElse(false); + if (disabled) { + return new EmptyCiConfiguration(); + } + + List detectedVendors = Arrays.stream(ciVendors) + .filter(CiVendor::isDetected) + .collect(Collectors.toList()); + + if (detectedVendors.size() > 1) { + List names = detectedVendors.stream().map(CiVendor::getName).collect(Collectors.toList()); + throw MessageException.of("Multiple CI environments are detected: " + names + ". Please check environment variables or set property " + PROP_DISABLED + " to true."); + } + + if (detectedVendors.size() == 1) { + CiVendor vendor = detectedVendors.get(0); + LOG.info("Detected {}", vendor.getName()); + return vendor.loadConfiguration(); + } + return new EmptyCiConfiguration(); + } + + private static class EmptyCiConfiguration implements CiConfiguration { + @Override + public Optional getScmRevision() { + return Optional.empty(); + } + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/CiVendor.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/CiVendor.java new file mode 100644 index 00000000000..30aefcd90a0 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/CiVendor.java @@ -0,0 +1,43 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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; + +import org.sonar.api.scanner.ScannerSide; + +@ScannerSide +public interface CiVendor { + + /** + * The display name, for example "Jenkins" + */ + String getName(); + + /** + * Whether the analyser runs in the CI vendor or not. + */ + boolean isDetected(); + + /** + * The configuration guessed by CI vendor. Called only + * if {@link #isDetected()} is true. + */ + CiConfiguration loadConfiguration(); + +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/package-info.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/package-info.java new file mode 100644 index 00000000000..d7a896d0b70 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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. + */ +@ParametersAreNonnullByDefault +package org.sonar.scanner.ci; + +import javax.annotation.ParametersAreNonnullByDefault; diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/vendors/BitbucketPipelines.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/vendors/BitbucketPipelines.java new file mode 100644 index 00000000000..7e86de90b91 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/vendors/BitbucketPipelines.java @@ -0,0 +1,54 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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; + +import static org.apache.commons.lang.StringUtils.isNotEmpty; + +public class BitbucketPipelines implements CiVendor { + + private final System2 system; + + public BitbucketPipelines(System2 system) { + this.system = system; + } + + @Override + public String getName() { + return "Bitbucket Pipelines"; + } + + @Override + public boolean isDetected() { + String ci = system.envVariable("CI"); + String revision = system.envVariable("BITBUCKET_COMMIT"); + return "true".equals(ci) && isNotEmpty(revision); + } + + @Override + public CiConfiguration loadConfiguration() { + String revision = system.envVariable("BITBUCKET_COMMIT"); + return new CiConfigurationImpl(revision); + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/vendors/CirrusCi.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/vendors/CirrusCi.java new file mode 100644 index 00000000000..9e2bc921b13 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/vendors/CirrusCi.java @@ -0,0 +1,62 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.api.utils.log.Loggers; +import org.sonar.scanner.ci.CiConfiguration; +import org.sonar.scanner.ci.CiConfigurationImpl; +import org.sonar.scanner.ci.CiVendor; + +import static org.apache.commons.lang.StringUtils.isEmpty; + +/** + * Support https://cirrus-ci.org/ + * + * Environment variables are documented at https://cirrus-ci.org/guide/writing-tasks/#environment-variables + */ +public class CirrusCi implements CiVendor { + + private static final String PROPERTY_COMMIT = "CIRRUS_CHANGE_IN_REPO"; + private final System2 system; + + public CirrusCi(System2 system) { + this.system = system; + } + + @Override + public String getName() { + return "CirrusCI"; + } + + @Override + public boolean isDetected() { + return "true".equals(system.envVariable("CIRRUS_CI")); + } + + @Override + public CiConfiguration loadConfiguration() { + String revision = system.envVariable(PROPERTY_COMMIT); + if (isEmpty(revision)) { + Loggers.get(getClass()).warn("Missing environment variable " + PROPERTY_COMMIT); + } + return new CiConfigurationImpl(revision); + } +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/vendors/package-info.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/vendors/package-info.java new file mode 100644 index 00000000000..daae39d4d1b --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/ci/vendors/package-info.java @@ -0,0 +1,23 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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. + */ +@ParametersAreNonnullByDefault +package org.sonar.scanner.ci.vendors; + +import javax.annotation.ParametersAreNonnullByDefault; 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 dc79f7262f4..06f20e70e95 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 @@ -23,7 +23,6 @@ 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; @@ -44,6 +43,7 @@ import org.sonar.scanner.rule.QualityProfiles; import org.sonar.scanner.scan.ScanProperties; import org.sonar.scanner.scan.branch.BranchConfiguration; import org.sonar.scanner.scm.ScmConfiguration; +import org.sonar.scanner.scm.ScmRevision; import static java.util.Optional.ofNullable; @@ -58,13 +58,14 @@ public class MetadataPublisher implements ReportPublisherStep { private final CpdSettings cpdSettings; private final ScannerPluginRepository pluginRepository; private final BranchConfiguration branchConfiguration; + private final ScmRevision scmRevision; @Nullable private final ScmConfiguration scmConfiguration; public MetadataPublisher(ProjectInfo projectInfo, InputModuleHierarchy moduleHierarchy, ScanProperties properties, QualityProfiles qProfiles, CpdSettings cpdSettings, ScannerPluginRepository pluginRepository, BranchConfiguration branchConfiguration, - @Nullable ScmConfiguration scmConfiguration) { + ScmRevision scmRevision, @Nullable ScmConfiguration scmConfiguration) { this.projectInfo = projectInfo; this.moduleHierarchy = moduleHierarchy; this.properties = properties; @@ -72,12 +73,13 @@ public class MetadataPublisher implements ReportPublisherStep { this.cpdSettings = cpdSettings; this.pluginRepository = pluginRepository; this.branchConfiguration = branchConfiguration; + this.scmRevision = scmRevision; this.scmConfiguration = scmConfiguration; } public MetadataPublisher(ProjectInfo projectInfo, InputModuleHierarchy moduleHierarchy, ScanProperties properties, - QualityProfiles qProfiles, CpdSettings cpdSettings, ScannerPluginRepository pluginRepository, BranchConfiguration branchConfiguration) { - this(projectInfo, moduleHierarchy, properties, qProfiles, cpdSettings, pluginRepository, branchConfiguration, null); + QualityProfiles qProfiles, CpdSettings cpdSettings, ScannerPluginRepository pluginRepository, BranchConfiguration branchConfiguration, ScmRevision scmRevision) { + this(projectInfo, moduleHierarchy, properties, qProfiles, cpdSettings, pluginRepository, branchConfiguration, scmRevision, null); } @Override @@ -100,9 +102,7 @@ public class MetadataPublisher implements ReportPublisherStep { ofNullable(rootProject.getBranch()).ifPresent(builder::setDeprecatedBranch); - if (scmConfiguration != null) { - addScmInformation(builder); - } + addScmInformation(builder); for (QProfile qp : qProfiles.findAll()) { builder.putQprofilesPerLanguage(qp.getLanguage(), ScannerReport.Metadata.QProfile.newBuilder() @@ -134,33 +134,26 @@ public class MetadataPublisher implements ReportPublisherStep { builder.putModulesProjectRelativePathByKey(module.key(), relativePath); } } - } - private void addScmInformation(ScannerReport.Metadata.Builder builder) { - ScmProvider scmProvider = scmConfiguration.provider(); - if (scmProvider != null) { - Path projectBasedir = moduleHierarchy.root().getBaseDir(); - try { - builder.setRelativePathFromScmRoot(toSonarQubePath(scmProvider.relativePathFromScmRoot(projectBasedir))); - } catch (UnsupportedOperationException e) { - LOG.debug(e.getMessage()); - } - try { - computeScmRevision().ifPresent(builder::setScmRevisionId); - } catch (UnsupportedOperationException e) { - LOG.debug(e.getMessage()); + if (scmConfiguration != null) { + ScmProvider scmProvider = scmConfiguration.provider(); + if (scmProvider != null) { + Path projectBasedir = moduleHierarchy.root().getBaseDir(); + try { + builder.setRelativePathFromScmRoot(toSonarQubePath(scmProvider.relativePathFromScmRoot(projectBasedir))); + } catch (UnsupportedOperationException e) { + LOG.debug(e.getMessage()); + } } } } - private Optional computeScmRevision() { - Optional scmRevision = properties.getScmRevision(); - ScmProvider scmProvider = scmConfiguration.provider(); - if (!scmRevision.isPresent() && scmProvider != null) { - scmRevision = Optional.ofNullable(scmProvider.revisionId(moduleHierarchy.root().getBaseDir())); + private void addScmInformation(ScannerReport.Metadata.Builder builder) { + try { + scmRevision.get().ifPresent(builder::setScmRevisionId); + } catch (UnsupportedOperationException e) { + LOG.debug(e.getMessage()); } - - return scmRevision; } private void addBranchInformation(ScannerReport.Metadata.Builder builder) { diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java index d31dfe56c1b..edb43371cd2 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ProjectScanContainer.java @@ -46,6 +46,9 @@ import org.sonar.scanner.bootstrap.GlobalAnalysisMode; import org.sonar.scanner.bootstrap.MetricProvider; import org.sonar.scanner.bootstrap.PostJobExtensionDictionnary; import org.sonar.scanner.bootstrap.ProcessedScannerProperties; +import org.sonar.scanner.ci.CiConfigurationProvider; +import org.sonar.scanner.ci.vendors.BitbucketPipelines; +import org.sonar.scanner.ci.vendors.CirrusCi; import org.sonar.scanner.cpd.CpdExecutor; import org.sonar.scanner.cpd.CpdSettings; import org.sonar.scanner.cpd.index.SonarCpdBlockIndex; @@ -109,6 +112,7 @@ import org.sonar.scanner.scan.measure.DefaultMetricFinder; import org.sonar.scanner.scm.ScmChangedFilesProvider; import org.sonar.scanner.scm.ScmConfiguration; import org.sonar.scanner.scm.ScmPublisher; +import org.sonar.scanner.scm.ScmRevisionImpl; import org.sonar.scanner.sensor.DefaultSensorStorage; import org.sonar.scanner.sensor.ProjectSensorContext; import org.sonar.scanner.sensor.ProjectSensorExtensionDictionnary; @@ -248,6 +252,7 @@ public class ProjectScanContainer extends ComponentContainer { // SCM ScmConfiguration.class, ScmPublisher.class, + ScmRevisionImpl.class, // Sensors DefaultSensorStorage.class, @@ -260,6 +265,11 @@ public class ProjectScanContainer extends ComponentContainer { // Filesystem DefaultProjectFileSystem.class, + // CI + new CiConfigurationProvider(), + BitbucketPipelines.class, + CirrusCi.class, + AnalysisObservers.class); addIfMissing(DefaultProjectSettingsLoader.class, ProjectSettingsLoader.class); diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ScanProperties.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ScanProperties.java index 36fd9b5d41e..1033292a4b8 100644 --- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ScanProperties.java +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/ScanProperties.java @@ -79,10 +79,6 @@ public class ScanProperties { } } - public Optional getScmRevision() { - return configuration.get(SCM_REVISION); - } - /** * This should be called in the beginning of the analysis to fail fast */ diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmRevision.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmRevision.java new file mode 100644 index 00000000000..28af0127b6f --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmRevision.java @@ -0,0 +1,37 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.scm; + +import java.util.Optional; +import org.sonar.api.scanner.ScannerSide; + +/** + * The SCM revision that triggered the analysis. It may be different than + * the effective revision checked-out on disk, as provided by {@link org.sonar.api.batch.scm.ScmProvider}. + * + * For instance on pull requests it's not the merge-commit that some CI services check out. It is + * the commit that was pushed by user to the branch. + */ +@ScannerSide +public interface ScmRevision { + + Optional get(); + +} diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmRevisionImpl.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmRevisionImpl.java new file mode 100644 index 00000000000..6eb15be6e81 --- /dev/null +++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scm/ScmRevisionImpl.java @@ -0,0 +1,77 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.scm; + +import java.util.Optional; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; +import org.sonar.api.batch.scm.ScmProvider; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers; +import org.sonar.scanner.bootstrap.RawScannerProperties; +import org.sonar.scanner.ci.CiConfiguration; + +import static org.apache.commons.lang.StringUtils.isBlank; +import static org.sonar.scanner.scan.ScanProperties.SCM_REVISION; + +public class ScmRevisionImpl implements ScmRevision { + + private static final Logger LOG = Loggers.get(ScmRevisionImpl.class); + + private final CiConfiguration ciConfiguration; + private final RawScannerProperties scannerConfiguration; + private final ScmConfiguration scmConfiguration; + private final InputModuleHierarchy moduleHierarchy; + + public ScmRevisionImpl(CiConfiguration ciConfiguration, RawScannerProperties scannerConfiguration, ScmConfiguration scmConfiguration, InputModuleHierarchy moduleHierarchy) { + this.ciConfiguration = ciConfiguration; + this.scannerConfiguration = scannerConfiguration; + this.scmConfiguration = scmConfiguration; + this.moduleHierarchy = moduleHierarchy; + } + + @Override + public Optional get() { + Optional revision = Optional.ofNullable(scannerConfiguration.property(SCM_REVISION)); + if (isSet(revision)) { + return revision; + } + revision = ciConfiguration.getScmRevision(); + if (isSet(revision)) { + return revision; + } + ScmProvider scmProvider = scmConfiguration.provider(); + if (scmProvider != null) { + try { + revision = Optional.ofNullable(scmProvider.revisionId(moduleHierarchy.root().getBaseDir())); + } catch (UnsupportedOperationException e) { + LOG.debug(e.getMessage()); + revision = Optional.empty(); + } + } + if (isSet(revision)) { + return revision; + } + return Optional.empty(); + } + + private static boolean isSet(Optional opt) { + return opt.isPresent() && !isBlank(opt.get()); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/ci/CiConfigurationImplTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/ci/CiConfigurationImplTest.java new file mode 100644 index 00000000000..3b17b86508e --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/ci/CiConfigurationImplTest.java @@ -0,0 +1,35 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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; + +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class CiConfigurationImplTest { + + @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"); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/ci/CiConfigurationProviderTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/ci/CiConfigurationProviderTest.java new file mode 100644 index 00000000000..57e1aa0efd4 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/ci/CiConfigurationProviderTest.java @@ -0,0 +1,118 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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; + +import org.junit.Test; +import org.sonar.api.config.internal.MapSettings; +import org.sonar.api.utils.MessageException; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +public class CiConfigurationProviderTest { + + private MapSettings cli = new MapSettings(); + private CiConfigurationProvider underTest = new CiConfigurationProvider(); + + @Test + public void empty_configuration_if_no_ci_vendors() { + CiConfiguration CiConfiguration = underTest.provide(cli.asConfig(), new CiVendor[0]); + + assertThat(CiConfiguration.getScmRevision()).isEmpty(); + } + + @Test + public void empty_configuration_if_no_ci_detected() { + 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")}); + + 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")})); + + assertThat(thrown) + .isInstanceOf(MessageException.class) + .hasMessage("Multiple CI environments are detected: [vendor1, vendor2]. Please check environment variables or set property sonar.ci.autoconfig.disabled to true."); + } + + @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")}); + + assertThat(ciConfiguration.getScmRevision()).isEmpty(); + } + + private static class DisabledCiVendor implements CiVendor { + private final String name; + + private DisabledCiVendor(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean isDetected() { + return false; + } + + @Override + public CiConfiguration loadConfiguration() { + throw new IllegalStateException("should not be called"); + } + } + + private static class EnabledCiVendor implements CiVendor { + private static final String SHA = "abc12df"; + private final String name; + + private EnabledCiVendor(String name) { + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public boolean isDetected() { + return true; + } + + @Override + public CiConfiguration loadConfiguration() { + return new CiConfigurationImpl(SHA); + } + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/ci/vendors/BitbucketPipelinesTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/ci/vendors/BitbucketPipelinesTest.java new file mode 100644 index 00000000000..397c1c43ebe --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/ci/vendors/BitbucketPipelinesTest.java @@ -0,0 +1,73 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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 BitbucketPipelinesTest { + + private System2 system = mock(System2.class); + private CiVendor underTest = new BitbucketPipelines(system); + + @Test + public void getName() { + assertThat(underTest.getName()).isEqualTo("Bitbucket Pipelines"); + } + + @Test + public void isDetected() { + setEnvVariable("CI", "true"); + setEnvVariable("BITBUCKET_COMMIT", "bdf12fe"); + assertThat(underTest.isDetected()).isTrue(); + + setEnvVariable("CI", "true"); + setEnvVariable("BITBUCKET_COMMIT", null); + assertThat(underTest.isDetected()).isFalse(); + } + + @Test + public void configuration_of_pull_request() { + setEnvVariable("CI", "true"); + setEnvVariable("BITBUCKET_COMMIT", "abd12fc"); + setEnvVariable("BITBUCKET_PR_ID", "1234"); + + assertThat(underTest.loadConfiguration().getScmRevision()).hasValue("abd12fc"); + } + + @Test + public void configuration_of_branch() { + setEnvVariable("CI", "true"); + setEnvVariable("BITBUCKET_COMMIT", "abd12fc"); + setEnvVariable("BITBUCKET_PR_ID", null); + + assertThat(underTest.loadConfiguration().getScmRevision()).hasValue("abd12fc"); + } + + private void setEnvVariable(String key, @Nullable String value) { + when(system.envVariable(key)).thenReturn(value); + } +} diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/ci/vendors/CirrusCiTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/ci/vendors/CirrusCiTest.java new file mode 100644 index 00000000000..6e7f988a01f --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/ci/vendors/CirrusCiTest.java @@ -0,0 +1,86 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.Rule; +import org.junit.Test; +import org.sonar.api.utils.System2; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel; +import org.sonar.scanner.ci.CiConfiguration; +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 CirrusCiTest { + + private System2 system = mock(System2.class); + private CiVendor underTest = new CirrusCi(system); + + @Rule + public LogTester logs = new LogTester(); + + @Test + public void getName() { + assertThat(underTest.getName()).isEqualTo("CirrusCI"); + } + + @Test + public void isDetected() { + setEnvVariable("CIRRUS_CI", "true"); + assertThat(underTest.isDetected()).isTrue(); + + setEnvVariable("CIRRUS_CI", null); + assertThat(underTest.isDetected()).isFalse(); + } + + @Test + public void configuration_of_pull_request() { + setEnvVariable("CIRRUS_PR", "1234"); + setEnvVariable("CIRRUS_BASE_SHA", "abd12fc"); + setEnvVariable("CIRRUS_CHANGE_IN_REPO", "fd355db"); + + assertThat(underTest.loadConfiguration().getScmRevision()).hasValue("fd355db"); + } + + @Test + public void configuration_of_branch() { + setEnvVariable("CIRRUS_CHANGE_IN_REPO", "abd12fc"); + + assertThat(underTest.loadConfiguration().getScmRevision()).hasValue("abd12fc"); + } + + @Test + public void log_warning_if_missing_commit_variable() { + setEnvVariable("CIRRUS_PR", "1234"); + + CiConfiguration configuration = underTest.loadConfiguration(); + + assertThat(configuration.getScmRevision()).isEmpty(); + assertThat(logs.logs(LoggerLevel.WARN)).contains("Missing environment variable CIRRUS_CHANGE_IN_REPO"); + } + + private void setEnvVariable(String key, @Nullable String value) { + when(system.envVariable(key)).thenReturn(value); + } +} 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 cf83df2448d..3c1d845f785 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 @@ -56,6 +56,7 @@ import org.sonar.scanner.scan.ScanProperties; import org.sonar.scanner.scan.branch.BranchConfiguration; import org.sonar.scanner.scan.branch.BranchType; import org.sonar.scanner.scm.ScmConfiguration; +import org.sonar.scanner.scm.ScmRevision; import static java.util.Arrays.asList; import static java.util.Collections.emptyMap; @@ -83,6 +84,7 @@ public class MetadataPublisherTest { private BranchConfiguration branches; private ScmConfiguration scmConfiguration; private ScmProvider scmProvider = mock(ScmProvider.class); + private ScmRevision scmRevision = mock(ScmRevision.class); @Before public void prepare() throws IOException { @@ -115,7 +117,7 @@ public class MetadataPublisherTest { scmConfiguration = mock(ScmConfiguration.class); when(scmConfiguration.provider()).thenReturn(scmProvider); underTest = new MetadataPublisher(projectInfo, inputModuleHierarchy, properties, qProfiles, cpdSettings, - pluginRepository, branches, scmConfiguration); + pluginRepository, branches, scmRevision, scmConfiguration); } @Test @@ -294,7 +296,7 @@ public class MetadataPublisherTest { @Test public void write_revision_id() throws Exception { String revisionId = "some-sha1"; - when(scmProvider.revisionId(any(Path.class))).thenReturn(revisionId); + when(scmRevision.get()).thenReturn(Optional.of(revisionId)); File outputDir = temp.newFolder(); underTest.publish(new ScannerReportWriter(outputDir)); @@ -304,58 +306,13 @@ public class MetadataPublisherTest { assertThat(metadata.getScmRevisionId()).isEqualTo(revisionId); } - public void revision_from_scanner_props_overrides_scm_provider_revision_if_specified() throws IOException { - String revisionId = "some-sha1"; - when(scmProvider.revisionId(any(Path.class))).thenReturn(revisionId); - when(properties.getScmRevision()).thenReturn(Optional.of("123")); - - File outputDir = temp.newFolder(); - underTest.publish(new ScannerReportWriter(outputDir)); - - ScannerReportReader reader = new ScannerReportReader(outputDir); - ScannerReport.Metadata metadata = reader.readMetadata(); - assertThat(metadata.getScmRevisionId()).isEqualTo("123"); - } - @Test public void should_not_crash_when_scm_provider_does_not_support_relativePathFromScmRoot() throws IOException { - String revisionId = "some-sha1"; - ScmProvider fakeScmProvider = new ScmProvider() { @Override public String key() { return "foo"; } - - @Override - public String revisionId(Path path) { - return revisionId; - } - }; - when(scmConfiguration.provider()).thenReturn(fakeScmProvider); - - File outputDir = temp.newFolder(); - underTest.publish(new ScannerReportWriter(outputDir)); - - ScannerReportReader reader = new ScannerReportReader(outputDir); - ScannerReport.Metadata metadata = reader.readMetadata(); - assertThat(metadata.getScmRevisionId()).isEqualTo(revisionId); - } - - @Test - public void should_not_crash_when_scm_provider_does_not_support_revisionId() throws IOException { - String relativePathFromScmRoot = "some/path"; - - ScmProvider fakeScmProvider = new ScmProvider() { - @Override - public String key() { - return "foo"; - } - - @Override - public Path relativePathFromScmRoot(Path path) { - return Paths.get(relativePathFromScmRoot); - } }; when(scmConfiguration.provider()).thenReturn(fakeScmProvider); @@ -364,6 +321,6 @@ public class MetadataPublisherTest { ScannerReportReader reader = new ScannerReportReader(outputDir); ScannerReport.Metadata metadata = reader.readMetadata(); - assertThat(metadata.getRelativePathFromScmRoot()).isEqualTo(relativePathFromScmRoot); + assertThat(metadata.getRelativePathFromScmRoot()).isEmpty(); } } diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmRevisionImplTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmRevisionImplTest.java new file mode 100644 index 00000000000..ff6ca35bca7 --- /dev/null +++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/scm/ScmRevisionImplTest.java @@ -0,0 +1,85 @@ +/* + * SonarQube + * Copyright (C) 2009-2019 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.scm; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import javax.annotation.Nullable; +import org.junit.Test; +import org.sonar.api.batch.fs.internal.InputModuleHierarchy; +import org.sonar.scanner.bootstrap.RawScannerProperties; +import org.sonar.scanner.ci.CiConfiguration; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.RETURNS_DEEP_STUBS; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ScmRevisionImplTest { + + @Test + public void test_priority_of_revision_sources() { + assertThat(testGet(null, null, null)).isEmpty(); + assertThat(testGet("1b34d77", null, null)).hasValue("1b34d77"); + assertThat(testGet(null, "1b34d77", null)).hasValue("1b34d77"); + assertThat(testGet(null, null, "1b34d77")).hasValue("1b34d77"); + assertThat(testGet("1b34d77", "f6e62c5", "f6e62c5")).hasValue("1b34d77"); + assertThat(testGet(null, "1b34d77", "f6e62c5")).hasValue("1b34d77"); + } + + @Test + public void test_empty_values() { + assertThat(testGet("", "", "")).isEmpty(); + assertThat(testGet("1b34d77", "", "")).hasValue("1b34d77"); + assertThat(testGet("", "1b34d77", "")).hasValue("1b34d77"); + assertThat(testGet("", "", "1b34d77")).hasValue("1b34d77"); + assertThat(testGet("1b34d77", "f6e62c5", "f6e62c5")).hasValue("1b34d77"); + assertThat(testGet("", "1b34d77", "f6e62c5")).hasValue("1b34d77"); + } + + @Test + public void ignore_failure_if_scm_does_not_support_revisions() { + CiConfiguration ciConfiguration = mock(CiConfiguration.class); + when(ciConfiguration.getScmRevision()).thenReturn(Optional.empty()); + Map scannerConfiguration = new HashMap<>(); + ScmConfiguration scmConfiguration = mock(ScmConfiguration.class, RETURNS_DEEP_STUBS); + when(scmConfiguration.provider().revisionId(any())).thenThrow(new UnsupportedOperationException("BOOM")); + InputModuleHierarchy moduleHierarchy = mock(InputModuleHierarchy.class, RETURNS_DEEP_STUBS); + + ScmRevisionImpl underTest = new ScmRevisionImpl(ciConfiguration, new RawScannerProperties(scannerConfiguration), scmConfiguration, moduleHierarchy); + + assertThat(underTest.get()).isEmpty(); + } + + private Optional testGet(@Nullable String cliValue, @Nullable String ciValue, @Nullable String scmValue) { + CiConfiguration ciConfiguration = mock(CiConfiguration.class); + when(ciConfiguration.getScmRevision()).thenReturn(Optional.ofNullable(ciValue)); + Map scannerConfiguration = new HashMap<>(); + scannerConfiguration.put("sonar.scm.revision", cliValue); + ScmConfiguration scmConfiguration = mock(ScmConfiguration.class, RETURNS_DEEP_STUBS); + when(scmConfiguration.provider().revisionId(any())).thenReturn(scmValue); + InputModuleHierarchy moduleHierarchy = mock(InputModuleHierarchy.class, RETURNS_DEEP_STUBS); + + ScmRevisionImpl underTest = new ScmRevisionImpl(ciConfiguration, new RawScannerProperties(scannerConfiguration), scmConfiguration, moduleHierarchy); + return underTest.get(); + } +} -- 2.39.5