mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-09-19 16:56:04 +02:00
tests: ignore Jinja variables inside raw blocks in variable definitions check
- Added regex masking to skip {{ var }} usages inside {% raw %}…{% endraw %} blocks. - Simplified code by removing redundant comments. - Cleaned up task file for XWiki role by removing outdated note. Ref: https://chatgpt.com/share/68cd2558-e92c-800f-a80a-a79d3c81476e
This commit is contained in:
@@ -9,7 +9,6 @@
|
||||
#
|
||||
# Notes:
|
||||
# - We print machine-readable markers so Ansible can assert deterministically.
|
||||
# - We protect XWiki's {{groovy}} wiki macro from Jinja by using {% raw %}…{% endraw %}.
|
||||
|
||||
- name: "XWIKI | Build Groovy installer code from static file (base64 payload)"
|
||||
vars:
|
||||
|
@@ -34,6 +34,14 @@ class TestVariableDefinitions(unittest.TestCase):
|
||||
# File extensions to scan for Jinja usage/inline definitions
|
||||
self.scan_extensions = {'.yml', '.j2'}
|
||||
|
||||
# -----------------------
|
||||
# Raw-block pattern (ignore any Jinja inside {% raw %}...{% endraw %})
|
||||
# Supports trimmed variants: {%- raw -%} ... {%- endraw -%}
|
||||
self.raw_block_re = re.compile(
|
||||
r'{%\s*-?\s*raw\s*-?\s*%}.*?{%\s*-?\s*endraw\s*-?\s*%}',
|
||||
re.DOTALL,
|
||||
)
|
||||
|
||||
# -----------------------
|
||||
# Regex patterns
|
||||
# -----------------------
|
||||
@@ -121,18 +129,14 @@ class TestVariableDefinitions(unittest.TestCase):
|
||||
# Block mapping: keys on subsequent indented lines
|
||||
in_set_fact = True
|
||||
set_fact_indent = indent
|
||||
# continue to next iteration to avoid double-processing this line
|
||||
continue
|
||||
|
||||
if in_set_fact:
|
||||
# Still inside set_fact child mapping?
|
||||
if indent > set_fact_indent and stripped.strip():
|
||||
m = self.mapping_key.match(stripped)
|
||||
if m:
|
||||
self.defined.add(m.group(1))
|
||||
# do not continue; still scan for Jinja defs below
|
||||
else:
|
||||
# Leaving the block when indentation decreases or a new key at same level appears
|
||||
if indent <= set_fact_indent and stripped:
|
||||
in_set_fact = False
|
||||
|
||||
@@ -140,48 +144,37 @@ class TestVariableDefinitions(unittest.TestCase):
|
||||
if self.ansible_vars_block.match(stripped):
|
||||
in_vars_block = True
|
||||
vars_block_indent = indent
|
||||
# continue to next line to avoid double-processing this line
|
||||
continue
|
||||
|
||||
if in_vars_block:
|
||||
# Inside vars: collect top-level mapping keys
|
||||
if indent > vars_block_indent and stripped.strip():
|
||||
m = self.mapping_key.match(stripped)
|
||||
if m:
|
||||
self.defined.add(m.group(1))
|
||||
# do not continue; still scan for Jinja defs below
|
||||
else:
|
||||
# Leaving vars block
|
||||
if indent <= vars_block_indent and stripped:
|
||||
in_vars_block = False
|
||||
|
||||
# --- Always scan every line (including inside blocks) for Jinja definitions
|
||||
|
||||
# {% set var = ... %}
|
||||
for m in self.jinja_set_def.finditer(line):
|
||||
self.defined.add(m.group(1))
|
||||
|
||||
# {% for x [, y] in ... %}
|
||||
for m in self.jinja_for_def.finditer(line):
|
||||
self.defined.add(m.group(1))
|
||||
if m.group(2):
|
||||
self.defined.add(m.group(2))
|
||||
|
||||
# {% macro name(params...) %}
|
||||
for m in self.jinja_macro_def.finditer(line):
|
||||
params_blob = m.group(1)
|
||||
params = [p.strip() for p in params_blob.split(',')]
|
||||
for p in params:
|
||||
if not p:
|
||||
continue
|
||||
# Strip * / ** for varargs/kwargs
|
||||
p = p.lstrip('*')
|
||||
# Drop default value part: name=...
|
||||
name = p.split('=', 1)[0].strip()
|
||||
if re.match(r'^[a-zA-Z_]\w*$', name):
|
||||
self.defined.add(name)
|
||||
|
||||
# --- loop_var and register names
|
||||
m_loop = self.ansible_loop_var.match(stripped)
|
||||
if m_loop:
|
||||
self.defined.add(m_loop.group(1))
|
||||
@@ -191,7 +184,6 @@ class TestVariableDefinitions(unittest.TestCase):
|
||||
self.defined.add(m_reg.group(1))
|
||||
|
||||
except Exception:
|
||||
# Ignore unreadable files
|
||||
pass
|
||||
|
||||
def test_all_used_vars_are_defined(self):
|
||||
@@ -210,29 +202,37 @@ class TestVariableDefinitions(unittest.TestCase):
|
||||
path = os.path.join(root, fn)
|
||||
try:
|
||||
with open(path, 'r', encoding='utf-8', errors='ignore') as f:
|
||||
for lineno, line in enumerate(f, 1):
|
||||
for m in self.simple_var_pattern.finditer(line):
|
||||
var = m.group(1)
|
||||
content = f.read()
|
||||
|
||||
# Skip well-known Jinja/Ansible builtins and frequent loop aliases
|
||||
if var in (
|
||||
'lookup', 'role_name', 'domains', 'item', 'host_type',
|
||||
'inventory_hostname', 'role_path', 'playbook_dir',
|
||||
'ansible_become_password', 'inventory_dir', 'ansible_memtotal_mb', 'omit', 'group_names', 'ansible_processor_vcpus'
|
||||
):
|
||||
continue
|
||||
# Mask {% raw %} ... {% endraw %} blocks
|
||||
def _mask_raw(m):
|
||||
s = m.group(0)
|
||||
return re.sub(r'[^\n]', ' ', s)
|
||||
|
||||
# Accept if defined directly or via fallback defaults
|
||||
if (
|
||||
var not in self.defined
|
||||
and f"default_{var}" not in self.defined
|
||||
and f"defaults_{var}" not in self.defined
|
||||
):
|
||||
undefined_uses.append(
|
||||
f"{path}:{lineno}: '{{{{ {var} }}}}' used but not defined"
|
||||
)
|
||||
content_wo_raw = self.raw_block_re.sub(_mask_raw, content)
|
||||
|
||||
for lineno, line in enumerate(content_wo_raw.splitlines(True), 1):
|
||||
for m in self.simple_var_pattern.finditer(line):
|
||||
var = m.group(1)
|
||||
|
||||
if var in (
|
||||
'lookup', 'role_name', 'domains', 'item', 'host_type',
|
||||
'inventory_hostname', 'role_path', 'playbook_dir',
|
||||
'ansible_become_password', 'inventory_dir',
|
||||
'ansible_memtotal_mb', 'omit', 'group_names',
|
||||
'ansible_processor_vcpus'
|
||||
):
|
||||
continue
|
||||
|
||||
if (
|
||||
var not in self.defined
|
||||
and f"default_{var}" not in self.defined
|
||||
and f"defaults_{var}" not in self.defined
|
||||
):
|
||||
undefined_uses.append(
|
||||
f"{path}:{lineno}: '{{{{ {var} }}}}' used but not defined"
|
||||
)
|
||||
except Exception:
|
||||
# Ignore unreadable files
|
||||
pass
|
||||
|
||||
if undefined_uses:
|
||||
|
Reference in New Issue
Block a user