From c6677ca61bbad416349406c51d94c26d73ea1f4c Mon Sep 17 00:00:00 2001 From: Kevin Veen-Birkenbach Date: Fri, 19 Sep 2025 11:42:01 +0200 Subject: [PATCH] tests: ignore Jinja variables inside raw blocks in variable definitions check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- roles/web-app-xwiki/tasks/04_extensions.yml | 1 - .../integration/test_variable_definitions.py | 72 +++++++++---------- 2 files changed, 36 insertions(+), 37 deletions(-) diff --git a/roles/web-app-xwiki/tasks/04_extensions.yml b/roles/web-app-xwiki/tasks/04_extensions.yml index e452fdba..72887541 100644 --- a/roles/web-app-xwiki/tasks/04_extensions.yml +++ b/roles/web-app-xwiki/tasks/04_extensions.yml @@ -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: diff --git a/tests/integration/test_variable_definitions.py b/tests/integration/test_variable_definitions.py index 26700154..9178609d 100644 --- a/tests/integration/test_variable_definitions.py +++ b/tests/integration/test_variable_definitions.py @@ -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: