Files
vh f115c982bc elway: add tier 1 + tier 2 idempotency
Tier 1 — pre-step skip conditions:
  when:    <remote shell expr>   skip unless expr exits 0
  creates: <remote path>         skip if path already exists
  removes: <remote path>         skip if path is already absent
Any of the three saying "skip" marks the step `skipped` and moves on.
Evaluated under bash -c on the remote so `!`, `[[`, pipes etc. behave
consistently regardless of the default remote shell.

Tier 2 — post-step change detection:
  changed_when: <remote shell expr>
Evaluated after a successful step. Exit 0 → step counts as `changed`
(default). Exit != 0 → `ok` (ran, nothing actually different).
Without this field, successful steps default to `changed`, matching
Ansible's shell/command defaults. Useful on verify steps:
`changed_when: "false"` reports them as `ok` since they only attest.

Status model moved from pass/fail to four states:
  ok / changed / failed / skipped
Summary reports each count; overall outcome is CHANGED if any step
changed, OK if none did, FAILED on any non-skipped failure.

Rerunnable smoke: playbooks/elway-smoke.yaml now proves it. On a
clean target the cold run reports 4 changed, 3 ok. Rerunning with
the same vars reports 2 skipped / 2 changed (upload + log-record
have no idempotency hooks and are always `changed`). Overriding
--var greeting=... re-runs the gated step exactly as intended.

Doc block at the top of the script updated with the new schema
fields and state machine.
2026-04-24 10:39:48 -07:00

50 lines
1.7 KiB
YAML

# elway's own smoke-test playbook. Exercises:
# - vars (inline default + CLI override)
# - multi-step flow
# - upload with mode
# - tier 1 idempotency: `creates:` skip
# - tier 2 idempotency: `changed_when:` to report ok vs changed
# - verify phase that tolerates already-set-up state
#
# Rerunnable: second run should show mostly `ok`/`skipped`, proving
# idempotency is wired correctly.
vars:
scratch_dir: /tmp/elway-smoke
greeting: hello from elway
steps:
- name: Create scratch dir
shell: mkdir -p {{ scratch_dir }}
creates: "{{ scratch_dir }}"
- name: Drop a greeting file
shell: echo "{{ greeting }}" > {{ scratch_dir }}/greeting.txt
# Skip the echo entirely if the file already contains exactly this line.
# `when:` gates BEFORE running; `changed_when:` decides ok-vs-changed AFTER.
# For "is the new state already the desired state?", `when:` is correct.
when: "! grep -qxF '{{ greeting }}' {{ scratch_dir }}/greeting.txt 2>/dev/null"
- name: Upload this playbook into the scratch dir
upload:
src: playbooks/elway-smoke.yaml
dest: "{{ scratch_dir }}/uploaded.yaml"
mode: "0644"
- name: Record that we ran (a step with no idempotency hooks — always `changed`)
shell: date -Iseconds > {{ scratch_dir }}/last-run.txt
verify:
- name: Greeting file has the expected content
shell: grep -qxF "{{ greeting }}" {{ scratch_dir }}/greeting.txt
# Verify steps don't actually "change" anything — they only attest.
changed_when: "false"
- name: Uploaded yaml is non-empty
shell: test -s {{ scratch_dir }}/uploaded.yaml
changed_when: "false"
- name: last-run.txt exists
shell: test -s {{ scratch_dir }}/last-run.txt
changed_when: "false"