diff options
Diffstat (limited to 'src/ansiblelint/rules/var_naming.py')
-rw-r--r-- | src/ansiblelint/rules/var_naming.py | 104 |
1 files changed, 95 insertions, 9 deletions
diff --git a/src/ansiblelint/rules/var_naming.py b/src/ansiblelint/rules/var_naming.py index 389530d..14a4c40 100644 --- a/src/ansiblelint/rules/var_naming.py +++ b/src/ansiblelint/rules/var_naming.py @@ -1,4 +1,5 @@ """Implementation of var-naming rule.""" + from __future__ import annotations import keyword @@ -9,13 +10,19 @@ from typing import TYPE_CHECKING, Any from ansible.parsing.yaml.objects import AnsibleUnicode from ansible.vars.reserved import get_reserved_names -from ansiblelint.config import options -from ansiblelint.constants import ANNOTATION_KEYS, LINE_NUMBER_KEY, RC +from ansiblelint.config import Options, options +from ansiblelint.constants import ( + ANNOTATION_KEYS, + LINE_NUMBER_KEY, + PLAYBOOK_ROLE_KEYWORDS, + RC, +) from ansiblelint.errors import MatchError from ansiblelint.file_utils import Lintable from ansiblelint.rules import AnsibleLintRule, RulesCollection from ansiblelint.runner import Runner from ansiblelint.skip_utils import get_rule_skips_from_line +from ansiblelint.text import has_jinja, is_fqcn_or_name from ansiblelint.utils import parse_yaml_from_file if TYPE_CHECKING: @@ -160,10 +167,15 @@ class VariableNamingRule(AnsibleLintRule): rule=self, ) - if prefix and not ident.startswith(f"{prefix}_"): + if ( + prefix + and not ident.lstrip("_").startswith(f"{prefix}_") + and not has_jinja(prefix) + and is_fqcn_or_name(prefix) + ): return MatchError( tag="var-naming[no-role-prefix]", - message="Variables names from within roles should use role_name_ as a prefix.", + message=f"Variables names from within roles should use {prefix}_ as a prefix.", rule=self, ) return None @@ -187,6 +199,37 @@ class VariableNamingRule(AnsibleLintRule): else our_vars[LINE_NUMBER_KEY] ) raw_results.append(match_error) + roles = data.get("roles", []) + for role in roles: + if isinstance(role, AnsibleUnicode): + continue + role_fqcn = role.get("role", role.get("name")) + prefix = role_fqcn.split("/" if "/" in role_fqcn else ".")[-1] + for key in list(role.keys()): + if key not in PLAYBOOK_ROLE_KEYWORDS: + match_error = self.get_var_naming_matcherror(key, prefix=prefix) + if match_error: + match_error.filename = str(file.path) + match_error.message += f" (vars: {key})" + match_error.lineno = ( + key.ansible_pos[1] + if isinstance(key, AnsibleUnicode) + else role[LINE_NUMBER_KEY] + ) + raw_results.append(match_error) + + our_vars = role.get("vars", {}) + for key in our_vars: + match_error = self.get_var_naming_matcherror(key, prefix=prefix) + if match_error: + match_error.filename = str(file.path) + match_error.message += f" (vars: {key})" + match_error.lineno = ( + key.ansible_pos[1] + if isinstance(key, AnsibleUnicode) + else our_vars[LINE_NUMBER_KEY] + ) + raw_results.append(match_error) if raw_results: lines = file.content.splitlines() for match in raw_results: @@ -266,7 +309,8 @@ class VariableNamingRule(AnsibleLintRule): if str(file.kind) == "vars" and file.data: meta_data = parse_yaml_from_file(str(file.path)) for key in meta_data: - match_error = self.get_var_naming_matcherror(key) + prefix = file.role if file.role else "" + match_error = self.get_var_naming_matcherror(key, prefix=prefix) if match_error: match_error.filename = filename match_error.lineno = key.ansible_pos[1] @@ -298,13 +342,21 @@ if "pytest" in sys.modules: @pytest.mark.parametrize( ("file", "expected"), ( - pytest.param("examples/playbooks/rule-var-naming-fail.yml", 7, id="0"), + pytest.param( + "examples/playbooks/var-naming/rule-var-naming-fail.yml", + 7, + id="0", + ), pytest.param("examples/Taskfile.yml", 0, id="1"), ), ) - def test_invalid_var_name_playbook(file: str, expected: int) -> None: + def test_invalid_var_name_playbook( + file: str, + expected: int, + config_options: Options, + ) -> None: """Test rule matches.""" - rules = RulesCollection(options=options) + rules = RulesCollection(options=config_options) rules.register(VariableNamingRule()) results = Runner(Lintable(file), rules=rules).run() assert len(results) == expected @@ -337,6 +389,40 @@ if "pytest" in sys.modules: assert result.tag == expected_errors[idx][0] assert result.lineno == expected_errors[idx][1] + def test_var_naming_with_role_prefix( + default_rules_collection: RulesCollection, + ) -> None: + """Test rule matches.""" + results = Runner( + Lintable("examples/roles/role_vars_prefix_detection"), + rules=default_rules_collection, + ).run() + assert len(results) == 2 + for result in results: + assert result.tag == "var-naming[no-role-prefix]" + + def test_var_naming_with_role_prefix_plays( + default_rules_collection: RulesCollection, + ) -> None: + """Test rule matches.""" + results = Runner( + Lintable("examples/playbooks/role_vars_prefix_detection.yml"), + rules=default_rules_collection, + exclude_paths=["examples/roles/role_vars_prefix_detection"], + ).run() + expected_errors = ( + ("var-naming[no-role-prefix]", 9), + ("var-naming[no-role-prefix]", 12), + ("var-naming[no-role-prefix]", 15), + ("var-naming[no-role-prefix]", 25), + ("var-naming[no-role-prefix]", 32), + ("var-naming[no-role-prefix]", 45), + ) + assert len(results) == len(expected_errors) + for idx, result in enumerate(results): + assert result.tag == expected_errors[idx][0] + assert result.lineno == expected_errors[idx][1] + def test_var_naming_with_pattern() -> None: """Test rule matches.""" role_path = "examples/roles/var_naming_pattern/tasks/main.yml" @@ -364,7 +450,7 @@ if "pytest" in sys.modules: def test_var_naming_with_include_role_import_role() -> None: """Test with include role and import role.""" - role_path = "examples/test_collection/roles/my_role/tasks/main.yml" + role_path = "examples/.test_collection/roles/my_role/tasks/main.yml" result = run_ansible_lint(role_path) assert result.returncode == RC.SUCCESS assert "var-naming" not in result.stdout |