`make quality-high` on main (fea6f31d9f) rewrote two files in its
autoformat step:
- roles/sys-service/handlers/refresh_systemctl_service.yml: the
handler's `when` was wrapped by hand; the formatter wraps it at line
width. Both forms parse to the same YAML.
- tests/unit/python/roles/web-app-nextcloud/test_onlyoffice_check_deadline.py:
two over-long assertIn calls that ruff format splits.
A second autoformat run leaves the tree unchanged, so main is stable
under the formatter again.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The handlers after "Set systemctl service state" read Result and
ExecMainStatus and fail the deploy with "Unable to start service ..."
when either reports a failure. Most roles leave system_service_state
omitted (SYS_SERVICE_DEFAULT_STATE is omit outside MODE_DEBUG, and the
backup roles start their unit only in MODE_BACKUP), so the state task
changes nothing, and the two properties describe the unit's last run,
whenever it was.
Measured on a local stack: the volume backup unit had failed on
2026-10-12 in the CLI test of another deploy, when a container it had
stopped was recreated before it could start it again. The next day a
deploy of web-app-seaweedfs and web-app-prometheus, which did not run
the unit, read Result=exit-code and failed in the backup role.
The diagnostics now fire on a stale result only when the deploy set a
state itself. A failed state change still fails as before.
Verified in the stack container against a oneshot unit whose ExecStart
is /bin/false, left failed before each run, with the real handler file:
with the state omitted, main's handler fails with "Unable to start
service" and the new one passes; with state=started, which reruns the
unit, both fail. ansible lints and test_comments_valid pass.
Closes#914
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The generic unit template could express neither an ordering dependency nor an Install section, so a role needing either shipped its own templates/systemctl.service.j2 and re-answered the daemon reload, the enable and the flush behaviour by hand.
Add After, Before, Wants, RemainAfterExit and a WantedBy driven Install section, all inert at their defaults, and route the unit path, its owner, become and the systemctl probes through a system_service_scope so a per-login unit lands under the user's own systemd directory.
00_core is gated on the system scope rather than only its prune and lockdown steps: it sets run_once_sys_service, so a user-scope caller running first would otherwise skip the lockdown for every system-scope role behind it in the same play.
Rendering the previous template against the same inputs drops no directive and adds none; the only difference is the blank line an empty directive used to leave behind.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A handler name was doing two jobs at once: the line an operator reads in the
play output, and the identifier a `notify:` binds to. The two pull in opposite
directions -- the log wants "restart the SSH service, with diagnostics", the
binding wants a token that survives quoting, YAML flow style and a shell
round-trip -- and a name that changes for readability silently changes which
handler runs.
Every handler that a notify reached by name now carries a `listen:` with that
exact wording, so the binding is explicit before anything is renamed. The
topics themselves become identifiers: `compose up` reads `compose-up`,
`Restart NFS ganesha` reads `restart-nfs-ganesha`, 22 of them across both
sides of every notify. Where the topic is assembled at runtime, both branches
moved together: the conditional in sys-service carries two literals, and the
LDAP renderer interpolates `import-{{ folder }}-ldif-files`, which is what
makes those handlers reachable at all.
Freed from the binding, the names take the emoji prefix every other task line
already has.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The async on these two handlers is not an oversight to be swept with the
rest: system_service_async resolves to omit whenever a caller sets
system_service_force_linear_sync, so it is an opt-out the calling role
decides. The backup and repair roles ask for linear execution, the cleanup
and alarm roles deliberately do not.
That also settles the Result gate below them, which reads systemctl show
and looked pointless under fire and forget. It is not: it runs meaningfully
for exactly the callers that asked to wait.
Comments only, no behaviour change.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Pure cosmetic YAML reflow of the `or`-chain in the refresh handler.
Same operands, same precedence — no behavioural change.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The `Set systemctl service state` handler called the systemd module with
the resolved `{{ system_service_name }}` (e.g.
`mtn-cert-deploy.7.0.0.infinito.nexus.service`), but the subsequent
`systemctl show -p Result/ExecMainStatus` queries used
`{{ system_service_id }}.infinito.nexus.service` (e.g.
`sys-ctl-mtn-cert-deploy.infinito.nexus.service`) — a different,
non-existent unit. systemctl returned the default `success`/`0` for the
non-existent unit, so the diagnostic-on-failure task was skipped and
real oneshot failures (Mailu cert-deploy in the test-deploy-server CI
runs) were silently swallowed by the playbook.
Use `{{ system_service_name }}` for both queries so the actual unit's
result is inspected, and add `systemd_set_state.failed` to the
diagnostic trigger as a belt-and-suspenders against any future
systemctl-show false positives.
* scripts/lint/autoformat.sh:
- rename run_sort_claude_settings worker to run_claude (matches the
renamed scripts/lint/claude.sh).
- drop the EXIT trap that wiped status_dir; cleanup happens explicitly
after `wait` returns, so a parallel worker that interrupts the
parent never races a vanished status dir.
- each worker now captures its tool's exit code and writes one of
OK / SKIP / FAIL. Tolerated `expected violations remain`-style exits
(markdownlint-cli2 rc≤1, ansible-lint rc≤2) still count as OK; only
real failures count as FAIL.
- all workers run to completion regardless of individual tool failures
(so the user always sees the full picture); after the summary, the
script exits 1 if any worker reported FAIL.
* scripts/lint/sort_claude_settings.sh -> scripts/lint/claude.sh
(history preserved via git mv); update test_claude_settings_sorted.py
fix-hint accordingly.
Plus working-tree drift across roles/, group_vars/, inventories/,
requirements/, and .pre-commit-config.yaml accumulated from the
make-test setup phase regenerating generated artefacts during recent
test runs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Extract repeated systemctl/journalctl diagnostic blocks from
sys-service, sys-service-terminator, sys-svc-container, and
sys-svc-sshd handlers into a reusable files/debug.sh SPOT
- Add tasks/utils/debug.yml as the canonical Ansible wrapper
- Wrap sys-svc-sshd restart handler in block/rescue to emit
diagnostics and sshd -t config validation on failure
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Force sys-pip-install to use system python (/usr/bin/python3) instead of active venv
- Add explicit PATH for system-wide pip installs
- Add diagnostics task to verify installation context with system python
- Replace systemctl is-active check with Result/ExecMainStatus to correctly handle oneshot units
- Prevent false failures when oneshot services exit successfully
https://chatgpt.com/share/695123ba-8da4-800f-ae0d-2f09a5efb1e7
- Refactor sys-service handler to use include_tasks for better structure
- Add robust systemctl restart diagnostics based on exit code (is-active)
- Ensure systemctl status and journalctl output are shown before failing
- Prevent premature handler failure by making systemd restart non-fatal
- Minor style cleanup in filter plugin (PEP8 / quoting)
https://chatgpt.com/share/6950f59e-d6cc-800f-9981-f9af4cbb5590