From c99c0719f9deb8e058c535b686c50587a94ba9d2 Mon Sep 17 00:00:00 2001 From: Tobias Hahnen Date: Mon, 29 Jan 2024 16:41:13 +0100 Subject: [PATCH] SLCORE-668: Download sonar-text on SQ 10.4 as well (#847) It wasn't downloaded on SQ 10.4 DEV because it was still concidered to be embedded when the synchonization was happening. --- .../LocalStorageSynchronizer.java | 8 ++- .../serverconnection/PluginsSynchronizer.java | 11 +++- .../serverconnection/ServerConnection.java | 2 +- .../PluginsSynchronizerTests.java | 65 +++++++++++++++---- 4 files changed, 70 insertions(+), 16 deletions(-) diff --git a/server-connection/src/main/java/org/sonarsource/sonarlint/core/serverconnection/LocalStorageSynchronizer.java b/server-connection/src/main/java/org/sonarsource/sonarlint/core/serverconnection/LocalStorageSynchronizer.java index cf22855196..a0e3e1fded 100644 --- a/server-connection/src/main/java/org/sonarsource/sonarlint/core/serverconnection/LocalStorageSynchronizer.java +++ b/server-connection/src/main/java/org/sonarsource/sonarlint/core/serverconnection/LocalStorageSynchronizer.java @@ -52,7 +52,13 @@ public SynchronizationResult synchronize(ServerApi serverApi, Set projec serverInfoSynchronizer.synchronize(serverApi); var version = storage.serverInfo().read().orElseThrow().getVersion(); - var anyPluginUpdated = pluginsSynchronizer.synchronize(serverApi, progressMonitor); + // INFO: In order to download `sonar-text` alongside `sonar-text-enterprise` on SQ 10.4+ we have to change the + // plug-in synchronizer to work correctly the moment the connection is established and the plug-ins are + // downloaded for the first time and also everytime the plug-ins are refreshed (e.g. after IDE restart). + var supportsCustomSecrets = !serverApi.isSonarCloud() + && version.compareToIgnoreQualifier(ServerConnection.CUSTOM_SECRETS_MIN_SQ_VERSION) >= 0; + var anyPluginUpdated = pluginsSynchronizer.synchronize(serverApi, progressMonitor, supportsCustomSecrets); + projectKeys.stream() .collect(Collectors.toMap(Function.identity(), projectKey -> synchronizeAnalyzerConfig(serverApi, projectKey, progressMonitor))) .forEach((projectKey, analyzerConfig) -> storage.project(projectKey).analyzerConfiguration().store(analyzerConfig)); diff --git a/server-connection/src/main/java/org/sonarsource/sonarlint/core/serverconnection/PluginsSynchronizer.java b/server-connection/src/main/java/org/sonarsource/sonarlint/core/serverconnection/PluginsSynchronizer.java index 6bee682980..b7ccc16274 100644 --- a/server-connection/src/main/java/org/sonarsource/sonarlint/core/serverconnection/PluginsSynchronizer.java +++ b/server-connection/src/main/java/org/sonarsource/sonarlint/core/serverconnection/PluginsSynchronizer.java @@ -21,6 +21,7 @@ import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; @@ -37,8 +38,8 @@ public class PluginsSynchronizer { private final Set sonarSourceDisabledPluginKeys; private final ConnectionStorage storage; - private final Set embeddedPluginKeys; private final PluginsMinVersions pluginsMinVersions = new PluginsMinVersions(); + private Set embeddedPluginKeys; public PluginsSynchronizer(Set enabledLanguages, ConnectionStorage storage, Set embeddedPluginKeys) { this.sonarSourceDisabledPluginKeys = getSonarSourceDisabledPluginKeys(enabledLanguages); @@ -46,7 +47,13 @@ public PluginsSynchronizer(Set enabledLanguages, ConnectionStorage sto this.embeddedPluginKeys = embeddedPluginKeys; } - public boolean synchronize(ServerApi serverApi, ProgressMonitor progressMonitor) { + public boolean synchronize(ServerApi serverApi, ProgressMonitor progressMonitor, boolean supportsCustomSecrets) { + if (supportsCustomSecrets) { + var embeddedPluginKeysCopy = new HashSet<>(embeddedPluginKeys); + embeddedPluginKeysCopy.remove(Language.SECRETS.getPluginKey()); + embeddedPluginKeys = embeddedPluginKeysCopy; + } + var storedPluginsByKey = storage.plugins().getStoredPluginsByKey(); var serverPlugins = serverApi.plugins().getInstalled(); var pluginsToDownload = serverPlugins.stream() diff --git a/server-connection/src/main/java/org/sonarsource/sonarlint/core/serverconnection/ServerConnection.java b/server-connection/src/main/java/org/sonarsource/sonarlint/core/serverconnection/ServerConnection.java index dd507dece1..4154bd1909 100644 --- a/server-connection/src/main/java/org/sonarsource/sonarlint/core/serverconnection/ServerConnection.java +++ b/server-connection/src/main/java/org/sonarsource/sonarlint/core/serverconnection/ServerConnection.java @@ -51,7 +51,7 @@ public class ServerConnection { private static final SonarLintLogger LOG = SonarLintLogger.get(); private static final Version SECRET_ANALYSIS_MIN_SQ_VERSION = Version.create("9.9"); - private static final Version CUSTOM_SECRETS_MIN_SQ_VERSION = Version.create("10.4"); + public static final Version CUSTOM_SECRETS_MIN_SQ_VERSION = Version.create("10.4"); private static final Version CLEAN_CODE_TAXONOMY_MIN_SQ_VERSION = Version.create("10.2"); diff --git a/server-connection/src/test/java/org/sonarsource/sonarlint/core/serverconnection/PluginsSynchronizerTests.java b/server-connection/src/test/java/org/sonarsource/sonarlint/core/serverconnection/PluginsSynchronizerTests.java index 104181f1ec..e9b01f7b39 100644 --- a/server-connection/src/test/java/org/sonarsource/sonarlint/core/serverconnection/PluginsSynchronizerTests.java +++ b/server-connection/src/test/java/org/sonarsource/sonarlint/core/serverconnection/PluginsSynchronizerTests.java @@ -55,7 +55,7 @@ void should_synchronize_plugins(@TempDir Path tmp) throws Exception { mockServer.addStringResponse("/api/plugins/download?plugin=javascript", "content-js"); underTest = new PluginsSynchronizer(Set.of(Language.JAVA, Language.JS), new ConnectionStorage(dest, tmp, "connectionId"), emptySet()); - var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null)); + var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null), false); var references = ProtobufFileUtil.readFile(dest.resolve("636f6e6e656374696f6e4964/plugins/plugin_references.pb"), PluginReferences.parser()); assertThat(references.getPluginsByKeyMap().values()).extracting("key", "hash", "filename") @@ -76,10 +76,10 @@ void should_not_synchronize_an_up_to_date_plugin(@TempDir Path tmp) throws Excep "]}"); mockServer.addStringResponse("/api/plugins/download?plugin=java", "content-java"); underTest = new PluginsSynchronizer(Set.of(Language.JAVA), new ConnectionStorage(dest, tmp, "connectionId"), emptySet()); - underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null)); + underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null), false); mockServer.removeResponse("/api/plugins/download?plugin=java"); - var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null)); + var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null), false); assertThat(anyPluginUpdated).isFalse(); } @@ -93,13 +93,13 @@ void should_synchronize_a_plugin_when_hash_is_different(@TempDir Path tmp) throw "]}"); mockServer.addStringResponse("/api/plugins/download?plugin=java", "content-java"); underTest = new PluginsSynchronizer(Set.of(Language.JAVA), new ConnectionStorage(dest, tmp, "connectionId"), emptySet()); - underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null)); + underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null), false); mockServer.addStringResponse("/api/plugins/installed", "{\"plugins\": [" + "{\"key\": \"java\", \"hash\": \"79dba9cab72d8d31767f47c03d169598\", \"filename\": \"sonar-java-plugin-5.14.0.18485.jar\", \"sonarLintSupported\": true}" + "]}"); mockServer.addStringResponse("/api/plugins/download?plugin=java", "content-java2"); - var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null)); + var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null), false); var references = ProtobufFileUtil.readFile(dest.resolve("636f6e6e656374696f6e4964/plugins/plugin_references.pb"), PluginReferences.parser()); assertThat(references.getPluginsByKeyMap().values()).extracting("key", "hash", "filename") @@ -116,7 +116,7 @@ void should_not_synchronize_plugins_that_do_not_support_sonarlint(@TempDir Path "]}"); underTest = new PluginsSynchronizer(Set.of(Language.JAVA), new ConnectionStorage(dest, dest, "connectionId"), Set.of()); - var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null)); + var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null), false); assertThat(dest.resolve("636f6e6e656374696f6e4964/plugins/plugin_references.pb")).doesNotExist(); assertThat(dest.resolve("636f6e6e656374696f6e4964/plugins/sonar-java-plugin-5.13.1.18282.jar")).doesNotExist(); @@ -130,7 +130,7 @@ void should_not_synchronize_plugins_with_unsupported_version(@TempDir Path dest) "]}"); underTest = new PluginsSynchronizer(Set.of(Language.JAVA), new ConnectionStorage(dest, dest, "connectionId"), Set.of()); - var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null)); + var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null), false); assertThat(dest.resolve("636f6e6e656374696f6e4964/plugins/plugin_references.pb")).doesNotExist(); assertThat(dest.resolve("636f6e6e656374696f6e4964/plugins/sonar-java-plugin-5.12.0.jar")).doesNotExist(); @@ -144,7 +144,7 @@ void should_not_synchronize_embedded_plugins(@TempDir Path dest) { "]}"); underTest = new PluginsSynchronizer(Set.of(Language.JAVA), new ConnectionStorage(dest, dest, "connectionId"), Set.of("java")); - var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null)); + var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null), false); assertThat(dest.resolve("636f6e6e656374696f6e4964/plugins/plugin_references.pb")).doesNotExist(); assertThat(dest.resolve("636f6e6e656374696f6e4964/plugins/sonar-java-plugin-5.13.1.18282.jar")).doesNotExist(); @@ -159,7 +159,7 @@ void should_not_synchronize_plugins_for_not_enabled_languages(@TempDir Path tmp) "]}"); underTest = new PluginsSynchronizer(Set.of(Language.JS), new ConnectionStorage(dest, tmp, "connectionId"), emptySet()); - var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null)); + var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null), false); assertThat(dest.resolve("636f6e6e656374696f6e4964/plugins/plugin_references.pb")).doesNotExist(); assertThat(dest.resolve("636f6e6e656374696f6e4964/plugins/sonar-java-plugin-5.13.1.18282.jar")).doesNotExist(); @@ -175,7 +175,7 @@ void should_synchronize_unknown_plugins_for_custom_rules(@TempDir Path tmp) { mockServer.addStringResponse("/api/plugins/download?plugin=java-custom", "content-java-custom"); underTest = new PluginsSynchronizer(Set.of(Language.JS), new ConnectionStorage(dest, tmp, "connectionId"), emptySet()); - var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null)); + var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null), false); var references = ProtobufFileUtil.readFile(dest.resolve("636f6e6e656374696f6e4964/plugins/plugin_references.pb"), PluginReferences.parser()); assertThat(references.getPluginsByKeyMap().values()).extracting("key", "hash", "filename") @@ -195,7 +195,7 @@ void should_synchronize_the_old_typescript_plugin_if_language_enabled(@TempDir P mockServer.addStringResponse("/api/plugins/download?plugin=typescript", "content-ts"); underTest = new PluginsSynchronizer(Set.of(Language.TS), new ConnectionStorage(dest, tmp, "connectionId"), emptySet()); - var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null)); + var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null), false); var references = ProtobufFileUtil.readFile(dest.resolve("636f6e6e656374696f6e4964/plugins/plugin_references.pb"), PluginReferences.parser()); assertThat(references.getPluginsByKeyMap().values()).extracting("key", "hash", "filename") @@ -214,10 +214,51 @@ void should_not_synchronize_the_old_typescript_plugin_if_language_not_enabled(@T "]}"); underTest = new PluginsSynchronizer(Set.of(Language.JAVA), new ConnectionStorage(dest, tmp, "connectionId"), emptySet()); - var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null)); + var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null), false); assertThat(dest.resolve("636f6e6e656374696f6e4964/plugins/636f6e6e656374696f6e4964/plugin_references.pb")).doesNotExist(); assertThat(dest.resolve("636f6e6e656374696f6e4964/plugins/sonar-typescript-plugin-1.9.0.3766.jar")).doesNotExist(); assertThat(anyPluginUpdated).isFalse(); } + + /** + * Emulating SonarQube 10.3 where custom secrets are not enabled and `sonar-text` is embedded and + * `sonar-text-enterprise` not downloaded because it is not supporting SonarLint yet! + */ + @Test + void should_not_synchronize_sonar_text_pre_104(@TempDir Path dest) { + mockServer.addStringResponse("/api/plugins/installed", "{\"plugins\": [" + + "{\"key\": \"text\", \"hash\": \"de5308f43260d357acc97712ce4c5475\", \"filename\": \"sonar-text-plugin-1.2.3.4.jar\", \"sonarLintSupported\": true}," + + "{\"key\": \"textenterprise\", \"hash\": \"de5308f43260d357acc97712ce4c5475\", \"filename\": \"sonar-text-enterprise-plugin-5.6.7.8.jar\", \"sonarLintSupported\": false}" + + "]}"); + + underTest = new PluginsSynchronizer(Set.of(Language.SECRETS), new ConnectionStorage(dest, dest, "connectionId"), Set.of("text")); + var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null), false); + + assertThat(dest.resolve("636f6e6e656374696f6e4964/plugins/plugin_references.pb")).doesNotExist(); + assertThat(dest.resolve("636f6e6e656374696f6e4964/plugins/sonar-text-plugin-1.2.3.4.jar")).doesNotExist(); + assertThat(anyPluginUpdated).isFalse(); + } + + /** + * Emulating SonarQube 10.4 where custom secrets are enabled and `sonar-text` is embedded but will be downloaded + * alongside `sonar-text-enterprise` because it is now supporting SonarLint! + */ + @Test + void should_not_synchronize_sonar_text_post_103(@TempDir Path dest) { + mockServer.addStringResponse("/api/plugins/installed", "{\"plugins\": [" + + "{\"key\": \"text\", \"hash\": \"de5308f43260d357acc97712ce4c5475\", \"filename\": \"sonar-text-plugin-1.2.3.4.jar\", \"sonarLintSupported\": true}," + + "{\"key\": \"textenterprise\", \"hash\": \"de5308f43260d357acc97712ce4c5475\", \"filename\": \"sonar-text-enterprise-plugin-5.6.7.8.jar\", \"sonarLintSupported\": true}" + + "]}"); + mockServer.addStringResponse("/api/plugins/download?plugin=text", "content-text"); + mockServer.addStringResponse("/api/plugins/download?plugin=textenterprise", "content-textenterprise"); + + underTest = new PluginsSynchronizer(Set.of(Language.SECRETS), new ConnectionStorage(dest, dest, "connectionId"), Set.of("text")); + var anyPluginUpdated = underTest.synchronize(new ServerApi(mockServer.serverApiHelper()), new ProgressMonitor(null), true); + + assertThat(dest.resolve("636f6e6e656374696f6e4964/plugins/plugin_references.pb")).exists(); + assertThat(dest.resolve("636f6e6e656374696f6e4964/plugins/sonar-text-plugin-1.2.3.4.jar")).exists(); + assertThat(dest.resolve("636f6e6e656374696f6e4964/plugins/sonar-text-enterprise-plugin-5.6.7.8.jar")).exists(); + assertThat(anyPluginUpdated).isTrue(); + } }