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:
2025-09-19 11:42:01 +02:00
parent 83ce88a048
commit c6677ca61b
2 changed files with 36 additions and 37 deletions

View File

@@ -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:

View File

@@ -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: