@@ -99,6 +99,5 @@ Key | Description | Default | |||
Key | Description | |||
---|----|--- | |||
`sonar.branch` **![](/images/cross.svg)Deprecated since SQ 6.7** | _The Developer Edition provides fuller-featured branch functionality._ Manage SCM branches. Two branches of the same project are considered to be different projects in SonarQube. As a consequence issues found in a project A in a branch B1 are not linked to issues found for this project A in a branch B2. There is no way to automatically resolve issues from B2 when they are resolved in B1 as again A-B1 & A-B2 are considered separated projects. | |||
`sonar.profile` **![](/images/cross.svg)Deprecated since SQ 4.5** | Override the profile to be used. This should be set on a per-langauge basis through the UI instead. | |||
`sonar.links.scm_dev` **![](/images/cross.svg)Deprecated since SQ 7.1** | Developer connection. | `<scm><developerConnection>` for Maven projects | |||
<!-- /sonarqube --> |
@@ -27,7 +27,6 @@ import java.util.List; | |||
import java.util.Map; | |||
import java.util.function.BinaryOperator; | |||
import java.util.function.Supplier; | |||
import javax.annotation.Nullable; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.scanner.bootstrap.ScannerWsClient; | |||
import org.sonar.scanner.scan.ScanProperties; | |||
@@ -52,20 +51,20 @@ public class DefaultQualityProfileLoader implements QualityProfileLoader { | |||
} | |||
@Override | |||
public List<QualityProfile> loadDefault(@Nullable String profileName) { | |||
public List<QualityProfile> loadDefault() { | |||
StringBuilder url = new StringBuilder(WS_URL + "?defaults=true"); | |||
return handleErrors(profileName, url, () -> "Failed to load the default quality profiles"); | |||
return handleErrors(url, () -> "Failed to load the default quality profiles"); | |||
} | |||
@Override | |||
public List<QualityProfile> load(String projectKey, @Nullable String profileName) { | |||
public List<QualityProfile> load(String projectKey) { | |||
StringBuilder url = new StringBuilder(WS_URL + "?projectKey=").append(encodeForUrl(projectKey)); | |||
return handleErrors(profileName, url, () -> String.format("Failed to load the quality profiles of project '%s'", projectKey)); | |||
return handleErrors(url, () -> String.format("Failed to load the quality profiles of project '%s'", projectKey)); | |||
} | |||
private List<QualityProfile> handleErrors(@Nullable String profileName, StringBuilder url, Supplier<String> errorMsg) { | |||
private List<QualityProfile> handleErrors(StringBuilder url, Supplier<String> errorMsg) { | |||
try { | |||
return loadAndOverrideIfNeeded(profileName, url); | |||
return doLoad(url); | |||
} catch (HttpException e) { | |||
if (e.code() == 404) { | |||
throw MessageException.of(errorMsg.get() + ": " + ScannerWsClient.createErrorMessage(e)); | |||
@@ -78,16 +77,10 @@ public class DefaultQualityProfileLoader implements QualityProfileLoader { | |||
} | |||
} | |||
private List<QualityProfile> loadAndOverrideIfNeeded(@Nullable String profileName, StringBuilder url) throws IOException { | |||
private List<QualityProfile> doLoad(StringBuilder url) throws IOException { | |||
properties.organizationKey().ifPresent(k -> url.append("&organization=").append(encodeForUrl(k))); | |||
Map<String, QualityProfile> result = call(url.toString()); | |||
if (profileName != null) { | |||
StringBuilder urlForName = new StringBuilder(WS_URL + "?profileName="); | |||
urlForName.append(encodeForUrl(profileName)); | |||
properties.organizationKey().ifPresent(k -> urlForName.append("&organization=").append(encodeForUrl(k))); | |||
result.putAll(call(urlForName.toString())); | |||
} | |||
if (result.isEmpty()) { | |||
throw MessageException.of("No quality profiles have been found, you probably don't have any language plugin installed."); | |||
} |
@@ -20,11 +20,10 @@ | |||
package org.sonar.scanner.repository; | |||
import java.util.List; | |||
import javax.annotation.Nullable; | |||
import org.sonarqube.ws.Qualityprofiles.SearchWsResponse.QualityProfile; | |||
public interface QualityProfileLoader { | |||
List<QualityProfile> load(String projectKey, @Nullable String profileName); | |||
List<QualityProfile> load(String projectKey); | |||
List<QualityProfile> loadDefault(@Nullable String profileName); | |||
List<QualityProfile> loadDefault(); | |||
} |
@@ -20,7 +20,6 @@ | |||
package org.sonar.scanner.repository; | |||
import java.util.List; | |||
import javax.annotation.CheckForNull; | |||
import org.picocontainer.injectors.ProviderAdapter; | |||
import org.sonar.api.utils.log.Logger; | |||
import org.sonar.api.utils.log.Loggers; | |||
@@ -39,9 +38,9 @@ public class QualityProfilesProvider extends ProviderAdapter { | |||
List<QualityProfile> profileList; | |||
Profiler profiler = Profiler.create(LOG).startInfo(LOG_MSG); | |||
if (!projectRepositories.exists()) { | |||
profileList = loader.loadDefault(getSonarProfile(props)); | |||
profileList = loader.loadDefault(); | |||
} else { | |||
profileList = loader.load(props.getKeyWithBranch(), getSonarProfile(props)); | |||
profileList = loader.load(props.getKeyWithBranch()); | |||
} | |||
profiler.stopInfo(); | |||
profiles = new QualityProfiles(profileList); | |||
@@ -50,14 +49,4 @@ public class QualityProfilesProvider extends ProviderAdapter { | |||
return profiles; | |||
} | |||
@CheckForNull | |||
private static String getSonarProfile(ProcessedScannerProperties props) { | |||
String profile = props.property(QualityProfiles.SONAR_PROFILE_PROP); | |||
if (profile != null) { | |||
LOG.warn("Ability to set quality profile from command line using '" + QualityProfiles.SONAR_PROFILE_PROP | |||
+ "' is deprecated and will be dropped in a future SonarQube version. Please configure quality profile used by your project on SonarQube server."); | |||
} | |||
return profile; | |||
} | |||
} |
@@ -20,25 +20,18 @@ | |||
package org.sonar.scanner.rule; | |||
import com.google.common.annotations.VisibleForTesting; | |||
import org.apache.commons.lang.StringUtils; | |||
import org.slf4j.Logger; | |||
import org.slf4j.LoggerFactory; | |||
import org.sonar.api.config.Configuration; | |||
import org.sonar.api.utils.MessageException; | |||
import org.sonar.scanner.scan.filesystem.InputComponentStore; | |||
import static org.apache.commons.lang.StringUtils.isNotEmpty; | |||
public class QProfileVerifier { | |||
private static final Logger LOG = LoggerFactory.getLogger(QProfileVerifier.class); | |||
private final Configuration config; | |||
private final InputComponentStore store; | |||
private final QualityProfiles profiles; | |||
public QProfileVerifier(Configuration config, InputComponentStore store, QualityProfiles profiles) { | |||
this.config = config; | |||
public QProfileVerifier(InputComponentStore store, QualityProfiles profiles) { | |||
this.store = store; | |||
this.profiles = profiles; | |||
} | |||
@@ -49,21 +42,13 @@ public class QProfileVerifier { | |||
@VisibleForTesting | |||
void execute(Logger logger) { | |||
String defaultName = config.get(QualityProfiles.SONAR_PROFILE_PROP).orElse(null); | |||
boolean defaultNameUsed = StringUtils.isBlank(defaultName); | |||
for (String lang : store.languages()) { | |||
QProfile profile = profiles.findByLanguage(lang); | |||
if (profile == null) { | |||
logger.warn("No Quality profile found for language {}", lang); | |||
} else { | |||
logger.info("Quality profile for {}: {}", lang, profile.getName()); | |||
if (isNotEmpty(defaultName) && defaultName.equals(profile.getName())) { | |||
defaultNameUsed = true; | |||
} | |||
} | |||
} | |||
if (!defaultNameUsed && !store.languages().isEmpty()) { | |||
throw MessageException.of("sonar.profile was set to '" + defaultName + "' but didn't match any profile for any language. Please check your configuration."); | |||
} | |||
} | |||
} |
@@ -34,7 +34,6 @@ import org.sonarqube.ws.Qualityprofiles.SearchWsResponse.QualityProfile; | |||
@Immutable | |||
public class QualityProfiles { | |||
public static final String SONAR_PROFILE_PROP = "sonar.profile"; | |||
private final Map<String, QProfile> byLanguage; | |||
public QualityProfiles(Collection<QualityProfile> profiles) { |
@@ -446,12 +446,12 @@ public class ScannerMediumTester extends ExternalResource { | |||
} | |||
@Override | |||
public List<QualityProfile> load(String projectKey, String profileName) { | |||
public List<QualityProfile> load(String projectKey) { | |||
return qualityProfiles; | |||
} | |||
@Override | |||
public List<QualityProfile> loadDefault(String profileName) { | |||
public List<QualityProfile> loadDefault() { | |||
return qualityProfiles; | |||
} | |||
} |
@@ -47,52 +47,32 @@ public class DefaultQualityProfileLoaderTest { | |||
private ScanProperties properties = mock(ScanProperties.class); | |||
private DefaultQualityProfileLoader underTest = new DefaultQualityProfileLoader(properties, wsClient); | |||
@Test | |||
public void load_gets_profiles_for_specified_project_and_profile_name() throws IOException { | |||
WsTestUtil.mockStream(wsClient, "/api/qualityprofiles/search.protobuf?projectKey=foo", createStreamOfProfiles("qp")); | |||
WsTestUtil.mockStream(wsClient, "/api/qualityprofiles/search.protobuf?profileName=bar", createStreamOfProfiles("qp")); | |||
underTest.load("foo", "bar"); | |||
verifyCalledPath("/api/qualityprofiles/search.protobuf?projectKey=foo"); | |||
verifyCalledPath("/api/qualityprofiles/search.protobuf?profileName=bar"); | |||
} | |||
@Test | |||
public void load_gets_all_profiles_for_specified_project() throws IOException { | |||
prepareCallWithResults(); | |||
underTest.load("foo", null); | |||
underTest.load("foo"); | |||
verifyCalledPath("/api/qualityprofiles/search.protobuf?projectKey=foo"); | |||
} | |||
@Test | |||
public void load_encodes_url_parameters() throws IOException { | |||
WsTestUtil.mockStream(wsClient, "/api/qualityprofiles/search.protobuf?projectKey=foo%232", createStreamOfProfiles("qp")); | |||
WsTestUtil.mockStream(wsClient, "/api/qualityprofiles/search.protobuf?profileName=bar%232", createStreamOfProfiles("qp")); | |||
underTest.load("foo#2", "bar#2"); | |||
underTest.load("foo#2"); | |||
verifyCalledPath("/api/qualityprofiles/search.protobuf?projectKey=foo%232"); | |||
verifyCalledPath("/api/qualityprofiles/search.protobuf?profileName=bar%232"); | |||
} | |||
@Test | |||
public void load_sets_organization_parameter_if_defined_in_settings() throws IOException { | |||
when(properties.organizationKey()).thenReturn(Optional.of("my-org")); | |||
prepareCallWithResults(); | |||
underTest.load("foo", null); | |||
underTest.load("foo"); | |||
verifyCalledPath("/api/qualityprofiles/search.protobuf?projectKey=foo&organization=my-org"); | |||
} | |||
@Test | |||
public void loadDefault_gets_profiles_with_specified_name() throws IOException { | |||
WsTestUtil.mockStream(wsClient, "/api/qualityprofiles/search.protobuf?defaults=true", createStreamOfProfiles("qp")); | |||
WsTestUtil.mockStream(wsClient, "/api/qualityprofiles/search.protobuf?profileName=foo", createStreamOfProfiles("qp")); | |||
underTest.loadDefault("foo"); | |||
verifyCalledPath("/api/qualityprofiles/search.protobuf?defaults=true"); | |||
verifyCalledPath("/api/qualityprofiles/search.protobuf?profileName=foo"); | |||
} | |||
@Test | |||
public void loadDefault_gets_all_default_profiles() throws IOException { | |||
prepareCallWithResults(); | |||
underTest.loadDefault(null); | |||
underTest.loadDefault(); | |||
verifyCalledPath("/api/qualityprofiles/search.protobuf?defaults=true"); | |||
} | |||
@@ -100,10 +80,8 @@ public class DefaultQualityProfileLoaderTest { | |||
public void loadDefault_sets_organization_parameter_if_defined_in_settings() throws IOException { | |||
when(properties.organizationKey()).thenReturn(Optional.of("my-org")); | |||
WsTestUtil.mockStream(wsClient, "/api/qualityprofiles/search.protobuf?defaults=true&organization=my-org", createStreamOfProfiles("qp")); | |||
WsTestUtil.mockStream(wsClient, "/api/qualityprofiles/search.protobuf?profileName=foo&organization=my-org", createStreamOfProfiles("qp")); | |||
underTest.loadDefault("foo"); | |||
underTest.loadDefault(); | |||
verifyCalledPath("/api/qualityprofiles/search.protobuf?defaults=true&organization=my-org"); | |||
verifyCalledPath("/api/qualityprofiles/search.protobuf?profileName=foo&organization=my-org"); | |||
} | |||
@Test | |||
@@ -113,7 +91,7 @@ public class DefaultQualityProfileLoaderTest { | |||
exception.expect(MessageException.class); | |||
exception.expectMessage("No quality profiles"); | |||
underTest.load("project", null); | |||
underTest.load("project"); | |||
verifyNoMoreInteractions(wsClient); | |||
} | |||
@@ -125,7 +103,7 @@ public class DefaultQualityProfileLoaderTest { | |||
exception.expect(MessageException.class); | |||
exception.expectMessage("Failed to load the quality profiles of project 'project': No organization with key 'myorg'"); | |||
underTest.load("project", null); | |||
underTest.load("project"); | |||
verifyNoMoreInteractions(wsClient); | |||
} | |||
@@ -72,55 +72,46 @@ public class QualityProfileProviderTest { | |||
@Test | |||
public void testProvide() { | |||
when(loader.load("project", null)).thenReturn(response); | |||
when(loader.load("project")).thenReturn(response); | |||
QualityProfiles qps = qualityProfileProvider.provide(loader, projectRepo, props); | |||
assertResponse(qps); | |||
verify(loader).load("project", null); | |||
verify(loader).load("project"); | |||
verifyNoMoreInteractions(loader); | |||
} | |||
@Test | |||
public void testProjectDoesntExist() { | |||
when(projectRepo.exists()).thenReturn(false); | |||
when(loader.loadDefault(anyString())).thenReturn(response); | |||
when(props.property(QualityProfiles.SONAR_PROFILE_PROP)).thenReturn("profile"); | |||
when(loader.loadDefault()).thenReturn(response); | |||
QualityProfiles qps = qualityProfileProvider.provide(loader, projectRepo, props); | |||
assertResponse(qps); | |||
verify(loader).loadDefault(anyString()); | |||
verify(loader).loadDefault(); | |||
verifyNoMoreInteractions(loader); | |||
} | |||
@Test | |||
public void testProfileProp() { | |||
when(loader.load(eq("project"), eq("custom"))).thenReturn(response); | |||
when(props.property(QualityProfiles.SONAR_PROFILE_PROP)).thenReturn("custom"); | |||
when(props.properties()).thenReturn(ImmutableMap.of(QualityProfiles.SONAR_PROFILE_PROP, "custom")); | |||
when(loader.load(eq("project"))).thenReturn(response); | |||
QualityProfiles qps = qualityProfileProvider.provide(loader, projectRepo, props); | |||
assertResponse(qps); | |||
verify(loader).load(eq("project"), eq("custom")); | |||
verify(loader).load(eq("project")); | |||
verifyNoMoreInteractions(loader); | |||
assertThat(logTester.logs(LoggerLevel.WARN)).contains("Ability to set quality profile from command line using '" + QualityProfiles.SONAR_PROFILE_PROP | |||
+ "' is deprecated and will be dropped in a future SonarQube version. Please configure quality profile used by your project on SonarQube server."); | |||
} | |||
@Test | |||
public void testProfilePropDefault() { | |||
when(projectRepo.exists()).thenReturn(false); | |||
when(loader.loadDefault(eq("custom"))).thenReturn(response); | |||
when(props.property(QualityProfiles.SONAR_PROFILE_PROP)).thenReturn("custom"); | |||
when(props.properties()).thenReturn(ImmutableMap.of(QualityProfiles.SONAR_PROFILE_PROP, "custom")); | |||
when(loader.loadDefault()).thenReturn(response); | |||
QualityProfiles qps = qualityProfileProvider.provide(loader, projectRepo, props); | |||
assertResponse(qps); | |||
verify(loader).loadDefault(eq("custom")); | |||
verify(loader).loadDefault(); | |||
verifyNoMoreInteractions(loader); | |||
assertThat(logTester.logs(LoggerLevel.WARN)).contains("Ability to set quality profile from command line using '" + QualityProfiles.SONAR_PROFILE_PROP | |||
+ "' is deprecated and will be dropped in a future SonarQube version. Please configure quality profile used by your project on SonarQube server."); | |||
} | |||
private void assertResponse(QualityProfiles qps) { |
@@ -41,7 +41,6 @@ public class QProfileVerifierTest { | |||
private InputComponentStore store; | |||
private QualityProfiles profiles; | |||
private MapSettings settings = new MapSettings(); | |||
@Before | |||
public void before() throws Exception { | |||
@@ -58,7 +57,7 @@ public class QProfileVerifierTest { | |||
store.put("foo", new TestInputFileBuilder("foo", "src/Bar.java").setLanguage("java").build()); | |||
store.put("foo", new TestInputFileBuilder("foo", "src/Baz.cbl").setLanguage("cobol").build()); | |||
QProfileVerifier profileLogger = new QProfileVerifier(settings.asConfig(), store, profiles); | |||
QProfileVerifier profileLogger = new QProfileVerifier(store, profiles); | |||
Logger logger = mock(Logger.class); | |||
profileLogger.execute(logger); | |||
@@ -66,37 +65,18 @@ public class QProfileVerifierTest { | |||
verify(logger).info("Quality profile for {}: {}", "cobol", "My Cobol profile"); | |||
} | |||
@Test | |||
public void should_fail_if_default_profile_not_used() { | |||
store.put("foo", new TestInputFileBuilder("foo", "src/Bar.java").setLanguage("java").build()); | |||
settings.setProperty("sonar.profile", "Unknown"); | |||
QProfileVerifier profileLogger = new QProfileVerifier(settings.asConfig(), store, profiles); | |||
thrown.expect(MessageException.class); | |||
thrown.expectMessage("sonar.profile was set to 'Unknown' but didn't match any profile for any language. Please check your configuration."); | |||
profileLogger.execute(); | |||
} | |||
@Test | |||
public void should_not_fail_if_no_language_on_project() { | |||
settings.setProperty("sonar.profile", "Unknown"); | |||
QProfileVerifier profileLogger = new QProfileVerifier(settings.asConfig(), store, profiles); | |||
QProfileVerifier profileLogger = new QProfileVerifier(store, profiles); | |||
profileLogger.execute(); | |||
} | |||
@Test | |||
public void should_not_fail_if_default_profile_used_at_least_once() { | |||
store.put("foo", new TestInputFileBuilder("foo", "src/Bar.java").setLanguage("java").build()); | |||
settings.setProperty("sonar.profile", "My Java profile"); | |||
QProfileVerifier profileLogger = new QProfileVerifier(settings.asConfig(), store, profiles); | |||
QProfileVerifier profileLogger = new QProfileVerifier(store, profiles); | |||
profileLogger.execute(); | |||
} |
@@ -282,7 +282,6 @@ public class ProjectReactorBuilderTest { | |||
ProjectDefinition projectDefinition = loadProjectDefinition("big-multi-module-definitions-all-in-root"); | |||
assertThat(projectDefinition.properties().get("module11.property")).isNull(); | |||
assertThat(projectDefinition.properties().get("sonar.profile")).isEqualTo("Foo"); | |||
ProjectDefinition module1 = null; | |||
ProjectDefinition module2 = null; | |||
for (ProjectDefinition prj : projectDefinition.getSubProjects()) { | |||
@@ -294,10 +293,8 @@ public class ProjectReactorBuilderTest { | |||
} | |||
assertThat(module1.properties().get("module11.property")).isNull(); | |||
assertThat(module1.properties().get("property")).isNull(); | |||
assertThat(module1.properties().get("sonar.profile")).isEqualTo("Foo"); | |||
assertThat(module2.properties().get("module11.property")).isNull(); | |||
assertThat(module2.properties().get("property")).isNull(); | |||
assertThat(module2.properties().get("sonar.profile")).isEqualTo("Foo"); | |||
ProjectDefinition module11 = null; | |||
ProjectDefinition module12 = null; | |||
@@ -311,10 +308,8 @@ public class ProjectReactorBuilderTest { | |||
assertThat(module11.properties().get("module1.module11.property")).isNull(); | |||
assertThat(module11.properties().get("module11.property")).isNull(); | |||
assertThat(module11.properties().get("property")).isEqualTo("My module11 property"); | |||
assertThat(module11.properties().get("sonar.profile")).isEqualTo("Foo"); | |||
assertThat(module12.properties().get("module11.property")).isNull(); | |||
assertThat(module12.properties().get("property")).isNull(); | |||
assertThat(module12.properties().get("sonar.profile")).isEqualTo("Foo"); | |||
} | |||
@Test |
@@ -6,7 +6,6 @@ sonar.projectDescription=Description of Foo Project | |||
sonar.sources=sources | |||
sonar.tests=tests | |||
sonar.binaries=target/classes | |||
sonar.profile=Foo | |||
sonar.modules=module1,module2 | |||