Skip to content

Commit 754c3c1

Browse files
SCANJLIB-244 Log server type (label) info on bootstrap
1 parent f545139 commit 754c3c1

7 files changed

Lines changed: 155 additions & 5 deletions

File tree

lib/src/main/java/org/sonarsource/scanner/lib/ScannerEngineBootstrapper.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,14 +138,18 @@ public ScannerEngineBootstrapResult bootstrap() {
138138
scannerHttpClient.init(httpConfig);
139139

140140
var serverVersion = !isSonarCloud ? getServerVersion(scannerHttpClient) : null;
141+
ScannerEngineFacade scannerFacade;
141142
if (isSonarCloud || VersionUtils.isAtLeastIgnoringQualifier(serverVersion, SQ_VERSION_NEW_BOOTSTRAPPING)) {
142143
var launcher = scannerEngineLauncherFactory.createLauncher(scannerHttpClient, fileCache, immutableProperties);
143-
return new SuccessfulBootstrap(new NewScannerEngineFacade(immutableProperties, launcher, isSonarCloud, serverVersion));
144+
scannerFacade = new NewScannerEngineFacade(immutableProperties, launcher, isSonarCloud, serverVersion);
144145
} else {
145146
var launcher = launcherFactory.createLauncher(scannerHttpClient, fileCache);
146147
var adaptedProperties = adaptDeprecatedPropertiesForInProcessBootstrapping(immutableProperties, httpConfig);
147-
return new SuccessfulBootstrap(new InProcessScannerEngineFacade(adaptedProperties, launcher, false, serverVersion));
148+
scannerFacade = new InProcessScannerEngineFacade(adaptedProperties, launcher, false, serverVersion);
148149
}
150+
151+
logServerType(scannerFacade);
152+
return new SuccessfulBootstrap(scannerFacade);
149153
} catch (MessageException e) {
150154
return handleException(e);
151155
}
@@ -182,6 +186,13 @@ private static void logWithStacktraceOnlyIfDebug(String message, Throwable t) {
182186
}
183187
}
184188

189+
private static void logServerType(ScannerEngineFacade engine) {
190+
if (engine.isSonarCloud()) {
191+
LOG.info("Communicating with SonarQube Cloud");
192+
} else {
193+
LOG.info("Communicating with {} {}", engine.getServerLabel(), engine.getServerVersion());
194+
}
195+
}
185196

186197
/**
187198
* Older SonarQube versions used to rely on some different properties, or even {@link System} properties.

lib/src/main/java/org/sonarsource/scanner/lib/ScannerEngineFacade.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,11 @@ public interface ScannerEngineFacade extends AutoCloseable {
4848
* @return true if the analysis succeeded, false otherwise.
4949
*/
5050
boolean analyze(Map<String, String> analysisProps);
51+
52+
/**
53+
* Get the label of the server that the scanner is connected to. Distinguishes SonarQube Cloud, SonarQube Server and SonarQube Community Build.
54+
*
55+
* @return whether scanner is connected to SonarQube Cloud, Server or Community Build.
56+
*/
57+
String getServerLabel();
5158
}

lib/src/main/java/org/sonarsource/scanner/lib/internal/facade/AbstractScannerEngineFacade.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import javax.annotation.Nullable;
2525
import org.sonarsource.scanner.lib.ScannerEngineFacade;
2626
import org.sonarsource.scanner.lib.internal.facade.forked.JreCacheHit;
27+
import org.sonarsource.scanner.lib.internal.util.VersionUtils;
2728

