From 86394017fb402a1733a7122cb8088ee81675c01c Mon Sep 17 00:00:00 2001 From: Damien Urruty Date: Tue, 30 Jun 2026 19:39:27 +0200 Subject: [PATCH] SLE-1534 Support Shell and Azure Pipelines --- .../res/azurepipelines-sonarlint.xml | 13 +++ .../res/shell-sonarlint.xml | 13 +++ .../AbstractSonarQubeConnectedModeTest.java | 46 +++++++--- .../sq/SonarQubeConnectedModeTest.java | 84 +++++++++++++++++++ .../azurepipelines-simple/azure-pipelines.yml | 7 ++ its/projects/shell/shell-simple/install.sh | 3 + .../META-INF/MANIFEST.MF | 2 +- .../core/analysis/SonarLintLanguage.java | 2 + .../core/internal/utils/SonarLintUtils.java | 6 +- org.sonarlint.eclipse.ui/META-INF/MANIFEST.MF | 2 +- pom.xml | 2 +- target-platforms/commons-build.target | 2 +- 12 files changed, 165 insertions(+), 17 deletions(-) create mode 100644 its/org.sonarlint.eclipse.its.connected.sq/res/azurepipelines-sonarlint.xml create mode 100644 its/org.sonarlint.eclipse.its.connected.sq/res/shell-sonarlint.xml create mode 100644 its/projects/azurepipelines/azurepipelines-simple/azure-pipelines.yml create mode 100644 its/projects/shell/shell-simple/install.sh diff --git a/its/org.sonarlint.eclipse.its.connected.sq/res/azurepipelines-sonarlint.xml b/its/org.sonarlint.eclipse.its.connected.sq/res/azurepipelines-sonarlint.xml new file mode 100644 index 0000000000..bb4afbd0a0 --- /dev/null +++ b/its/org.sonarlint.eclipse.its.connected.sq/res/azurepipelines-sonarlint.xml @@ -0,0 +1,13 @@ + + + SonarLint IT Azure Pipelines + azurepipelines + + + + azurepipelines + S1135 + MAJOR + + + diff --git a/its/org.sonarlint.eclipse.its.connected.sq/res/shell-sonarlint.xml b/its/org.sonarlint.eclipse.its.connected.sq/res/shell-sonarlint.xml new file mode 100644 index 0000000000..0291ec5a8f --- /dev/null +++ b/its/org.sonarlint.eclipse.its.connected.sq/res/shell-sonarlint.xml @@ -0,0 +1,13 @@ + + + SonarLint IT Shell + shell + + + + shell + S6505 + MAJOR + + + diff --git a/its/org.sonarlint.eclipse.its.connected.sq/src/org/sonarlint/eclipse/its/connected/sq/AbstractSonarQubeConnectedModeTest.java b/its/org.sonarlint.eclipse.its.connected.sq/src/org/sonarlint/eclipse/its/connected/sq/AbstractSonarQubeConnectedModeTest.java index d787c4a6e3..03baebb1ee 100644 --- a/its/org.sonarlint.eclipse.its.connected.sq/src/org/sonarlint/eclipse/its/connected/sq/AbstractSonarQubeConnectedModeTest.java +++ b/its/org.sonarlint.eclipse.its.connected.sq/src/org/sonarlint/eclipse/its/connected/sq/AbstractSonarQubeConnectedModeTest.java @@ -19,6 +19,7 @@ */ package org.sonarlint.eclipse.its.connected.sq; +import com.google.gson.JsonParser; import com.sonar.orchestrator.build.MavenBuild; import com.sonar.orchestrator.container.Server; import com.sonar.orchestrator.junit4.OrchestratorRule; @@ -49,6 +50,7 @@ import org.sonarqube.ws.client.HttpConnector; import org.sonarqube.ws.client.WsClient; import org.sonarqube.ws.client.WsClientFactories; +import org.sonarqube.ws.client.languages.ListRequest; import org.sonarqube.ws.client.projects.CreateRequest; import org.sonarqube.ws.client.settings.SetRequest; import org.sonarqube.ws.client.usertokens.GenerateRequest; @@ -74,21 +76,24 @@ public static void prepare(OrchestratorRule orchestrator) { adminWsClient.settings().set(new SetRequest().setKey("sonar.forceAuthentication").setValue("true")); try { - orchestrator.getServer().restoreProfile( - URLLocation.create(FileLocator.toFileURL(FileLocator.find(FrameworkUtil.getBundle(SonarQubeConnectedModeTest.class), new Path("res/java-sonarlint.xml"), null)))); - orchestrator.getServer().restoreProfile( - URLLocation.create(FileLocator.toFileURL(FileLocator.find(FrameworkUtil.getBundle(SonarQubeConnectedModeTest.class), new Path("res/java-sonarlint-new-code.xml"), null)))); + restoreQualityProfile(orchestrator, "res/java-sonarlint.xml"); + restoreQualityProfile(orchestrator, "res/java-sonarlint-new-code.xml"); if (orchestrator.getServer().version().isGreaterThanOrEquals(10, 4)) { - orchestrator.getServer().restoreProfile( - URLLocation.create(FileLocator.toFileURL(FileLocator.find(FrameworkUtil.getBundle(SonarQubeConnectedModeTest.class), new Path("res/custom-secrets.xml"), null)))); + restoreQualityProfile(orchestrator, "res/custom-secrets.xml"); } if (orchestrator.getServer().version().isGreaterThanOrEquals(10, 6)) { - orchestrator.getServer().restoreProfile( - URLLocation.create(FileLocator.toFileURL(FileLocator.find(FrameworkUtil.getBundle(SonarQubeConnectedModeTest.class), new Path("res/java-sonarlint-dbd.xml"), null)))); - orchestrator.getServer().restoreProfile( - URLLocation.create(FileLocator.toFileURL(FileLocator.find(FrameworkUtil.getBundle(SonarQubeConnectedModeTest.class), new Path("res/python-sonarlint-dbd.xml"), null)))); + restoreQualityProfile(orchestrator, "res/java-sonarlint-dbd.xml"); + restoreQualityProfile(orchestrator, "res/python-sonarlint-dbd.xml"); + } + + if (isLanguageSupported("azurepipelines")) { + restoreQualityProfile(orchestrator, "res/azurepipelines-sonarlint.xml"); + } + + if (isLanguageSupported("shell")) { + restoreQualityProfile(orchestrator, "res/shell-sonarlint.xml"); } } catch (IOException e) { fail("Unable to load quality profile", e); @@ -109,6 +114,22 @@ protected static WsClient newAdminWsClient(Server server) { .build()); } + protected static boolean isLanguageSupported(String languageKey) { + var response = JsonParser.parseString(adminWsClient.languages().list(new ListRequest().setQ(languageKey))).getAsJsonObject(); + var languages = response.getAsJsonArray("languages"); + if (languages == null) { + return false; + } + + for (var language : languages) { + var key = language.getAsJsonObject().get("key"); + if (key != null && languageKey.equals(key.getAsString())) { + return true; + } + } + return false; + } + /** Create a project on SonarQube via Web API with corresponding quality profile assigned */ public static void createProjectOnSonarQube(OrchestratorRule orchestrator, String projectKey, String qualityProfile) { adminWsClient.projects() @@ -207,6 +228,11 @@ protected static void createConnectionAndBindProject(OrchestratorRule orchestrat projectBindingWizard.finish(); } + private static void restoreQualityProfile(OrchestratorRule orchestrator, String resourcePath) throws IOException { + orchestrator.getServer().restoreProfile( + URLLocation.create(FileLocator.toFileURL(FileLocator.find(FrameworkUtil.getBundle(SonarQubeConnectedModeTest.class), new Path(resourcePath), null)))); + } + protected static void bindProjectFromContextMenu(Project project, String projectKey) { new ContextMenu(project.getTreeItem()).getItem("SonarQube", "Bind to SonarQube (Server, Cloud)...").select(); diff --git a/its/org.sonarlint.eclipse.its.connected.sq/src/org/sonarlint/eclipse/its/connected/sq/SonarQubeConnectedModeTest.java b/its/org.sonarlint.eclipse.its.connected.sq/src/org/sonarlint/eclipse/its/connected/sq/SonarQubeConnectedModeTest.java index c572f29e43..f877ef01c3 100644 --- a/its/org.sonarlint.eclipse.its.connected.sq/src/org/sonarlint/eclipse/its/connected/sq/SonarQubeConnectedModeTest.java +++ b/its/org.sonarlint.eclipse.its.connected.sq/src/org/sonarlint/eclipse/its/connected/sq/SonarQubeConnectedModeTest.java @@ -90,6 +90,12 @@ public class SonarQubeConnectedModeTest extends AbstractSonarQubeConnectedModeTe private static final String MAVEN2_PROJECT_KEY = "maven2"; private static final String MAVEN_TAINT_PROJECT_KEY = "maven-taint"; private static final String DBD_PROJECT_KEY = "dbd"; + private static final String AZUREPIPELINES_PROJECT_KEY = "azurepipelines-simple"; + private static final String AZUREPIPELINES_QUALITY_PROFILE = "SonarLint IT Azure Pipelines"; + private static final String AZUREPIPELINES_LANGUAGE_KEY = "azurepipelines"; + private static final String SHELL_PROJECT_KEY = "shell-simple"; + private static final String SHELL_QUALITY_PROFILE = "SonarLint IT Shell"; + private static final String SHELL_LANGUAGE_KEY = "shell"; private static final String CUSTOM_SECRETS_PROJECT_KEY = "secrets-custom"; private static final String INSUFFICIENT_PERMISSION_USER = "iHaveNoRights"; private static final MarkerDescriptionMatcher ISSUE_MATCHER = new MarkerDescriptionMatcher( @@ -612,6 +618,68 @@ public void test_Java_Python_DBD() { new DefaultEditor().close(); } + @Test + public void test_AzurePipelines() { + Assume.assumeTrue(isLanguageSupported(AZUREPIPELINES_LANGUAGE_KEY)); + + adminWsClient.projects() + .create(new CreateRequest() + .setName(AZUREPIPELINES_PROJECT_KEY) + .setProject(AZUREPIPELINES_PROJECT_KEY)); + orchestrator.getServer().associateProjectToQualityProfile(AZUREPIPELINES_PROJECT_KEY, AZUREPIPELINES_LANGUAGE_KEY, AZUREPIPELINES_QUALITY_PROFILE); + + new JavaPerspective().open(); + var rootProject = importExistingProjectIntoWorkspace("azurepipelines/azurepipelines-simple", AZUREPIPELINES_PROJECT_KEY); + closeUnsupportedLanguageNotifications(); + + var onTheFlyView = new OnTheFlyView(); + onTheFlyView.open(); + + openFileAndWaitForAnalysisCompletion(rootProject.getResource("azure-pipelines.yml")); + closeUnsupportedLanguageNotifications(); + waitForNoSonarLintMarkers(onTheFlyView); + new DefaultEditor().close(); + + createConnectionAndBindProject(orchestrator, AZUREPIPELINES_PROJECT_KEY); + shellByName("SonarQube - Binding Suggestion").ifPresent(shell -> new DefaultLink(shell, "Don't ask again").click()); + waitForAnalysisReady(AZUREPIPELINES_PROJECT_KEY); + + openFileAndWaitForAnalysisCompletion(rootProject.getResource("azure-pipelines.yml")); + waitForSingleIssueOnResource(onTheFlyView, "azure-pipelines.yml"); + new DefaultEditor().close(); + } + + @Test + public void test_Shell() { + Assume.assumeTrue(isLanguageSupported(SHELL_LANGUAGE_KEY)); + + adminWsClient.projects() + .create(new CreateRequest() + .setName(SHELL_PROJECT_KEY) + .setProject(SHELL_PROJECT_KEY)); + orchestrator.getServer().associateProjectToQualityProfile(SHELL_PROJECT_KEY, SHELL_LANGUAGE_KEY, SHELL_QUALITY_PROFILE); + + new JavaPerspective().open(); + var rootProject = importExistingProjectIntoWorkspace("shell/shell-simple", SHELL_PROJECT_KEY); + closeUnsupportedLanguageNotifications(); + + var onTheFlyView = new OnTheFlyView(); + onTheFlyView.open(); + + openFileAndWaitForAnalysisCompletion(rootProject.getResource("install.sh")); + closeUnsupportedLanguageNotifications(); + waitForNoSonarLintMarkers(onTheFlyView); + new DefaultEditor().close(); + + createConnectionAndBindProject(orchestrator, SHELL_PROJECT_KEY); + shellByName("SonarQube - Binding Suggestion").ifPresent(shell -> new DefaultLink(shell, "Don't ask again").click()); + waitForAnalysisReady(SHELL_PROJECT_KEY); + + openFileAndWaitForAnalysisCompletion(rootProject.getResource("install.sh")); + waitForSingleIssueOnResource(onTheFlyView, "install.sh"); + new DefaultEditor().close(); + } + @Test public void test_custom_secrets() { // INFO: Since 10.4 this is supported for SonarLint for Eclipse! @@ -687,4 +755,20 @@ private static void setNewCodePeriodToPreviousVersion(String projectKey) { .setParam("type", "PREVIOUS_VERSION") .execute(); } + + private static void closeUnsupportedLanguageNotifications() { + shellByName("SonarQube for Eclipse - Language could not be analyzed").ifPresent(DefaultShell::close); + shellByName("SonarQube for Eclipse - Languages could not be analyzed").ifPresent(DefaultShell::close); + } + + private static void waitForSingleIssueOnResource(OnTheFlyView onTheFlyView, String resourceName) { + await().atMost(20, TimeUnit.SECONDS).untilAsserted(() -> { + var issues = onTheFlyView.getIssues(); + assertThat(issues).hasSize(1); + assertThat(issues) + .extracting(SonarLintIssueMarker::getResource) + .containsOnly(resourceName); + assertThat(issues.get(0).getDescription()).isNotBlank(); + }); + } } diff --git a/its/projects/azurepipelines/azurepipelines-simple/azure-pipelines.yml b/its/projects/azurepipelines/azurepipelines-simple/azure-pipelines.yml new file mode 100644 index 0000000000..464e55da6a --- /dev/null +++ b/its/projects/azurepipelines/azurepipelines-simple/azure-pipelines.yml @@ -0,0 +1,7 @@ +trigger: + - main + +steps: + - script: echo "Build started" + displayName: "Run initial script" + # TODO: Add deployment step diff --git a/its/projects/shell/shell-simple/install.sh b/its/projects/shell/shell-simple/install.sh new file mode 100644 index 0000000000..05e5a27eae --- /dev/null +++ b/its/projects/shell/shell-simple/install.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +npm install diff --git a/org.sonarlint.eclipse.core/META-INF/MANIFEST.MF b/org.sonarlint.eclipse.core/META-INF/MANIFEST.MF index f9fe87eb0c..94770eb038 100644 --- a/org.sonarlint.eclipse.core/META-INF/MANIFEST.MF +++ b/org.sonarlint.eclipse.core/META-INF/MANIFEST.MF @@ -43,6 +43,6 @@ Require-Bundle: org.eclipse.equinox.security;resolution:=optional, org.eclipse.jdt.annotation;resolution:=optional, org.eclipse.text, org.eclipse.wildwebdeveloper.embedder.node;resolution:=optional, - org.sonarsource.sonarlint.core.sonarlint-java-client-osgi;bundle-version="[11.5.0,11.6.0)" + org.sonarsource.sonarlint.core.sonarlint-java-client-osgi;bundle-version="[11.6.0,11.7.0)" Bundle-ActivationPolicy: lazy Bundle-RequiredExecutionEnvironment: JavaSE-11 diff --git a/org.sonarlint.eclipse.core/src/org/sonarlint/eclipse/core/analysis/SonarLintLanguage.java b/org.sonarlint.eclipse.core/src/org/sonarlint/eclipse/core/analysis/SonarLintLanguage.java index 705e9f61dd..0185f7f26c 100644 --- a/org.sonarlint.eclipse.core/src/org/sonarlint/eclipse/core/analysis/SonarLintLanguage.java +++ b/org.sonarlint.eclipse.core/src/org/sonarlint/eclipse/core/analysis/SonarLintLanguage.java @@ -27,6 +27,7 @@ public enum SonarLintLanguage { ABAP, APEX, + AZUREPIPELINES, C, CPP, CSS, @@ -45,6 +46,7 @@ public enum SonarLintLanguage { RUBY, SCALA, SECRETS, + SHELL, TSQL, TS, XML; diff --git a/org.sonarlint.eclipse.core/src/org/sonarlint/eclipse/core/internal/utils/SonarLintUtils.java b/org.sonarlint.eclipse.core/src/org/sonarlint/eclipse/core/internal/utils/SonarLintUtils.java index 1d690c98fa..b87eda9640 100644 --- a/org.sonarlint.eclipse.core/src/org/sonarlint/eclipse/core/internal/utils/SonarLintUtils.java +++ b/org.sonarlint.eclipse.core/src/org/sonarlint/eclipse/core/internal/utils/SonarLintUtils.java @@ -69,9 +69,9 @@ public class SonarLintUtils { private static final Set OPTIONAL_LANGUAGES = EnumSet.of(SonarLintLanguage.JAVA, SonarLintLanguage.JSP, SonarLintLanguage.C, SonarLintLanguage.CPP); private static final Set DEFAULT_CONNECTED_LANGUAGES = EnumSet.of(SonarLintLanguage.ABAP, - SonarLintLanguage.APEX, SonarLintLanguage.COBOL, SonarLintLanguage.JCL, SonarLintLanguage.KOTLIN, - SonarLintLanguage.PLI, SonarLintLanguage.PLSQL, SonarLintLanguage.RPG, SonarLintLanguage.RUBY, - SonarLintLanguage.SCALA, SonarLintLanguage.TSQL); + SonarLintLanguage.APEX, SonarLintLanguage.AZUREPIPELINES, SonarLintLanguage.COBOL, SonarLintLanguage.JCL, + SonarLintLanguage.KOTLIN, SonarLintLanguage.PLI, SonarLintLanguage.PLSQL, SonarLintLanguage.RPG, + SonarLintLanguage.RUBY, SonarLintLanguage.SCALA, SonarLintLanguage.SHELL, SonarLintLanguage.TSQL); private SonarLintUtils() { // utility class, forbidden constructor diff --git a/org.sonarlint.eclipse.ui/META-INF/MANIFEST.MF b/org.sonarlint.eclipse.ui/META-INF/MANIFEST.MF index 4d79f57339..93d35ed7e1 100644 --- a/org.sonarlint.eclipse.ui/META-INF/MANIFEST.MF +++ b/org.sonarlint.eclipse.ui/META-INF/MANIFEST.MF @@ -27,7 +27,7 @@ Require-Bundle: org.eclipse.core.runtime, org.eclipse.text, org.eclipse.compare, org.eclipse.core.expressions, - org.sonarsource.sonarlint.core.sonarlint-java-client-osgi;bundle-version="[11.5.0,11.6.0)" + org.sonarsource.sonarlint.core.sonarlint-java-client-osgi;bundle-version="[11.6.0,11.7.0)" Export-Package: org.sonarlint.eclipse.ui.internal;x-friends:="org.sonarlint.eclipse.core.tests", org.sonarlint.eclipse.ui.internal.backend;x-friends:="org.sonarlint.eclipse.core.tests", org.sonarlint.eclipse.ui.internal.notifications;x-friends:="org.sonarlint.eclipse.core.tests", diff --git a/pom.xml b/pom.xml index b41bd8a182..ab668983e6 100644 --- a/pom.xml +++ b/pom.xml @@ -77,7 +77,7 @@ 4.0.12 - 11.5.0.85871 + 11.6.0.85933 11 diff --git a/target-platforms/commons-build.target b/target-platforms/commons-build.target index 506c2b8857..c3439911ab 100644 --- a/target-platforms/commons-build.target +++ b/target-platforms/commons-build.target @@ -12,7 +12,7 @@ org.sonarsource.sonarlint.core sonarlint-java-client-osgi - 11.5.0.85871 + 11.6.0.85933 jar