Commit Graph
6 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5.5 e90bab5b66 fix(web-app-akaunting): run the swarm setup job on the service's own image
The akaunting service is built from the akaunting/docker repository
(ref master), whose Dockerfile downloads the latest Akaunting release into
/var/www/html, the shared data volume. Since 2026-10-26 that Dockerfile
builds on php:8.2, and Akaunting 3.3.0 (released 2026-10-27) requires
PHP >= 8.2.

The swarm setup job instead ran the upstream tag akaunting/akaunting:3.1.21
(PHP 8.1.34) against the same volume. It executed the 3.3.0 code there and
failed with "Ask your hosting provider to use PHP 8.2.0 or higher" (main
run 40507642995, Akaunting#0). Compose mode was unaffected because its
setup uses `compose run akaunting`, i.e. the built image.

Read the image from the service spec and start the setup job with it, so
code and runtime always come from the same build. Drop the now unused
AKAUNTING_IMAGE/AKAUNTING_VERSION vars.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 03:07:40 +01:00
kevinveenbirkenbachandClaude Opus 5 575a5a87ac refactor(layout): sort shipped scripts by language and split the unit suite
Every script a role ships now lives under `files/<language>/`, and the unit
suite is split the same way: `tests/unit/python/` beside new sibling trees for
the other languages. `tests/lint/filesystem/test_role_files_layout.py` keeps it
that way; directories whose shape is dictated from outside carry a `.nocheck`
naming the constraint (Docker build contexts, WordPress mu-plugins, the Joomla
extension, Odoo addons, Playwright suites, the `files/test/test.sh` harness
that `test-e2e-cli` resolves generically).

This cannot be applied in halves. Moving a file and repointing what names it
are the same change: 14 references resolve only at deploy time, so the tree is
green either before or after, never between.

Those 14 were found by static resolution over every reference class, not by the
test suite -- a full `make quality-high` was green while all of them were
broken:

  * 7 `src:` values built from a role variable holding a bare filename
    (svc-bkp-remote-2-local, decidim, espocrm, friendica, joomla, pixelfed x2).
    The variable doubles as the destination name inside the container, so the
    language segment belongs at the call site, not in the variable.
  * `sys-service/tasks/04_base.yml` resolved every consumer's systemd script
    through `first_found(..., errors='strict')` over `files/script.{sh,py}`.
    18 roles ship exactly that and have no template fallback: the play would
    have died on each.
  * `sys-front-inj-javascript` fell back to `<role>/files/javascript.js`; the
    interface now reads `files/javascript/javascript.js`.
  * `sys-front-inj-logout/templates/head_sub.j2` built the cache-busting mtime
    path through `path_absolute(... 'files', ...)`.
  * 4 `ansible.builtin.script:` invocations naming a bare command
    (pull.py, prune.sh, set_postgres_superuser_password.sh, ganesha-serve.sh).
  * `dev-python/files/shell/install.sh` pointed at a sibling role's script.

Depth-dependent paths inside the moved trees were bumped accordingly: 59
`parents[N]` computations, 12 relative climbs, and the per-role recover CLI,
whose path `cli/administration/recover/recoverers.py` still assembled from the
pre-move location.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 01:31:09 +02:00
kevinveenbirkenbachandClaude Opus 5 29015311b6 feat(lint): derive container paths from meta/volumes.yml everywhere
meta/volumes.yml says where a volume lands inside its container. Wherever a
second file spelled the same path out, the two drifted independently: the mount
moves, the consumer keeps pointing at the old place, and nothing fails until the
process looks for content that is not there. e031b7f61 fixed exactly this for
web-app-moodle; this generalises it and makes the rule hold.

The lookup gains what a caller needs. `targets` maps each mounting service to
its container path, and `target` is the one path an entry mounts at -- present
only when every mount agrees. An entry that lands at different paths per service
omits `target` rather than picking one, and the caller names the service:

    "{{ lookup('volume', application_id, 'prometheus_config').target }}"
    "{{ lookup('volume', 'web-app-prometheus', 'host_sys').targets['node-exporter'] }}"

66 literals across 48 role files now derive. Replacement only happened where the
path stood alone -- /srv/app inside /srv/app/data is a different path -- and
every rewritten file was re-parsed, Jinja and YAML alike. Three sites the tool
refused rather than guess were done by hand: zammad's path sat in a Jinja list
literal, seaweedfs' inside a quoted string feeding a filter, and openresty's
/var/run/docker.sock is not a mount target at all but the socket on the host,
which only happens to spell the same string. That one carries the single
nocheck.

tests/lint/repository/spot/test_volumes_path_single_spot.py keeps it that way.
roles/*/files/ is out of scope by construction: it is copied verbatim into an
image and executed there, so a Dockerfile or entrypoint has no Ansible to
evaluate a lookup with.

One defect this found, and it was in the tooling rather than the roles: deciding
ambiguity AFTER filtering short paths made web-app-prometheus' host_sys look
single-valued -- it mounts /sys for cadvisor and /host/sys for node-exporter,
and /sys is below the length filter. The template would have rendered an
undefined `.target`. Both the converter and the rule now decide ambiguity over
every target and filter afterwards, and that call reads targets['node-exporter'].

Verified by resolving rather than by reading: all 70 accesses in the diff were
run against the real registry and every one yields a non-empty path equal to the
literal it replaced. make test-lint: 328 passed.

Not verified: no Ansible render, so a call site whose surrounding context cannot
build the applications registry would still fail at deploy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 02:02:29 +02:00
kevinveenbirkenbachandClaude Opus 4.8 5628408b11 fix(swarm): gate one-shot jobs on Complete and retry transient crashes
The install and realm-import waits accepted Shutdown as success, so a crashed job passed silently: akaunting went green without ever installing, and a keycloak import crash left synapse against a realm-less provider, surfacing as matrix Playwright OIDC timeouts. The state probe moves to a shared sys-svc-compose script where only Complete passes, Shutdown fails loudly with the task error, and Failed/Rejected count as pending because the jobs now restart on-failure with three bounded attempts, turning the observed import race from a coin flip into self-healing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 13:03:24 +02:00
kevinveenbirkenbachandClaude Opus 4.8 0864a2e813 fix(akaunting): run the one-shot install through the image entrypoint
The setup passed --no-start as the container command, which the ca-trust wrapper exec'd as a binary (exit 127); the flag does not exist in the image's entrypoint at all. Invoking /usr/local/bin/akaunting.sh with no arguments is the real one-shot contract: with AKAUNTING_SETUP=true it runs artisan install and exits without starting Apache. The swarm wait gate also accepted Shutdown as success and let the crashed job pass without ever installing; the state probe now lives in install_job_state.sh and only Complete passes, while terminal states fail loudly with the task error.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 01:11:48 +02:00
kevinveenbirkenbachandClaude Opus 4.8 7f6ff017c8 fix(akaunting): install via mode-symmetric one-shot instead of a persistent env flag
AKAUNTING_SETUP=true in the rendered env re-ran the installer on every container recreation between deploys (restore drill, repair restart) and crash-looped against the installed database. The flag is gone from the env entirely: compose now installs like swarm through an ephemeral one-shot (stop, compose run --rm -e AKAUNTING_SETUP=true akaunting --no-start, up --force-recreate), included mode-agnostically from tasks/setup/{{ DEPLOYMENT_MODE }}.yml with the swarm job renamed into the same folder. The first-run gate reads the marker stat directly instead of a set_fact indirection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 14:44:35 +02:00