44import sys
55from typing import TYPE_CHECKING , Any , Dict , List , Optional , Union
66
7+ from ansible .parsing .yaml .objects import AnsibleUnicode
8+
79from ansiblelint .config import options
810from ansiblelint .file_utils import Lintable
911from ansiblelint .rules import AnsibleLintRule
12+ from ansiblelint .skip_utils import get_rule_skips_from_line
1013from ansiblelint .utils import LINE_NUMBER_KEY , parse_yaml_from_file
1114
1215if 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
0 commit comments