mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 02:54:26 +00:00
Move files that share a delimiter-bounded name prefix within a folder (threshold 2) into a subfolder named after the prefix, dropping the prefix from the filename. Covers plugins/filter (docker, generate, has, merge, get/all), env handler subpackages, utils/roles/entity, utils/tests/swarm/write, role SQL/scripts/templates, keycloak/mailu task-includes, shared tasks/utils/load, and CI scripts. Update every import, query_file/script/include_tasks/src/lookup target, compose bind mount, and python -m path to the new locations. Extend the prefix-grouped-files lint to threshold 2 with dir-level .nocheck and file-head nocheck opt-outs, and skip groups whose shortest member's stem equals the shared prefix. Keep flat only where a loader forbids nesting: WordPress mu-plugins and the docs/requirements namespace. Ansible filter discovery is recursive, so nested filters stay Jinja-resolvable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
113 lines
4.0 KiB
Python
113 lines
4.0 KiB
Python
from __future__ import annotations
|
|
|
|
from functools import cache
|
|
from typing import TYPE_CHECKING, Any
|
|
|
|
from utils.roles.entity.name import get_entity_name
|
|
|
|
from . import yaml_io
|
|
|
|
if TYPE_CHECKING:
|
|
from pathlib import Path
|
|
|
|
|
|
def build_network_index(networks_file: Path) -> dict[str, dict[str, Any]]:
|
|
networks = yaml_io.load(networks_file) or {}
|
|
raw = networks.get("defaults_networks", {}) if isinstance(networks, dict) else {}
|
|
local = raw.get("local", {}) if isinstance(raw, dict) else {}
|
|
if not isinstance(local, dict):
|
|
return {}
|
|
|
|
result: dict[str, dict[str, Any]] = {}
|
|
for role_name, value in local.items():
|
|
if isinstance(value, str):
|
|
result[role_name] = {"subnet": value}
|
|
elif isinstance(value, dict):
|
|
entry: dict[str, Any] = {}
|
|
for k in ("subnet", "dns_resolver"):
|
|
if k in value:
|
|
entry[k] = value[k]
|
|
if entry:
|
|
result[role_name] = entry
|
|
return result
|
|
|
|
|
|
def build_port_index(
|
|
ports_file: Path, roles_dir: Path
|
|
) -> tuple[
|
|
dict[str, dict[str, dict[str, Any]]],
|
|
dict[str, dict[str, dict[str, int]]],
|
|
]:
|
|
"""Return ``(ports_for_role, relays_for_role)`` indexed by role -> entity."""
|
|
ports_doc = yaml_io.load(ports_file) or {}
|
|
ports_root = ports_doc.get("ports", {}) if isinstance(ports_doc, dict) else {}
|
|
if not isinstance(ports_root, dict):
|
|
return {}, {}
|
|
|
|
ports_for_role: dict[str, dict[str, dict[str, Any]]] = {}
|
|
relays_for_role: dict[str, dict[str, dict[str, int]]] = {}
|
|
|
|
for scope in ("localhost", "public"):
|
|
scope_block = ports_root.get(scope, {})
|
|
if not isinstance(scope_block, dict):
|
|
continue
|
|
scope_label = "local" if scope == "localhost" else "public"
|
|
for category, entries in scope_block.items():
|
|
if category == "relay_port_ranges":
|
|
_absorb_relays(entries, relays_for_role, roles_dir)
|
|
continue
|
|
if not isinstance(entries, dict):
|
|
continue
|
|
for combined_key, port_value in entries.items():
|
|
if not isinstance(port_value, int):
|
|
continue
|
|
role_name, entity = _split_role_and_entity(combined_key, roles_dir)
|
|
entity_key = entity or get_entity_name(role_name) or role_name
|
|
role_ports = ports_for_role.setdefault(role_name, {})
|
|
entity_ports = role_ports.setdefault(entity_key, {})
|
|
scope_map = entity_ports.setdefault(scope_label, {})
|
|
scope_map[category] = port_value
|
|
|
|
return ports_for_role, relays_for_role
|
|
|
|
|
|
def _absorb_relays(
|
|
entries: Any,
|
|
relays_for_role: dict[str, dict[str, dict[str, int]]],
|
|
roles_dir: Path,
|
|
) -> None:
|
|
if not isinstance(entries, dict):
|
|
return
|
|
for combined_key, port_value in entries.items():
|
|
if not isinstance(port_value, int):
|
|
continue
|
|
if combined_key.endswith("_start"):
|
|
base, kind = combined_key[: -len("_start")], "start"
|
|
elif combined_key.endswith("_end"):
|
|
base, kind = combined_key[: -len("_end")], "end"
|
|
else:
|
|
continue
|
|
role_name, entity = _split_role_and_entity(base, roles_dir)
|
|
entity_key = entity or get_entity_name(role_name) or role_name
|
|
role_relays = relays_for_role.setdefault(role_name, {})
|
|
role_relays.setdefault(entity_key, {})[kind] = port_value
|
|
|
|
|
|
def _split_role_and_entity(
|
|
combined_key: str, roles_dir: Path
|
|
) -> tuple[str, str | None]:
|
|
"""Return ``(role_name, entity_or_None)`` for a `09_ports.yml` key."""
|
|
known = _all_role_names(roles_dir)
|
|
if combined_key in known:
|
|
return combined_key, None
|
|
if "_" in combined_key:
|
|
prefix, suffix = combined_key.rsplit("_", 1)
|
|
if prefix in known:
|
|
return prefix, suffix
|
|
return combined_key, None
|
|
|
|
|
|
@cache
|
|
def _all_role_names(roles_dir: Path) -> frozenset[str]:
|
|
return frozenset(child.name for child in roles_dir.iterdir() if child.is_dir())
|