diff --git a/lib/ansiblelint/__init__.py b/lib/ansiblelint/__init__.py index 89a98a67e7..2a8c776571 100644 --- a/lib/ansiblelint/__init__.py +++ b/lib/ansiblelint/__init__.py @@ -140,8 +140,13 @@ def matchyaml(self, file, text): class RulesCollection(object): - def __init__(self): + def __init__(self, rulesdirs=None): + if rulesdirs is None: + rulesdirs = [] + self.rulesdirs = ansiblelint.utils.expand_paths_vars(rulesdirs) self.rules = [] + for rulesdir in self.rulesdirs: + self.extend(ansiblelint.utils.load_plugins(rulesdir)) def register(self, obj): self.rules.append(obj) @@ -193,12 +198,6 @@ def listtags(self): results.append("{0} {1}".format(tag, tags[tag])) return "\n".join(results) - @classmethod - def create_from_directory(cls, rulesdir): - result = cls() - result.rules = ansiblelint.utils.load_plugins(os.path.expanduser(rulesdir)) - return result - class Match(object): @@ -239,7 +238,7 @@ def __init__(self, rules, playbook, tags, skip_list, exclude_paths, def _update_exclude_paths(self, exclude_paths): if exclude_paths: # These will be (potentially) relative paths - paths = [s.strip() for s in exclude_paths] + paths = ansiblelint.utils.expand_paths_vars(exclude_paths) # Since ansiblelint.utils.find_children returns absolute paths, # and the list of files we create in `Runner.run` can contain both # relative and absolute paths, we need to cover both bases. diff --git a/lib/ansiblelint/__main__.py b/lib/ansiblelint/__main__.py index d756082a64..3162c4cb7d 100755 --- a/lib/ansiblelint/__main__.py +++ b/lib/ansiblelint/__main__.py @@ -157,10 +157,7 @@ def main(): rulesdirs = options.rulesdir + [default_rulesdir] else: rulesdirs = options.rulesdir or [default_rulesdir] - - rules = RulesCollection() - for rulesdir in rulesdirs: - rules.extend(RulesCollection.create_from_directory(rulesdir)) + rules = RulesCollection(rulesdirs) if options.listrules: print(rules) diff --git a/lib/ansiblelint/utils.py b/lib/ansiblelint/utils.py index 252e2e748b..114badb875 100644 --- a/lib/ansiblelint/utils.py +++ b/lib/ansiblelint/utils.py @@ -861,3 +861,17 @@ def get_playbooks_and_roles(options=None): print('Found playbooks: ' + ' '.join(playbooks)) return role_dirs + playbooks + + +def expand_path_vars(path): + """Expand the environment or ~ variables in a path string.""" + path = path.strip() + path = os.path.expanduser(path) + path = os.path.expandvars(path) + return path + + +def expand_paths_vars(paths): + """Expand the environment or ~ variables in a list.""" + paths = [expand_path_vars(p) for p in paths] + return paths diff --git a/test/TestDependenciesInMeta.py b/test/TestDependenciesInMeta.py index 34aaed843e..6a7b0990e1 100644 --- a/test/TestDependenciesInMeta.py +++ b/test/TestDependenciesInMeta.py @@ -8,7 +8,7 @@ class TestDependenciesInMeta(unittest.TestCase): def setUp(self): rulesdir = os.path.join('lib', 'ansiblelint', 'rules') - self.rules = RulesCollection.create_from_directory(rulesdir) + self.rules = RulesCollection([rulesdir]) def test_bitbucket_in_meta_dependency_is_ok(self): filename = 'test/dependency-in-meta/bitbucket.yml' diff --git a/test/TestImportIncludeRole.py b/test/TestImportIncludeRole.py index 28cba9da68..58d37e292d 100644 --- a/test/TestImportIncludeRole.py +++ b/test/TestImportIncludeRole.py @@ -49,7 +49,7 @@ class TestImportIncludeRole(unittest.TestCase): def setUp(self): rulesdir = os.path.join('lib', 'ansiblelint', 'rules') - self.rules = RulesCollection.create_from_directory(rulesdir) + self.rules = RulesCollection([rulesdir]) # make dir and write role tasks to import or include self.play_root = tempfile.mkdtemp() diff --git a/test/TestPretaskIncludePlaybook.py b/test/TestPretaskIncludePlaybook.py index b13658cc1b..891df1128b 100644 --- a/test/TestPretaskIncludePlaybook.py +++ b/test/TestPretaskIncludePlaybook.py @@ -8,7 +8,7 @@ class TestTaskIncludes(unittest.TestCase): def setUp(self): rulesdir = os.path.join('lib', 'ansiblelint', 'rules') - self.rules = RulesCollection.create_from_directory(rulesdir) + self.rules = RulesCollection([rulesdir]) def test_pre_task_include_playbook(self): filename = 'test/playbook-include/playbook_pre.yml' diff --git a/test/TestRuleProperties.py b/test/TestRuleProperties.py index 77bf86353e..b2c3fec5ad 100644 --- a/test/TestRuleProperties.py +++ b/test/TestRuleProperties.py @@ -8,7 +8,7 @@ class TestAlwaysRun(unittest.TestCase): def setUp(self): self.collection.extend( - RulesCollection.create_from_directory(default_rulesdir) + RulesCollection([default_rulesdir]) ) def test_serverity_valid(self): diff --git a/test/TestRulesCollection.py b/test/TestRulesCollection.py index 74f679df36..d6b3d17bee 100644 --- a/test/TestRulesCollection.py +++ b/test/TestRulesCollection.py @@ -20,6 +20,7 @@ import collections import unittest +import os from ansiblelint import RulesCollection @@ -31,7 +32,7 @@ class TestRulesCollection(unittest.TestCase): bracketsmatchtestfile = dict(path='test/bracketsmatchtest.yml', type='playbook') def setUp(self): - self.rules = RulesCollection.create_from_directory('./test/rules') + self.rules = RulesCollection(['./test/rules']) def test_load_collection_from_directory(self): self.assertEqual(len(self.rules), 2) @@ -75,6 +76,19 @@ def test_skip_non_existent_id(self): self.assertEqual(len(matches), 3) def test_no_duplicate_rule_ids(self): - real_rules = RulesCollection.create_from_directory('./lib/ansiblelint/rules') + real_rules = RulesCollection(['./lib/ansiblelint/rules']) rule_ids = [rule.id for rule in real_rules] self.assertEqual([x for x, y in collections.Counter(rule_ids).items() if y > 1], []) + + +def test_rulesdir_var_expansion(monkeypatch): + test_path = '/test/path' + monkeypatch.setenv('TEST_PATH', test_path) + test_rulesdirs = ['$TEST_PATH'] + expansion_rules = RulesCollection(test_rulesdirs) + assert test_path in expansion_rules.rulesdirs + + +def test_rulesdir_user_expansion(): + expansion_rules = RulesCollection(['~']) + assert os.path.expanduser('~') in expansion_rules.rulesdirs diff --git a/test/TestRunner.py b/test/TestRunner.py index 6d79c5ada9..6bce895c0c 100644 --- a/test/TestRunner.py +++ b/test/TestRunner.py @@ -22,7 +22,7 @@ import unittest import ansiblelint -from ansiblelint import Runner, RulesCollection +from ansiblelint import default_rulesdir, Runner, RulesCollection import ansiblelint.formatters @@ -30,7 +30,7 @@ class TestRule(unittest.TestCase): def setUp(self): rulesdir = os.path.join('lib', 'ansiblelint', 'rules') - self.rules = RulesCollection.create_from_directory(rulesdir) + self.rules = RulesCollection([rulesdir]) def test_runner_count(self): filename = 'test/nomatchestest.yml' @@ -118,3 +118,20 @@ def test_files_not_scanned_twice(self): run2 = runner.run() assert ((len(run1) + len(run2)) == 1) + + +def test_runner_exclude_var_expansion(monkeypatch): + rules = RulesCollection([default_rulesdir]) + filename = 'example/lots_of_warnings.yml' + monkeypatch.setenv('EXCLUDE_PATH', filename) + excludes = ['$EXCLUDE_PATH'] + runner = Runner(rules, filename, [], [], excludes) + assert filename in runner.exclude_paths + + +def test_runner_exclude_user_expansion(): + rules = RulesCollection([default_rulesdir]) + filename = 'example/lots_of_warnings.yml' + excludes = ['~'] + runner = Runner(rules, filename, [], [], excludes) + assert os.path.expanduser('~') in runner.exclude_paths diff --git a/test/TestSkipImportPlaybook.py b/test/TestSkipImportPlaybook.py index 6abf7cd41a..d9680dffdf 100644 --- a/test/TestSkipImportPlaybook.py +++ b/test/TestSkipImportPlaybook.py @@ -27,7 +27,7 @@ class TestSkipBeforeImport(unittest.TestCase): def setUp(self): rulesdir = os.path.join('lib', 'ansiblelint', 'rules') - self.rules = RulesCollection.create_from_directory(rulesdir) + self.rules = RulesCollection([rulesdir]) # make dir and write role tasks to import or include self.play_root = tempfile.mkdtemp() diff --git a/test/TestSkipInsideYaml.py b/test/TestSkipInsideYaml.py index 57334146fb..87cff4e7c0 100644 --- a/test/TestSkipInsideYaml.py +++ b/test/TestSkipInsideYaml.py @@ -90,7 +90,7 @@ class TestSkipInsideYaml(unittest.TestCase): rulesdir = os.path.join('lib', 'ansiblelint', 'rules') - collection = RulesCollection.create_from_directory(rulesdir) + collection = RulesCollection([rulesdir]) def setUp(self): self.runner = RunFromText(self.collection) diff --git a/test/TestSkipPlaybookItems.py b/test/TestSkipPlaybookItems.py index cd6cc6d965..79af77bb5a 100644 --- a/test/TestSkipPlaybookItems.py +++ b/test/TestSkipPlaybookItems.py @@ -91,7 +91,7 @@ class TestSkipPlaybookItems(unittest.TestCase): rulesdir = os.path.join('lib', 'ansiblelint', 'rules') - collection = RulesCollection.create_from_directory(rulesdir) + collection = RulesCollection([rulesdir]) def setUp(self): self.runner = RunFromText(self.collection) diff --git a/test/TestTaskIncludes.py b/test/TestTaskIncludes.py index fd0cc86f7b..40334ce12b 100644 --- a/test/TestTaskIncludes.py +++ b/test/TestTaskIncludes.py @@ -8,7 +8,7 @@ class TestTaskIncludes(unittest.TestCase): def setUp(self): rulesdir = os.path.join('lib', 'ansiblelint', 'rules') - self.rules = RulesCollection.create_from_directory(rulesdir) + self.rules = RulesCollection([rulesdir]) def test_block_included_tasks(self): filename = 'test/blockincludes.yml' diff --git a/test/TestUtils.py b/test/TestUtils.py index 9107679e19..01c5b381cd 100644 --- a/test/TestUtils.py +++ b/test/TestUtils.py @@ -21,6 +21,7 @@ # THE SOFTWARE. import unittest +import os try: from pathlib import Path except ImportError: @@ -154,3 +155,17 @@ def test_normpath_with_string(self): self.assertEqual( utils.normpath("a/b/../"), "a") + + +def test_expand_path_vars(monkeypatch): + test_path = '/test/path' + monkeypatch.setenv('TEST_PATH', test_path) + assert utils.expand_path_vars('~') == os.path.expanduser('~') + assert utils.expand_path_vars('$TEST_PATH') == test_path + + +def test_expand_paths_vars(monkeypatch): + test_path = '/test/path' + monkeypatch.setenv('TEST_PATH', test_path) + assert utils.expand_paths_vars(['~']) == [os.path.expanduser('~')] + assert utils.expand_paths_vars(['$TEST_PATH']) == [test_path]