From d5416682571f02af82bd2c7216377141aa664ccb Mon Sep 17 00:00:00 2001 From: Eric Giffon Date: Thu, 4 Jan 2024 17:30:49 +0100 Subject: [PATCH] SONARJNKNS-374 Fix withSonarQubeEnv step hanging due to symlinks --- .../plugins/sonar/utils/SonarUtils.java | 25 +++++++++++++- .../plugins/sonar/utils/SonarUtilsTest.java | 34 ++++++++++++++++++- 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/src/main/java/hudson/plugins/sonar/utils/SonarUtils.java b/src/main/java/hudson/plugins/sonar/utils/SonarUtils.java index 4c67bd3f..d61e06a6 100644 --- a/src/main/java/hudson/plugins/sonar/utils/SonarUtils.java +++ b/src/main/java/hudson/plugins/sonar/utils/SonarUtils.java @@ -22,6 +22,7 @@ import com.cloudbees.plugins.credentials.CredentialsProvider; import hudson.EnvVars; import hudson.FilePath; +import hudson.Util; import hudson.model.Action; import hudson.model.Actionable; import hudson.model.Result; @@ -31,6 +32,7 @@ import hudson.plugins.sonar.action.SonarAnalysisAction; import hudson.plugins.sonar.client.HttpClient; import hudson.plugins.sonar.client.WsClient; +import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; @@ -44,6 +46,9 @@ import java.util.stream.Stream; import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import org.apache.tools.ant.DirectoryScanner; +import org.apache.tools.ant.Project; +import org.apache.tools.ant.types.FileSet; import org.jenkinsci.plugins.plaincredentials.StringCredentials; public final class SonarUtils { @@ -94,7 +99,7 @@ public static T getPersistentAction(Actionable actionable, Cl public static Properties extractReportTask(TaskListener listener, FilePath workspace) throws IOException, InterruptedException { FilePath[] candidates = null; if (workspace.exists()) { - candidates = workspace.list("**/" + REPORT_TASK_FILE_NAME); + candidates = find(workspace, "**/" + REPORT_TASK_FILE_NAME); } if (candidates == null || candidates.length == 0) { listener.getLogger().println("WARN: Unable to locate '" + REPORT_TASK_FILE_NAME + "' in the workspace. Did the SonarScanner succeed?"); @@ -114,6 +119,24 @@ public static Properties extractReportTask(TaskListener listener, FilePath works } + /** + * This method does the same as {@link FilePath#list(String)} but does not follow symlinks. + */ + private static FilePath[] find(FilePath workspace, String includes) { + FileSet fs = Util.createFileSet(new File(workspace.getRemote()), includes, null); + fs.setDefaultexcludes(true); + fs.setFollowSymlinks(false); + + DirectoryScanner ds = fs.getDirectoryScanner(new Project()); + String[] includedFiles = ds.getIncludedFiles(); + + FilePath[] candidates = new FilePath[includedFiles.length]; + for (int i = 0; i < candidates.length; i++) { + candidates[i] = new FilePath(new File(workspace.getRemote(), includedFiles[i])); + } + return candidates; + } + @Nullable /** * Collects as much information as it finds from the sonar analysis in the build and adds it as an action to the build. diff --git a/src/test/java/hudson/plugins/sonar/utils/SonarUtilsTest.java b/src/test/java/hudson/plugins/sonar/utils/SonarUtilsTest.java index b126b759..70b0cbba 100644 --- a/src/test/java/hudson/plugins/sonar/utils/SonarUtilsTest.java +++ b/src/test/java/hudson/plugins/sonar/utils/SonarUtilsTest.java @@ -29,17 +29,20 @@ import hudson.plugins.sonar.action.SonarAnalysisAction; import hudson.plugins.sonar.client.HttpClient; import hudson.plugins.sonar.client.WsClient; -import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; import java.io.PrintStream; import java.io.StringReader; import java.util.Arrays; import java.util.Collections; +import java.util.List; +import org.apache.commons.io.FileUtils; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.junit.rules.TemporaryFolder; +import static hudson.plugins.sonar.utils.SonarUtils.REPORT_TASK_FILE_NAME; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -53,6 +56,8 @@ public class SonarUtilsTest { @Rule public ExpectedException exception = ExpectedException.none(); + @Rule + public TemporaryFolder workspaceFolder = new TemporaryFolder(); @Test public void testMajorMinor() { @@ -92,6 +97,33 @@ public void testAddBuildInfoFromLastBuild() throws Exception { assertThat(action.getUrl()).isEqualTo("url1"); } + @Test + public void testAddBuildInfoTo() throws Exception { + AbstractBuild build = mockedBuild("log"); + + File scannerFolder = workspaceFolder.newFolder("scanner"); + FileUtils.writeLines(new File(scannerFolder, REPORT_TASK_FILE_NAME), List.of( + "serverUrl=http://url", + "dashboardUrl=http://url/dashboard?id=test", + "ceTaskId=AYzPsI8CN2oYarIFiK6r" + )); + + SonarInstallation sonarInstallation = new SonarInstallation("inst", "https://url.com", "credentialsId", null, null, null, null, null, + null); + SonarAnalysisAction action = SonarUtils.addBuildInfoTo(build, mock(TaskListener.class), new FilePath(workspaceFolder.getRoot()), + sonarInstallation, "credId", false); + + assertThat(action.getInstallationName()).isEqualTo("inst"); + assertThat(action.getInstallationUrl()).isEqualTo("https://url.com"); + assertThat(action.getCredentialsId()).isEqualTo("credId"); + assertThat(action.isSkipped()).isFalse(); + assertThat(action.getCeTaskId()).isEqualTo("AYzPsI8CN2oYarIFiK6r"); + assertThat(action.getUrl()).isEqualTo("http://url/dashboard?id=test"); + assertThat(action.getServerUrl()).isEqualTo("http://url"); + + verify(build).addAction(action); + } + @Test public void should_mark_build_as_unstable_when_java_warning_is_logged() throws Exception { Run r = mockedBuild("The version of Java (1.8.0_101) you have used to run this analysis is deprecated and we will stop accepting it from October 2020. Please update to at least Java 11.");