From dea95bf526c74bbfa02cfd71711e818c6cab6f52 Mon Sep 17 00:00:00 2001 From: Vuong Hoang Date: Fri, 24 Apr 2026 10:09:16 -0700 Subject: [PATCH] elway: add mini playbook runner + smoke playbook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `scripts/elway` is a ~600-line Python tool (stdlib + python3-yaml) for driving one-off ssh commands, ad-hoc file uploads, and YAML playbooks against a single host. Fills the gap between "single ssh one-liner" and "reach for Ansible." Highlights: - Three invocation modes: --shell, --upload (LOCAL:REMOTE[:MODE]), and --playbook - Playbook schema: inline vars, list of steps, optional verify block. Template via {{ var }}; CLI --var overrides inline defaults - stop_on_fail global (default on), per-step override. Verify phase always runs, even after a halt — you see end-state regardless - Sudo handled once: probes NOPASSWD; if not, prompts locally via getpass, validates up-front, then feeds via `sudo -S` per step. Password never written to disk/logs. Upload-with-sudo stages to /tmp then sudo-mv + sudo-chmod - SSH connection reuse via ControlMaster (60s persist) keeps multi-step playbooks responsive (~30ms/step reuse vs ~550ms cold) - Live interleaved stdout/stderr with per-step prefix and colored pass/fail summary. --dry-run prints the plan without executing - Shebang pinned to /usr/bin/python3 to bypass venv-shadowing when python3-yaml lives in the system site-packages Smoke test (playbooks/elway-smoke.yaml) covers vars + upload + verify; drove out a YAML-scalar-coercion bug before first commit (`shell: false` parsed to Python bool, crashed the templater — now coerced to string at load time with a clear error on nulls). --- playbooks/elway-smoke.yaml | 33 ++ scripts/elway | 620 +++++++++++++++++++++++++++++++++++++ 2 files changed, 653 insertions(+) create mode 100644 playbooks/elway-smoke.yaml create mode 100755 scripts/elway diff --git a/playbooks/elway-smoke.yaml b/playbooks/elway-smoke.yaml new file mode 100644 index 0000000..73ef356 --- /dev/null +++ b/playbooks/elway-smoke.yaml @@ -0,0 +1,33 @@ +# elway's own smoke-test playbook. Exercises: vars (inline + CLI override), +# multi-step flow, upload with mode, stop-on-error semantics, verify phase. +# Target: any Linux host reachable over ssh that has /tmp and curl. + +vars: + scratch_dir: /tmp/elway-smoke + greeting: hello from elway + +steps: + - name: Clean any prior scratch dir + shell: rm -rf {{ scratch_dir }} + + - name: Create scratch dir + shell: mkdir -p {{ scratch_dir }} + + - name: Drop a greeting file + shell: echo "{{ greeting }}" > {{ scratch_dir }}/greeting.txt + + - name: Upload this playbook into the scratch dir + upload: + src: playbooks/elway-smoke.yaml + dest: "{{ scratch_dir }}/uploaded.yaml" + mode: "0644" + +verify: + - name: Greeting file has the expected content + shell: grep -q "{{ greeting }}" {{ scratch_dir }}/greeting.txt + + - name: Uploaded file is a non-empty yaml + shell: test -s {{ scratch_dir }}/uploaded.yaml && head -1 {{ scratch_dir }}/uploaded.yaml + + - name: Scratch dir exists and is listable + shell: ls -la {{ scratch_dir }} diff --git a/scripts/elway b/scripts/elway new file mode 100755 index 0000000..88fd133 --- /dev/null +++ b/scripts/elway @@ -0,0 +1,620 @@ +#!/usr/bin/python3 +# Pinned to /usr/bin/python3 rather than `env python3` so the script is +# immune to an active user virtualenv shadowing system site-packages +# (where PyYAML lives on Debian — `apt install python3-yaml`). +""" +elway — a mini playbook runner over SSH. + +Named for John Elway: quarterbacks run plays. You hand it a play (a single +shell command or an upload), or a playbook (a YAML list of steps + an +optional verify phase), and it shoots them across the ssh link with live +output, structured reporting, and stop-on-error semantics. + +Conventions: + * Single host per run. Fleet-wide = shell loop outside. + * Leverages your ~/.ssh/config aliases. No inventory file. + * SSH connection reuse via ControlMaster for multi-step speed. + * Sudo password (if needed) prompted once at start, reused via `sudo -S`, + never written to disk, never logged. + * Simple `{{ var }}` substitution. No Jinja, no filters, no loops. + +Quick starts: + elway irv-ml1 --shell 'docker compose ls' + elway irv-ml1 --shell 'mkdir -p /worktank/foo' --sudo + elway irv-ml1 --upload stacks/comfyui/compose.yaml:/opt/docker/compose/comfyui/compose.yaml + elway irv-ml1 --playbook playbooks/deploy-stack.yaml --var stack=comfyui + +Playbook schema (YAML): + vars: # optional inline defaults; --var CLI overrides + stack: comfyui + steps: # required list + - name: