Commit Graph
11 Commits
Author SHA1 Message Date
kevinveenbirkenbachandClaude Opus 5 f713ba8350 refactor(credentials): address them at secrets.credentials, like every other meta file
meta/schema.yml became meta/secrets.yml, but its content still landed at
applications.<app>.credentials — the one meta file whose payload did not sit
under a root named after the file. Every other topic obeys that rule:
meta/volumes.yml goes to volumes, meta/rbac.yml to rbac. Credentials now go to
secrets.credentials.

The path exists exactly once, in utils/manager/credential_key.py, and
OVERRIDE_SECTION is derived from its two segments rather than spelled beside
them, so the dotted form used by --set and the nested form used by the
inventory cannot drift. Six consumers read it: the loader, the generator, the
CLI override parser, the two emit helpers, and the four lookup validators.

--set now accepts exactly one key shape,
applications.<app>.secrets.credentials.<key>. The three short forms are
rejected instead of guessed at: the same short key names different credentials
on different apps. That also retired primary_app_id, which only existed to
disambiguate them.

Three defects surfaced while wiring this up. _apply_one_role_schema still wrote
to the old root, so an app's own credentials would have moved while its
providers' stayed behind. web-app-pgadmin reads through
lookup('applications', ...).credentials.<key>, an access path no regex over
lookup('config', ...) could see, and would have deployed with an empty postgres
password. And _META_NON_GENERIC_TOPICS still listed 'schema' — a bare topic
name, invisible to any path search — so the generic file-root loop assigned the
raw schema under secrets and the follow-up write landed inside the cached
document, truncating keycloak's four credentials to one for every later
reader.

tests/integration/ansible/lookups/config/test_credentials_roundtrip.py pins both
directions of the seam and the cache invariant that last defect broke.

Existing inventories still carry the pre-rename root; migrating them is
deliberately out of scope here.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-12 16:51:56 +02:00
kevinveenbirkenbachandClaude Opus 4.8 4039313266 refactor(tasks): enforce and apply a single task-file numbering scheme
Add tests/lint/repository/test_task_file_numbering.py: numbered task
files use exactly two leading digits, a numbered *_core.yml must be
00_core.yml, per directory the numbers are unique/consecutive and start
at 01 (00 with a core file), numbered includes appear in ascending
order, and shared helpers live in tasks/utils/ instead of the numbered
chain.

Apply it across every role: rename NN_core.yml to 00_core.yml with a
consecutive down-shift of the siblings, move letter-suffix children into
a subdirectory named after their parent task, relocate helpers included
from more than one place into tasks/utils/, and update every reference
(include_tasks, path_absolute, .ansible-lint-ignore, requirement docs).
Legacy narration comments in the touched files are deleted; the upstream
xwiki.properties reference block opts out via nocheck. The unused
MAILU_IP6_PUBLIC var (only ever named in now-removed comments) is dropped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 20:01:31 +02:00
kevinveenbirkenbachandClaude Opus 4.8 c057b1a1c8 fix(roles): never quote container_address swarm resolver fragments
In swarm mode the container_address lookup returns a $(/usr/bin/resolve-container-id ...) fragment that must expand at exec time; piping it through Jinja quote hands docker the literal string and every exec fails with 'No such container' (broke all three snipe-it CI variants). Remove the quote at the seven snipe-it sites and the live matrix/openproject sites, convert openproject's argv exec to shell so the fragment can expand, split the matrix bridge restart per deployment mode with a manager gate, and mark the compose-only or eval-contract sites with nocheck. A new lint (container-address-quote) bans the pattern repo-wide.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 17:30:36 +02:00
kevinveenbirkenbachandClaude Opus 4.8 cddb0dc59b feat(lint): require emoji-prefixed task/step/job names + convert existing
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>
2026-06-28 22:45:06 +02:00
kevinveenbirkenbachandClaude Opus 4.8 36ebf34b64 Merge branch 'main' into feature/swarm-nfs
Resolve 69 conflicts across ~70 files: union main's image-version bumps and new service fields (bond, resource limits) with the swarm branch's default_placement / NFS / IS_STACK_HOST config; retire the three per-deploy-type test workflows main still edited (consolidated into compose+swarm); adopt main's run-once 01_core dispatchers while re-applying each role's swarm guard. Fix the post-merge lint surfaced by main's stricter rules: bond + resource-budget keys, container-exec stack-host gating + resolver routing, leaky/dead proxy vars, suitecrm once-flag, xwiki variant nocheck, nextcloud oversized-block to gated include, matrix-mdad capped at 3g. make autoformat clean, make test green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 17:13:55 +02:00
kevinveenbirkenbachandClaude Opus 4.8 5687643b46 style(deployment-mode): positive == 'compose' gates + enforce via lint
DEPLOYMENT_MODE is binary (swarm / compose), so a negated `!= 'swarm'` gate
always means `== 'compose'`. Replace every negated gate across roles/ and
group_vars with the positive form for readability, and add a lint
(tests/lint/ansible/variables/test_no_deployment_mode_negation.py) that forbids
`DEPLOYMENT_MODE !=` in .yml/.yaml/.j2 with a `# nocheck:
deployment-mode-negation` escape.

The compose-verb / chdir / jinja-gate lint guidance messages now suggest the
positive form too; the gate matcher (utils/annotations/task_gate.py) and its
test inputs keep recognising the legacy `!= 'swarm'` so a nocheck-marked
exception is still detected as compose-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:31:36 +02:00
kevinveenbirkenbachandClaude Opus 4.8 7eaebbe895 fix(matrix): make the migrated bridgev2 bridges actually deploy
Follow-ups after the bridgev2 config migration, each verified in the live DiD stack: (1) URL-encode the bridge DB password in the postgres uri (the credential contains '/', which the stricter bridgev2 Go URL parser rejected as an invalid port) so signal/whatsapp/slack connect to postgres and generate their registration; (2) drop the telegram bridge from MATRIX_BRIDGES when its Telegram api_id/api_hash credentials are absent (telegram requires them at startup and crash-loops without — unlike the other bridges — blocking synapse); (3) give the bridge healthcheck a 5m start_period so the registration generation has grace before it is marked unhealthy.

(4) Two-phase synapse start: drop the synapse->bridges service_healthy depends_on (a single 'compose up -d' did not respect the start_period and aborted with 'dependency ... unhealthy' before the bridges generated registration) and instead restart synapse after the Ansible 'wait for registration files' step so it loads the freshly written appservice registrations. The bridges generate registration independently of synapse, so synapse no longer needs to block on their health.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 03:25:58 +02:00
kevinveenbirkenbachandClaude Opus 4.8 f7603cc0c2 feat(matrix): migrate mautrix bridges to meta/addons (req 026)
Six mautrix network bridges declared as mechanism: bridge, enabled: false, with DB creds referenced from meta/schema.yml. MATRIX_BRIDGES derived from addons; vars/bridges.yml, filter_plugins/bridge_filters.py and its unit test removed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 11:30:46 +02:00
kevinveenbirkenbach 29519603ef Merge remote-tracking branch 'origin/main' into feature/swarm-nfs 2026-06-14 20:32:59 +02:00
kevinveenbirkenbachandClaude Opus 4.7 0aed4aaebe refactor(web-app-matrix): full DiD isolation for ansible flavor
matrix-mdad is now a long-running compose service with systemd PID 1
and its own bundled docker daemon. MDAD writes its systemd unit files
and runs matrix-synapse/coturn/element/bridges/jitsi/element-call
entirely INSIDE that container via the inner docker daemon. Nothing
leaks to the host beyond standard /opt/compose/matrix/ role state.

Container layout:
- /sbin/init (systemd) as PID 1
- bundled docker.io daemon (inner DinD)
- bootstrap.service oneshot: runs `ansible-playbook setup.yml` once on
  first boot; marker at /var/lib/matrix-mdad/bootstrap.done
- Named volumes: matrix_mdad_docker (inner /var/lib/docker),
  matrix_mdad_systemd (/etc/systemd/system), matrix_mdad_matrix (/matrix)
- /sys/fs/cgroup:rw + cgroup: host for systemd-in-container
- Outer compose publishes synapse/element/federation ports to host loopback

mdad-vars.yml.j2:
- postgres_enabled: true (MDAD bundled postgres inside DiD)
- valkey_enabled: true (bundled redis)
- exim_relay_enabled: true when no outbound Mailu
- host_bind_port: 0.0.0.0 inside container so outer compose port-publish
  can forward to host loopback

Dropped (no longer needed):
- 02a_database.yml — MDAD provisions postgres inside the DiD container
- 00_cleanup_mdad_residue.yml — no host residue to clean
- ensure rshared mount task — no host bind-propagation needed
- container wait matrix-mdad-runner — service is long-running not one-shot
- matrix_synapse_container_additional_networks_custom — DiD has its own
  internal docker network
- matrix_synapse_container_extra_arguments CA mount — bundled CA inside
- coturn_container_additional_volumes — anonymous volumes live in inner
  docker daemon, outer hlth-volumes can't see them

04_proxy_wiring.yml:
- admin user creation now uses two-hop exec: outer `container exec
  matrix-mdad` -> inner `docker exec matrix-synapse register_new_matrix_user`

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-05 10:39:01 +02:00
kevinveenbirkenbachandClaude Opus 4.7 70c1a03f13 feat(web-app-matrix): add ansible flavor via unified compose.yml.j2
Implements requirement 025 (docs/requirements/025-web-app-matrix-ansible-flavor.md).

Architecture:
- services.matrix.flavor: ansible | compose
- Single templates/compose.yml.j2 branches on matrix_flavor
- ansible flavor: defines matrix-mdad-runner one-shot compose service that
  DiD-executes upstream matrix-docker-ansible-deploy (pinned ref + migration
  validated version). MDAD writes /etc/systemd/system/matrix-*.service units
  on the host; post-up Ansible task container-waits the runner, then probes
  loopback ports + wires sys-stk-front-proxy.
- compose flavor: legacy in-repo stack (synapse/element/coturn/bridges) kept
  for regression coverage, marked deprecated.

Variant-switch ansible -> compose:
- 00_cleanup_mdad_residue.yml tears down MDAD-installed systemd units +
  leftover matrix-* containers before compose-flavor brings up its own
  matrix-synapse (else: name conflict, compose down doesn't touch
  systemd-managed state).

Reorganization:
- roles/web-app-matrix/{tasks,templates,files}/flavor/{ansible,compose}/
- Renamed legacy compose-flavor task files into flavor/compose/
- New runner Dockerfile (multi-stage, ARG-parameterized FROM via build-args)

Verified: 3 variants x 2 passes (sync+async) full_cycle green, all 6
PLAY RECAPs failed=0. V1 (ansible all-on), V2 (ansible no-auth), V3
(compose deprecated all-on). Playwright matrix tests pass per variant.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-05 10:21:41 +02:00