Skip to content

Commit

Permalink
Fixes #16: the list of detected YAML files is now configurable + new …
Browse files Browse the repository at this point in the history
…env variables to give path to yamllint conf file
  • Loading branch information
sbaudoin committed Nov 7, 2020
1 parent 671a0d2 commit 706655d
Show file tree
Hide file tree
Showing 5 changed files with 156 additions and 13 deletions.
18 changes: 15 additions & 3 deletions src/main/java/com/github/sbaudoin/yamllint/Cli.java
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -257,13 +262,16 @@ private CommandLine parseCommandLine(Options options, String[] args) {
}

private void getYamlLintConfig(Map<String, Object> 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));
Expand All @@ -273,6 +281,10 @@ private void getYamlLintConfig(Map<String, Object> 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 {
Expand Down Expand Up @@ -300,7 +312,7 @@ private List<String> 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);
}
}
Expand Down
37 changes: 35 additions & 2 deletions src/main/java/com/github/sbaudoin/yamllint/YamlLintConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -59,6 +64,11 @@ public class YamlLintConfig {
*/
protected List<String> ignore = null;

/**
* List of regexp patterns used to identify YAML files, defaulted to .yaml and .yml
*/
protected List<String> yamlFiles = Arrays.asList(".*\\.yaml$", ".*\\.yml$");


/**
* Constructs a <code>YamlLintConfig</code> from a YAML string
Expand Down Expand Up @@ -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 <code>true</code> if a YAML file, <code>false</code> 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
*
Expand Down Expand Up @@ -134,7 +154,7 @@ public Object getRuleConf(String id) {
}

/**
* Updates the <var>ruleConf</var> 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
*/
Expand All @@ -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;
}
Expand Down Expand Up @@ -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<String>)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"));
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/conf/default.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
---

yaml-files:
- '.*\.yaml'
- '.*\.yml'

rules:
braces:
min-spaces-inside: 0
Expand Down
61 changes: 60 additions & 1 deletion src/test/java/com/github/sbaudoin/yamllint/CliTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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(() -> {
Expand All @@ -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();
Expand All @@ -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 });
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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" +
Expand All @@ -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");
Expand All @@ -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");
Expand All @@ -90,7 +90,7 @@ public void testUnknownRule() throws IOException {
}
}

public void testMissingOption() throws IOException {
public void testMissingOption() {
try {
new YamlLintConfig("rules:\n" +
" colons:\n" +
Expand All @@ -101,7 +101,7 @@ public void testMissingOption() throws IOException {
}
}

public void testUnknownOption() throws IOException {
public void testUnknownOption() {
try {
new YamlLintConfig("rules:\n" +
" colons:\n" +
Expand All @@ -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" +
Expand Down Expand Up @@ -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" +
Expand Down Expand Up @@ -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();
Expand Down

0 comments on commit 706655d

Please sign in to comment.