2829
public abstract class AbstractScannerEngineFacade implements ScannerEngineFacade {
2930

@@ -82,4 +83,17 @@ private static void initAnalysisProperties(Map<String, String> p) {
8283
public Map<String, String> getBootstrapProperties() {
8384
return bootstrapProperties;
8485
}
86+
87+
@Override
88+
public String getServerLabel() {
89+
if (isSonarCloud()) {
90+
return "SonarQube Cloud";
91+
}
92+
93+
String version = getServerVersion();
94+
if (VersionUtils.compareMajor(version, 10) <= 0 || VersionUtils.compareMajor(version, 2025) >= 0) {
95+
return "SonarQube Server";
96+
}
97+
return "SonarQube Community Build";
98+
}
8599
}

lib/src/main/java/org/sonarsource/scanner/lib/internal/util/VersionUtils.java

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,19 @@
2020
package org.sonarsource.scanner.lib.internal.util;
2121

2222
import java.lang.module.ModuleDescriptor.Version;
23+
import java.util.regex.Pattern;
2324
import javax.annotation.Nullable;
2425
import org.apache.commons.lang3.StringUtils;
2526

27+
import static org.apache.commons.lang3.StringUtils.substringAfter;
28+
import static org.apache.commons.lang3.StringUtils.substringBefore;
29+
import static org.apache.commons.lang3.StringUtils.trimToEmpty;
30+
2631
public class VersionUtils {
32+
33+
private static final String SEQUENCE_SEPARATOR = ".";
34+
private static final String QUALIFIER_SEPARATOR = "-";
35+
2736
private VersionUtils() {
2837
// only util static methods
2938
}
@@ -36,9 +45,32 @@ private VersionUtils() {
3645
* @return true if the version is at least the target version
3746
*/
3847
public static boolean isAtLeastIgnoringQualifier(@Nullable String version, String targetVersion) {
39-
if (StringUtils.isBlank(version) || String.valueOf(version.charAt(0)).matches("\\D")) {
48+
if (isOfUnexpectedFormat(version)) {
4049
return false;
4150
}
4251
return Version.parse(StringUtils.substringBefore(version, "-")).compareTo(Version.parse(targetVersion)) >= 0;
4352
}
53+
54+
public static int compareMajor(@Nullable String version, int number) {
55+
if (isOfUnexpectedFormat(version)) {
56+
return Integer.compare(0, number);
57+
}
58+
59+
String s = trimToEmpty(version);
60+
String qualifier = substringAfter(s, QUALIFIER_SEPARATOR);
61+
if (!qualifier.isEmpty()) {
62+
s = substringBefore(s, QUALIFIER_SEPARATOR);
63+
}
64+
65+
String[] fields = s.split(Pattern.quote(SEQUENCE_SEPARATOR));
66+
try {
67+
return Integer.compare(Integer.parseInt(fields[0]), number);
68+
} catch (NumberFormatException e) {
69+
return Integer.compare(0, number);
70+
}
71+
}
72+
73+
private static boolean isOfUnexpectedFormat(@Nullable String version) {
74+
return StringUtils.isBlank(version) || String.valueOf(version.trim().charAt(0)).matches("\\D");
75+
}
4476
}

lib/src/test/java/org/sonarsource/scanner/lib/ScannerEngineBootstrapperTest.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.util.HashMap;
3232
import java.util.Map;
3333
import java.util.Properties;
34+
import java.util.stream.Collectors;
3435
import org.junit.jupiter.api.BeforeEach;
3536
import org.junit.jupiter.api.Test;
3637
import org.junit.jupiter.api.extension.RegisterExtension;
@@ -99,6 +100,7 @@ void should_use_new_bootstrapping_with_default_url() throws Exception {
99100
try (var bootstrapResult = underTest.bootstrap()) {
100101
verify(scannerEngineLauncherFactory).createLauncher(eq(scannerHttpClient), any(FileCache.class), anyMap());
101102
assertThat(bootstrapResult.getEngineFacade().isSonarCloud()).isTrue();
103+
verifyCloudServerTypeLogged();
102104
}
103105
}
104106

@@ -108,6 +110,7 @@ void should_use_new_bootstrapping_with_sonarcloud_url() throws Exception {
108110
verify(scannerEngineLauncherFactory).createLauncher(eq(scannerHttpClient), any(FileCache.class), anyMap());
109111
assertThat(bootstrapResult.isSuccessful()).isTrue();
110112
assertThat(bootstrapResult.getEngineFacade().isSonarCloud()).isTrue();
113+
verifyCloudServerTypeLogged();
111114
}
112115
}
113116

@@ -117,10 +120,23 @@ void should_use_new_bootstrapping_with_sonarqube_10_6() throws Exception {
117120
try (var bootstrapResult = underTest.setBootstrapProperty(ScannerProperties.HOST_URL, "http://localhost").bootstrap()) {
118121
verify(scannerEngineLauncherFactory).createLauncher(eq(scannerHttpClient), any(FileCache.class), anyMap());
119122
assertThat(bootstrapResult.getEngineFacade().isSonarCloud()).isFalse();
123+
verifySonarQubeServerTypeLogged(SQ_VERSION_NEW_BOOTSTRAPPING);
120124
assertThat(bootstrapResult.getEngineFacade().getServerVersion()).isEqualTo(SQ_VERSION_NEW_BOOTSTRAPPING);
121125
}
122126
}
123127

128+
@Test
129+
void should_log_cb_server_type() throws Exception {
130+
String testCBVersion = "24.12.0.23452";
131+
when(scannerHttpClient.callRestApi("/analysis/version")).thenReturn(testCBVersion);
132+
try (var bootstrapResult = underTest.setBootstrapProperty(ScannerProperties.HOST_URL, "http://localhost").bootstrap()) {
133+
verify(scannerEngineLauncherFactory).createLauncher(eq(scannerHttpClient), any(FileCache.class), anyMap());
134+
assertThat(bootstrapResult.getEngineFacade().isSonarCloud()).isFalse();
135+
assertThat(logTester.logs(Level.INFO)).contains("Communicating with SonarQube Community Build ".concat(testCBVersion));
136+
assertThat(bootstrapResult.getEngineFacade().getServerVersion()).isEqualTo(testCBVersion);
137+
}
138+
}
139+
124140
@Test
125141
void should_use_old_bootstrapping_with_sonarqube_10_5() throws Exception {
126142
IsolatedLauncherFactory launcherFactory = mock(IsolatedLauncherFactory.class);
@@ -136,6 +152,7 @@ void should_use_old_bootstrapping_with_sonarqube_10_5() throws Exception {
136152
verify(launcherFactory).createLauncher(eq(scannerHttpClient), any(FileCache.class));
137153
assertThat(bootstrapResult.getEngineFacade().isSonarCloud()).isFalse();
138154
assertThat(bootstrapResult.getEngineFacade().getServerVersion()).isEqualTo("10.5");
155+
verifySonarQubeServerTypeLogged("10.5");
139156
}
140157
}
141158

@@ -155,7 +172,9 @@ void should_show_help_on_proxy_auth_error() throws Exception {
155172
assertThat(result.isSuccessful()).isFalse();
156173
}
157174

158-
assertThat(logTester.logs(Level.ERROR)).contains("Failed to query server version: Proxy Authentication Required. Please check the properties sonar.scanner.proxyUser and sonar.scanner.proxyPassword.");
175+
assertThat(logTester.logs(Level.ERROR)).contains("Failed to query server version: Proxy Authentication Required. Please check the properties sonar.scanner.proxyUser and " +
176+
"sonar.scanner.proxyPassword.");
177+
assertThatNoServerTypeIsLogged();
159178
}
160179

161180
@Test
@@ -182,6 +201,7 @@ void should_preserve_both_exceptions_when_checking_version() throws Exception {
182201
.containsSubsequence(
183202
"Suppressed: org.sonarsource.scanner.lib.internal.http.HttpException: Not Found",
184203
"Caused by: org.sonarsource.scanner.lib.internal.http.HttpException: Server Error");
204+
assertThatNoServerTypeIsLogged();
185205
}
186206

187207

@@ -201,7 +221,9 @@ void should_log_user_friendly_message_when_auth_error(int code) throws Exception
201221
assertThat(result.isSuccessful()).isFalse();
202222
}
203223

