diff options
| author | Elena Vilchik | 2018-06-25 15:20:12 +0200 | 
|---|---|---|
| committer | GitHub | 2018-06-25 15:20:12 +0200 | 
| commit | 7a31cb18a718601f31766cf8e02c6ad85f3916f0 (patch) | |
| tree | 3f8c71f48b8e23c1af06a53ab405e5bb23357900 | |
| parent | b45e6c9923f9ab8a27a81430dfea81d5302922d3 (diff) | |
| download | sonar-css-7a31cb18a718601f31766cf8e02c6ad85f3916f0.tar.bz2 | |
Not fail analysis if Node is not available (#73)
6 files changed, 125 insertions, 19 deletions
| diff --git a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssRuleSensor.java b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssRuleSensor.java index c669b30..d13038e 100644 --- a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssRuleSensor.java +++ b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssRuleSensor.java @@ -30,6 +30,7 @@ import java.nio.file.Paths;  import java.util.Collections;  import java.util.regex.Matcher;  import java.util.regex.Pattern; +import org.apache.commons.io.IOUtils;  import org.sonar.api.batch.fs.FileSystem;  import org.sonar.api.batch.fs.InputFile;  import org.sonar.api.batch.rule.CheckFactory; @@ -39,6 +40,8 @@ import org.sonar.api.batch.sensor.SensorDescriptor;  import org.sonar.api.batch.sensor.issue.NewIssue;  import org.sonar.api.batch.sensor.issue.NewIssueLocation;  import org.sonar.api.rule.RuleKey; +import org.sonar.api.utils.log.Logger; +import org.sonar.api.utils.log.Loggers;  import org.sonar.css.plugin.CssRules.StylelintConfig;  import org.sonar.css.plugin.StylelintReport.Issue;  import org.sonar.css.plugin.StylelintReport.IssuesPerFile; @@ -46,6 +49,9 @@ import org.sonar.css.plugin.bundle.BundleHandler;  public class CssRuleSensor implements Sensor { +  private static final Logger LOG = Loggers.get(CssRuleSensor.class); +  private static final int MIN_NODE_VERSION = 6; +    private final BundleHandler bundleHandler;    private final CssRules cssRules;    private final LinterCommandProvider linterCommandProvider; @@ -65,6 +71,10 @@ public class CssRuleSensor implements Sensor {    @Override    public void execute(SensorContext context) { +    if (!checkCompatibleNodeVersion(context)) { +      return; +    } +      File deployDestination = context.fileSystem().workDir();      bundleHandler.deployBundle(deployDestination); @@ -89,6 +99,39 @@ public class CssRuleSensor implements Sensor {      }    } +  private boolean checkCompatibleNodeVersion(SensorContext context) { +    String nodeExecutable = linterCommandProvider.nodeExecutable(context.config()); +    LOG.debug("Checking node version"); +    String messageSuffix = "No CSS files will be analyzed."; + +    String version; +    try { +      Process process = Runtime.getRuntime().exec(nodeExecutable + " -v"); +      version = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8).trim(); +    } catch (Exception e) { +      LOG.error("Failed to get Node.js version. " + messageSuffix, e); +      return false; +    } + +    Pattern versionPattern = Pattern.compile("v?(\\d+)\\.\\d+\\.\\d+"); +    Matcher versionMatcher = versionPattern.matcher(version); +    if (versionMatcher.matches()) { +      int major = Integer.parseInt(versionMatcher.group(1)); +      if (major < MIN_NODE_VERSION) { +        String message = String.format("Only Node.js v%s or later is supported, got %s. %s", MIN_NODE_VERSION, version, messageSuffix); +        LOG.error(message); +        return false; +      } +    } else { +      String message = String.format("Failed to parse Node.js version, got '%s'. %s", version, messageSuffix); +      LOG.error(message); +      return false; +    } + +    LOG.debug(String.format("Using Node.js %s", version)); +    return true; +  } +    private void createConfig(File deployDestination) throws IOException {      String configPath = linterCommandProvider.configPath(deployDestination);      StylelintConfig config = cssRules.getConfig(); diff --git a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/LinterCommandProvider.java b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/LinterCommandProvider.java index 3ca334a..761b88d 100644 --- a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/LinterCommandProvider.java +++ b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/LinterCommandProvider.java @@ -21,10 +21,13 @@ package org.sonar.css.plugin;  import java.io.File;  import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.config.Configuration;  public interface LinterCommandProvider {    String[] commandParts(File deployDestination, SensorContext context);    String configPath(File deployDestination); + +  String nodeExecutable(Configuration configuration);  } diff --git a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/StylelintCommandProvider.java b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/StylelintCommandProvider.java index d97293d..b2f50b5 100644 --- a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/StylelintCommandProvider.java +++ b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/StylelintCommandProvider.java @@ -23,11 +23,13 @@ import java.io.File;  import java.nio.file.Paths;  import org.sonar.api.batch.ScannerSide;  import org.sonar.api.batch.sensor.SensorContext; +import org.sonar.api.config.Configuration;  @ScannerSide  public class StylelintCommandProvider implements LinterCommandProvider {    private static final String CONFIG_PATH = "css-bundle/stylelintconfig.json"; +  private static final String NODE_EXECUTABLE = "node";    @Override    public String[] commandParts(File deployDestination, SensorContext context) { @@ -38,7 +40,7 @@ public class StylelintCommandProvider implements LinterCommandProvider {      filesToAnalyze = filesToAnalyze.replace("TOREPLACE", filesGlob);      return new String[]{ -      "node", +      nodeExecutable(context.config()),        new File(deployDestination, "css-bundle/node_modules/stylelint/bin/stylelint").getAbsolutePath(),        filesToAnalyze,        "--config", new File(deployDestination, CONFIG_PATH).getAbsolutePath(), @@ -50,4 +52,9 @@ public class StylelintCommandProvider implements LinterCommandProvider {    public String configPath(File deployDestination) {      return new File(deployDestination, CONFIG_PATH).getAbsolutePath();    } + +  @Override +  public String nodeExecutable(Configuration configuration) { +    return NODE_EXECUTABLE; +  }  } diff --git a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRuleSensorTest.java b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRuleSensorTest.java index 06295d7..030a987 100644 --- a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRuleSensorTest.java +++ b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRuleSensorTest.java @@ -26,6 +26,7 @@ import java.nio.charset.StandardCharsets;  import java.nio.file.Files;  import java.nio.file.Path;  import java.nio.file.Paths; +import org.junit.Before;  import org.junit.Rule;  import org.junit.Test;  import org.junit.rules.ExpectedException; @@ -38,6 +39,9 @@ import org.sonar.api.batch.sensor.SensorContext;  import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor;  import org.sonar.api.batch.sensor.internal.SensorContextTester;  import org.sonar.api.batch.sensor.issue.Issue; +import org.sonar.api.config.Configuration; +import org.sonar.api.utils.log.LogTester; +import org.sonar.api.utils.log.LoggerLevel;  import org.sonar.css.plugin.bundle.BundleHandler;  import org.sonar.css.plugin.bundle.CssBundleHandler; @@ -46,13 +50,25 @@ import static org.assertj.core.api.Assertions.assertThat;  public class CssRuleSensorTest {    @Rule +  public final LogTester logTester = new LogTester(); + +  @Rule +  public TemporaryFolder tmpDir = new TemporaryFolder(); + +  @Rule    public ExpectedException thrown= ExpectedException.none();    private static CheckFactory checkFactory = new CheckFactory(new TestActiveRules("S4647")); -  private File BASE_DIR = new File("src/test/resources").getAbsoluteFile(); -  @Rule -  public TemporaryFolder tmpDir = new TemporaryFolder(); +  private static final File BASE_DIR = new File("src/test/resources").getAbsoluteFile(); + +  private SensorContextTester context = SensorContextTester.create(BASE_DIR); +  private DefaultInputFile inputFile = createInputFile(context, "some css content\n on 2 lines", "dir/file.css"); + +  @Before +  public void setUp() throws Exception { +    context.fileSystem().setWorkDir(tmpDir.getRoot().toPath()); +  }    @Test    public void test_descriptor() { @@ -65,11 +81,8 @@ public class CssRuleSensorTest {    @Test    public void test_execute() throws IOException { -    SensorContextTester context = SensorContextTester.create(BASE_DIR); -    context.fileSystem().setWorkDir(tmpDir.getRoot().toPath()); -    DefaultInputFile inputFile = createInputFile(context, "some css content\n on 2 lines", "dir/file.css"); -    TestLinterCommandProvider rulesExecution = TestLinterCommandProvider.nodeScript("/executables/mockStylelint.js", inputFile.absolutePath()); -    CssRuleSensor sensor = new CssRuleSensor(new TestBundleHandler(), checkFactory, rulesExecution); +    TestLinterCommandProvider commandProvider = getCommandProvider(); +    CssRuleSensor sensor = new CssRuleSensor(new TestBundleHandler(), checkFactory, commandProvider);      sensor.execute(context);      assertThat(context.allIssues()).hasSize(1); @@ -81,15 +94,45 @@ public class CssRuleSensorTest {    }    @Test +  public void test_invalid_node() throws IOException { +    TestLinterCommandProvider commandProvider = getCommandProvider(); +    commandProvider.nodeExecutable += " " + TestLinterCommandProvider.resourceScript("/executables/invalidNodeVersion.js"); +    CssRuleSensor sensor = new CssRuleSensor(new TestBundleHandler(), checkFactory, commandProvider); +    sensor.execute(context); + +    assertThat(context.allIssues()).hasSize(0); +    assertThat(logTester.logs(LoggerLevel.ERROR)).contains("Failed to parse Node.js version, got 'Invalid version'. No CSS files will be analyzed."); +  } + +  @Test +  public void test_no_node() throws IOException { +    TestLinterCommandProvider commandProvider = getCommandProvider(); +    commandProvider.nodeExecutable = TestLinterCommandProvider.resourceScript("/executables/invalidNodeVersion.js"); +    CssRuleSensor sensor = new CssRuleSensor(new TestBundleHandler(), checkFactory, commandProvider); +    sensor.execute(context); + +    assertThat(context.allIssues()).hasSize(0); +    assertThat(logTester.logs(LoggerLevel.ERROR)).contains("Failed to get Node.js version. No CSS files will be analyzed."); +  } + +  @Test +  public void test_old_node() throws IOException { +    TestLinterCommandProvider commandProvider = getCommandProvider(); +    commandProvider.nodeExecutable += " " + TestLinterCommandProvider.resourceScript("/executables/oldNodeVersion.js"); +    CssRuleSensor sensor = new CssRuleSensor(new TestBundleHandler(), checkFactory, commandProvider); +    sensor.execute(context); + +    assertThat(context.allIssues()).hasSize(0); +    assertThat(logTester.logs(LoggerLevel.ERROR)).contains("Only Node.js v6 or later is supported, got 3.2.1. No CSS files will be analyzed."); +  } + +  @Test    public void test_error() throws IOException {      thrown.expect(IllegalStateException.class);      thrown.expectMessage("Failed to parse json result of external process execution"); -    SensorContextTester context = SensorContextTester.create(BASE_DIR); -    context.fileSystem().setWorkDir(tmpDir.getRoot().toPath()); -    DefaultInputFile inputFile = createInputFile(context, "some css content\n on 2 lines", "dir/file.css"); -    TestLinterCommandProvider rulesExecution = TestLinterCommandProvider.nodeScript("/executables/mockError.js", inputFile.absolutePath()); -    CssRuleSensor sensor = new CssRuleSensor(new TestBundleHandler(), checkFactory, rulesExecution); +    TestLinterCommandProvider commandProvider = new TestLinterCommandProvider().nodeScript("/executables/mockError.js", inputFile.absolutePath()); +    CssRuleSensor sensor = new CssRuleSensor(new TestBundleHandler(), checkFactory, commandProvider);      sensor.execute(context);    } @@ -106,9 +149,13 @@ public class CssRuleSensorTest {      return inputFile;    } +  private TestLinterCommandProvider getCommandProvider() { +    return new TestLinterCommandProvider().nodeScript("/executables/mockStylelint.js", inputFile.absolutePath()); +  } +    private static class TestLinterCommandProvider implements LinterCommandProvider { -    private static String nodeExecutable = findNodeExecutable(); +    String nodeExecutable = findNodeExecutable();      private String[] elements; @@ -130,10 +177,9 @@ public class CssRuleSensorTest {        }      } -    static TestLinterCommandProvider nodeScript(String script, String args) { -      TestLinterCommandProvider testRulesExecution = new TestLinterCommandProvider(); -      testRulesExecution.elements = new String[]{ nodeExecutable, resourceScript(script), args}; -      return testRulesExecution; +    TestLinterCommandProvider nodeScript(String script, String args) { +      this.elements = new String[]{ nodeExecutable, resourceScript(script), args}; +      return this;      }      @Override @@ -145,6 +191,11 @@ public class CssRuleSensorTest {      public String configPath(File deployDestination) {        return new File(deployDestination, "testconfig.json").getAbsolutePath();      } + +    @Override +    public String nodeExecutable(Configuration configuration) { +      return nodeExecutable; +    }    }    private static class TestBundleHandler implements BundleHandler { diff --git a/sonar-css-plugin/src/test/resources/executables/invalidNodeVersion.js b/sonar-css-plugin/src/test/resources/executables/invalidNodeVersion.js new file mode 100644 index 0000000..772b9f3 --- /dev/null +++ b/sonar-css-plugin/src/test/resources/executables/invalidNodeVersion.js @@ -0,0 +1 @@ +console.log("Invalid version"); diff --git a/sonar-css-plugin/src/test/resources/executables/oldNodeVersion.js b/sonar-css-plugin/src/test/resources/executables/oldNodeVersion.js new file mode 100644 index 0000000..817b98a --- /dev/null +++ b/sonar-css-plugin/src/test/resources/executables/oldNodeVersion.js @@ -0,0 +1 @@ +console.log("3.2.1"); | 
