diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 89d099f88b..dc2f77ac13 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -67,22 +67,10 @@ jobs: # proof that we failed to catch a bug by not running it. Using # distribution should be preferred instead of custom builds. env: - # vars safe to be passed to wsl: - WSLENV: FORCE_COLOR:PYTEST_REQPASS:TOXENV:GITHUB_STEP_SUMMARY # Number of expected test passes, safety measure for accidental skip of # tests. Update value if you add/remove tests. PYTEST_REQPASS: 828 steps: - - name: Activate WSL1 - if: "contains(matrix.shell, 'wsl')" - uses: Vampire/setup-wsl@v2 - - - name: MacOS workaround for https://github.com/actions/virtual-environments/issues/1187 - if: ${{ matrix.os == 'macOS-latest' }} - run: | - sudo sysctl -w net.link.generic.system.hwcksum_tx=0 - sudo sysctl -w net.link.generic.system.hwcksum_rx=0 - - uses: actions/checkout@v4 with: fetch-depth: 0 # needed by setuptools-scm diff --git a/examples/playbooks/role_vars_prefix_detection.yml b/examples/playbooks/role_vars_prefix_detection.yml new file mode 100644 index 0000000000..823f6641f0 --- /dev/null +++ b/examples/playbooks/role_vars_prefix_detection.yml @@ -0,0 +1,51 @@ +--- +- name: Test role-prefix + hosts: localhost + connection: local + roles: + - role_vars_prefix_detection + + - role: role_vars_prefix_detection + var1: val1 + + - role: role_vars_prefix_detection + var1: val1 + become: true + vars: + var2: val2 + + - role: role_vars_prefix_detection + become: true + environment: + FOO: /bar/barr + role_vars_prefix_detection_var1: val1 + + - role: role_vars_prefix_detection + vars: + var1: val1 + + - role: role_vars_prefix_detection + become: true + environment: + BAR: /baz + vars: + var1: val1 + + - role: role_vars_prefix_detection + become: true + environment: + BAR: /baz + vars: + role_vars_prefix_detection_var1: val1 + tasks: + - name: Include1 + ansible.builtin.include_role: + name: role_vars_prefix_detection + vars: + var1: val1 + + - name: Include2 + ansible.builtin.include_role: + name: role_vars_prefix_detection + vars: + role_vars_prefix_detection_var2: val2 diff --git a/src/ansiblelint/constants.py b/src/ansiblelint/constants.py index 6b8bd12d01..2b663e5247 100644 --- a/src/ansiblelint/constants.py +++ b/src/ansiblelint/constants.py @@ -126,6 +126,36 @@ def main(): "pre_tasks", "post_tasks", ] +PLAYBOOK_ROLE_KEYWORDS = [ + "any_errors_fatal", + "become", + "become_exe", + "become_flags", + "become_method", + "become_user", + "check_mode", + "collections", + "connection", + "debugger", + "delegate_facts", + "delegate_to", + "diff", + "environment", + "ignore_errors", + "ignore_unreachable", + "module_defaults", + "name", + "role", + "no_log", + "port", + "remote_user", + "run_once", + "tags", + "throttle", + "timeout", + "vars", + "when", +] NESTED_TASK_KEYS = [ "block", "always", diff --git a/src/ansiblelint/rules/var_naming.py b/src/ansiblelint/rules/var_naming.py index 33abb489a4..ac0f314ed2 100644 --- a/src/ansiblelint/rules/var_naming.py +++ b/src/ansiblelint/rules/var_naming.py @@ -10,7 +10,12 @@ 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.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 @@ -193,6 +198,37 @@ def matchplay(self, file: Lintable, data: dict[str, Any]) -> list[MatchError]: 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: @@ -360,6 +396,28 @@ def test_var_naming_with_role_prefix( 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"