204-
assertThat(logTester.logs(Level.ERROR)).contains("Failed to query server version: Unauthorized. Please check the property sonar.token or the environment variable SONAR_TOKEN.");
224+
assertThat(logTester.logs(Level.ERROR)).contains("Failed to query server version: Unauthorized. Please check the property sonar.token or the environment variable SONAR_TOKEN" +
225+
".");
226+
assertThatNoServerTypeIsLogged();
205227
}
206228

207229
@Test
@@ -430,4 +452,16 @@ void should_not_change_ssl_properties_if_already_set_as_scanner_props(@TempDir P
430452
assertThat(mutableProps).containsExactlyInAnyOrderEntriesOf(properties);
431453
}
432454

455+
private void verifyCloudServerTypeLogged() {
456+
assertThat(logTester.logs(Level.INFO)).contains("Communicating with SonarQube Cloud");
457+
}
458+
459+
private void verifySonarQubeServerTypeLogged(String version) {
460+
assertThat(logTester.logs(Level.INFO)).contains("Communicating with SonarQube Server ".concat(version));
461+
}
462+
463+
private void assertThatNoServerTypeIsLogged() {
464+
assertThat(logTester.logs(Level.INFO).stream().filter(logMessage -> logMessage.startsWith("Communicating with")).collect(Collectors.toList())).isEmpty();
465+
}
466+
433467
}

