From a14d3a20b8855871e9728b969a23c44eb2aa6a2e Mon Sep 17 00:00:00 2001 From: dswiecki Date: Sun, 7 Mar 2021 11:43:01 +0100 Subject: [PATCH] Add shellcheck support (#190) * Add basic shellcheck support * Add shellcheck exclude rules * Update junit annotations * Shellcheck review --- .../sputnik/configuration/GeneralOption.java | 5 +- .../pl/touk/sputnik/exec/ExternalProcess.java | 3 +- .../shellcheck/ShellcheckException.java | 11 ++ .../shellcheck/ShellcheckExecutor.java | 47 +++++++ .../shellcheck/ShellcheckProcessor.java | 57 +++++++++ .../ShellcheckProcessorFactory.java | 17 +++ .../shellcheck/ShellcheckResultParser.java | 66 ++++++++++ .../shellcheck/json/ShellcheckMessage.java | 18 +++ .../sputnik/review/filter/ShellFilter.java | 10 ++ .../shellcheck/ShellcheckProcessorTest.java | 117 ++++++++++++++++++ .../ShellcheckResultParserTest.java | 73 +++++++++++ src/test/resources/shellcheck/empty-file.txt | 0 .../resources/shellcheck/no-violations.txt | 1 + .../resources/shellcheck/sample-output.txt | 26 ++++ .../sputnik/noConfigurationFile.properties | 2 + .../shellcheck/testFiles/excludedViolation.sh | 3 + .../testFiles/multipleViolations.sh | 10 ++ .../shellcheck/testFiles/oneViolation.sh | 3 + .../shellcheck/unknown-message-output.txt | 10 ++ 19 files changed, 477 insertions(+), 2 deletions(-) create mode 100644 src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckException.java create mode 100644 src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckExecutor.java create mode 100644 src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckProcessor.java create mode 100644 src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckProcessorFactory.java create mode 100644 src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckResultParser.java create mode 100644 src/main/java/pl/touk/sputnik/processor/shellcheck/json/ShellcheckMessage.java create mode 100644 src/main/java/pl/touk/sputnik/review/filter/ShellFilter.java create mode 100644 src/test/java/pl/touk/sputnik/processor/shellcheck/ShellcheckProcessorTest.java create mode 100644 src/test/java/pl/touk/sputnik/processor/shellcheck/ShellcheckResultParserTest.java create mode 100644 src/test/resources/shellcheck/empty-file.txt create mode 100644 src/test/resources/shellcheck/no-violations.txt create mode 100644 src/test/resources/shellcheck/sample-output.txt create mode 100644 src/test/resources/shellcheck/sputnik/noConfigurationFile.properties create mode 100755 src/test/resources/shellcheck/testFiles/excludedViolation.sh create mode 100755 src/test/resources/shellcheck/testFiles/multipleViolations.sh create mode 100755 src/test/resources/shellcheck/testFiles/oneViolation.sh create mode 100644 src/test/resources/shellcheck/unknown-message-output.txt diff --git a/src/main/java/pl/touk/sputnik/configuration/GeneralOption.java b/src/main/java/pl/touk/sputnik/configuration/GeneralOption.java index ec93c80c..75286f55 100644 --- a/src/main/java/pl/touk/sputnik/configuration/GeneralOption.java +++ b/src/main/java/pl/touk/sputnik/configuration/GeneralOption.java @@ -89,7 +89,10 @@ public enum GeneralOption implements ConfigurationOption { JAVA_TEST_DIR("java.test.dir", "Java root test directory", Paths.SRC_TEST), DETEKT_ENABLED("detekt.enabled", "Detekt enabled", "false"), - DETEKT_CONFIG_FILE("detekt.config.file", "Detekt configuration file location", null); + DETEKT_CONFIG_FILE("detekt.config.file", "Detekt configuration file location", null), + + SHELLCHECK_ENABLED("shellcheck.enabled", "Shellcheck enabled", "false"), + SHELLCHECK_EXCLUDE("shellcheck.exclude", "Shellcheck exclude rules", null); private final String key; private final String description; diff --git a/src/main/java/pl/touk/sputnik/exec/ExternalProcess.java b/src/main/java/pl/touk/sputnik/exec/ExternalProcess.java index adcfd905..0f5dbfd5 100644 --- a/src/main/java/pl/touk/sputnik/exec/ExternalProcess.java +++ b/src/main/java/pl/touk/sputnik/exec/ExternalProcess.java @@ -20,7 +20,8 @@ public String executeCommand(String... args) { return executor().command(args) .timeout(60, TimeUnit.SECONDS) .redirectError(Slf4jStream.of(getClass()).asInfo()) - .readOutput(true).execute() + .readOutput(true) + .execute() .outputUTF8(); } catch (Exception e) { log.warn("Exception while calling command " + Arrays.asList(args) + ": " + e); diff --git a/src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckException.java b/src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckException.java new file mode 100644 index 00000000..83e572f2 --- /dev/null +++ b/src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckException.java @@ -0,0 +1,11 @@ +package pl.touk.sputnik.processor.shellcheck; + +class ShellcheckException extends RuntimeException { + ShellcheckException(String message) { + super(message); + } + + ShellcheckException(String message, Throwable t) { + super(message, t); + } +} diff --git a/src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckExecutor.java b/src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckExecutor.java new file mode 100644 index 00000000..be650c00 --- /dev/null +++ b/src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckExecutor.java @@ -0,0 +1,47 @@ +package pl.touk.sputnik.processor.shellcheck; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.jetbrains.annotations.NotNull; +import pl.touk.sputnik.exec.ExternalProcess; + +import java.util.List; + +@Slf4j +class ShellcheckExecutor { + private static final String SHELLCHECK_EXECUTABLE = "shellcheck"; + private static final String SHELLCHECK_OUTPUT_FORMAT = "-fjson"; + private static final String SHELLCHECK_EXCLUDE_RULES = "-e"; + + private final List excludeRules; + + ShellcheckExecutor(List excludeRules) { + this.excludeRules = excludeRules; + } + + String runOnFile(String filePath) { + log.info("Review on file: {}", filePath); + return new ExternalProcess().executeCommand(buildParams(filePath)); + } + + @NotNull + private String[] buildParams(String filePath) { + List shellcheckArgs = ImmutableList.of( + SHELLCHECK_EXECUTABLE, + SHELLCHECK_OUTPUT_FORMAT); + List filePathArg = ImmutableList.of(filePath); + List excludeRulesArg = getExcludeRulesArgs(); + List allArgs = Lists.newArrayList(Iterables.concat(shellcheckArgs, filePathArg, excludeRulesArg)); + return allArgs.toArray(new String[allArgs.size()]); + } + + private List getExcludeRulesArgs() { + if (excludeRules.isEmpty()) { + return ImmutableList.of(); + } + return ImmutableList.of(SHELLCHECK_EXCLUDE_RULES + StringUtils.join(excludeRules, ",")); + } +} diff --git a/src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckProcessor.java b/src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckProcessor.java new file mode 100644 index 00000000..2278be1a --- /dev/null +++ b/src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckProcessor.java @@ -0,0 +1,57 @@ +package pl.touk.sputnik.processor.shellcheck; + +import lombok.extern.slf4j.Slf4j; +import org.jetbrains.annotations.NotNull; +import pl.touk.sputnik.configuration.Configuration; +import pl.touk.sputnik.configuration.GeneralOption; +import pl.touk.sputnik.processor.tools.externalprocess.ExternalProcessResultParser; +import pl.touk.sputnik.processor.tools.externalprocess.ProcessorRunningExternalProcess; +import pl.touk.sputnik.review.filter.FileFilter; +import pl.touk.sputnik.review.filter.ShellFilter; + +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@Slf4j +class ShellcheckProcessor extends ProcessorRunningExternalProcess { + + private ShellcheckExecutor shellcheckExecutor; + private ShellcheckResultParser shellcheckResultParser; + + ShellcheckProcessor(Configuration configuration) { + shellcheckExecutor = new ShellcheckExecutor(getExcludeRules(configuration)); + shellcheckResultParser = new ShellcheckResultParser(); + } + + @NotNull + @Override + public String getName() { + return "Shellcheck"; + } + + @Override + public FileFilter getReviewFileFilter() { + return new ShellFilter(); + } + + @Override + public ExternalProcessResultParser getParser() { + return shellcheckResultParser; + } + + @Override + public String processFileAndDumpOutput(File fileToReview) { + return shellcheckExecutor.runOnFile(fileToReview.getAbsolutePath()); + } + + @NotNull + private List getExcludeRules(Configuration configuration) { + String excludeRules = configuration.getProperty(GeneralOption.SHELLCHECK_EXCLUDE); + if (excludeRules == null) { + return new ArrayList<>(); + } + return Arrays.asList(excludeRules.split(",")); + } +} diff --git a/src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckProcessorFactory.java b/src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckProcessorFactory.java new file mode 100644 index 00000000..78191353 --- /dev/null +++ b/src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckProcessorFactory.java @@ -0,0 +1,17 @@ +package pl.touk.sputnik.processor.shellcheck; + +import pl.touk.sputnik.configuration.Configuration; +import pl.touk.sputnik.configuration.GeneralOption; +import pl.touk.sputnik.processor.ReviewProcessorFactory; + +public class ShellcheckProcessorFactory implements ReviewProcessorFactory { + @Override + public boolean isEnabled(Configuration configuration) { + return Boolean.valueOf(configuration.getProperty(GeneralOption.SHELLCHECK_ENABLED)); + } + + @Override + public ShellcheckProcessor create(Configuration configuration) { + return new ShellcheckProcessor(configuration); + } +} diff --git a/src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckResultParser.java b/src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckResultParser.java new file mode 100644 index 00000000..904e598f --- /dev/null +++ b/src/main/java/pl/touk/sputnik/processor/shellcheck/ShellcheckResultParser.java @@ -0,0 +1,66 @@ +package pl.touk.sputnik.processor.shellcheck; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.apache.commons.lang3.StringUtils; +import pl.touk.sputnik.processor.shellcheck.json.ShellcheckMessage; +import pl.touk.sputnik.processor.tools.externalprocess.ExternalProcessResultParser; +import pl.touk.sputnik.review.Severity; +import pl.touk.sputnik.review.Violation; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import static java.util.stream.Collectors.toList; + +class ShellcheckResultParser implements ExternalProcessResultParser { + + private final ObjectMapper objectMapper = new ObjectMapper(); + + private static final String SHELLCHECK_ERROR = "error"; + private static final String SHELLCHECK_WARNING = "warning"; + private static final String SHELLCHECK_INFO = "info"; + + @Override + public List parse(String shellcheckOutput) { + if (StringUtils.isEmpty(shellcheckOutput)) { + return Collections.emptyList(); + } + try { + List violationMessages = objectMapper.readValue(shellcheckOutput, + new TypeReference>() { + }); + + return violationMessages + .stream() + .map(violationMessage -> mapToViolation(violationMessage)) + .collect(toList()); + } catch (IOException e) { + throw new ShellcheckException("Error when converting from json format", e); + } + } + + private Violation mapToViolation(ShellcheckMessage violationMessage) { + return new Violation(violationMessage.getFile(), + violationMessage.getLine(), + formatViolationMessage(violationMessage), + shellcheckMessageTypeToSeverity(violationMessage.getLevel())); + } + + private String formatViolationMessage(ShellcheckMessage violationMessage) { + return "[Code:" + violationMessage.getCode() + "] Message: " + violationMessage.getMessage(); + } + + private Severity shellcheckMessageTypeToSeverity(String messageType) { + if (SHELLCHECK_ERROR.equals(messageType)) { + return Severity.ERROR; + } else if (SHELLCHECK_WARNING.equals(messageType)) { + return Severity.WARNING; + } else if (SHELLCHECK_INFO.equals(messageType)) { + return Severity.INFO; + } else { + throw new ShellcheckException("Unknown message type returned by shellcheck (type = " + messageType + ")"); + } + } +} diff --git a/src/main/java/pl/touk/sputnik/processor/shellcheck/json/ShellcheckMessage.java b/src/main/java/pl/touk/sputnik/processor/shellcheck/json/ShellcheckMessage.java new file mode 100644 index 00000000..9362884d --- /dev/null +++ b/src/main/java/pl/touk/sputnik/processor/shellcheck/json/ShellcheckMessage.java @@ -0,0 +1,18 @@ +package pl.touk.sputnik.processor.shellcheck.json; + +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +public class ShellcheckMessage { + private String file; + private int line; + private Integer endLine; + private int column; + private Integer endColumn; + private String level; + private String code; + private String message; + private String fix; +} diff --git a/src/main/java/pl/touk/sputnik/review/filter/ShellFilter.java b/src/main/java/pl/touk/sputnik/review/filter/ShellFilter.java new file mode 100644 index 00000000..25210c4a --- /dev/null +++ b/src/main/java/pl/touk/sputnik/review/filter/ShellFilter.java @@ -0,0 +1,10 @@ +package pl.touk.sputnik.review.filter; + +import java.util.Collections; + +public class ShellFilter extends FileExtensionFilter { + + public ShellFilter() { + super(Collections.singletonList("sh")); + } +} diff --git a/src/test/java/pl/touk/sputnik/processor/shellcheck/ShellcheckProcessorTest.java b/src/test/java/pl/touk/sputnik/processor/shellcheck/ShellcheckProcessorTest.java new file mode 100644 index 00000000..b1170c1b --- /dev/null +++ b/src/test/java/pl/touk/sputnik/processor/shellcheck/ShellcheckProcessorTest.java @@ -0,0 +1,117 @@ +package pl.touk.sputnik.processor.shellcheck; + +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.platform.commons.util.StringUtils; +import pl.touk.sputnik.TestEnvironment; +import pl.touk.sputnik.configuration.ConfigurationBuilder; +import pl.touk.sputnik.exec.ExternalProcess; +import pl.touk.sputnik.review.Review; +import pl.touk.sputnik.review.ReviewFile; +import pl.touk.sputnik.review.ReviewFormatterFactory; +import pl.touk.sputnik.review.ReviewResult; +import pl.touk.sputnik.review.Severity; +import pl.touk.sputnik.review.Violation; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ShellcheckProcessorTest extends TestEnvironment { + + private static final String CONFIGURATION_WITH_SHELLCHECK_ENABLED = "shellcheck/sputnik/noConfigurationFile.properties"; + private static final String REVIEW_FILE_WITH_ONE_VIOLATION = "src/test/resources/shellcheck/testFiles/oneViolation.sh"; + private static final String REVIEW_FILE_WITH_EXCLUDED_VIOLATION = "src/test/resources/shellcheck/testFiles/excludedViolation.sh"; + private static final String REVIEW_FILE_WITH_MULTIPLE_VIOLATIONS = "src/test/resources/shellcheck/testFiles/multipleViolations.sh"; + private ShellcheckProcessor sut; + + @BeforeEach + public void setUp() { + config = ConfigurationBuilder.initFromResource(CONFIGURATION_WITH_SHELLCHECK_ENABLED); + sut = new ShellcheckProcessor(config); + } + + @Test + public void shouldReturnNoViolationsWhenThereIsNoFileToReview() { + ReviewResult reviewResult = sut.process(nonExistentReview()); + + assertThat(reviewResult).isNotNull(); + assertThat(reviewResult.getViolations()).isEmpty(); + } + + @Test + public void shouldReturnOneViolationsForFile() { + Assumptions.assumeTrue(isShellcheckInstalled()); + Review review = getReview(REVIEW_FILE_WITH_ONE_VIOLATION); + + ReviewResult result = sut.process(review); + + assertThat(result).isNotNull(); + assertThat(result.getViolations()) + .hasSize(1); + Violation violation = result.getViolations().get(0); + assertThat(violation.getFilenameOrJavaClassName()).contains(REVIEW_FILE_WITH_ONE_VIOLATION); + assertThat(violation.getLine()).isEqualTo(3); + assertThat(violation.getMessage()).isEqualTo("[Code:2154] Message: variable is referenced but not assigned."); + assertThat(violation.getSeverity()).isEqualTo(Severity.WARNING); + } + + @Test + public void shouldReturnMultipleViolationsForFile() { + Assumptions.assumeTrue(isShellcheckInstalled()); + Review review = getReview(REVIEW_FILE_WITH_MULTIPLE_VIOLATIONS); + + ReviewResult result = sut.process(review); + + assertThat(result).isNotNull(); + assertThat(result.getViolations()) + .hasSize(3); + Violation violation1 = result.getViolations().get(0); + assertThat(violation1.getFilenameOrJavaClassName()).contains(REVIEW_FILE_WITH_MULTIPLE_VIOLATIONS); + assertThat(violation1.getLine()).isEqualTo(3); + assertThat(violation1.getMessage()).isEqualTo("[Code:2154] Message: variable is referenced but not assigned."); + assertThat(violation1.getSeverity()).isEqualTo(Severity.WARNING); + + Violation violation2 = result.getViolations().get(1); + assertThat(violation2.getFilenameOrJavaClassName()).contains(REVIEW_FILE_WITH_MULTIPLE_VIOLATIONS); + assertThat(violation2.getLine()).isEqualTo(5); + assertThat(violation2.getMessage()).isEqualTo("[Code:1037] Message: Braces are required for positionals over 9, e.g. ${10}."); + assertThat(violation2.getSeverity()).isEqualTo(Severity.ERROR); + + Violation violation3 = result.getViolations().get(2); + assertThat(violation3.getFilenameOrJavaClassName()).contains(REVIEW_FILE_WITH_MULTIPLE_VIOLATIONS); + assertThat(violation3.getLine()).isEqualTo(7); + assertThat(violation3.getMessage()).isEqualTo("[Code:2078] Message: This expression is constant. Did you forget a $ somewhere?"); + assertThat(violation3.getSeverity()).isEqualTo(Severity.ERROR); + } + + @Test + public void shouldReturnNoViolationWhenRuleExcludedInConfig() { + Assumptions.assumeTrue(isShellcheckInstalled()); + Review review = getReview(REVIEW_FILE_WITH_EXCLUDED_VIOLATION); + + ReviewResult result = sut.process(review); + + assertThat(result).isNotNull(); + assertThat(result.getViolations()).isEmpty(); + } + + private boolean isShellcheckInstalled() { + try { + String result = new ExternalProcess().executeCommand("shellcheck"); + return StringUtils.isBlank(result); + } catch (Exception e) { + return false; + } + } + + private Review getReview(String... filePaths) { + List files = new ArrayList<>(); + for (String filePath : filePaths) { + files.add(new ReviewFile(filePath)); + } + return new Review(files, ReviewFormatterFactory.get(config)); + } +} diff --git a/src/test/java/pl/touk/sputnik/processor/shellcheck/ShellcheckResultParserTest.java b/src/test/java/pl/touk/sputnik/processor/shellcheck/ShellcheckResultParserTest.java new file mode 100644 index 00000000..e9498f35 --- /dev/null +++ b/src/test/java/pl/touk/sputnik/processor/shellcheck/ShellcheckResultParserTest.java @@ -0,0 +1,73 @@ +package pl.touk.sputnik.processor.shellcheck; + +import com.google.common.io.Resources; +import org.apache.commons.io.IOUtils; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import pl.touk.sputnik.review.Violation; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.catchThrowable; + +public class ShellcheckResultParserTest { + + private ShellcheckResultParser shellcheckResultParser; + + @BeforeEach + public void setUp() { + shellcheckResultParser = new ShellcheckResultParser(); + } + + @ParameterizedTest + @ValueSource(strings = { + "shellcheck/empty-file.txt", + "shellcheck/no-violations.txt" + }) + void shouldParseNoViolations(String filePath) throws IOException, URISyntaxException { + // given + String response = IOUtils.toString(Resources.getResource(filePath).toURI()); + + // when + List violations = shellcheckResultParser.parse(response); + + // then + assertThat(violations).hasSize(0); + } + + @ParameterizedTest + @ValueSource(strings = { + "shellcheck/sample-output.txt" + }) + void shouldParseSampleViolations(String filePath) throws IOException, URISyntaxException { + // given + String response = IOUtils.toString(Resources.getResource(filePath).toURI()); + + // when + List violations = shellcheckResultParser.parse(response); + + // then + assertThat(violations).hasSize(3); + assertThat(violations).extracting("filenameOrJavaClassName").contains("myotherscript", "myscript"); + assertThat(violations) + .extracting("message") + .contains("[Code:1035] Message: You need a space after the [ and before the ].") + .contains("[Code:2039] Message: In POSIX sh, echo flags are undefined.") + .contains("[Code:2086] Message: Double quote to prevent globbing and word splitting."); + } + + @Test + public void shouldThrowExceptionWhenViolationWithUnknownMessageType() throws IOException, URISyntaxException { + String response = IOUtils.toString(Resources.getResource("shellcheck/unknown-message-output.txt").toURI()); + + Throwable thrown = catchThrowable(() -> shellcheckResultParser.parse(response)); + + assertThat(thrown).isInstanceOf(ShellcheckException.class) + .hasMessageStartingWith("Unknown message type returned by shellcheck (type = fatal)"); + } +} diff --git a/src/test/resources/shellcheck/empty-file.txt b/src/test/resources/shellcheck/empty-file.txt new file mode 100644 index 00000000..e69de29b diff --git a/src/test/resources/shellcheck/no-violations.txt b/src/test/resources/shellcheck/no-violations.txt new file mode 100644 index 00000000..0637a088 --- /dev/null +++ b/src/test/resources/shellcheck/no-violations.txt @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/src/test/resources/shellcheck/sample-output.txt b/src/test/resources/shellcheck/sample-output.txt new file mode 100644 index 00000000..ad0c6633 --- /dev/null +++ b/src/test/resources/shellcheck/sample-output.txt @@ -0,0 +1,26 @@ +[ + { + "file": "myotherscript", + "line": 2, + "column": 2, + "level": "error", + "code": 1035, + "message": "You need a space after the [ and before the ]." + }, + { + "file": "myscript", + "line": 2, + "column": 6, + "level": "warning", + "code": 2039, + "message": "In POSIX sh, echo flags are undefined." + }, + { + "file": "myscript", + "line": 2, + "column": 9, + "level": "info", + "code": 2086, + "message": "Double quote to prevent globbing and word splitting." + } +] \ No newline at end of file diff --git a/src/test/resources/shellcheck/sputnik/noConfigurationFile.properties b/src/test/resources/shellcheck/sputnik/noConfigurationFile.properties new file mode 100644 index 00000000..26ed421c --- /dev/null +++ b/src/test/resources/shellcheck/sputnik/noConfigurationFile.properties @@ -0,0 +1,2 @@ +shellcheck.enabled=true +shellcheck.exclude=2153,1091,1090 diff --git a/src/test/resources/shellcheck/testFiles/excludedViolation.sh b/src/test/resources/shellcheck/testFiles/excludedViolation.sh new file mode 100755 index 00000000..dd8be116 --- /dev/null +++ b/src/test/resources/shellcheck/testFiles/excludedViolation.sh @@ -0,0 +1,3 @@ +#!/bin/bash -ex + +source somefile diff --git a/src/test/resources/shellcheck/testFiles/multipleViolations.sh b/src/test/resources/shellcheck/testFiles/multipleViolations.sh new file mode 100755 index 00000000..d0d49772 --- /dev/null +++ b/src/test/resources/shellcheck/testFiles/multipleViolations.sh @@ -0,0 +1,10 @@ +#!/bin/bash -ex + +echo "$variable" + +echo "Tenth parameter: $10" + +if [ "myvar" ] +then + echo "myvar is set" +fi \ No newline at end of file diff --git a/src/test/resources/shellcheck/testFiles/oneViolation.sh b/src/test/resources/shellcheck/testFiles/oneViolation.sh new file mode 100755 index 00000000..d25d3be3 --- /dev/null +++ b/src/test/resources/shellcheck/testFiles/oneViolation.sh @@ -0,0 +1,3 @@ +#!/bin/bash -ex + +echo "$variable" diff --git a/src/test/resources/shellcheck/unknown-message-output.txt b/src/test/resources/shellcheck/unknown-message-output.txt new file mode 100644 index 00000000..ace3cbf7 --- /dev/null +++ b/src/test/resources/shellcheck/unknown-message-output.txt @@ -0,0 +1,10 @@ +[ + { + "file": "myotherscript", + "line": 2, + "column": 2, + "level": "fatal", + "code": 1035, + "message": "You need a space after the [ and before the ]." + } +] \ No newline at end of file