mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
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>
128 lines
4.7 KiB
Python
128 lines
4.7 KiB
Python
import unittest
|
|
from collections import Counter
|
|
|
|
import yaml
|
|
|
|
from utils.cache.yaml import load_yaml_any
|
|
|
|
from . import PROJECT_ROOT
|
|
|
|
ROLES_DIR = PROJECT_ROOT / "roles"
|
|
|
|
|
|
class TestDomainsStructure(unittest.TestCase):
|
|
def test_domains_keys_types_and_uniqueness(self):
|
|
"""Ensure that under 'domains' only 'canonical' and 'aliases' keys exist,
|
|
'aliases' is a list of strings, 'canonical' is either a list of strings
|
|
or a dict with string values, and no domain is defined more than once
|
|
across all roles."""
|
|
failed_roles = []
|
|
all_domains = []
|
|
|
|
for role_path in ROLES_DIR.iterdir():
|
|
if not role_path.is_dir():
|
|
continue
|
|
vars_dir = role_path / "vars"
|
|
if not vars_dir.exists():
|
|
continue
|
|
|
|
for vars_file in vars_dir.glob("*.yml"):
|
|
try:
|
|
data = load_yaml_any(vars_file) or {}
|
|
except yaml.YAMLError as e:
|
|
failed_roles.append(
|
|
(role_path.name, vars_file.name, f"YAML error: {e}")
|
|
)
|
|
continue
|
|
|
|
if "domains" not in data:
|
|
continue
|
|
|
|
domains = data.get("server", {}).get("domains")
|
|
if not isinstance(domains, dict):
|
|
failed_roles.append(
|
|
(role_path.name, vars_file.name, "'domains' should be a dict")
|
|
)
|
|
continue
|
|
|
|
allowed_keys = {"canonical", "aliases"}
|
|
extra_keys = set(domains.keys()) - allowed_keys
|
|
if extra_keys:
|
|
failed_roles.append(
|
|
(
|
|
role_path.name,
|
|
vars_file.name,
|
|
f"Unexpected keys in 'domains': {extra_keys}",
|
|
)
|
|
)
|
|
|
|
if "aliases" in domains:
|
|
aliases = domains["aliases"]
|
|
if not isinstance(aliases, list) or not all(
|
|
isinstance(item, str) for item in aliases
|
|
):
|
|
failed_roles.append(
|
|
(
|
|
role_path.name,
|
|
vars_file.name,
|
|
"'aliases' must be a list of strings",
|
|
)
|
|
)
|
|
else:
|
|
all_domains.extend(aliases)
|
|
|
|
if "canonical" in domains:
|
|
canonical = domains["canonical"]
|
|
if isinstance(canonical, list):
|
|
if not all(isinstance(item, str) for item in canonical):
|
|
failed_roles.append(
|
|
(
|
|
role_path.name,
|
|
vars_file.name,
|
|
"'canonical' list items must be strings",
|
|
)
|
|
)
|
|
else:
|
|
all_domains.extend(canonical)
|
|
elif isinstance(canonical, dict):
|
|
if not all(
|
|
isinstance(k, str) and isinstance(v, str)
|
|
for k, v in canonical.items()
|
|
):
|
|
failed_roles.append(
|
|
(
|
|
role_path.name,
|
|
vars_file.name,
|
|
"All keys and values in 'canonical' dict must be strings",
|
|
)
|
|
)
|
|
else:
|
|
all_domains.extend(canonical.values())
|
|
else:
|
|
failed_roles.append(
|
|
(
|
|
role_path.name,
|
|
vars_file.name,
|
|
"'canonical' must be a list or a dict",
|
|
)
|
|
)
|
|
|
|
duplicates = [
|
|
domain for domain, count in Counter(all_domains).items() if count > 1
|
|
]
|
|
if duplicates:
|
|
failed_roles.append(
|
|
("GLOBAL", "", f"Duplicate domain entries found: {duplicates}")
|
|
)
|
|
|
|
if failed_roles:
|
|
messages = []
|
|
for role, file, reason in failed_roles:
|
|
entry = f"{role}/{file}: {reason}" if file else f"{role}: {reason}"
|
|
messages.append(entry)
|
|
self.fail("Domain structure errors found:\n" + "\n".join(messages))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|