lib/src/test/java/org/sonarsource/scanner/lib/internal/facade/simulation/SimulationScannerEngineFacadeTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,13 @@
2525
import java.util.HashMap;
2626
import java.util.Map;
2727
import java.util.Properties;
28+
import java.util.stream.Stream;
2829
import org.junit.jupiter.api.BeforeEach;
2930
import org.junit.jupiter.api.Test;
3031
import org.junit.jupiter.api.io.TempDir;
32+
import org.junit.jupiter.params.ParameterizedTest;
33+
import org.junit.jupiter.params.provider.Arguments;
34+
import org.junit.jupiter.params.provider.MethodSource;
3135
import org.sonarsource.scanner.lib.internal.InternalProperties;
3236

3337
import static org.assertj.core.api.Assertions.assertThat;
@@ -73,6 +77,26 @@ void error_dump() {
7377
.hasMessage("Fail to export scanner properties");
7478
}
7579

80+
@Test
81+
void test_get_type_cloud() {
82+
assertThat(underTest.getServerLabel()).isEqualTo("SonarQube Cloud");
83+
}
84+
85+
@ParameterizedTest
86+
@MethodSource("provideServerVersionAndTypeArgumentPairs")
87+
void test_get_types_server_and_community_build(String serverVersion, String serverType) {
88+
assertThat(new SimulationScannerEngineFacade(new HashMap<>(), false, serverVersion).getServerLabel()).isEqualTo(serverType);
89+
}
90+
91+
private static Stream<Arguments> provideServerVersionAndTypeArgumentPairs() {
92+
return Stream.of(
93+
Arguments.of("10.6", "SonarQube Server"),
94+
Arguments.of("2025.1.0.1234", "SonarQube Server"),
95+
Arguments.of("24.12", "SonarQube Community Build"),
96+
Arguments.of("25.1.0.1234", "SonarQube Community Build")
97+
);
98+
}
99+
76100
private Map<String, String> createProperties() {
77101
Map<String, String> prop = new HashMap<>();
78102
prop.put("key1:subkey", "value1");

lib/src/test/java/org/sonarsource/scanner/lib/internal/util/VersionUtilsTest.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,4 +42,32 @@ void isAtLeast_IgnoringQualifier_shouldCompareCorrectly() {
4242
assertThat(VersionUtils.isAtLeastIgnoringQualifier("10.6-SNAPSHOT", "10.5")).isTrue();
4343
assertThat(VersionUtils.isAtLeastIgnoringQualifier("10.5.0.1234", "10.5")).isTrue();
4444
}
45+
46+
@Test
47+
void compareMajor_shouldCompareMajorCorrectly() {
48+
assertThat(VersionUtils.compareMajor(null, 10)).isNegative();
49+
assertThat(VersionUtils.compareMajor("fhk10.5.0.1234", 10)).isNegative();
50+
51+
assertThat(VersionUtils.compareMajor("10.5.0.1234", 10)).isZero();
52+
assertThat(VersionUtils.compareMajor("11.5.0.1234", 10)).isPositive();
53+
assertThat(VersionUtils.compareMajor("8.5.0.1234", 10)).isNegative();
54+
55+
assertThat(VersionUtils.compareMajor("10.0-SNAPSHOT", 10)).isZero();
56+
assertThat(VersionUtils.compareMajor(" 10.5.0.1234", 10)).isZero();
57+
assertThat(VersionUtils.compareMajor("", 10)).isNegative();
58+
59+
assertThat(VersionUtils.compareMajor("2025.1.0.1234", 10)).isPositive();
60+
assertThat(VersionUtils.compareMajor("2025.1-SNAPSHOT", 10)).isPositive();
61+
assertThat(VersionUtils.compareMajor("klf2025.1-SNAPSHOT", 10)).isNegative();
62+
assertThat(VersionUtils.compareMajor(" 2025.1-SNAPSHOT", 10)).isPositive();
63+
64+
assertThat(VersionUtils.compareMajor("25.1.0.1234", 10)).isPositive();
65+
assertThat(VersionUtils.compareMajor("25.1-SNAPSHOT", 10)).isPositive();
66+
assertThat(VersionUtils.compareMajor("fko25.1-SNAPSHOT", 10)).isNegative();
67+
assertThat(VersionUtils.compareMajor(" 25.1-SNAPSHOT", 10)).isPositive();
68+
69+
assertThat(VersionUtils.compareMajor("25.1.0.1234", 26)).isNegative();
70+
assertThat(VersionUtils.compareMajor("25.1-SNAPSHOT", 26)).isNegative();
71+
assertThat(VersionUtils.compareMajor(" 25.1.0.1234", 26)).isNegative();
72+
}
4573
}

0 commit comments

Comments
 (0)