From 05db79b7f259971f449a002dd9dd34f59af3ecc7 Mon Sep 17 00:00:00 2001 From: stopendy <4696765+stopendy@users.noreply.github.com> Date: Sun, 19 Oct 2025 02:07:47 +0900 Subject: [PATCH 1/2] feat: add role_var_prefix option --- .ansible-lint | 6 ++++++ src/ansiblelint/config.py | 1 + src/ansiblelint/rules/var_naming.md | 9 +++------ src/ansiblelint/rules/var_naming.py | 18 +++++++++--------- .../schemas/ansible-lint-config.json | 4 ++++ 5 files changed, 23 insertions(+), 15 deletions(-) diff --git a/.ansible-lint b/.ansible-lint index 099fe016c3..11b359baa1 100644 --- a/.ansible-lint +++ b/.ansible-lint @@ -32,6 +32,12 @@ mock_roles: # Enable checking of loop variable prefixes in roles loop_var_prefix: "^(__|{role}_)" +# Enforce role-related variable names to start with the pattern below. +# By default Ansible Lint accepts "role name" prefix with optional leading underscore(s). +# With this option for example you can prefix role variables with the collection name +# which the role belongs to instead of role name. +role_var_prefix: "^_*{role}_" + # Enforce variable names to follow pattern below, in addition to Ansible own # requirements, like avoiding python identifiers. To disable add `var-naming` # to skip_list. diff --git a/src/ansiblelint/config.py b/src/ansiblelint/config.py index 559808450d..83bd0901d5 100644 --- a/src/ansiblelint/config.py +++ b/src/ansiblelint/config.py @@ -183,6 +183,7 @@ class Options: # pylint: disable=too-many-instance-attributes # Refer to https://docs.ansible.com/ansible/latest/reference_appendices/release_and_maintenance.html#ansible-core-support-matrix _default_supported = ["2.15.", "2.16.", "2.17.", "2.18.", "2.19."] supported_ansible_also: list[str] = field(default_factory=list) + role_var_prefix: str | None = None @property def nodeps(self) -> bool: diff --git a/src/ansiblelint/rules/var_naming.md b/src/ansiblelint/rules/var_naming.md index a9a4b1182e..bda12529db 100644 --- a/src/ansiblelint/rules/var_naming.md +++ b/src/ansiblelint/rules/var_naming.md @@ -29,12 +29,9 @@ Possible errors messages: !!! note When using `include_role` or `import_role` with `vars`, vars should start - with included role name prefix. As this role might not be compliant - with this rule yet, you might need to temporarily disable this rule using - a `# noqa: var-naming[no-role-prefix]` comment. - - In all other task types variable names defined in `vars` are considered - task-scoped and do not require the role prefix. + with included role name prefix. If you want to tweak the role vars' + prefix pattern such as prefixing them with the collection name, you could + use `role_var_prefix` configuration option. ## Settings diff --git a/src/ansiblelint/rules/var_naming.py b/src/ansiblelint/rules/var_naming.py index e5d265abc2..d0d2d726e7 100644 --- a/src/ansiblelint/rules/var_naming.py +++ b/src/ansiblelint/rules/var_naming.py @@ -10,11 +10,7 @@ from ansible.vars.reserved import get_reserved_names from ansiblelint.config import Options, options -from ansiblelint.constants import ( - ANNOTATION_KEYS, - PLAYBOOK_ROLE_KEYWORDS, - RC, -) +from ansiblelint.constants import ANNOTATION_KEYS, PLAYBOOK_ROLE_KEYWORDS, RC from ansiblelint.file_utils import Lintable from ansiblelint.rules import AnsibleLintRule, RulesCollection from ansiblelint.runner import Runner @@ -44,6 +40,7 @@ class VariableNamingRule(AnsibleLintRule): needs_raw_task = True re_pattern_str = options.var_naming_pattern or "^[a-z_][a-z0-9_]*$" re_pattern = re.compile(re_pattern_str) + re_role_var_prefix_pattern_str = options.role_var_prefix or "^_*{role}_" reserved_names = get_reserved_names() # List of special variables that should be treated as read-only. This list # does not include connection variables, which we expect users to tune in @@ -178,13 +175,16 @@ def get_var_naming_matcherror( if ( prefix - and not ident.lstrip("_").startswith(f"{prefix.value}_") + and not re.match( + self.re_role_var_prefix_pattern_str.format(role=prefix.value), + ident, + ) and not has_jinja(prefix.value) and is_fqcn_or_name(prefix.value) ): return self.create_matcherror( tag="var-naming[no-role-prefix]", - message=f"Variables names from within roles should use {prefix.value}_ as a prefix.", + message=f"Variables names from within roles should use /{self.re_role_var_prefix_pattern_str.format(role=prefix.value)}/ pattern as a prefix.", filename=file, data=ident, ) @@ -346,8 +346,8 @@ def _parse_prefix(self, fqcn: str) -> Prefix: if "pytest" in sys.modules: import pytest - from ansiblelint.testing import ( # pylint: disable=ungrouped-imports - run_ansible_lint, + from ansiblelint.testing import ( + run_ansible_lint, # pylint: disable=ungrouped-imports ) @pytest.mark.parametrize( diff --git a/src/ansiblelint/schemas/ansible-lint-config.json b/src/ansiblelint/schemas/ansible-lint-config.json index f205a00f78..615434b2a4 100644 --- a/src/ansiblelint/schemas/ansible-lint-config.json +++ b/src/ansiblelint/schemas/ansible-lint-config.json @@ -134,6 +134,10 @@ "title": "Quiet", "type": "boolean" }, + "role_var_prefix": { + "title": "Role Var Prefix", + "type": "string" + }, "rules": { "additionalProperties": { "$ref": "#/$defs/rule" From 4196fbb1dad556d45a20e5885bdfae44a25cd237 Mon Sep 17 00:00:00 2001 From: stopendy <4696765+stopendy@users.noreply.github.com> Date: Sun, 19 Oct 2025 02:48:27 +0900 Subject: [PATCH 2/2] chore: update test cases --- .../roles/role_vars_prefix_detection/.ansible-lint | 2 ++ src/ansiblelint/rules/var_naming.py | 11 +++++++++++ test/test_cli_role_paths.py | 6 ++---- 3 files changed, 15 insertions(+), 4 deletions(-) create mode 100644 examples/roles/role_vars_prefix_detection/.ansible-lint diff --git a/examples/roles/role_vars_prefix_detection/.ansible-lint b/examples/roles/role_vars_prefix_detection/.ansible-lint new file mode 100644 index 0000000000..038d6b506c --- /dev/null +++ b/examples/roles/role_vars_prefix_detection/.ansible-lint @@ -0,0 +1,2 @@ +--- +role_var_prefix: "^_*({role}_|fo|bar)" diff --git a/src/ansiblelint/rules/var_naming.py b/src/ansiblelint/rules/var_naming.py index d0d2d726e7..f5147c5ae9 100644 --- a/src/ansiblelint/rules/var_naming.py +++ b/src/ansiblelint/rules/var_naming.py @@ -430,6 +430,17 @@ def test_var_naming_with_role_prefix( for result in results: assert result.tag == "var-naming[no-role-prefix]" + def test_var_naming_with_custom_role_prefix() -> None: + """Test rule matches.""" + role_path = "examples/roles/role_vars_prefix_detection" + conf_path = "examples/roles/role_vars_prefix_detection/.ansible-lint" + result = run_ansible_lint( + f"--config-file={conf_path}", + role_path, + ) + assert result.returncode == RC.SUCCESS + assert "var-naming[no-role-prefix]" not in result.stdout + @pytest.mark.libyaml def test_var_naming_with_role_prefix_plays( default_rules_collection: RulesCollection, diff --git a/test/test_cli_role_paths.py b/test/test_cli_role_paths.py index bb80b3cda5..22fd887633 100644 --- a/test/test_cli_role_paths.py +++ b/test/test_cli_role_paths.py @@ -232,8 +232,6 @@ def test_run_role_identified_prefix_missing(local_test_dir: Path) -> None: ) assert result.returncode == RC.VIOLATIONS_FOUND assert ( - "Variables names from within roles should use bar_ as a prefix" in result.stdout - ) - assert ( - "Variables names from within roles should use bar_ as a prefix" in result.stdout + "Variables names from within roles should use /^_*bar_/ pattern as a prefix." + in result.stdout )