summaryrefslogtreecommitdiffstats
path: root/src/ansiblelint/rules/var_naming.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/ansiblelint/rules/var_naming.py')
-rw-r--r--src/ansiblelint/rules/var_naming.py104
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