The stock image CMD exits on an empty DB ("Run --install."), so the server
crash-looped on first deploy while the role exec'd 'yes | ./listmonk --install'
into that same crashing container, which was SIGKILLed (rc 137) and never
converged. Adopt listmonk's own recommended container command (idempotent
install, upgrade, then serve; the super admin is seeded from LISTMONK_ADMIN_USER
/ _PASSWORD), and replace the exec dance with a DB-reachability probe plus a
wait for the users table to be seeded. Pin latest to the current stable v6.2.0
so the CLI/schema contract cannot drift. Same flow in compose and swarm.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduce plugins/lookup/path_absolute.py as the single source for the
repo-root anchor: lookup('path_absolute', 'roles/...') returns an absolute
path under playbook_dir, joining every term. Replace all hand-built
playbook_dir path constructions across .yml/.yaml and .j2 templates with it:
[playbook_dir, '<rel>'] | path_join -> lookup('path_absolute', '<rel>')
playbook_dir ~ '/<rel>' -> lookup('path_absolute', '<rel>')
{{ playbook_dir }}/<rel> -> lookup('path_absolute', '<rel>')
Unrelated repo_path symbols in cli/meta/roles/applications/resolution and the
single bare discover_playwright_roles(playbook_dir) call are left untouched.
Add tests/lint/ansible/test_playbook_dir_via_lookup.py: fails the build if any
of the three construction forms reappears in .yml/.yaml/.j2, pointing callers
at the lookup. Unit test covers the plugin.
Touching these files pulled their pre-existing narration comments into
test_comments_valid scope; clean them (delete restatement/banners, keep real
trip-wires as Workaround/Upstream/Invariant markers). Drop caution, important
and pitfall from the comment-lint marker vocabulary and remove the comments
that used them.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add tests/lint/repository/test_name_emoji_and_length.py: every Ansible task
name and GitHub Actions workflow/job/step name must start with an emoji (first
char not 0-9/A-Za-z) and be <= 80 chars.
- emit emoji names at the source: cli/build/include generator now writes
"🔧 setup <app>" / "🚿 flush handlers after <app>" so generated
tasks/groups/*-roles.yml satisfy the convention too
- convert existing task/step/job/workflow names to emoji-prefixed, choosing a
task-fitting icon per action (📦 install, ✅ ensure, 🔍 probe, 🥾 bootstrap,
🚀 deploy, 🧩 service-module load, ⬇️ checkout, 📂 include-routines, ...)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the per-role swarm host-node/container-id resolve duplicated across fider, matomo, decidim, keycloak (login + realm-import), collabora and listmonk with two shared task utils under roles/sys-svc-compose/tasks/utils/swarm/. resolve_host_cid sets <prefix>_HOST_NODE / <prefix>_LOCAL_CID (swarm: converge + node + cid delegated to the node; compose: inventory host + given cid); wait_converged is the bare convergence gate.
Teach test_variable_definitions to read templated set_fact keys like {{ prefix }}_HOST_NODE as a *_HOST_NODE name pattern (guarded so a pure {{ x }} key cannot become a catch-all), and add the playbook_dir-relative include load-failure exemptions to .ansible-lint-ignore.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
In swarm mode *_CONTAINER_ADDRESS resolves to "$(/usr/bin/resolve-container-id
<stack> <service>)", which must reach /bin/sh unquoted so the subshell runs.
- listmonk: drop the `| quote` filter; shlex.quote single-quoted the whole
"$(...)", suppressing the substitution, so docker received the literal token
("No such container: $(...)").
- joomla: rewrite the docker/container exec tasks from the shell argv-list form
(executed directly, never via /bin/sh, so the $() was never evaluated) to the
free-form string shell, and switch the raw `docker exec` calls to the
`container` wrapper (the argv form had hidden them from the no-raw-docker lint).
- joomla: move the three inline PHP scripts (configuration.php debug toggle, DB
settings patch, OIDC plugin ZIP packaging) out of inline `vars:` into
files/php/*.php, fed via `lookup('file', ...)` over stdin to
`container exec -i ... php --`, with their inputs passed as J_* env vars
(the files/php + stdin convention already used by friendica/espocrm/snipe-it).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace compose-mode-specific `{{ BIN_COMPOSE }} exec ... {{ SERVICE }}`
+ `args.chdir: lookup('container', ..., 'directories.instance')`
invocations with `container exec -i {{ ..._CONTAINER_ADDRESS | quote }}`
(plus `--workdir` flag where chdir was load-bearing). The container
exec wrapper resolves the actual runtime container name on both
compose (compose-suffixed name) and swarm (stack-task-id-suffixed
name) via the `container_address` lookup, so the task no longer
assumes the compose-mode service-name address.
Pure-syntax adaptation only — no behavioural change, same register /
when / failed_when / no_log semantics preserved on every task.
Roles touched (12 task files):
- web-app-espocrm (1)
- web-app-friendica (2)
- web-app-listmonk (1)
- web-app-openproject (2)
- web-app-snipe-it (3)
- web-app-taiga (1)
- web-app-wordpress (2)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds a mode-aware `container_address` lookup plugin and a
`resolve-container-id` helper script that resolve swarm task IDs at
exec time (compose mode returns bare names unchanged). Migrates ~25
roles + ~28 task files from `command:` to `shell:` so the lookup's
`$(...)` subshell evaluates correctly, and extends `database` lookup
with an `address` key for cross-role DB exec.
Three new lint tests enforce the contract: every container exec
target goes through the resolver, lives behind an IS_STACK_HOST gate
(walks the include graph one hop deep), and uses `shell:` rather than
`command:` when the target is a Jinja reference. Existing nocheck
suppressions on bare-name DB exec sites (nextcloud redis, listmonk,
moodle OIDC, mailu) are replaced with the lookup-based form.
Fixes the systemd Environment= injection in sys-ctl-cln-disc-space
(it never expanded $(...)) by passing STACK / SERVICE_KEY parts and
resolving inside the script. Extracts shopware admin and mediawiki
extension install bodies into dedicated .sh files invoked via env.
Rewrites password-bearing shell tasks to either `| quote` directly or
the `-e VAR=... | quote` + `sh -c '... ${VAR} ...'` env-bridge pattern.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The previous emptiness check ran `\dt` against the listmonk database and
treated the absence of `No relations found.` / `Did not find any relations.`
in the output as proof that listmonk's schema was already installed.
Postgres now phrases the empty-result message as `Did not find any
relation` / `No matching relations found`, so the check kept reporting
the schema as present even when only system tables were there. The
`./listmonk --upgrade` path then ran against a structurally empty
database and exited with `the database does not appear to be setup. Run
--install.`.
Probe the canonical `subscribers` table directly and update the match
strings to postgres's current phrasing so `listmonk_db_empty` flips to
true on a fresh database and the install path runs first.
- Rename lookup plugin from `docker` to `container`
- Switch all lookups from `lookup('docker', ...)` to `lookup('container', ...)`
- Normalize compose file keys:
- docker_compose → compose
- docker_compose_override → compose_override
- docker_compose_ca_override → compose_ca_override
- Centralize compose path resolution via docker_paths_utils
- Update compose_file_args to always derive paths from DIR_COMPOSITIONS
- Align all roles, templates, and vars with container/compose naming
- Update and rename unit tests accordingly
This prepares the codebase for engine-agnostic container backends
(e.g. Docker ↔ Podman) and removes legacy docker-specific assumptions.
https://chatgpt.com/share/6987a7cc-d374-800f-99ad-bb4c8f92c59e
- Replace lookup('docker', ..., want='...') with lookup('docker', ..., 'path')
- Allow both top-level keys (directories/files) and dotted paths
- Simplify docker lookup API and reduce lazy dict propagation
- Update unit tests to reflect new lookup semantics
https://chatgpt.com/share/6984ccea-8290-800f-a6d7-ec0e94df95a0
Remove implicit docker_compose variable usage and resolve all docker paths
(files, directories, env) via lookup('docker', application_id).
This prevents scope leakage, centralizes path resolution, and makes docker
context deterministic across roles and handlers.
https://chatgpt.com/share/6984ccea-8290-800f-a6d7-ec0e94df95a0
Refactor Listmonk database setup into a dedicated task routine.
- Add 01_database.yml for centralized DB lifecycle handling
- Detect reachable and empty database reliably
- Run --install only on empty databases
- Run --upgrade only when DB is initialized and MODE_UPDATE is enabled
- Tolerate SIGPIPE (rc=141) from yes-pipe via failed_when
- Use docker compose run for non-interactive install/upgrade
This prevents failing CI runs and ensures deterministic Listmonk bootstrap.
https://chatgpt.com/share/6967c1e9-0818-800f-a327-7407195cc281