Files
computer-playbook/utils/cache/users/placeholders.py
kevinveenbirkenbachandClaude Opus 5 5137465c6f style: delete every comment the comments-valid policy rejects
3529 invalid comments in 528 files, grandfathered in while the linter
only saw the working-tree diff. Removal ran through the linter's own
collectors, so detection and deletion cannot drift: a comment whose
opener line is blank before it and whose closer line is blank after it
loses all its lines, anything else loses only the comment region, and a
line left holding a bare delimiter goes with it.

Five files keep their comments behind a nocheck marker instead. Four are
upstream configs whose inline docs are upstream's own (mautrix, systemd
journald, nextcloud-oidc-login, MediaWiki). In littlejs the
commented-out entries ARE the deactivation mechanism for an example
tile, so deleting them would delete configuration.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 03:22:02 +02:00

107 lines
3.3 KiB
Python

"""Eager placeholder substitution for cached user dicts.
Ansible 2.19's TrustedAsTemplate gate leaves untagged Jinja silently
unrendered, so values like `lastname: "{{ ORGANIZATION }}"` reach
downstream tasks as their literal string. These helpers substitute a
small set of group-vars scalars into the merged users dict before the
templar render pass, so consumers always see resolved values.
Extend `_SCALAR_USER_PLACEHOLDERS` (or call sites) when new scalar
group-vars appear in `roles/user-*/meta/users.yml` fields like
`firstname`, `lastname`, or `description`.
"""
from __future__ import annotations
from collections.abc import Mapping
from typing import Any
from urllib.parse import urlparse
_SCALAR_USER_PLACEHOLDERS: tuple[str, ...] = (
"ORGANIZATION",
"SOFTWARE_NAME",
)
def substitute_primary_domain_placeholder(
users: dict[str, Any],
variables: dict[str, Any],
*,
templar: Any,
) -> dict[str, Any]:
raw = variables.get("DOMAIN_PRIMARY")
if not raw:
return users
text = str(raw).strip()
if not text:
return users
if "{{" in text or "{%" in text:
from utils.templating.ansible import _templar_render_best_effort
text = str(_templar_render_best_effort(templar, text, dict(variables))).strip()
if "://" in text:
parsed = urlparse(text)
text = parsed.hostname or text
text = text.split("/", 1)[0].split(":", 1)[0].strip()
if not text or "{{" in text or "{%" in text:
return users
placeholder = "{{ DOMAIN_PRIMARY }}"
def _walk(value: Any) -> Any:
if isinstance(value, str):
return value.replace(placeholder, text) if placeholder in value else value
if isinstance(value, Mapping):
return {k: _walk(v) for k, v in value.items()}
if isinstance(value, list):
return [_walk(v) for v in value]
if isinstance(value, tuple):
return tuple(_walk(v) for v in value)
return value
return _walk(users)
def substitute_scalar_placeholders(
users: dict[str, Any],
variables: dict[str, Any],
*,
templar: Any,
) -> dict[str, Any]:
from utils.templating.ansible import _templar_render_best_effort
resolved: dict[str, str] = {}
for var in _SCALAR_USER_PLACEHOLDERS:
raw = variables.get(var)
if raw is None:
continue
text = str(raw).strip()
if not text:
continue
if "{{" in text or "{%" in text:
text = str(
_templar_render_best_effort(templar, text, dict(variables))
).strip()
if "{{" in text or "{%" in text:
continue
resolved[f"{{{{ {var} }}}}"] = text
if not resolved:
return users
def _walk(value: Any) -> Any:
if isinstance(value, str):
out = value
for placeholder, replacement in resolved.items():
if placeholder in out:
out = out.replace(placeholder, replacement)
return out
if isinstance(value, Mapping):
return {k: _walk(v) for k, v in value.items()}
if isinstance(value, list):
return [_walk(v) for v in value]
if isinstance(value, tuple):
return tuple(_walk(v) for v in value)
return value
return _walk(users)