Files
computer-playbook/plugins/lookup/path_absolute.py
T
kevinveenbirkenbachandClaude Opus 4.8 23d1447566 refactor(ansible): add path_absolute lookup, replace playbook_dir joins
Introduce plugins/lookup/path_absolute.py as the single source for the
repo-root anchor: lookup('path_absolute', 'roles/...') returns an absolute
path under playbook_dir, joining every term. Replace all hand-built
playbook_dir path constructions across .yml/.yaml and .j2 templates with it:

  [playbook_dir, '<rel>'] | path_join   -> lookup('path_absolute', '<rel>')
  playbook_dir ~ '/<rel>'               -> lookup('path_absolute', '<rel>')
  {{ playbook_dir }}/<rel>              -> lookup('path_absolute', '<rel>')

Unrelated repo_path symbols in cli/meta/roles/applications/resolution and the
single bare discover_playwright_roles(playbook_dir) call are left untouched.

Add tests/lint/ansible/test_playbook_dir_via_lookup.py: fails the build if any
of the three construction forms reappears in .yml/.yaml/.j2, pointing callers
at the lookup. Unit test covers the plugin.

Touching these files pulled their pre-existing narration comments into
test_comments_valid scope; clean them (delete restatement/banners, keep real
trip-wires as Workaround/Upstream/Invariant markers). Drop caution, important
and pitfall from the comment-lint marker vocabulary and remove the comments
that used them.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 20:36:57 +02:00

36 lines
1.3 KiB
Python

from __future__ import annotations
from pathlib import Path
from typing import Any
from ansible.errors import AnsibleError
from ansible.plugins.lookup import LookupBase
class LookupModule(LookupBase):
"""
lookup('path_absolute', 'roles/sys-svc-compose/tasks/utils/swarm/x.yml')
-> absolute path under the repository root (``playbook_dir``).
Single point of truth for the repo-root anchor, replacing the repeated
``[playbook_dir, '<rel>'] | path_join`` boilerplate at cross-role
include/template/script/src call sites. Joins every term, so a path may be
passed whole ('roles/x/y.yml') or split across terms when a segment is a
variable (``lookup('path_absolute', 'roles', system_service_role_name)``).
"""
def run(
self, terms, variables: dict[str, Any] | None = None, **kwargs
) -> list[str]:
base = (variables or {}).get("playbook_dir")
if not base:
raise AnsibleError(
"lookup('path_absolute'): 'playbook_dir' is unavailable."
)
parts: list[str] = []
for term in terms:
text = str(term).strip()
if text:
parts.extend(seg for seg in text.strip("/").split("/") if seg)
return [str(Path(str(base), *parts))]