feat(ansible): Playbooks as one list, and a page per playbook that runs it (#5331, #5330)
CI / lint (push) Successful in 2s
CI / integration (push) Successful in 55s
CI / unit (push) Failing after 1m20s
CI / publish (push) Skipped

- /ansible/playbooks lists every playbook across sources: what it says it
  does, its category and destructive flag, how it last ran; the whole row
  opens the playbook. A source with none is one quiet line.
- /ansible/playbooks/<source>/<path>: the verdict (what it does, flagged
  destructive, last ran), the run form in place (dry run beside the button,
  options folded), the schedules that use it, its recent runs, the playbook
  itself folded. ?from=<run> fills the form from that run: Run again on a
  run's page.
- /ansible/browse, its view page and the /run-form fragment are gone, not
  redirected (rule 122); every link points at the new pages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 23:49:05 -04:00
co-authored by Claude Opus 5.5
parent a02f224f22
commit 72a3261582
17 changed files with 447 additions and 230 deletions
+35 -40
View File
@@ -1,67 +1,62 @@
{# Run form for a single playbook — loaded via HTMX from /ansible/run-form/… #}
<div style="background:var(--bg-elevated);border-radius:6px;padding:1rem;border:1px solid var(--border-mid);">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:0.75rem;">
<h4 class="section-title" style="margin-bottom:0;">Run <span style="font-family:ui-monospace,monospace;font-weight:normal;">{{ playbook_path }}</span></h4>
<a href="#" onclick="this.closest('div[id^=run-form-]').innerHTML='';return false;"
class="btn btn-ghost btn-sm">Cancel</a>
</div>
<form hx-post="/ansible/runs" hx-target="next .run-result" hx-swap="innerHTML">
{# ansible/_run_form.html — the form that runs one playbook, on its page
(#5331): where to run it, its declared variables, more options folded
away, and the dry-run choice beside the button. `prefill` holds an earlier
run's choices ("Run again"); the run starts in place, under the form. #}
<form id="run-form" class="run-form" hx-post="/ansible/runs" hx-target="#run-result" hx-swap="innerHTML">
<input type="hidden" name="source_name" value="{{ source_name }}">
<input type="hidden" name="playbook_path" value="{{ playbook_path }}">
<div class="form-group">
<label>Run against</label>
<select name="inventory_scope">
<label for="run-scope">Run against</label>
<select id="run-scope" name="inventory_scope">
{% if targets %}
<option value="steward:all">All targets ({{ targets | length }})</option>
<option value="steward:all"{% if prefill.scope == 'steward:all' %} selected{% endif %}>All targets ({{ targets | length }})</option>
{% if groups %}
<optgroup label="Group">
{% for grp in groups %}<option value="steward:group:{{ grp.id }}">Group: {{ grp.name }}</option>{% endfor %}
{% for grp in groups %}{% set v = "steward:group:" ~ grp.id %}<option value="{{ v }}"{% if prefill.scope == v %} selected{% endif %}>Group: {{ grp.name }}</option>{% endfor %}
</optgroup>
{% endif %}
<optgroup label="Single target">
{% for tgt in targets %}<option value="steward:target:{{ tgt.id }}">{{ tgt.name }}</option>{% endfor %}
{% for tgt in targets %}{% set v = "steward:target:" ~ tgt.id %}<option value="{{ v }}"{% if prefill.scope == v %} selected{% endif %}>{{ tgt.name }}</option>{% endfor %}
</optgroup>
{% endif %}
{% if inventories %}
<optgroup label="Repo inventory file">
{% for inv in inventories %}<option value="repo:{{ source_name }}:{{ inv }}">{{ source_name }}/{{ inv }}</option>{% endfor %}
<optgroup label="Inventory file in {{ source_name }}">
{% for inv in inventories %}{% set v = "repo:" ~ source_name ~ ":" ~ inv %}<option value="{{ v }}"{% if prefill.scope == v %} selected{% endif %}>{{ source_name }}/{{ inv }}</option>{% endfor %}
</optgroup>
{% endif %}
{% if not targets and not inventories %}
<option value="" disabled>No targets or inventory files — add targets at /ansible/inventory/targets</option>
<option value="" disabled selected>Nothing to run against yet: add a target under Inventory</option>
{% endif %}
</select>
</div>
{% include "ansible/_playbook_vars.html" %}
{% with description="", category="", values=prefill["values"] %}{% include "ansible/_playbook_vars.html" %}{% endwith %}
<details style="margin:0.25rem 0 0.75rem;">
<summary style="cursor:pointer;font-size:0.82rem;color:var(--text-muted);">Advanced</summary>
<div class="form-group" style="margin-top:0.6rem;">
<label>Extra vars <span style="color:var(--text-muted);font-weight:normal;">(one key=value per line)</span></label>
<textarea name="extra_vars" rows="2" placeholder="custom_var=value"
style="width:100%;font-family:ui-monospace,monospace;font-size:0.85rem;"></textarea>
</div>
<details class="run-more"{% if prefill.extra or prefill.limit or prefill.tags %} open{% endif %}>
<summary>More options</summary>
<div class="form-group">
<label>Limit to host(s) <span style="color:var(--text-muted);font-weight:normal;">(--limit)</span></label>
<input type="text" name="limit" placeholder="web01,db*">
<label for="run-extra">Extra vars <span class="field-hint">one key=value per line</span></label>
<textarea id="run-extra" name="extra_vars" rows="2" class="mono" placeholder="custom_var=value">{{ prefill.extra }}</textarea>
</div>
<div class="form-group">
<label>Tags <span style="color:var(--text-muted);font-weight:normal;">(--tags)</span></label>
<input type="text" name="tags" placeholder="deploy,config">
</div>
<div class="form-group">
<label style="display:flex;align-items:center;gap:0.5rem;font-weight:normal;">
<input type="checkbox" name="check">
Dry-run (--check --diff) — preview changes without applying
</label>
<div class="form-pair">
<div class="form-group">
<label for="run-limit">Only these hosts <span class="field-hint">--limit</span></label>
<input id="run-limit" type="text" name="limit" value="{{ prefill.limit }}" placeholder="web01,db*">
</div>
<div class="form-group">
<label for="run-tags">Only these tags <span class="field-hint">--tags</span></label>
<input id="run-tags" type="text" name="tags" value="{{ prefill.tags }}" placeholder="deploy,config">
</div>
</div>
</details>
<div style="display:flex;gap:0.75rem;align-items:center;">
<button type="submit" class="btn">Execute</button>
<div class="form-actions">
<button type="submit" class="btn">Run</button>
<label class="field-check">
<input type="checkbox" name="check"{% if prefill.check %} checked{% endif %}>
Dry run: show what would change, change nothing
</label>
</div>
</form>
<div class="run-result" style="margin-top:1rem;"></div>
</div>
</form>
<div id="run-result"></div>