aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--gradle.properties2
-rw-r--r--server/sonar-db-dao/src/it/java/org/sonar/db/purge/PurgeDaoIT.java38
-rw-r--r--server/sonar-db-dao/src/it/java/org/sonar/db/sca/ScaIssuesReleasesDetailsDaoIT.java9
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java19
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java1
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java6
-rw-r--r--server/sonar-db-dao/src/main/java/org/sonar/db/sca/ScaIssueReleaseDetailsDto.java21
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml11
-rw-r--r--server/sonar-db-dao/src/main/resources/org/sonar/db/sca/ScaIssuesReleasesDetailsMapper.xml5
-rw-r--r--server/sonar-db-dao/src/test/java/org/sonar/db/sca/ScaIssueReleaseDetailsDtoTest.java3
-rw-r--r--server/sonar-db-dao/src/testFixtures/java/org/sonar/db/sca/ScaIssuesReleasesDetailsDbTester.java3
-rw-r--r--server/sonar-telemetry/src/main/java/org/sonar/telemetry/TelemetryDaemon.java19
-rw-r--r--server/sonar-telemetry/src/test/java/org/sonar/telemetry/TelemetryDaemonTest.java40
-rw-r--r--sonar-core/src/main/java/org/sonar/core/config/AiCodeFixEnablementConstants.java4
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/sca/CliService.java7
-rw-r--r--sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/SpringProjectScanContainer.java5
-rw-r--r--sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/CliServiceTest.java93
-rw-r--r--sonar-scanner-engine/src/test/resources/org/sonar/scanner/sca/echo_args.bat2
-rwxr-xr-xsonar-scanner-engine/src/test/resources/org/sonar/scanner/sca/echo_args.sh2
19 files changed, 198 insertions, 92 deletions
diff --git a/gradle.properties b/gradle.properties
index ec5fb845e29..91ec695bb14 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -15,4 +15,4 @@ elasticSearchServerVersion=8.16.3
projectType=application
artifactoryUrl=https://repox.jfrog.io/repox
jre_release_name=jdk-17.0.13+11
-webappVersion=2025.2.0.13338
+webappVersion=2025.2.0.13464
diff --git a/server/sonar-db-dao/src/it/java/org/sonar/db/purge/PurgeDaoIT.java b/server/sonar-db-dao/src/it/java/org/sonar/db/purge/PurgeDaoIT.java
index f93eaafd3aa..a01c48130d9 100644
--- a/server/sonar-db-dao/src/it/java/org/sonar/db/purge/PurgeDaoIT.java
+++ b/server/sonar-db-dao/src/it/java/org/sonar/db/purge/PurgeDaoIT.java
@@ -89,6 +89,10 @@ import org.sonar.db.property.PropertyDto;
import org.sonar.db.report.ReportScheduleDto;
import org.sonar.db.report.ReportSubscriptionDto;
import org.sonar.db.rule.RuleDto;
+import org.sonar.db.sca.ScaDependenciesDbTester;
+import org.sonar.db.sca.ScaIssueReleaseDto;
+import org.sonar.db.sca.ScaReleasesDbTester;
+import org.sonar.db.sca.ScaSeverity;
import org.sonar.db.source.FileSourceDto;
import org.sonar.db.user.UserDismissedMessageDto;
import org.sonar.db.user.UserDto;
@@ -1960,6 +1964,40 @@ oldCreationDate));
}
+ @Test
+ void deleteBranch_purgesScaActivity() {
+ ProjectDto project = db.components().insertPublicProject().getProjectDto();
+ BranchDto branch1 = db.components().insertProjectBranch(project);
+ BranchDto branch2 = db.components().insertProjectBranch(project);
+
+ ScaReleasesDbTester scaReleasesDbTester = new ScaReleasesDbTester(db);
+ var release1 = scaReleasesDbTester.insertScaRelease(branch1.getUuid(), "1");
+ var release2 = scaReleasesDbTester.insertScaRelease(branch2.getUuid(), "2");
+
+ ScaDependenciesDbTester scaDependenciesDbTester = new ScaDependenciesDbTester(db);
+ scaDependenciesDbTester.insertScaDependency(branch1.getUuid(), release1.uuid(), "1",false);
+ scaDependenciesDbTester.insertScaDependency(branch2.getUuid(), release2.uuid(), "2", false);
+
+ ScaIssueReleaseDto issueRelease1 = new ScaIssueReleaseDto.Builder().setUuid("foo1").setScaIssueUuid("baz").setSeverity(ScaSeverity.LOW).setScaReleaseUuid(release1.uuid()).build();
+ ScaIssueReleaseDto issueRelease2 = new ScaIssueReleaseDto.Builder().setUuid("foo2").setScaIssueUuid("baz").setSeverity(ScaSeverity.LOW).setScaReleaseUuid(release2.uuid()).build();
+ dbClient.scaIssuesReleasesDao().insert(dbSession, issueRelease1);
+ dbClient.scaIssuesReleasesDao().insert(dbSession, issueRelease2);
+
+ assertThat(dbClient.scaReleasesDao().selectByBranchUuid(dbSession, branch1.getUuid())).isNotEmpty();
+ assertThat(dbClient.scaDependenciesDao().selectByBranchUuid(dbSession, branch1.getUuid())).isNotEmpty();
+ assertThat(dbClient.scaIssuesReleasesDao().selectByBranchUuid(dbSession, branch1.getUuid())).isNotEmpty();
+
+ underTest.deleteBranch(dbSession, branch1.getUuid());
+
+ assertThat(dbClient.scaReleasesDao().selectByBranchUuid(dbSession, branch1.getUuid())).isEmpty();
+ assertThat(dbClient.scaDependenciesDao().selectByBranchUuid(dbSession, branch1.getUuid())).isEmpty();
+ assertThat(dbClient.scaIssuesReleasesDao().selectByBranchUuid(dbSession, branch1.getUuid())).isEmpty();
+
+ assertThat(dbClient.scaReleasesDao().selectByBranchUuid(dbSession, branch2.getUuid())).isNotEmpty();
+ assertThat(dbClient.scaDependenciesDao().selectByBranchUuid(dbSession, branch2.getUuid())).isNotEmpty();
+ assertThat(dbClient.scaIssuesReleasesDao().selectByBranchUuid(dbSession, branch2.getUuid())).isNotEmpty();
+ }
+
private AnticipatedTransitionDto getAnticipatedTransitionsDto(String uuid, String projectUuid, Date creationDate) {
return new AnticipatedTransitionDto(uuid, projectUuid, "userUuid", "transition", null, null, null, null, "rule:key", "filepath",
creationDate.getTime());
diff --git a/server/sonar-db-dao/src/it/java/org/sonar/db/sca/ScaIssuesReleasesDetailsDaoIT.java b/server/sonar-db-dao/src/it/java/org/sonar/db/sca/ScaIssuesReleasesDetailsDaoIT.java
index ddce535af50..cfd4604660b 100644
--- a/server/sonar-db-dao/src/it/java/org/sonar/db/sca/ScaIssuesReleasesDetailsDaoIT.java
+++ b/server/sonar-db-dao/src/it/java/org/sonar/db/sca/ScaIssuesReleasesDetailsDaoIT.java
@@ -74,7 +74,8 @@ class ScaIssuesReleasesDetailsDaoIT {
ScaIssueDto.NULL_VALUE,
ScaSeverity.INFO,
List.of("cwe1"),
- new BigDecimal("7.1"));
+ new BigDecimal("7.1"),
+ 1L);
ScaIssueReleaseDetailsDto expected2 = new ScaIssueReleaseDetailsDto(
issue2.scaIssueReleaseUuid(),
issue2.severity(),
@@ -87,7 +88,8 @@ class ScaIssuesReleasesDetailsDaoIT {
"0BSD",
null,
null,
- null);
+ null,
+ 1L);
assertThat(foundPage).hasSize(1).isSubsetOf(expected1, expected2);
var foundAllIssues = scaIssuesReleasesDetailsDao.selectByBranchUuid(db.getSession(), componentDto.branchUuid(), Pagination.forPage(1).andSize(10));
@@ -565,7 +567,8 @@ class ScaIssuesReleasesDetailsDaoIT {
ScaIssueDto.NULL_VALUE,
ScaSeverity.INFO,
List.of("cwe1"),
- new BigDecimal("7.1"));
+ new BigDecimal("7.1"),
+ 1L);
var foundIssue = scaIssuesReleasesDetailsDao.selectByScaIssueReleaseUuid(db.getSession(), issue1.scaIssueReleaseUuid());
assertThat(foundIssue).isEqualTo(expected);
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java
index 9847cbc75a3..230d9aff010 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeCommands.java
@@ -523,4 +523,23 @@ class PurgeCommands {
session.commit();
profiler.stop();
}
+
+ public void deleteScaActivity(String componentUuid) {
+ profiler.start("deleteScaDependencies (sca_dependencies)");
+ purgeMapper.deleteScaDependenciesByComponentUuid(componentUuid);
+ session.commit();
+ profiler.stop();
+
+ profiler.start("deleteScaIssuesReleases (sca_issues_releases)");
+ purgeMapper.deleteScaIssuesReleasesByComponentUuid(componentUuid);
+ session.commit();
+ profiler.stop();
+
+ // sca_releases MUST be deleted last because dependencies and
+ // issues_releases only join to the component through sca_releases
+ profiler.start("deleteScaReleases (sca_releases)");
+ purgeMapper.deleteScaReleasesByComponentUuid(componentUuid);
+ session.commit();
+ profiler.stop();
+ }
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java
index 0d0e13b89ab..ce5e0cf5e70 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeDao.java
@@ -280,6 +280,7 @@ public class PurgeDao implements Dao {
commands.deleteReportSchedules(branchUuid);
commands.deleteReportSubscriptions(branchUuid);
commands.deleteIssuesFixed(branchUuid);
+ commands.deleteScaActivity(branchUuid);
}
private static void deleteProject(String projectUuid, PurgeMapper mapper, PurgeCommands commands) {
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java
index c7a312c905a..5ca08a12d7a 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/purge/PurgeMapper.java
@@ -194,4 +194,10 @@ public interface PurgeMapper {
void deleteAnticipatedTransitionsByProjectUuidAndCreationDate(@Param("projectUuid") String projectUuid, @Param("createdAtBefore") Long createdAtBefore);
void deleteIssuesFixedByBranchUuid(@Param("branchUuid") String branchUuid);
+
+ void deleteScaDependenciesByComponentUuid(@Param("componentUuid") String componentUuid);
+
+ void deleteScaIssuesReleasesByComponentUuid(@Param("componentUuid") String componentUuid);
+
+ void deleteScaReleasesByComponentUuid(@Param("componentUuid") String componentUuid);
}
diff --git a/server/sonar-db-dao/src/main/java/org/sonar/db/sca/ScaIssueReleaseDetailsDto.java b/server/sonar-db-dao/src/main/java/org/sonar/db/sca/ScaIssueReleaseDetailsDto.java
index 3176985c7e3..827ceddf64f 100644
--- a/server/sonar-db-dao/src/main/java/org/sonar/db/sca/ScaIssueReleaseDetailsDto.java
+++ b/server/sonar-db-dao/src/main/java/org/sonar/db/sca/ScaIssueReleaseDetailsDto.java
@@ -22,6 +22,7 @@ package org.sonar.db.sca;
import java.math.BigDecimal;
import java.util.List;
import javax.annotation.Nullable;
+import org.sonar.api.utils.DateUtils;
/**
* <p>A "read-only" DTO used to query the join of sca_issues_releases, sca_issues, and sca_*_issues.
@@ -51,7 +52,14 @@ public record ScaIssueReleaseDetailsDto(String scaIssueReleaseUuid,
String spdxLicenseId,
@Nullable ScaSeverity vulnerabilityBaseSeverity,
@Nullable List<String> cweIds,
- @Nullable BigDecimal cvssScore) implements ScaIssueIdentity {
+ @Nullable BigDecimal cvssScore,
+ long createdAt) implements ScaIssueIdentity {
+
+ // DateUtils says that this returns an RFC 822 timestamp
+ // but it is really a ISO 8601 timestamp.
+ public String createdAtIso8601() {
+ return DateUtils.formatDateTime(createdAt);
+ }
public Builder toBuilder() {
return new Builder()
@@ -66,7 +74,8 @@ public record ScaIssueReleaseDetailsDto(String scaIssueReleaseUuid,
.setSpdxLicenseId(spdxLicenseId)
.setVulnerabilityBaseSeverity(vulnerabilityBaseSeverity)
.setCweIds(cweIds)
- .setCvssScore(cvssScore);
+ .setCvssScore(cvssScore)
+ .setCreatedAt(createdAt);
}
public static class Builder {
@@ -82,6 +91,7 @@ public record ScaIssueReleaseDetailsDto(String scaIssueReleaseUuid,
private ScaSeverity vulnerabilityBaseSeverity;
private List<String> cweIds;
private BigDecimal cvssScore;
+ private long createdAt;
public Builder setScaIssueReleaseUuid(String scaIssueReleaseUuid) {
this.scaIssueReleaseUuid = scaIssueReleaseUuid;
@@ -143,9 +153,14 @@ public record ScaIssueReleaseDetailsDto(String scaIssueReleaseUuid,
return this;
}
+ public Builder setCreatedAt(long createdAt) {
+ this.createdAt = createdAt;
+ return this;
+ }
+
public ScaIssueReleaseDetailsDto build() {
return new ScaIssueReleaseDetailsDto(scaIssueReleaseUuid, severity, scaIssueUuid, scaReleaseUuid, scaIssueType,
- newInPullRequest, packageUrl, vulnerabilityId, spdxLicenseId, vulnerabilityBaseSeverity, cweIds, cvssScore);
+ newInPullRequest, packageUrl, vulnerabilityId, spdxLicenseId, vulnerabilityBaseSeverity, cweIds, cvssScore, createdAt);
}
}
}
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml
index 3700a8df647..4a64f3cdeab 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/purge/PurgeMapper.xml
@@ -669,4 +669,15 @@
<delete id="deleteIssuesFixedByBranchUuid">
delete from issues_fixed where pull_request_uuid = #{branchUuid,jdbcType=VARCHAR}
</delete>
+
+
+ <delete id="deleteScaDependenciesByComponentUuid">
+ delete from sca_dependencies where sca_release_uuid in (select uuid from sca_releases where component_uuid = #{componentUuid,jdbcType=VARCHAR})
+ </delete>
+ <delete id="deleteScaIssuesReleasesByComponentUuid">
+ delete from sca_issues_releases where sca_release_uuid in (select uuid from sca_releases where component_uuid = #{componentUuid,jdbcType=VARCHAR})
+ </delete>
+ <delete id="deleteScaReleasesByComponentUuid">
+ delete from sca_releases where component_uuid = #{componentUuid,jdbcType=VARCHAR}
+ </delete>
</mapper>
diff --git a/server/sonar-db-dao/src/main/resources/org/sonar/db/sca/ScaIssuesReleasesDetailsMapper.xml b/server/sonar-db-dao/src/main/resources/org/sonar/db/sca/ScaIssuesReleasesDetailsMapper.xml
index 253050069b9..4c3189af393 100644
--- a/server/sonar-db-dao/src/main/resources/org/sonar/db/sca/ScaIssuesReleasesDetailsMapper.xml
+++ b/server/sonar-db-dao/src/main/resources/org/sonar/db/sca/ScaIssuesReleasesDetailsMapper.xml
@@ -18,6 +18,8 @@
<arg column="cwe_ids" typeHandler="org.sonar.db.sca.ListOfStringsTypeHandler" jdbcType="VARCHAR"
javaType="java.util.List"/>
<arg column="cvss_score" javaType="java.math.BigDecimal"/>
+ <!-- the underscore prefix means to use the primitive type instead of boxed type -->
+ <arg column="created_at" javaType="_long"/>
</constructor>
</resultMap>
@@ -34,7 +36,8 @@
si.spdx_license_id,
svi.base_severity,
svi.cwe_ids,
- svi.cvss_score
+ svi.cvss_score,
+ sir.created_at
</sql>
<sql id="sqlBaseJoins">
diff --git a/server/sonar-db-dao/src/test/java/org/sonar/db/sca/ScaIssueReleaseDetailsDtoTest.java b/server/sonar-db-dao/src/test/java/org/sonar/db/sca/ScaIssueReleaseDetailsDtoTest.java
index 4511f4b468e..de7a64448a1 100644
--- a/server/sonar-db-dao/src/test/java/org/sonar/db/sca/ScaIssueReleaseDetailsDtoTest.java
+++ b/server/sonar-db-dao/src/test/java/org/sonar/db/sca/ScaIssueReleaseDetailsDtoTest.java
@@ -39,7 +39,8 @@ class ScaIssueReleaseDetailsDtoTest {
"spdxLicenseId",
ScaSeverity.BLOCKER,
List.of("cwe1"),
- BigDecimal.ONE);
+ BigDecimal.ONE,
+ 42L);
assertThat(dto.toBuilder().build()).isEqualTo(dto);
}
}
diff --git a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/sca/ScaIssuesReleasesDetailsDbTester.java b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/sca/ScaIssuesReleasesDetailsDbTester.java
index c4c184df38d..7c29799376e 100644
--- a/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/sca/ScaIssuesReleasesDetailsDbTester.java
+++ b/server/sonar-db-dao/src/testFixtures/java/org/sonar/db/sca/ScaIssuesReleasesDetailsDbTester.java
@@ -41,7 +41,8 @@ public class ScaIssuesReleasesDetailsDbTester {
newInPullRequest, scaIssueDto.packageUrl(), scaIssueDto.vulnerabilityId(), scaIssueDto.spdxLicenseId(),
scaVulnerabilityIssueDtoOptional.map(ScaVulnerabilityIssueDto::baseSeverity).orElse(null),
scaVulnerabilityIssueDtoOptional.map(ScaVulnerabilityIssueDto::cweIds).orElse(null),
- scaVulnerabilityIssueDtoOptional.map(ScaVulnerabilityIssueDto::cvssScore).orElse(null));
+ scaVulnerabilityIssueDtoOptional.map(ScaVulnerabilityIssueDto::cvssScore).orElse(null),
+ scaIssueReleaseDto.createdAt());
}
private ScaIssueReleaseDetailsDto insertIssue(ScaIssueDto scaIssue, Optional<ScaVulnerabilityIssueDto> scaVulnerabilityIssueDtoOptional,
diff --git a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/TelemetryDaemon.java b/server/sonar-telemetry/src/main/java/org/sonar/telemetry/TelemetryDaemon.java
index 8d6ac35d955..e0dd552ec97 100644
--- a/server/sonar-telemetry/src/main/java/org/sonar/telemetry/TelemetryDaemon.java
+++ b/server/sonar-telemetry/src/main/java/org/sonar/telemetry/TelemetryDaemon.java
@@ -116,27 +116,28 @@ public class TelemetryDaemon extends AbstractStoppableScheduledExecutorServiceIm
private Runnable telemetryCommand() {
return () -> {
- if (!lockManager.tryLock(LOCK_NAME, lockDuration())) {
- return;
- }
-
- long now = system2.now();
try {
+ if (!lockManager.tryLock(LOCK_NAME, lockDuration())) {
+ return;
+ }
+
+ long now = system2.now();
if (shouldUploadStatistics(now)) {
uploadLegacyTelemetry();
uploadMetrics();
- writeTelemetrySequence();
+
+ updateTelemetryProps(now);
}
} catch (Exception e) {
LOG.debug("Error while checking SonarQube statistics: {}", e.getMessage(), e);
- } finally {
- writeLastPing(now);
}
// do not check at start up to exclude test instance which are not up for a long time
};
}
- private void writeTelemetrySequence() {
+ private void updateTelemetryProps(long now) {
+ internalProperties.write(I_PROP_LAST_PING, String.valueOf(now));
+
Optional<String> currentSequence = internalProperties.read(I_PROP_MESSAGE_SEQUENCE);
if (currentSequence.isEmpty()) {
internalProperties.write(I_PROP_MESSAGE_SEQUENCE, String.valueOf(1));
diff --git a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/TelemetryDaemonTest.java b/server/sonar-telemetry/src/test/java/org/sonar/telemetry/TelemetryDaemonTest.java
index d0028acc40f..8a1b9471019 100644
--- a/server/sonar-telemetry/src/test/java/org/sonar/telemetry/TelemetryDaemonTest.java
+++ b/server/sonar-telemetry/src/test/java/org/sonar/telemetry/TelemetryDaemonTest.java
@@ -49,6 +49,7 @@ import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.after;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.timeout;
@@ -115,32 +116,23 @@ class TelemetryDaemonTest {
void start_whenLastPingEarlierThanOneDayAgo_shouldNotSendData() throws IOException {
initTelemetrySettingsToDefaultValues();
when(lockManager.tryLock(any(), anyInt())).thenReturn(true);
- settings.setProperty("sonar.telemetry.frequencyInSeconds", "1");
long now = system2.now();
long twentyHoursAgo = now - (ONE_HOUR * 20L);
- mockDataJsonWriterDoingSomething();
-
+ long moreThanOneDayAgo = now - ONE_DAY - ONE_HOUR;
internalProperties.write("telemetry.lastPing", String.valueOf(twentyHoursAgo));
- underTest.start();
-
- verify(client, after(2_000).never()).upload(anyString());
- }
-
- @Test
- void start_whenExceptionThrown_shouldNotRepeatedlySendDataAndLastPingPropIsStillSet() throws IOException {
- initTelemetrySettingsToDefaultValues();
- when(lockManager.tryLock(any(), anyInt())).thenReturn(true);
settings.setProperty("sonar.telemetry.frequencyInSeconds", "1");
- long today = parseDate("2017-08-01").getTime();
- system2.setNow(today);
- settings.removeProperty("telemetry.lastPing");
+ when(dataLoader.load()).thenReturn(SOME_TELEMETRY_DATA);
mockDataJsonWriterDoingSomething();
- when(dataLoader.load()).thenThrow(new IllegalStateException("Some error was thrown."));
underTest.start();
- verify(client, after(2_000).never()).upload(anyString());
- verify(internalProperties, timeout(4_000).atLeastOnce()).write("telemetry.lastPing", String.valueOf(today));
+ verify(dataJsonWriter, after(2_000).never()).writeTelemetryData(any(JsonWriter.class), same(SOME_TELEMETRY_DATA));
+ verify(client, never()).upload(anyString());
+
+ internalProperties.write("telemetry.lastPing", String.valueOf(moreThanOneDayAgo));
+
+ verify(client, timeout(2_000)).upload(anyString());
+ verify(dataJsonWriter).writeTelemetryData(any(JsonWriter.class), same(SOME_TELEMETRY_DATA));
}
@Test
@@ -208,6 +200,12 @@ class TelemetryDaemonTest {
verify(internalProperties, timeout(4_000)).write("telemetry.messageSeq", "11");
}
+ private void initTelemetrySettingsToDefaultValues() {
+ settings.setProperty(SONAR_TELEMETRY_ENABLE.getKey(), SONAR_TELEMETRY_ENABLE.getDefaultValue());
+ settings.setProperty(SONAR_TELEMETRY_URL.getKey(), SONAR_TELEMETRY_URL.getDefaultValue());
+ settings.setProperty(SONAR_TELEMETRY_FREQUENCY_IN_SECONDS.getKey(), SONAR_TELEMETRY_FREQUENCY_IN_SECONDS.getDefaultValue());
+ }
+
private void mockDataJsonWriterDoingSomething() {
doAnswer(t -> {
JsonWriter json = t.getArgument(0);
@@ -218,10 +216,4 @@ class TelemetryDaemonTest {
.writeTelemetryData(any(), any());
}
- private void initTelemetrySettingsToDefaultValues() {
- settings.setProperty(SONAR_TELEMETRY_ENABLE.getKey(), SONAR_TELEMETRY_ENABLE.getDefaultValue());
- settings.setProperty(SONAR_TELEMETRY_URL.getKey(), SONAR_TELEMETRY_URL.getDefaultValue());
- settings.setProperty(SONAR_TELEMETRY_FREQUENCY_IN_SECONDS.getKey(), SONAR_TELEMETRY_FREQUENCY_IN_SECONDS.getDefaultValue());
- }
-
}
diff --git a/sonar-core/src/main/java/org/sonar/core/config/AiCodeFixEnablementConstants.java b/sonar-core/src/main/java/org/sonar/core/config/AiCodeFixEnablementConstants.java
index 36837a75065..c60222b3114 100644
--- a/sonar-core/src/main/java/org/sonar/core/config/AiCodeFixEnablementConstants.java
+++ b/sonar-core/src/main/java/org/sonar/core/config/AiCodeFixEnablementConstants.java
@@ -21,8 +21,8 @@ package org.sonar.core.config;
public final class AiCodeFixEnablementConstants {
public static final String SUGGESTION_FEATURE_ENABLED_PROPERTY = "sonar.ai.suggestions.enabled";
- public static final String SUGGESTION_PROVIDER_NAME_PROPERTY = "sonar.ai.suggestions.provider.name";
- public static final String SUGGESTION_PROVIDER_ENDPOINT_URL_PROPERTY = "sonar.ai.suggestions.provider.endpointUrl";
+ public static final String SUGGESTION_PROVIDER_KEY_PROPERTY = "sonar.ai.suggestions.provider.key";
+ public static final String SUGGESTION_PROVIDER_ENDPOINT_PROPERTY = "sonar.ai.suggestions.provider.endpoint";
public static final String SUGGESTION_PROVIDER_API_KEY_INTERNAL_PROPERTY = "sonar.ai.suggestions.provider.apiKey";
private AiCodeFixEnablementConstants() {
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sca/CliService.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sca/CliService.java
index 39f46bb00b7..c2a474c4e52 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/sca/CliService.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/sca/CliService.java
@@ -30,6 +30,7 @@ import java.util.function.Consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.platform.Server;
import org.sonar.api.utils.System2;
import org.sonar.core.util.ProcessWrapperFactory;
import org.sonar.scanner.config.DefaultConfiguration;
@@ -46,11 +47,13 @@ public class CliService {
private final ProcessWrapperFactory processWrapperFactory;
private final TelemetryCache telemetryCache;
private final System2 system2;
+ private final Server server;
- public CliService(ProcessWrapperFactory processWrapperFactory, TelemetryCache telemetryCache, System2 system2) {
+ public CliService(ProcessWrapperFactory processWrapperFactory, TelemetryCache telemetryCache, System2 system2, Server server) {
this.processWrapperFactory = processWrapperFactory;
this.telemetryCache = telemetryCache;
this.system2 = system2;
+ this.server = server;
}
public File generateManifestsZip(DefaultInputModule module, File cliExecutable, DefaultConfiguration configuration) throws IOException, IllegalStateException {
@@ -89,6 +92,8 @@ public class CliService {
// sending this will tell the CLI to skip checking for the latest available version on startup
envProperties.put("TIDELIFT_SKIP_UPDATE_CHECK", "1");
envProperties.put("TIDELIFT_ALLOW_MANIFEST_FAILURES", "1");
+ envProperties.put("TIDELIFT_CLI_INSIDE_SCANNER_ENGINE", "1");
+ envProperties.put("TIDELIFT_CLI_SQ_SERVER_VERSION", server.getVersion());
envProperties.putAll(ScaProperties.buildFromScannerProperties(configuration));
LOG.debug("Environment properties: {}", envProperties);
diff --git a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/SpringProjectScanContainer.java b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/SpringProjectScanContainer.java
index ed1909c0b7c..ead791bdeaf 100644
--- a/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/SpringProjectScanContainer.java
+++ b/sonar-scanner-engine/src/main/java/org/sonar/scanner/scan/SpringProjectScanContainer.java
@@ -19,9 +19,9 @@
*/
package org.sonar.scanner.scan;
+import jakarta.annotation.Priority;
import java.util.Collection;
import java.util.Set;
-import jakarta.annotation.Priority;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sonar.api.Plugin;
@@ -139,8 +139,7 @@ public class SpringProjectScanContainer extends SpringComponentContainer {
// SCA
CliService.class,
CliCacheService.class,
- ScaExecutor.class
- );
+ ScaExecutor.class);
}
static ExtensionMatcher getScannerProjectExtensionsFilter() {
diff --git a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/CliServiceTest.java b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/CliServiceTest.java
index 6b6dde4efcf..b8bb1a26961 100644
--- a/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/CliServiceTest.java
+++ b/sonar-scanner-engine/src/test/java/org/sonar/scanner/sca/CliServiceTest.java
@@ -34,6 +34,7 @@ import org.junit.jupiter.api.extension.RegisterExtension;
import org.junit.jupiter.api.io.TempDir;
import org.sonar.api.batch.bootstrap.ProjectDefinition;
import org.sonar.api.batch.fs.internal.DefaultInputModule;
+import org.sonar.api.platform.Server;
import org.sonar.api.testfixtures.log.LogTesterJUnit5;
import org.sonar.api.utils.System2;
import org.sonar.core.util.ProcessWrapperFactory;
@@ -48,29 +49,25 @@ import static org.slf4j.event.Level.INFO;
class CliServiceTest {
private TelemetryCache telemetryCache;
+ private DefaultInputModule rootInputModule;
+ private final Server server = mock(Server.class);
@RegisterExtension
private final LogTesterJUnit5 logTester = new LogTesterJUnit5();
+ @TempDir
+ Path rootModuleDir;
private CliService underTest;
@BeforeEach
void setup() {
telemetryCache = new TelemetryCache();
- underTest = new CliService(new ProcessWrapperFactory(), telemetryCache, System2.INSTANCE);
+ rootInputModule = new DefaultInputModule(
+ ProjectDefinition.create().setBaseDir(rootModuleDir.toFile()).setWorkDir(rootModuleDir.toFile()));
+ underTest = new CliService(new ProcessWrapperFactory(), telemetryCache, System2.INSTANCE, server);
}
@Test
- void generateZip_shouldCallProcessCorrectly_andRegisterTelemetry(@TempDir Path rootModuleDir) throws IOException, URISyntaxException {
- DefaultInputModule root = new DefaultInputModule(
- ProjectDefinition.create().setBaseDir(rootModuleDir.toFile()).setWorkDir(rootModuleDir.toFile()));
-
- // There is a custom test Bash script available in src/test/resources/org/sonar/scanner/sca that
- // will serve as our "CLI". This script will output some messages about what arguments were passed
- // to it and will try to generate a zip file in the location the process specifies. This allows us
- // to simulate a real CLI call without needing an OS specific CLI executable to run on a real project.
- URL scriptUrl = CliServiceTest.class.getResource(SystemUtils.IS_OS_WINDOWS ? "echo_args.bat" : "echo_args.sh");
- assertThat(scriptUrl).isNotNull();
- File scriptDir = new File(scriptUrl.toURI());
+ void generateZip_shouldCallProcessCorrectly_andRegisterTelemetry() throws IOException, URISyntaxException {
assertThat(rootModuleDir.resolve("test_file").toFile().createNewFile()).isTrue();
// We need to set the logging level to debug in order to be able to view the shell script's output
@@ -81,9 +78,9 @@ class CliServiceTest {
"save-lockfiles",
"--zip",
"--zip-filename",
- root.getWorkDir().resolve("dependency-files.zip").toString(),
+ rootInputModule.getWorkDir().resolve("dependency-files.zip").toString(),
"--directory",
- root.getBaseDir().toString(),
+ rootInputModule.getBaseDir().toString(),
"--debug");
String argumentOutput = "Arguments Passed In: " + String.join(" ", args);
@@ -91,7 +88,7 @@ class CliServiceTest {
when(configuration.getProperties()).thenReturn(Map.of("sonar.sca.recursiveManifestSearch", "true"));
when(configuration.get("sonar.sca.recursiveManifestSearch")).thenReturn(Optional.of("true"));
- File producedZip = underTest.generateManifestsZip(root, scriptDir, configuration);
+ File producedZip = underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration);
assertThat(producedZip).exists();
assertThat(logTester.logs(DEBUG))
@@ -106,17 +103,7 @@ class CliServiceTest {
}
@Test
- void generateZip_whenDebugLogLevel_shouldCallProcessCorrectly(@TempDir Path rootModuleDir) throws IOException, URISyntaxException {
- DefaultInputModule root = new DefaultInputModule(
- ProjectDefinition.create().setBaseDir(rootModuleDir.toFile()).setWorkDir(rootModuleDir.toFile()));
-
- // There is a custom test Bash script available in src/test/resources/org/sonar/scanner/sca that
- // will serve as our "CLI". This script will output some messages about what arguments were passed
- // to it and will try to generate a zip file in the location the process specifies. This allows us
- // to simulate a real CLI call without needing an OS specific CLI executable to run on a real project.
- URL scriptUrl = CliServiceTest.class.getResource(SystemUtils.IS_OS_WINDOWS ? "echo_args.bat" : "echo_args.sh");
- assertThat(scriptUrl).isNotNull();
- File scriptDir = new File(scriptUrl.toURI());
+ void generateZip_whenDebugLogLevel_shouldCallProcessCorrectly() throws IOException, URISyntaxException {
assertThat(rootModuleDir.resolve("test_file").toFile().createNewFile()).isTrue();
// We need to set the logging level to debug in order to be able to view the shell script's output
@@ -127,9 +114,9 @@ class CliServiceTest {
"save-lockfiles",
"--zip",
"--zip-filename",
- root.getWorkDir().resolve("dependency-files.zip").toString(),
+ rootInputModule.getWorkDir().resolve("dependency-files.zip").toString(),
"--directory",
- root.getBaseDir().toString(),
+ rootInputModule.getBaseDir().toString(),
"--debug");
String argumentOutput = "Arguments Passed In: " + String.join(" ", args);
@@ -137,24 +124,14 @@ class CliServiceTest {
when(configuration.getProperties()).thenReturn(Map.of("sonar.sca.recursiveManifestSearch", "true"));
when(configuration.get("sonar.sca.recursiveManifestSearch")).thenReturn(Optional.of("true"));
- underTest.generateManifestsZip(root, scriptDir, configuration);
+ underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration);
assertThat(logTester.logs(DEBUG))
.contains(argumentOutput);
}
@Test
- void generateZip_whenScaDebugEnabled_shouldCallProcessCorrectly(@TempDir Path rootModuleDir) throws IOException, URISyntaxException {
- DefaultInputModule root = new DefaultInputModule(
- ProjectDefinition.create().setBaseDir(rootModuleDir.toFile()).setWorkDir(rootModuleDir.toFile()));
-
- // There is a custom test Bash script available in src/test/resources/org/sonar/scanner/sca that
- // will serve as our "CLI". This script will output some messages about what arguments were passed
- // to it and will try to generate a zip file in the location the process specifies. This allows us
- // to simulate a real CLI call without needing an OS specific CLI executable to run on a real project.
- URL scriptUrl = CliServiceTest.class.getResource(SystemUtils.IS_OS_WINDOWS ? "echo_args.bat" : "echo_args.sh");
- assertThat(scriptUrl).isNotNull();
- File scriptDir = new File(scriptUrl.toURI());
+ void generateZip_whenScaDebugEnabled_shouldCallProcessCorrectly() throws IOException, URISyntaxException {
assertThat(rootModuleDir.resolve("test_file").toFile().createNewFile()).isTrue();
// Set the logging level to info so that we don't automatically set --debug flag
@@ -165,9 +142,9 @@ class CliServiceTest {
"save-lockfiles",
"--zip",
"--zip-filename",
- root.getWorkDir().resolve("dependency-files.zip").toString(),
+ rootInputModule.getWorkDir().resolve("dependency-files.zip").toString(),
"--directory",
- root.getBaseDir().toString(),
+ rootInputModule.getBaseDir().toString(),
"--debug");
String argumentOutput = "Arguments Passed In: " + String.join(" ", args);
@@ -176,9 +153,39 @@ class CliServiceTest {
when(configuration.get("sonar.sca.recursiveManifestSearch")).thenReturn(Optional.of("true"));
when(configuration.getBoolean("sonar.sca.debug")).thenReturn(Optional.of(true));
- underTest.generateManifestsZip(root, scriptDir, configuration);
+ underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration);
assertThat(logTester.logs(INFO))
.contains(argumentOutput);
}
+
+ @Test
+ void generateZip_shouldSendSQEnvVars() throws IOException, URISyntaxException {
+ // We need to set the logging level to debug in order to be able to view the shell script's output
+ logTester.setLevel(DEBUG);
+
+ var version = "1.0.0";
+ when(server.getVersion()).thenReturn(version);
+
+ DefaultConfiguration configuration = mock(DefaultConfiguration.class);
+ underTest.generateManifestsZip(rootInputModule, scriptDir(), configuration);
+
+ assertThat(logTester.logs(DEBUG))
+ .contains("TIDELIFT_CLI_INSIDE_SCANNER_ENGINE=1")
+ .contains("TIDELIFT_CLI_SQ_SERVER_VERSION=" + version);
+ }
+
+ private URL scriptUrl() {
+ // There is a custom test Bash script available in src/test/resources/org/sonar/scanner/sca that
+ // will serve as our "CLI". This script will output some messages about what arguments were passed
+ // to it and will try to generate a zip file in the location the process specifies. This allows us
+ // to simulate a real CLI call without needing an OS specific CLI executable to run on a real project.
+ URL scriptUrl = CliServiceTest.class.getResource(SystemUtils.IS_OS_WINDOWS ? "echo_args.bat" : "echo_args.sh");
+ assertThat(scriptUrl).isNotNull();
+ return scriptUrl;
+ }
+
+ private File scriptDir() throws URISyntaxException {
+ return new File(scriptUrl().toURI());
+ }
}
diff --git a/sonar-scanner-engine/src/test/resources/org/sonar/scanner/sca/echo_args.bat b/sonar-scanner-engine/src/test/resources/org/sonar/scanner/sca/echo_args.bat
index e2a4fd8687b..5677cf5c437 100644
--- a/sonar-scanner-engine/src/test/resources/org/sonar/scanner/sca/echo_args.bat
+++ b/sonar-scanner-engine/src/test/resources/org/sonar/scanner/sca/echo_args.bat
@@ -20,5 +20,7 @@ goto loop
echo TIDELIFT_SKIP_UPDATE_CHECK=%TIDELIFT_SKIP_UPDATE_CHECK%
echo TIDELIFT_ALLOW_MANIFEST_FAILURES=%TIDELIFT_ALLOW_MANIFEST_FAILURES%
echo TIDELIFT_RECURSIVE_MANIFEST_SEARCH=%TIDELIFT_RECURSIVE_MANIFEST_SEARCH%
+echo TIDELIFT_CLI_INSIDE_SCANNER_ENGINE=%TIDELIFT_CLI_INSIDE_SCANNER_ENGINE%
+echo TIDELIFT_CLI_SQ_SERVER_VERSION=%TIDELIFT_CLI_SQ_SERVER_VERSION%
echo ZIP FILE LOCATION = %FILENAME%
echo. > %FILENAME%
diff --git a/sonar-scanner-engine/src/test/resources/org/sonar/scanner/sca/echo_args.sh b/sonar-scanner-engine/src/test/resources/org/sonar/scanner/sca/echo_args.sh
index 3875827056f..881be2eaac5 100755
--- a/sonar-scanner-engine/src/test/resources/org/sonar/scanner/sca/echo_args.sh
+++ b/sonar-scanner-engine/src/test/resources/org/sonar/scanner/sca/echo_args.sh
@@ -24,6 +24,8 @@ set -- "${POSITIONAL_ARGS[@]}" # restore positional parameters
echo "TIDELIFT_SKIP_UPDATE_CHECK=${TIDELIFT_SKIP_UPDATE_CHECK}"
echo "TIDELIFT_ALLOW_MANIFEST_FAILURES=${TIDELIFT_ALLOW_MANIFEST_FAILURES}"
echo "TIDELIFT_RECURSIVE_MANIFEST_SEARCH=${TIDELIFT_RECURSIVE_MANIFEST_SEARCH}"
+echo "TIDELIFT_CLI_INSIDE_SCANNER_ENGINE=${TIDELIFT_CLI_INSIDE_SCANNER_ENGINE}"
+echo "TIDELIFT_CLI_SQ_SERVER_VERSION=${TIDELIFT_CLI_SQ_SERVER_VERSION}"
# print filename location for debug purposes
echo "ZIP FILE LOCATION = ${FILENAME}"