From 706655d27b61f1f15a90322ca9fa8c17130acb43 Mon Sep 17 00:00:00 2001 From: sbaudoin Date: Sat, 7 Nov 2020 16:39:07 +0100 Subject: [PATCH] Fixes #16: the list of detected YAML files is now configurable + new env variables to give path to yamllint conf file --- .../com/github/sbaudoin/yamllint/Cli.java | 18 +++++- .../sbaudoin/yamllint/YamlLintConfig.java | 37 ++++++++++- src/main/resources/conf/default.yaml | 4 ++ .../com/github/sbaudoin/yamllint/CliTest.java | 61 ++++++++++++++++++- .../yamllint/SimpleYamlLintConfigTest.java | 49 ++++++++++++--- 5 files changed, 156 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/github/sbaudoin/yamllint/Cli.java b/src/main/java/com/github/sbaudoin/yamllint/Cli.java index 3da3853..aa6fc7a 100644 --- a/src/main/java/com/github/sbaudoin/yamllint/Cli.java +++ b/src/main/java/com/github/sbaudoin/yamllint/Cli.java @@ -68,6 +68,11 @@ public final class Cli { */ public static final String XDG_CONFIG_HOME_ENV_VAR = "XDG_CONFIG_HOME"; + /** + * Name of the environment variable that can be used to pass a yamllint configuration file + */ + public static final String YAMLLINT_CONFIG_FILE_ENV_VAR = "YAMLLINT_CONFIG_FILE"; + private static final String ARG_FILES_OR_DIR = "FILES_OR_DIR"; private static final String ARG_CONFIG_FILE = "config_file"; @@ -257,13 +262,16 @@ private CommandLine parseCommandLine(Options options, String[] args) { } private void getYamlLintConfig(Map arguments) throws IOException, YamlLintConfigException { - Path userGlobalConfig; - if (System.getenv(XDG_CONFIG_HOME_ENV_VAR) != null) { + if (System.getenv(YAMLLINT_CONFIG_FILE_ENV_VAR) != null) { + userGlobalConfig = Paths.get(System.getenv(YAMLLINT_CONFIG_FILE_ENV_VAR)); + } else if (System.getenv(XDG_CONFIG_HOME_ENV_VAR) != null) { userGlobalConfig = Paths.get(System.getenv(XDG_CONFIG_HOME_ENV_VAR), APP_NAME, "config"); } else { userGlobalConfig = Paths.get(System.getProperty("user.home"), ".config", APP_NAME, "config"); } + + // Priority to the -d option, then -c if (arguments.containsKey(ARG_CONFIG_DATA) && arguments.get(ARG_CONFIG_DATA) != null) { if (!"".equals(arguments.get(ARG_CONFIG_DATA)) && !((String)arguments.get(ARG_CONFIG_DATA)).contains(":")) { arguments.put(ARG_CONFIG_DATA, "extends: " + arguments.get(ARG_CONFIG_DATA)); @@ -273,6 +281,10 @@ private void getYamlLintConfig(Map arguments) throws IOException conf = new YamlLintConfig(new File((String)arguments.get(ARG_CONFIG_FILE)).toURI().toURL()); } else if (fileExists(USER_CONF_FILENAME)) { conf = new YamlLintConfig(new File(USER_CONF_FILENAME).toURI().toURL()); + } else if (fileExists(USER_CONF_FILENAME + ".yaml")) { + conf = new YamlLintConfig(new File(USER_CONF_FILENAME + ".yaml").toURI().toURL()); + } else if (fileExists(USER_CONF_FILENAME + ".yml")) { + conf = new YamlLintConfig(new File(USER_CONF_FILENAME + ".yml").toURI().toURL()); } else if (fileExists(userGlobalConfig.toString())) { conf = new YamlLintConfig(userGlobalConfig.toUri().toURL()); } else { @@ -300,7 +312,7 @@ private List findFilesRecursively(String[] items) { findFilesRecursively( Arrays.stream(file.list()).map( name -> file.getPath() + File.separator + name).collect(Collectors.toList()).toArray(new String[]{}))); - } else if (file.isFile() && (item.endsWith(".yml") || item.endsWith(".yaml"))) { + } else if (file.isFile() && conf.isYamlFile(item)) { files.add(item); } } diff --git a/src/main/java/com/github/sbaudoin/yamllint/YamlLintConfig.java b/src/main/java/com/github/sbaudoin/yamllint/YamlLintConfig.java index 75eb6b8..50715bb 100644 --- a/src/main/java/com/github/sbaudoin/yamllint/YamlLintConfig.java +++ b/src/main/java/com/github/sbaudoin/yamllint/YamlLintConfig.java @@ -36,6 +36,11 @@ public class YamlLintConfig { */ public static final String EXTENDS_KEY = "extends"; + /** + * Configuration parameter that lists patterns used by the linter to be identify YAML files + */ + public static final String YAML_FILES_KEY = "yaml-files"; + /** * Configuration parameter that lists file patterns to be ignored by the linter */ @@ -59,6 +64,11 @@ public class YamlLintConfig { */ protected List ignore = null; + /** + * List of regexp patterns used to identify YAML files, defaulted to .yaml and .yml + */ + protected List yamlFiles = Arrays.asList(".*\\.yaml$", ".*\\.yml$"); + /** * Constructs a YamlLintConfig from a YAML string @@ -96,6 +106,16 @@ public YamlLintConfig(URL file) throws IOException, YamlLintConfigException { } + /** + * Tells if a file identified by its path a to be considered as a YAML file + * + * @param filepath the path of the file to be checked by this tool + * @return true if a YAML file, false otherwise + */ + public boolean isYamlFile(String filepath) { + return yamlFiles.stream().anyMatch(filepath::matches); + } + /** * Tells if a file identified by its path is to be ignored by this tool * @@ -134,7 +154,7 @@ public Object getRuleConf(String id) { } /** - * Updates the ruleConf attribute of this configuration instance with the one of the passed configuration. Existing entries are replaced (overridden). + * Updates the attributes of this configuration instance with the one of the passed configuration. Existing entries are replaced (overridden). * * @param baseConfig a configuration that will extend this instance's rule configuration */ @@ -155,6 +175,10 @@ public void extend(YamlLintConfig baseConfig) { ruleConf = newConf; + if (baseConfig.yamlFiles != null) { + yamlFiles = baseConfig.yamlFiles; + } + if (baseConfig.ignore != null) { ignore = baseConfig.ignore; } @@ -194,9 +218,18 @@ protected void parse(String rawContent) throws YamlLintConfigException { } } + // List of patterns used to identify YAML files + if (conf.containsKey(YAML_FILES_KEY)) { + if (!(conf.get(YAML_FILES_KEY) instanceof List)) { + throw new YamlLintConfigException("invalid config: '" + YAML_FILES_KEY + "' must be a list (of regexp patterns)"); + } + yamlFiles = (List)conf.get(YAML_FILES_KEY); + } + + // List of patterns used to ignore files if (conf.containsKey(IGNORE_KEY)) { if (!(conf.get(IGNORE_KEY) instanceof String)) { - throw new YamlLintConfigException("invalid config: 'ignore' should contain file patterns"); + throw new YamlLintConfigException("invalid config: '" + IGNORE_KEY + "' should contain file patterns"); } ignore = Arrays.asList(((String)conf.get(IGNORE_KEY)).split("\\r?\\n")); } diff --git a/src/main/resources/conf/default.yaml b/src/main/resources/conf/default.yaml index fcece4d..b9adef2 100644 --- a/src/main/resources/conf/default.yaml +++ b/src/main/resources/conf/default.yaml @@ -1,5 +1,9 @@ --- +yaml-files: + - '.*\.yaml' + - '.*\.yml' + rules: braces: min-spaces-inside: 0 diff --git a/src/test/java/com/github/sbaudoin/yamllint/CliTest.java b/src/test/java/com/github/sbaudoin/yamllint/CliTest.java index 81fdacf..08535e8 100644 --- a/src/test/java/com/github/sbaudoin/yamllint/CliTest.java +++ b/src/test/java/com/github/sbaudoin/yamllint/CliTest.java @@ -21,6 +21,7 @@ import org.junit.contrib.java.lang.system.ExpectedSystemExit; import org.junit.runner.RunWith; import org.powermock.api.mockito.PowerMockito; +import org.powermock.core.classloader.annotations.PowerMockIgnore; import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; @@ -359,6 +360,7 @@ public void testGlobalConfig2() { cli.setStdOutputStream(std); String userHome = System.getProperty("user.home"); + environmentVariables.clear(Cli.XDG_CONFIG_HOME_ENV_VAR, Cli.YAMLLINT_CONFIG_FILE_ENV_VAR); System.setProperty("user.home", System.getProperty("user.dir") + File.separator + "src" + File.separator + "test" + File.separator + "resources" + File.separator + "config" + File.separator + "home"); exit.expectSystemExitWithStatus(1); exit.checkAssertionAfterwards(() -> { @@ -371,7 +373,24 @@ public void testGlobalConfig2() { } @Test - public void testLocalConfig() throws IOException { + public void testGlobalConfig3() { + String path = "src" + File.separator + "test" + File.separator + "resources" + File.separator + "cli5.yml"; + + Cli cli = new Cli(); + + ByteArrayOutputStream std = new ByteArrayOutputStream(); + cli.setStdOutputStream(std); + + environmentVariables.set("YAMLLINT_CONFIG_FILE", "src" + File.separator + "test" + File.separator + "resources" + File.separator + "config" + File.separator + "XDG" + File.separator + "yamllint" + File.separator + "config"); + exit.expectSystemExitWithStatus(0); + exit.checkAssertionAfterwards(() -> assertEquals( + path + ":2:8:comments:warning:too few spaces before comment" + System.lineSeparator(), + std.toString())); + cli.run(new String[] { "-f", "parsable", path }); + } + + @Test + public void testLocalConfig1() throws IOException { String path = "src" + File.separator + "test" + File.separator + "resources" + File.separator + "cli5.yml"; Cli cli = new Cli(); @@ -389,4 +408,44 @@ public void testLocalConfig() throws IOException { }); cli.run(new String[] { "-f", "parsable", path }); } + + @Test + public void testLocalConfig2() throws IOException { + String path = "src" + File.separator + "test" + File.separator + "resources" + File.separator + "cli5.yml"; + + Cli cli = new Cli(); + + ByteArrayOutputStream std = new ByteArrayOutputStream(); + cli.setStdOutputStream(std); + + Files.copy(Paths.get("src", "test", "resources", "config", "local", Cli.USER_CONF_FILENAME), Paths.get(Cli.USER_CONF_FILENAME + ".yaml"), StandardCopyOption.REPLACE_EXISTING); + exit.expectSystemExitWithStatus(1); + exit.checkAssertionAfterwards(() -> { + assertEquals( + path + ":3:3:hyphens:error:too many spaces after hyphen" + System.lineSeparator(), std.toString()); + // Need to restore user.home for the other tests + Files.delete(Paths.get(Cli.USER_CONF_FILENAME + ".yaml")); + }); + cli.run(new String[] { "-f", "parsable", path }); + } + + @Test + public void testLocalConfig3() throws IOException { + String path = "src" + File.separator + "test" + File.separator + "resources" + File.separator + "cli5.yml"; + + Cli cli = new Cli(); + + ByteArrayOutputStream std = new ByteArrayOutputStream(); + cli.setStdOutputStream(std); + + Files.copy(Paths.get("src", "test", "resources", "config", "local", Cli.USER_CONF_FILENAME), Paths.get(Cli.USER_CONF_FILENAME + ".yml"), StandardCopyOption.REPLACE_EXISTING); + exit.expectSystemExitWithStatus(1); + exit.checkAssertionAfterwards(() -> { + assertEquals( + path + ":3:3:hyphens:error:too many spaces after hyphen" + System.lineSeparator(), std.toString()); + // Need to restore user.home for the other tests + Files.delete(Paths.get(Cli.USER_CONF_FILENAME + ".yml")); + }); + cli.run(new String[] { "-f", "parsable", path }); + } } diff --git a/src/test/java/com/github/sbaudoin/yamllint/SimpleYamlLintConfigTest.java b/src/test/java/com/github/sbaudoin/yamllint/SimpleYamlLintConfigTest.java index 878e1b2..72fe3dc 100644 --- a/src/test/java/com/github/sbaudoin/yamllint/SimpleYamlLintConfigTest.java +++ b/src/test/java/com/github/sbaudoin/yamllint/SimpleYamlLintConfigTest.java @@ -43,7 +43,7 @@ public void testConstructorWithNull() throws IOException, YamlLintConfigExceptio } @SuppressWarnings("unchecked") - public void testParseConfig() throws IOException, YamlLintConfigException { + public void testParseConfig() throws YamlLintConfigException { YamlLintConfig conf = new YamlLintConfig("rules:\n" + " colons:\n" + " max-spaces-before: 0\n" + @@ -57,7 +57,7 @@ public void testParseConfig() throws IOException, YamlLintConfigException { assertEquals(1, conf.getEnabledRules(null).size()); } - public void testInvalidConf() throws IOException { + public void testInvalidConf() { try { new YamlLintConfig(""); fail("Empty conf should be rejected"); @@ -80,7 +80,7 @@ public void testInvalidConf() throws IOException { } } - public void testUnknownRule() throws IOException { + public void testUnknownRule() { try { new YamlLintConfig("rules:\n" + " this-one-does-not-exist: enable\n"); @@ -90,7 +90,7 @@ public void testUnknownRule() throws IOException { } } - public void testMissingOption() throws IOException { + public void testMissingOption() { try { new YamlLintConfig("rules:\n" + " colons:\n" + @@ -101,7 +101,7 @@ public void testMissingOption() throws IOException { } } - public void testUnknownOption() throws IOException { + public void testUnknownOption() { try { new YamlLintConfig("rules:\n" + " colons:\n" + @@ -114,7 +114,7 @@ public void testUnknownOption() throws IOException { } } - public void testYesNoForBooleans() throws IOException, YamlLintConfigException { + public void testYesNoForBooleans() throws YamlLintConfigException { YamlLintConfig conf = new YamlLintConfig("rules:\n" + " indentation:\n" + " spaces: 2\n" + @@ -292,7 +292,7 @@ public void testValidateRuleConf() throws YamlLintConfigException { } } - public void testIgnore() throws IOException, YamlLintConfigException { + public void testIgnore() throws YamlLintConfigException { YamlLintConfig conf = new YamlLintConfig("rules:\n" + " indentation:\n" + " spaces: 2\n" + @@ -333,6 +333,41 @@ public void testIgnore() throws IOException, YamlLintConfigException { } } + public void testIsYamlFile() throws YamlLintConfigException { + try { + new YamlLintConfig("yaml-files:\n" + + " indentation:\n" + + " spaces: 2\n" + + " indent-sequences: true\n" + + " check-multi-line-strings: false\n"); + fail("Invalid yaml-files syntax accepted"); + } catch (YamlLintConfigException e) { + assertTrue(true); + } + + YamlLintConfig conf = new YamlLintConfig("extends: default\n"); + assertTrue(conf.isYamlFile("/my/file.yaml")); + assertTrue(conf.isYamlFile("foo.yml")); + assertFalse(conf.isYamlFile("/anything/that/a.yaml/donot.match")); + assertFalse(conf.isYamlFile("/foo.Yaml")); + + conf = new YamlLintConfig("rules:\n" + + " colons:\n" + + " max-spaces-before: 0\n" + + " max-spaces-after: 1\n"); + assertTrue(conf.isYamlFile("/my/file.yaml")); + assertTrue(conf.isYamlFile("foo.yml")); + assertFalse(conf.isYamlFile("/anything/that/a.yaml/donot.match")); + assertFalse(conf.isYamlFile("/foo.Yaml")); + + conf = new YamlLintConfig("yaml-files:\n" + + " - .*\\.match$\n"); + assertFalse(conf.isYamlFile("/my/file.yaml")); + assertFalse(conf.isYamlFile("foo.yml")); + assertTrue(conf.isYamlFile("/anything/that/a.yaml/donot.match")); + assertFalse(conf.isYamlFile("/foo.Yaml")); + } + private Map toMap(Object[][] o) { Map map = new HashMap();