## Analyzing with Pipelines
-SonarCloud integrates with Bitbucket Pipelines to make it easier to trigger analyses. Follow the steps:
+SonarCloud integrates with Bitbucket Pipelines to make it easier to trigger analyses. Follow these steps:
-1. On SonarCloud, once your project is created, follow the tutorial on the dashboard of the project. You can copy-paste the command line displayed at the end.
+1. On SonarCloud, once your project is created, follow the tutorial on the dashboard of the project. Copy-paste the command line displayed at the end but without the `sonar.login` setting.
-2. On Bitbucket Cloud, go to the "Settings > Pipelines > Environment variables" page of your team, and add a new SONAR_TOKEN variable that contains the value of the SonarCloud token (something like `9ad01c85336b265406fa6554a9a681a4b281135f`) which you created during the tutorial (and which is available inside the command line that you copy-pasted). **Make sure that you click on the "Lock" icon to encrypt and hide this token.**
+2. On Bitbucket Cloud, go to the "Settings > Pipelines > Account variables" page of your team, and add a new SONAR_TOKEN variable that contains the value of the SonarCloud token which you created during the tutorial (something like `9ad01c85336b265406fa6554a9a681a4b281135f`). **Make sure that you click on the "Lock" icon to encrypt and hide this token.**
-3. Inside the `bitbucket-pipelines.yml` file of your repository, copy the command line provided by the tutorial and replace the actual token by its variable name. For example, for a Java Maven-based project, you should have something like:
+3. Inside the `bitbucket-pipelines.yml` file of your repository paste the command you copied in step 1. For example, for a Java Maven-based project, you should have something like:
```
script:
- -mvn sonar:sonar -Dsonar.host.url=https://sonarcloud.io -Dsonar.projectKey=my-project -Dsonar.organization=my-team-org -Dsonar.login=$SONAR_TOKEN
+ -mvn sonar:sonar -Dsonar.host.url=https://sonarcloud.io -Dsonar.projectKey=my-project -Dsonar.organization=my-team-org
```
-When this change on `bitbucket-pipelines.yml` is committed and pushed, Pipelines should automatically run a new build and therefore trigger the analysis of the repository. Shortly after, your project will appear on SonarCloud in your organization.
+When this change on `bitbucket-pipelines.yml` is committed and pushed, Pipelines should automatically run a new build and therefore trigger the analysis of the repository. Shortly after, your project will appear on SonarCloud in your organization.
-From now on, everytime Pipelines triggers a build, SonarCloud will:
+## Analyzing pull requests with Pipelines
-* Analyze every new branch that contains the change on the `bitbucket-pipelines.yml` file.
-* Analyze and decorate every pull request based on such a branch.
+In order to trigger SonarCloud analysis on each pull request update, you have to supply the copied command in `pull-requests` section of `bitbucket-pipelines.yml` (check [Configure bitbucket-pipelines.yml](https://confluence.atlassian.com/bitbucket/configure-bitbucket-pipelines-yml-792298910.html#Configurebitbucket-pipelines.yml-ci_pull-requests) for more details about that section). Here is a sample configuration:
+```
+pipelines:
+ ...
+ pull-requests:
+ feature/*:
+ - step:
+ script:
+ - mvn -B verify sonar:sonar -Dsonar.host.url=https://sonarcloud.io -Dsonar.projectKey=... -Dsonar.organization=...
+ ...
+```
## Quality widget
import org.picocontainer.injectors.ProviderAdapter;
import org.sonar.api.CoreProperties;
import org.sonar.api.batch.ScannerSide;
+import org.sonar.api.utils.System2;
import org.sonar.batch.bootstrapper.EnvironmentInformation;
import org.sonarqube.ws.client.HttpConnector;
import org.sonarqube.ws.client.WsClientFactories;
private ScannerWsClient wsClient;
- public synchronized ScannerWsClient provide(final GlobalProperties settings, final EnvironmentInformation env, GlobalAnalysisMode globalMode) {
+ public synchronized ScannerWsClient provide(final GlobalProperties settings, final EnvironmentInformation env,
+ GlobalAnalysisMode globalMode, System2 system) {
if (wsClient == null) {
String url = defaultIfBlank(settings.property("sonar.host.url"), CoreProperties.SERVER_BASE_URL_DEFAULT_VALUE);
HttpConnector.Builder connectorBuilder = HttpConnector.newBuilder();
String timeoutSec = defaultIfBlank(settings.property(READ_TIMEOUT_SEC_PROPERTY), valueOf(DEFAULT_READ_TIMEOUT_SEC));
- String login = defaultIfBlank(settings.property(CoreProperties.LOGIN), null);
+ String token = defaultIfBlank(system.envVariable("SONAR_TOKEN"), null);
+ String login = defaultIfBlank(settings.property(CoreProperties.LOGIN), token);
connectorBuilder
.readTimeoutMilliseconds(parseInt(timeoutSec) * 1_000)
.connectTimeoutMilliseconds(CONNECT_TIMEOUT_MS)
private void writeEnvVariables(BufferedWriter fileWriter) throws IOException {
fileWriter.append("Environment variables:\n");
Map<String, String> envVariables = system.envVariables();
- for (String env : new TreeSet<>(envVariables.keySet())) {
- fileWriter.append(String.format(KEY_VALUE_FORMAT, env, envVariables.get(env))).append('\n');
- }
+ new TreeSet<>(envVariables.keySet())
+ .forEach(envKey -> {
+ try {
+ String envValue = isSensitiveEnvVariable(envKey) ? "******" : envVariables.get(envKey);
+ fileWriter.append(String.format(KEY_VALUE_FORMAT, envKey, envValue)).append('\n');
+ } catch (IOException e) {
+ throw new IllegalStateException(e);
+ }
+ });
}
private void writeGlobalSettings(BufferedWriter fileWriter) throws IOException {
}
private static void dumpPropIfNotSensitive(BufferedWriter fileWriter, String prop, String value) throws IOException {
- fileWriter.append(String.format(KEY_VALUE_FORMAT, prop, sensitive(prop) ? "******" : StringUtils.abbreviate(value, MAX_WIDTH))).append('\n');
+ fileWriter.append(String.format(KEY_VALUE_FORMAT, prop, isSensitiveProperty(prop) ? "******" : StringUtils.abbreviate(value, MAX_WIDTH))).append('\n');
}
/**
return propKey.startsWith(ENV_PROP_PREFIX) && system.envVariables().containsKey(propKey.substring(ENV_PROP_PREFIX.length()));
}
- private static boolean sensitive(String key) {
- return key.equals(CoreProperties.LOGIN) || key.contains(".password") || key.contains(".secured");
+ private static boolean isSensitiveEnvVariable(String key) {
+ return key.contains("_TOKEN") || key.contains("_PASSWORD") || key.contains("_SECURED");
+ }
+
+ private static boolean isSensitiveProperty(String key) {
+ return key.equals(CoreProperties.LOGIN) || key.contains(".password") || key.contains(".secured") || key.contains(".token");
}
}
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
+import org.sonar.api.utils.System2;
import org.sonar.batch.bootstrapper.EnvironmentInformation;
import org.sonarqube.ws.client.HttpConnector;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
public class ScannerWsClientProviderTest {
public void provide_client_with_default_settings() {
GlobalProperties settings = new GlobalProperties(new HashMap<>());
- ScannerWsClient client = underTest.provide(settings, env, new GlobalAnalysisMode(new GlobalProperties(Collections.emptyMap())));
+ ScannerWsClient client = underTest.provide(settings, env, new GlobalAnalysisMode(new GlobalProperties(Collections.emptyMap())), mock(System2.class));
assertThat(client).isNotNull();
assertThat(client.baseUrl()).isEqualTo("http://localhost:9000/");
props.put("sonar.ws.timeout", "42");
GlobalProperties settings = new GlobalProperties(props);
- ScannerWsClient client = underTest.provide(settings, env, new GlobalAnalysisMode(new GlobalProperties(Collections.emptyMap())));
+ ScannerWsClient client = underTest.provide(settings, env, new GlobalAnalysisMode(new GlobalProperties(Collections.emptyMap())), mock(System2.class));
assertThat(client).isNotNull();
HttpConnector httpConnector = (HttpConnector) client.wsConnector();
@Test
public void build_singleton() {
+ System2 system = mock(System2.class);
+
GlobalProperties settings = new GlobalProperties(new HashMap<>());
- ScannerWsClient first = underTest.provide(settings, env, new GlobalAnalysisMode(new GlobalProperties(Collections.emptyMap())));
- ScannerWsClient second = underTest.provide(settings, env, new GlobalAnalysisMode(new GlobalProperties(Collections.emptyMap())));
+ ScannerWsClient first = underTest.provide(settings, env, new GlobalAnalysisMode(new GlobalProperties(Collections.emptyMap())), system);
+ ScannerWsClient second = underTest.provide(settings, env, new GlobalAnalysisMode(new GlobalProperties(Collections.emptyMap())), system);
assertThat(first).isSameAs(second);
}
}