Skip to content

Commit bce29ae

Browse files
notokcognifloyd
andauthored
Fix var-naming rule to show line numbers and apply noqa (#2090)
Co-authored-by: Jacob Floyd <cognifloyd@gmail.com>
1 parent 9baea35 commit bce29ae

4 files changed

Lines changed: 85 additions & 9 deletions

File tree

.github/workflows/tox.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ jobs:
156156
WSLENV: FORCE_COLOR:PYTEST_REQPASS:TOXENV:TOX_PARALLEL_NO_SPINNER
157157
# Number of expected test passes, safety measure for accidental skip of
158158
# tests. Update value if you add/remove tests.
159-
PYTEST_REQPASS: 608
159+
PYTEST_REQPASS: 609
160160

161161
steps:
162162
- name: Activate WSL1

src/ansiblelint/rules/var_naming.py

Lines changed: 71 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,49 @@
44
import sys
55
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
66

7+
from ansible.parsing.yaml.objects import AnsibleUnicode
8+
79
from ansiblelint.config import options
810
from ansiblelint.file_utils import Lintable
911
from ansiblelint.rules import AnsibleLintRule
12+
from ansiblelint.skip_utils import get_rule_skips_from_line
1013
from ansiblelint.utils import LINE_NUMBER_KEY, parse_yaml_from_file
1114

1215
if TYPE_CHECKING:
1316
from ansiblelint.constants import odict
1417
from ansiblelint.errors import MatchError
1518

1619

17-
FAIL_PLAY = """
20+
# Should raise var-naming at line [4, 8].
21+
FAIL_PLAY = """---
1822
- hosts: localhost
1923
vars:
2024
CamelCaseIsBad: false # invalid
2125
this_is_valid: # valid because content is a dict, not a variable
2226
CamelCase: ...
2327
ALL_CAPS: ...
2428
ALL_CAPS_ARE_BAD_TOO: ... # invalid
29+
CamelCaseButErrorIgnored: true # noqa: var-naming
2530
2631
tasks:
2732
- name: foo
2833
ansible.builtin.set_fact:
2934
"{{ 'test_' }}var": "value" # valid
35+
- name: bar
36+
ansible.builtin.set_fact:
37+
CamelCaseButErrorIgnored: true # noqa: var-naming
38+
"""
39+
40+
41+
# Should raise var-naming at line [2, 6].
42+
FAIL_VARS = """---
43+
CamelCaseIsBad: false # invalid
44+
this_is_valid: # valid because content is a dict, not a variable
45+
CamelCase: ...
46+
ALL_CAPS: ...
47+
ALL_CAPS_ARE_BAD_TOO: ... # invalid
48+
"{{ 'test_' }}var": "value" # valid
49+
CamelCaseButErrorIgnored: true # noqa: var-naming
3050
"""
3151

3252

@@ -72,21 +92,31 @@ def matchplay(
7292
self, file: "Lintable", data: "odict[str, Any]"
7393
) -> List["MatchError"]:
7494
"""Return matches found for a specific playbook."""
75-
results = []
95+
results: List["MatchError"] = []
96+
raw_results: List["MatchError"] = []
7697

7798
# If the Play uses the 'vars' section to set variables
7899
our_vars = data.get("vars", {})
79100
for key in our_vars.keys():
80101
if self.is_invalid_variable_name(key):
81-
results.append(
102+
raw_results.append(
82103
self.create_matcherror(
83104
filename=file,
84-
linenumber=our_vars[LINE_NUMBER_KEY],
105+
linenumber=key.ansible_pos[1]
106+
if isinstance(key, AnsibleUnicode)
107+
else our_vars[LINE_NUMBER_KEY],
85108
message="Play defines variable '"
86109
+ key
87110
+ "' within 'vars' section that violates variable naming standards",
88111
)
89112
)
113+
if raw_results:
114+
lines = file.content.splitlines()
115+
for match in raw_results:
116+
# linenumber starts with 1, not zero
117+
skip_list = get_rule_skips_from_line(lines[match.linenumber - 1])
118+
if match.rule.id not in skip_list and match.tag not in skip_list:
119+
results.append(match)
90120

91121
return results
92122

@@ -118,21 +148,29 @@ def matchtask(
118148
def matchyaml(self, file: Lintable) -> List["MatchError"]:
119149
"""Return matches for variables defined in vars files."""
120150
results: List["MatchError"] = []
121-
meta_data: Dict[str, Any] = {}
151+
raw_results: List["MatchError"] = []
152+
meta_data: Dict[AnsibleUnicode, Any] = {}
122153

123154
if str(file.kind) == "vars":
124155
meta_data = parse_yaml_from_file(str(file.path))
125156
for key in meta_data.keys():
126157
if self.is_invalid_variable_name(key):
127-
results.append(
158+
raw_results.append(
128159
self.create_matcherror(
129160
filename=file,
130-
# linenumber=vars[LINE_NUMBER_KEY],
161+
linenumber=key.ansible_pos[1],
131162
message="File defines variable '"
132163
+ key
133164
+ "' that violates variable naming standards",
134165
)
135166
)
167+
if raw_results:
168+
lines = file.content.splitlines()
169+
for match in raw_results:
170+
# linenumber starts with 1, not zero
171+
skip_list = get_rule_skips_from_line(lines[match.linenumber - 1])
172+
if match.rule.id not in skip_list and match.tag not in skip_list:
173+
results.append(match)
136174
else:
137175
results.extend(super().matchyaml(file))
138176
return results
@@ -154,3 +192,29 @@ def test_invalid_var_name_playbook(rule_runner: RunFromText) -> None:
154192
assert len(results) == 2
155193
for result in results:
156194
assert result.rule.id == VariableNamingRule.id
195+
196+
# list unexpected error lines or non-matching error lines
197+
expected_error_lines = [4, 8]
198+
lines = [i.linenumber for i in results]
199+
error_lines_difference = list(
200+
set(expected_error_lines).symmetric_difference(set(lines))
201+
)
202+
assert len(error_lines_difference) == 0
203+
204+
@pytest.mark.parametrize(
205+
"rule_runner", (VariableNamingRule,), indirect=["rule_runner"]
206+
)
207+
def test_invalid_var_name_varsfile(rule_runner: RunFromText) -> None:
208+
"""Test rule matches."""
209+
results = rule_runner.run_role_defaults_main(FAIL_VARS)
210+
assert len(results) == 2
211+
for result in results:
212+
assert result.rule.id == VariableNamingRule.id
213+
214+
# list unexpected error lines or non-matching error lines
215+
expected_error_lines = [2, 6]
216+
lines = [i.linenumber for i in results]
217+
error_lines_difference = list(
218+
set(expected_error_lines).symmetric_difference(set(lines))
219+
)
220+
assert len(error_lines_difference) == 0

src/ansiblelint/testing/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,17 @@ def run_role_meta_main(self, meta_main_text: str) -> List[MatchError]:
6969
shutil.rmtree(role_path)
7070
return results
7171

72+
def run_role_defaults_main(self, defaults_main_text: str) -> List[MatchError]:
73+
"""Lints received text as vars file in defaults."""
74+
role_path = tempfile.mkdtemp(prefix="role_")
75+
defaults_path = os.path.join(role_path, "defaults")
76+
os.makedirs(defaults_path)
77+
with open(os.path.join(defaults_path, "main.yml"), "w", encoding="utf-8") as fh:
78+
fh.write(defaults_main_text)
79+
results = self._call_runner(role_path)
80+
shutil.rmtree(role_path)
81+
return results
82+
7283

7384
def run_ansible_lint(
7485
*argv: str,

test/test_skiputils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,5 @@ def test_get_rule_skips_from_line(line: str, expected: str) -> None:
3434
def test_playbook_noqa(default_text_runner: RunFromText) -> None:
3535
"""Check that noqa is properly taken into account on vars and tasks."""
3636
results = default_text_runner.run_playbook(PLAYBOOK_WITH_NOQA)
37-
assert len(results) == 2
37+
# Should raise error at "SOME_VAR".
38+
assert len(results) == 1

0 commit comments

Comments
 (0)