--- /dev/null
+/*
+ * 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<String> getScmRevision();
+
+}
--- /dev/null
+/*
+ * 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<String> getScmRevision() {
+ return Optional.ofNullable(scmRevision);
+ }
+}
--- /dev/null
+/*
+ * 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<CiVendor> detectedVendors = Arrays.stream(ciVendors)
+ .filter(CiVendor::isDetected)
+ .collect(Collectors.toList());
+
+ if (detectedVendors.size() > 1) {
+ List<String> 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<String> getScmRevision() {
+ return Optional.empty();
+ }
+ }
+}
--- /dev/null
+/*
+ * 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();
+
+}
--- /dev/null
+/*
+ * 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;
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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;
import java.nio.file.Path;
import java.util.LinkedList;
import java.util.Map.Entry;
-import java.util.Optional;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import org.sonar.api.batch.fs.internal.AbstractProjectOrModule;
import org.sonar.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;
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;
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
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()
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<String> computeScmRevision() {
- Optional<String> 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) {
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;
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;
// SCM
ScmConfiguration.class,
ScmPublisher.class,
+ ScmRevisionImpl.class,
// Sensors
DefaultSensorStorage.class,
// Filesystem
DefaultProjectFileSystem.class,
+ // CI
+ new CiConfigurationProvider(),
+ BitbucketPipelines.class,
+ CirrusCi.class,
+
AnalysisObservers.class);
addIfMissing(DefaultProjectSettingsLoader.class, ProjectSettingsLoader.class);
}
}
- public Optional<String> getScmRevision() {
- return configuration.get(SCM_REVISION);
- }
-
/**
* This should be called in the beginning of the analysis to fail fast
*/
--- /dev/null
+/*
+ * 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<String> get();
+
+}
--- /dev/null
+/*
+ * 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<String> get() {
+ Optional<String> 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<String> opt) {
+ return opt.isPresent() && !isBlank(opt.get());
+ }
+}
--- /dev/null
+/*
+ * 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");
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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);
+ }
+}
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;
private BranchConfiguration branches;
private ScmConfiguration scmConfiguration;
private ScmProvider scmProvider = mock(ScmProvider.class);
+ private ScmRevision scmRevision = mock(ScmRevision.class);
@Before
public void prepare() throws IOException {
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
@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));
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);
ScannerReportReader reader = new ScannerReportReader(outputDir);
ScannerReport.Metadata metadata = reader.readMetadata();
- assertThat(metadata.getRelativePathFromScmRoot()).isEqualTo(relativePathFromScmRoot);
+ assertThat(metadata.getRelativePathFromScmRoot()).isEmpty();
}
}
--- /dev/null
+/*
+ * 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<String,String> 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<String> testGet(@Nullable String cliValue, @Nullable String ciValue, @Nullable String scmValue) {
+ CiConfiguration ciConfiguration = mock(CiConfiguration.class);
+ when(ciConfiguration.getScmRevision()).thenReturn(Optional.ofNullable(ciValue));
+ Map<String,String> 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();
+ }
+}