fix(ansible): make --check succeed on a fresh host - #6
Conversation
`make check` / `make check-anvil` (ansible-playbook --check) crashed on a
not-yet-provisioned host. In check mode the package-install and user-creation
tasks are no-ops, so any later task depending on that state — authorized_key
(needs the admin user's home), become_user, systemd enable/start of packaged
units (nftables/fail2ban/chrony/anvil/caddy), the Foundry/cast install, the
mnemonic generation, the caddy basic-auth registry slurp, and the
flush_handlers restarts — hard-failed.
The Makefile contract is that check "mutates nothing", so rather than force
installs in check mode, probe real on-host state with read-only,
check-mode-safe modules (getent, service_facts, stat) and guard each
dependent task/handler with `when: not ansible_check_mode or <prerequisite>`.
Real deploys always run (the `not ansible_check_mode` short-circuits true);
check runs on an already-provisioned host still preview drift; only a fresh
host in check mode skips the install-dependent steps.
Notes:
- getent with fail_key:false stores a missing key as {key: None}, so existence
is tested via .get(key) truthiness, not `key in dict`.
- service-existence guards use `ansible_facts.services | default({})`
(service_facts is gathered once in baseline and reused across roles).
Verified: `make check-anvil` completes failed=0 on a fresh host; site.yml's
baseline + hardening now pass check (it stops only at the by-design
decdn_node_version assert). ansible-lint (production profile) + yamllint clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request introduces check-mode (--check) safety guards across the anvil, baseline, and caddy Ansible roles to prevent dry-run failures on unprovisioned hosts by probing for the existence of users, files, and services. The review feedback recommends skipping the Anvil URI reachability probe entirely during check mode to avoid false failures when the service is stopped, and suggests explicitly gathering service_facts within the anvil and caddy roles to ensure they remain robust and self-contained when executed independently.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Pull request overview
This PR aims to make make check / make check-anvil (ansible-playbook --check) succeed on a fresh (not-yet-provisioned) host by probing real host state with check-mode-safe modules and guarding tasks/handlers that would otherwise hard-fail when prerequisite users, files, or systemd units don’t exist yet.
Changes:
- Add service existence probing (
service_facts) and gate systemd tasks/handlers on unit presence during check mode. - Add user/file existence probing (
getent,stat) and gate dependent tasks to avoid check-mode failures on fresh hosts. - Apply the same guarding pattern across
baseline,anvil, andcaddyroles (including handlers and config validation).
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| ansible/roles/caddy/tasks/main.yml | Adds file existence probe for the TSV registry and check-mode guards for slurp/render, plus service-based guards for validation/systemd tasks. |
| ansible/roles/caddy/handlers/main.yml | Guards the restart handler to avoid failing in check mode when the unit doesn’t exist. |
| ansible/roles/baseline/tasks/main.yml | Gathers service_facts and guards systemd + authorized_key operations in check mode using host-state probes. |
| ansible/roles/baseline/handlers/main.yml | Guards nftables/fail2ban handlers in check mode based on probed unit existence. |
| ansible/roles/anvil/tasks/main.yml | Adds user probe and guards Foundry install/mnemonic/systemd/RPC probe steps in check mode using host-state probes. |
| ansible/roles/anvil/handlers/main.yml | Guards the restart handler to avoid failing in check mode when the unit doesn’t exist. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…ervice_facts - anvil RPC liveness probe: skip entirely in check mode (`when: not ansible_check_mode`). --check never (re)starts anvil, so guarding on unit existence would falsely fail against a stopped-but-provisioned host; the probe is changed_when:false and previews no drift. - anvil + caddy: gather service_facts when `ansible_facts.services is not defined`, so the roles stay correct when run standalone (--tags) without baseline gathering them first. Verified: `make check-anvil` still failed=0 on a fresh host; lint clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Mint block was gated only on `caddy_user_check.rc != 0`. Under --check the existence-check command auto-skips, leaving caddy_user_check without `rc`, so the guard relied on Ansible's lenient evaluation of an undefined attribute (version- dependent). Gate the block on `not ansible_check_mode` first — minting needs the caddy binary (hash-password, itself check-skipped), writes the TSV, and reveals a password, none of which a dry run should do — and default the rc so it can never error. Gating the mutating block (vs the whole include) keeps the username- validation assert running in check mode. Verified: `make check-anvil` failed=0 on a fresh host, Mint block skips cleanly; lint clean. Real-mode minting path is covered by molecule converge. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Problem
make check/make check-anvil(ansible-playbook --check) crashed on a not-yet-provisioned host. In check mode the package-install and user-creation tasks are no-ops, so every later task that depends on that state hard-failed in turn:authorized_key— "Either user must exist or you must provide full path to key file in check mode" (needs the admin user's home dir)systemdenable/start of packaged units (nftables/fail2ban/chrony/anvil/caddy) — "Could not find the requested service"become_user: anvilfor the Foundry install — "Failed to change ownership of the temporary files…"castmnemonic generation, the caddy basic-auth registryslurp, and theflush_handlersrestartsApproach
The Makefile contract is that
check"mutates nothing", so rather than force installs in check mode (check_mode: false), this probes real on-host state with read-only, check-mode-safe modules (getent,service_facts,stat) and guards each dependent task/handler:not ansible_check_modeshort-circuits totrue.Subtleties (documented inline)
getentwithfail_key: falsestores a missing key as{key: None}, so existence is tested via.get(key)truthiness, notkey in dict.ansible_facts.services | default({})—service_factsis gathered once inbaselineand reused across theanvil/caddyroles and all handlers.Verification
make check-anvilcompletesfailed=0on a fresh host (install-dependent tasksskipping).site.yml's baseline + DevSec hardening now pass check; it stops only at the by-designdecdn_node_versionassert (operator config, not a bug).make deployis unaffected (every guard short-circuits true in real runs).ansible-lint(production profile) +yamllintclean.Reviewed
Ran the multi-agent PR review (code / comments / silent-failure / tests). Applied: uniform
| default({})on the three baseline service guards, and a comment-accuracy fix incaddy/tasks/main.yml. One follow-up noted but not included: there is no automated check-mode test — a future molecule scenario that converges--checkagainst a fresh container would guard the pattern against regressions.🤖 Generated with Claude Code