feat(ansible): steward:category + steward:confirm playbook metadata
Extend the playbook metadata convention with a namespaced `# steward:<key>:` comment block: - steward:category — free-text grouping label, shown as a badge in the browse list and on the run form. - steward:confirm — true/yes/1/on marks a playbook destructive; the run form then requires a confirmation tick (required checkbox in the shared vars fragment) before it can launch. sources.discover_playbook_meta() parses description + category + confirm (first match per key; `# description:` still primary, `# steward:description:` alias). discover_playbook_description() now delegates to it. The browse list reads per-playbook meta to show category badges + descriptions; the run-form and playbook-vars fragments render the badge + confirm gate. Bundled playbooks tagged: docker_prune → category maintenance + confirm true; provision/install/update → category host-agent. Docs: docs/reference/playbook-authoring.md updated (keys now implemented) and a quick reference added next to the code at steward/ansible/PLAYBOOK_CONVENTIONS.md. Tests added for category/confirm/alias parsing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,23 @@
|
||||
{# Description + discovered variable fields for a selected playbook. Shared by
|
||||
the browse run form, host run form, and schedule form. Expects `variables`
|
||||
and `description`. Rendered standalone by /ansible/playbook-vars. #}
|
||||
{% if description %}
|
||||
{% if description or category %}
|
||||
<div style="background:var(--bg);border-left:3px solid var(--accent);border-radius:3px;
|
||||
padding:0.5rem 0.7rem;margin:0.25rem 0 0.6rem;font-size:0.82rem;color:var(--text-muted);">
|
||||
{% if category %}<span style="display:inline-block;font-size:0.68rem;text-transform:uppercase;letter-spacing:0.04em;
|
||||
background:var(--bg-elevated);color:var(--text-dim);border-radius:3px;padding:0.05em 0.45em;margin-right:0.4rem;">{{ category }}</span>{% endif %}
|
||||
{{ description }}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if confirm %}
|
||||
<label style="display:flex;align-items:flex-start;gap:0.5rem;background:color-mix(in srgb,var(--red) 10%,var(--bg-elevated));
|
||||
border:1px solid color-mix(in srgb,var(--red) 35%,var(--border));border-radius:6px;
|
||||
padding:0.6rem 0.8rem;margin:0 0 0.6rem;font-size:0.82rem;font-weight:normal;cursor:pointer;">
|
||||
<input type="checkbox" name="confirmed" required style="margin-top:0.15rem;">
|
||||
<span><strong style="color:var(--red);">Confirm:</strong> this playbook is marked as making
|
||||
significant or destructive changes. Tick to enable running it.</span>
|
||||
</label>
|
||||
{% endif %}
|
||||
{% if variables %}
|
||||
<div style="margin:0.5rem 0 0.25rem;font-size:0.82rem;color:var(--text-muted);font-weight:600;">
|
||||
Playbook variables
|
||||
|
||||
@@ -70,13 +70,19 @@
|
||||
<tbody>
|
||||
{% for pb in sd.playbooks %}
|
||||
<tr>
|
||||
<td style="font-family:ui-monospace,monospace;font-size:0.88rem;">{{ pb }}</td>
|
||||
<td>
|
||||
<div style="display:flex;align-items:center;gap:0.5rem;flex-wrap:wrap;">
|
||||
{% if pb.category %}<span style="font-size:0.68rem;text-transform:uppercase;letter-spacing:0.04em;background:var(--bg-elevated);color:var(--text-dim);border-radius:3px;padding:0.05em 0.45em;">{{ pb.category }}</span>{% endif %}
|
||||
<span style="font-family:ui-monospace,monospace;font-size:0.88rem;">{{ pb.path }}</span>
|
||||
</div>
|
||||
{% if pb.description %}<div style="font-size:0.78rem;color:var(--text-muted);margin-top:0.15rem;">{{ pb.description }}</div>{% endif %}
|
||||
</td>
|
||||
<td class="td-actions">
|
||||
<a href="/ansible/browse/{{ sd.source.name }}/{{ pb }}" class="btn btn-sm btn-ghost" style="margin-right:0.5rem;">View</a>
|
||||
<a href="/ansible/browse/{{ sd.source.name }}/{{ pb.path }}" class="btn btn-sm btn-ghost" style="margin-right:0.5rem;">View</a>
|
||||
{% if sd.editable and session.user_role == 'admin' %}
|
||||
<a href="/ansible/playbooks/edit/{{ sd.source.name }}/{{ pb }}" class="btn btn-sm btn-ghost" style="margin-right:0.5rem;">Edit</a>
|
||||
<a href="/ansible/playbooks/edit/{{ sd.source.name }}/{{ pb.path }}" class="btn btn-sm btn-ghost" style="margin-right:0.5rem;">Edit</a>
|
||||
{% endif %}
|
||||
<a hx-get="/ansible/run-form/{{ sd.source.name }}/{{ pb }}"
|
||||
<a hx-get="/ansible/run-form/{{ sd.source.name }}/{{ pb.path }}"
|
||||
hx-target="#run-form-{{ sd.source.name }}" hx-swap="innerHTML"
|
||||
style="cursor:pointer;" class="btn btn-sm btn-ghost">Run</a>
|
||||
</td>
|
||||
|
||||
Reference in New Issue
Block a user