feat(ansible): playbooks self-describe via "# description:" comment
Playbooks can ship a human description Steward reads and shows when one is selected. Convention: a `# description: <text>` magic comment (Ansible rejects unknown play keys, so a comment is the portable place — works for third-party playbooks too); falls back to the first play's name:. sources .discover_playbook_description(). Surfaced at the top of the shared _playbook_vars.html partial, which loads on playbook selection in the host run form, schedules form, and browse run form. All four bundled playbooks (provision/install/update/docker_prune) now carry a description line. Unit tests added. Scribe #900. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,10 @@ _SECRET_VAR_RE = re.compile(
|
||||
r"(password|passwd|secret|token|api[_-]?key|private[_-]?key|credential)", re.I
|
||||
)
|
||||
|
||||
# A playbook self-describes via a `# description:` magic comment (Ansible rejects
|
||||
# unknown play keys, so a comment is the portable place). First match wins.
|
||||
_DESCRIPTION_RE = re.compile(r"^\s*#\s*description:\s*(.+?)\s*$", re.I | re.M)
|
||||
|
||||
# Name of the always-present, read-only source of first-party playbooks shipped
|
||||
# inside the app (maintenance tasks, host-agent install). Not operator-editable.
|
||||
BUILTIN_SOURCE_NAME = "steward-builtin"
|
||||
@@ -195,6 +199,28 @@ def delete_playbook(source_path: str, relative_path: str) -> tuple[bool, str | N
|
||||
return True, None
|
||||
|
||||
|
||||
def discover_playbook_description(content: str) -> str:
|
||||
"""A human-readable description of what a playbook does.
|
||||
|
||||
Reads a ``# description: ...`` magic comment (first match), so any playbook
|
||||
can self-describe without touching its YAML structure. Falls back to the
|
||||
first play's ``name:``. Returns "" if neither is present.
|
||||
"""
|
||||
m = _DESCRIPTION_RE.search(content)
|
||||
if m:
|
||||
return m.group(1).strip()
|
||||
import yaml
|
||||
try:
|
||||
plays = yaml.safe_load(content)
|
||||
except yaml.YAMLError:
|
||||
return ""
|
||||
if isinstance(plays, list):
|
||||
for play in plays:
|
||||
if isinstance(play, dict) and play.get("name"):
|
||||
return str(play["name"]).strip()
|
||||
return ""
|
||||
|
||||
|
||||
def discover_playbook_variables(content: str) -> list[dict]:
|
||||
"""Parse a playbook and list the variables an operator can set at run time.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user