Commit Graph
14682 Commits
Author SHA1 Message Date
kevinveenbirkenbach ea2e01fb2c Merge pull request #303 from evangelostsak/feature/web-app-penpot
feat(web-app-penpot): add Penpot design & prototyping platform role
2026-06-10 20:43:16 +02:00
kevinveenbirkenbach a1bd538765 Reapply "fix(sys-svc-mail-msmtp): defer hlth-msmtp via service-loader post-run queue"
This reverts commit 992b39a789d078ebb6d8061fe0b9b06b04878860.
2026-06-10 18:53:08 +02:00
kevinveenbirkenbach fcec8db6cf Revert "fix(sys-svc-mail-msmtp): defer hlth-msmtp via service-loader post-run queue"
This reverts commit 75bcd499ce.
2026-06-10 18:53:08 +02:00
kevinveenbirkenbachandClaude Opus 4.7 5f44943d10 refactor(svc-swarm): categorise as svc.swarm + split into svc-swarm-node/svc-swarm-manager
Adds a `swarm:` subcategory under `svc:` in roles/categories.yml so the
shared-cluster role is properly invokable, and renames the existing
`svc-swarm` role + inventory group to `svc-swarm-node` (with
`get_entity_name`-aligned primary key `node:` in meta/services.yml).
`svc-swarm-manager` stays as the manager-marker role, becoming invokable
via the same new category and re-keyed to `manager:`.

Consumers of the cluster-orchestration service (svc-registry-cache,
svc-registry-docker, svc-swarm-manager) now reference the service-key
`node:` (was `swarm:`), matching the renamed primary entry. The planner
empirically resolves `service_registry['node']` -> `svc-swarm-node` and
emits it into --include for stack-host deploys, so the CI failure on
`universal-svc-registry-docker` (`svc-swarm: not present in inventory`)
no longer reaches `ValidDeployId`.

Breaking change for operators: inventory groups named `svc-swarm:` must
be renamed to `svc-swarm-node:`. `DEPLOYMENT_MODE` reads
`groups['svc-swarm-node']`, so a stale `svc-swarm:` group silently falls
back to compose mode.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 18:53:08 +02:00
kevinveenbirkenbachandClaude Opus 4.7 cb7020aad9 fix(web-app-wordpress): de-indent folded-scalar shell flags so they stay on one line
YAML's folded scalar (`shell: >`) only collapses newlines to spaces
when the continuation lines sit at the same column as the base
content; lines indented deeper preserve the newline. The earlier
`command:` form hid this because shlex.split flattened multi-line
input, but commit a4cb5b89e migrated these tasks to `shell:` for the
container_address subshell, so bash now sees real newlines and
executes each `--flag=value` as its own command, e.g.
`/bin/bash: line 2: --url=…: command not found`. Pulling the flag
lines back to the base column yields the same single-line command
the pre-swarm `command:` form produced.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 18:53:08 +02:00
kevinveenbirkenbachandClaude Opus 4.7 9e4b9de683 fix(lookup): database.address points to actual container, not consumer entity
In compose mode the `address` key resolved to `instance`, which for a
non-shared local DB is the consumer entity (e.g. "mailu") rather than
the real container_name (`{entity}-database`, e.g. "mailu-database").
Callers like roles/web-app-mailu/tasks/02_wait_for_mariadb.yml then ran
`container exec mailu ...` and crashed the deploy with "No such
container: mailu" (see CI run 27247587073 job 80468402392).

For shared central DBs `container == instance` so this is a no-op
there; the fix only affects the non-shared local-DB path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 18:53:08 +02:00
kevinveenbirkenbachandClaude Opus 4.7 0f06619e09 fix(svc-storage-nfs): trim premount probe stdout before membership check
`ansible.builtin.script` preserves the trailing newline that `echo`
emits in `premount_state.sh`, so the assert compared `"empty\n"` /
`"mounted\n"` to `["mounted", "empty"]` and always failed on a fresh
runner, blocking all swarm-CI deploys before any role could run.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-10 18:53:07 +02:00
evangelostsak 72a56b8aa8 fix(web-app-penpot): derive native_login/registration from sso flag (variant-aware)
The all-off matrix variant pins services.sso.enabled=false while
web-app-keycloak stays in group_names, so the previous group_names-based
defaults yielded disable-login-with-password AND no OIDC — leaving no usable
login method, and the native Playwright test failed (no password form).

Derive both toggles from PENPOT_SSO_ENABLED (the variant-aware sso flag),
matching how the oidc/ldap flags are already computed:
- native_login = not sso  (off under OIDC, on otherwise)
- registration = sso       (on under OIDC for JIT provisioning)

Drops the services.penpot.native_login / registration_enabled meta keys
(a group_names default can't see variant overrides). README + req-023 updated.
2026-06-10 18:03:04 +02:00
evangelostsak e7a799f542 feat(web-app-penpot): disable native login under OIDC (Kevin review)
- meta/services.yml: new `penpot.native_login` toggle, default off when
  web-app-keycloak is present (forces SSO), on otherwise.
- vars: PENPOT_FLAGS renders enable-/disable-login-with-password from it.
- tasks: administrator local-password bootstrap gated on native_login.
- playwright: test-login-native.js skips when `sso` is enabled.
- meta/main.yml: drop the Figma parenthetical from the description.
- README + req-023 updated.
2026-06-10 15:35:03 +02:00
evangelostsak e6afb12078 Merge upstream/main into feature/web-app-penpot
# Conflicts:
#	docs/requirements/README.md
2026-06-10 14:07:51 +02:00
evangelostsak 6b6b335a93 chore: sync .claude/settings.json allow-list with upstream (bind-mounted, staged via index) 2026-06-10 14:06:03 +02:00
kevinveenbirkenbach f4825d811a Merge pull request #308 from infinito-nexus/update/repository-refs-20260610-acb58d2
update(repos): bump git refs to latest semver tags
2026-06-10 10:24:18 +02:00
infinito-nexus-updater[bot] c30c7c665a update(repos): bump git refs to latest semver tags 2026-06-10 08:15:53 +00:00
kevinveenbirkenbach b304a78c3b Merge pull request #307 from infinito-nexus/update/docker-image-versions-20260610-524005b
update(images): update Docker image versions
2026-06-10 10:08:12 +02:00
kevinveenbirkenbach 87e4264fff Merge pull request #306 from infinito-nexus/update/repository-refs-20260610-9714798
update(repos): bump git refs to latest semver tags
2026-06-10 10:07:47 +02:00
infinito-nexus-updater[bot] c0bfe446dc update(images): update Docker image versions 2026-06-10 08:07:28 +00:00
kevinveenbirkenbach 6aca9fb753 Merge pull request #300 from kevinveenbirkenbach/feature/web-app-element-ansible
feat(web-app-matrix): ansible flavor (matrix-docker-ansible-deploy) via full DiD
2026-06-10 09:56:41 +02:00
infinito-nexus-updater[bot] 97afa53705 update(repos): bump git refs to latest semver tags 2026-06-10 02:09:57 +00:00
kevinveenbirkenbach 4fba2bda99 Merge pull request #305 from infinito-nexus/dependabot/docker_compose/coredns/coredns-1.14.4
chore(deps): bump coredns/coredns from 1.14.3 to 1.14.4
2026-06-10 03:47:42 +02:00
kevinveenbirkenbachandClaude Opus 4.7 a4cb5b89e1 feat(swarm): swarm-aware container exec via container_address lookup
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>
2026-06-10 03:46:28 +02:00
dependabot[bot] 4fa503ed05 chore(deps): bump coredns/coredns from 1.14.3 to 1.14.4
Bumps coredns/coredns from 1.14.3 to 1.14.4.

---
updated-dependencies:
- dependency-name: coredns/coredns
  dependency-version: 1.14.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-10 00:07:07 +00:00
evangelostsak 4c051d9939 fix(web-app-penpot): registration on for OIDC JIT + split login specs per persona
Biber OIDC login failed the deploy with `?error=registration-disabled`:
Penpot routes first-time OIDC logins through the registration path, so a
disabled flag blocks JIT-provisioning of not-yet-seen federated users
(admin already existed, biber did not). LDAP self-provisions without it.

- meta/services.yml: registration_enabled now follows OIDC (on when
  web-app-keycloak is present), off otherwise; native admin is bootstrapped
  and LDAP needs no registration. vars/README/req-023 updated to match.
- playwright: split the combined OIDC and LDAP login specs into one file
  per persona — test-login-{oidc,ldap}-{admin,biber}.js — wired through the
  orchestrator; README Testing section updated.
2026-06-10 01:02:55 +02:00
kevinveenbirkenbach 01af23b574 fix(svc-db-openldap): set default_placement to manager for swarm auto-placement
The web-app-keycloak swarm deploy failed at "docker stack deploy
keycloak" with `network "openldap" is declared as external, but
could not be found` (run 27063963773 job 79881947011 line 9772).
Root cause: swarm-mgr-01 was in the svc-db-openldap inventory group
on paper, but svc-db-openldap was never actually run during the play,
so the openldap overlay network never got created — Keycloak's
compose declared it as external and the stack deploy aborted.

svc-db-postgres and svc-db-mariadb both carry default_placement:
manager in their meta/services.yml so the constructor's auto-place
loop (tasks/stages/01_constructor.yml:115-127) pins them to the
first svc-swarm-manager. svc-db-openldap is structurally identical
but lacked the same declaration, so the auto-placement skipped it
and tasks/groups/svc-db-roles.yml's application_allowed gate
returned false (host not in group), short-circuiting the role.

Mirror the same one-line declaration so the constructor places
openldap on the swarm manager, the role runs, and the openldap
overlay network is created before any downstream consumer
(keycloak, mailu, mattermost, ...) declares it external.
2026-06-10 00:12:47 +02:00
kevinveenbirkenbachandClaude Opus 4.7 2870fb7ada fix(web-app-matrix): recover MDAD bootstrap from stale marker
When matrix-mdad is recreated, the systemd unit dir is fresh but the
state volume keeps the marker. ConditionPathExists then skips bootstrap
and Synapse never gets a unit. Drop the condition, gate skip on marker
AND matrix-synapse.service existence, and remove a stale marker on
mismatch so bootstrap re-runs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 23:57:05 +02:00
kevinveenbirkenbach 8618bb3b53 refactor(sys-svc-compose): extract stack-host prep tasks behind single IS_STACK_HOST gate
Four consecutive tasks in tasks/main.yml each carried an inline
"when: IS_STACK_HOST | bool" gate. Move them into a dedicated
02_stack_host_prep.yml and include that file once with the
IS_STACK_HOST gate on the include, so the condition lives in one
place. nfs_prep.yml stays inline because it intentionally runs on
every host (workers need their NFS mount points prepared too) and
up.yml stays inline because extracting a single-task file would not
reduce the duplication that the refactor is targeting.

Ansible's include_tasks `when` semantics mean a False gate on the
parent include skips the file entirely, while inner per-task `when`
clauses ANDed with the parent gate produce the same condition the
inline form expressed. The compose_repository_pull, has_domain and
TLS-mode conditions remain on their respective tasks. The
pre-existing TODO comment on the directories task ("This could lead
to problems in sys-svc-compose directories which are based on a git
repository") was an untracked planning note and is dropped per
CLAUDE.md's comment policy.
2026-06-09 23:40:15 +02:00
evangelostsak e88a102354 fix(web-app-penpot): drop dead default() flagged by lint
- tasks/main.yml: remove `| default('')` from changed_when on command
  results (stdout always set when the conditional fires) per
  test_no_redundant_default_on_module_io.
- env.j2: drop `| default('')` on LDAP.SERVER.SECURITY (defined
  unconditionally in group_vars/all/12_ldap.yml) per
  test_no_jinja_default_on_spot_path.
2026-06-09 23:39:38 +02:00
kevinveenbirkenbach 75bcd499ce fix(sys-svc-mail-msmtp): defer hlth-msmtp via service-loader post-run queue
sys-svc-mail-msmtp is loaded by sys-service-loader during the server
bucket pass. Its previous direct include of sys-ctl-hlth-msmtp ran
inside that pass — well before the web-app bucket pass deploys
web-app-mailu's SMTP listener. With MODE_DEBUG=true the eager flush
then fired msmtp against 127.0.0.1:465 and got "Connection refused",
which triggered sys-service's diagnostic-fail handler and aborted the
swarm baserow play (see run 27226937629 job 80398032232).

The bucket order (universal > server > web-svc > web-app) intentionally
forbids server-bucket services from declaring run_after a web-app
dependency, so the fix cannot be expressed as a run_after edge. Route
the hlth-msmtp include through sys-service-loader/list_or_shoot.yml
instead — the role is appended to _service_loader_post_run during
preload and only executes after every preload bucket (including
web-app) has finished, at which point web-app-mailu's compose-up
handler has already brought the SMTP container up.

list_or_shoot.yml gracefully degrades to a direct include when invoked
outside any preload context, so standalone callers (tests, ad-hoc
playbooks) remain unaffected. The new wiring mirrors the established
pattern in sys-svc-compose/tasks/main.yml which already routes
sys-ctl-cln-docker, sys-ctl-hlth-container, sys-ctl-hlth-volumes,
sys-ctl-rpr-container-hard and svc-bkp-container-2-local through the
same post-run queue.
2026-06-09 23:27:55 +02:00
kevinveenbirkenbach 26cfb5baf8 fix(group_vars/storage): extract NFS state path to top-level SPOT constants
The swarm-test rig provisions storage via utils/tests/swarm/write_extras.py
which emits storage.nfs as the complete {server, export_base, version}
triple, overwriting (not merging) the group_vars default. Any key nested
under storage.nfs that is NOT re-emitted by the provisioner is lost on
the test rig, which broke svc-storage-nfs-server with
"object of type 'dict' has no attribute 'state_subdir'" on the
e415f97b0 swarm baserow CI job (run 27226937629).

Move state_subdir out of the shadowable storage.nfs sub-dict to a
top-level constant STORAGE_NFS_STATE_SUBDIR, and DRY the four call
sites that previously hand-built `[export_base, state_subdir]|path_join`
into a single derived constant STORAGE_NFS_STATE_PATH. The provisioner
needs no change because the constants live outside its override target.

Migrates four consumers (svc-storage-nfs-server/tasks/03_exports.yml,
05_controller_mount.yml, templates/exports.j2 and
svc-storage-nfs-client/tasks/main.yml). Fixes Cluster A and the
cascading Cluster B (workers cannot probe-mount because
nfs-server never wrote /etc/exports).
2026-06-09 23:27:54 +02:00
evangelostsakandClaude Opus 4.8 532c2893d6 feat(web-app-penpot): native local-password login + docs
Bootstrap a local password for the administrator profile via the backend
PREPL (enable-prepl-server) so native email/password login works alongside
OIDC/LDAP. Adds enable-login-with-password and a registration toggle driven
by services.penpot.registration_enabled (off once OIDC is the login path).

- vars: PENPOT_FLAGS gains password login + prepl + conditional registration;
  PENPOT_REGISTRATION_ENABLED and PENPOT_ADMIN_{EMAIL,PASSWORD,FULLNAME}.
- tasks: idempotent create-profile / update-profile bootstrap (no_log).
- playwright: test-login-native.js drives the local password Login button.
- README + requirement 023: native login, registration toggle, canonical
  domain penpot.design, CA-trust gating, spec split; tick project/asset
  coverage; fix header 235 -> 023.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 23:21:20 +02:00
evangelostsak 8cccf8dee8 fix(web-app-penpot): address Kevin's PR review (web-app-penpot)
- spec: split the monolithic Playwright spec into per-surface companions
  (_shared.js + test-login-{native,oidc,ldap}.js) required by playwright.spec.js;
  OIDC and LDAP cover both administrator and biber; native registers a local
  account (skips when OIDC is the login path / registration disabled).
- meta/main.yml: description now describes the application, not the role.
- meta/server.yml: canonical domain -> penpot.design.{{ DOMAIN_PRIMARY }}.
- meta/info.yml: add upstream intro video.
- meta/services.yml + vars: registration_enabled default in services.yml
  (off when OIDC enabled, operator-overridable); PENPOT_FLAGS toggles
  enable/disable-registration accordingly.
- templates/env.j2: bind PENPOT_SMTP_ENABLED to email.enabled and
  PENPOT_LDAP_SSL/STARTTLS to LDAP.SERVER.SECURITY (constants, not literals).
- templates/compose.yml.j2: trim the backend comment to the exception and gate
  the JVM CA-trust import behind self-signed TLS (no-op in production); readable
  YAML block.
- templates/playwright.env.j2: dotenv_quote APP_BASE_URL; drop redundant blank lines.

Verified live: 5 passed, 6 skipped, 0 failed (OIDC/project/asset skip on this
sso-off deploy; native + LDAP + baseline + guest pass).
2026-06-09 23:02:26 +02:00
kevinveenbirkenbach e415f97b02 fix(web-app-mattermost): gate post-deploy tasks on IS_STACK_HOST
01_setup.yml and 02_keycloak.yml only contain shell tasks that exec
into MATTERMOST_CONTAINER / MATTERMOST_LDAP_CONTAINER /
MATTERMOST_KEYCLOAK_CONTAINER, which exist solely on STACK_HOST. On
swarm workers the chdir to the mattermost instance directory fails
during preprocessing, leaving mm_ready_wait as a module-failure dict
without .rc; the next "until: mm_ready_wait.rc == 0" then aborts with
"object of type 'dict' has no attribute 'rc'" at 0.4s, breaking the
swarm deploy for both workers.

Wrap both include_tasks in a single block gated on
"when: IS_STACK_HOST | bool" so workers skip the entire post-deploy
chain cleanly. The OIDC condition stays task-scoped on the keycloak
include and is ANDed with the inherited block gate at runtime.

The block-with-IS_STACK_HOST pattern is already used by
svc-bkp-container-2-local and web-app-pixelfed. Compose mode is
unaffected because IS_STACK_HOST evaluates true for non-swarm
deployments.
2026-06-09 20:23:54 +02:00
kevinveenbirkenbach b5247eeafb fix(tests/swarm/derive_includes): include svc-registry-docker in swarm test inventory
The swarm test rig provisions its inventory via derive_includes.py
walking each APP_ID's transitive meta-deps. svc-registry-docker is
not a meta-dep of any web app (intentionally — it would otherwise
spin up an unused registry container in compose-mode), but the
sys-svc-compose handler "swarm: retag built images for local
registry and push" (handlers/main.yml:116) requires the registry
to be reachable at swarm-mgr-01:5000 whenever an app builds its
own image (web-svc-simpleicons, etc). Without the include the
handler crashed with "dial tcp 192.168.244.3:5000: connection
refused".

Add svc-registry-docker to _EXPLICIT_INCLUDES alongside
svc-swarm-manager and svc-storage-nfs-client. The role already
carries default_placement: manager in meta/services.yml, so the
constructor auto-places swarm-mgr-01 in the svc-registry-docker
group and tasks/groups/svc-registry-roles.yml deploys it ahead of
the first app stack. Verified with:
  APP_ID=web-svc-simpleicons python -m utils.tests.swarm.derive_includes
which now emits svc-registry-docker in the include list.
2026-06-09 20:23:54 +02:00
kevinveenbirkenbach d9e022f684 fix(plugins/lookup/container_depends_on): template DEPLOYMENT_MODE before swarm check
The lookup read DEPLOYMENT_MODE via str(vars_.get(...)).strip() without
running it through templar.template(). DEPLOYMENT_MODE in
group_vars/all/18_swarm.yml is a Jinja expression
({{ 'swarm' if (groups['svc-swarm']|default([])|length) > 1 else 'compose' }}),
so the untemplated literal never equals 'swarm' and the lookup always
fell through to the compose branch — emitting depends_on as a mapping
(e.g. `redis: {condition: service_healthy}`). docker stack deploy
rejects that with `services.X.depends_on must be a list`, breaking
mastodon, friendica, gitea, onlyoffice and matomo on swarm.

Mirror the templating pattern already used by every sibling lookup
(compose_replicas.py, compose_volumes.py, compose_only.py,
compose_restart.py): capture self._templar, fetch DEPLOYMENT_MODE, and
pass it through templar.template() inside contextlib.suppress(Exception)
before comparing against 'swarm'. Behaviour for already-resolved values
('swarm', 'compose', missing default) is unchanged; only the unresolved
Jinja case is fixed.

Add a regression test (test_unresolved_jinja_deployment_mode_is_templated)
that drives the lookup with the exact group_vars expression plus a stub
templar and asserts the list form is emitted.
2026-06-09 19:49:29 +02:00
kevinveenbirkenbach fa732afd50 fix(web-app-matrix): render homeserver.yaml + log.config world-readable
Synapse runs as UID 991 inside the container but the two configs are
bind-mounted :ro from host paths owned root:root with the default mode
0640. UID 991 is neither owner nor in the root group, so the container
exits on every restart with PermissionError: /data/homeserver.yaml.

Render both bind-mounted files with mode 0644 via the new role-scoped
constant MATRIX_SYNAPSE_BIND_MOUNT_MODE so the container UID can read
them. The files are read-only inside the container and contain no
secrets that aren't already in the host group-readable layer.
2026-06-09 19:38:02 +02:00
kevinveenbirkenbachandClaude Opus 4.7 d06eb6ea15 fix(web-app-matrix): mount Infinito self-signed CA into MDAD runner + Synapse
Local V1 deploy with full Infinito stack reproduced the CI Playwright
failure: OIDC login showed 'This login provider is unavailable right
now.' because Synapse's Twisted HTTP client could not validate the
self-signed cert behind https://auth.infinito.example, leading to
OidcDiscoveryError. Verified the failure in-container via curl from
the matrix-synapse container (`SSL certificate problem: unable to get
local issuer certificate`).

The Infinito compose-CA injector wraps each compose service's
entrypoint with a trust-store install — but only the matrix-mdad
service is a compose service. The MDAD-managed inner containers
(synapse / element / bridges / jitsi) are launched by MDAD's docker
daemon inside matrix-mdad and never see Infinito's CA wrapper.

- services.yml.j2: bind-mount CA_TRUST.cert_host into matrix-mdad
  itself, gated on TLS_MODE == 'self_signed', so the CA file is
  reachable for re-mounting into inner containers. Same path inside
  matrix-mdad as on the host, so MDAD's docker run --mount src=...
  resolves correctly.
- vars.yml.j2: matrix_synapse_container_extra_arguments now bind-mounts
  the CA from {{ CA_TRUST.cert_host }} into the synapse container at
  MATRIX_CA_MOUNT_PATH and exports SSL_CERT_FILE / REQUESTS_CA_BUNDLE
  so synapse's Twisted client and any python-requests calls validate
  against it. Gated on TLS_MODE == 'self_signed' — no injection on
  letsencrypt / off TLS modes.
- vars/main.yml: MATRIX_CA_MOUNT_PATH constant for the in-container
  cert destination so the path is defined in exactly one place.

Verified live in-container after the patch: synapse curl to OIDC
discovery succeeds without -k, /_matrix/client/r0/login surfaces the
oidc-keycloak provider, and SSO redirect returns 302.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 19:12:06 +02:00
kevinveenbirkenbachandClaude Opus 4.7 ae81df392b style(dev-nix): autoformat collapse double-space before inline nocheck directive
`make autoformat` (mbake) rewrites multi-whitespace gaps in YAML
inline comments to a single space. Pure formatting touch on the
`# nocheck: stack-host-template` marker, no functional change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 19:02:58 +02:00
kevinveenbirkenbachandClaude Opus 4.7 43052313ad fix(compose_ca): emit both STDERR and STDOUT on build failure
CI run 27063963773 shopware compose build failed with rc=2 but the
operator only saw the Dockerfile-chunk summary because BuildKit
interleaves resolver chatter into stdout while the failure summary
lands in stderr. The old `(err or out)` fallback collapsed to err
and discarded the actual composer dependency-resolution error.

Pure diagnostics improvement: emits both streams labelled clearly so
future composer / pip / npm / cargo build failures surface their
real root cause without re-running with verbose progress flags.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 19:02:58 +02:00
kevinveenbirkenbachandClaude Opus 4.7 d92351f99a fix(web-app-mailu): raise admin container mem_limit to 2g
Resolves Cluster G.2 (2 compose jobs from CI run 27063963773): mailu
user-mgmt commands (`flask mailu user`, `flask mailu password`)
were SIGKILL'd with rc=137 after 4-6s. Flask CLI inside the admin
container hit the 1g cap while executing user-creation / password
ops.

Raising to 2g matches the headroom Mailu's upstream docker-compose
recommends for non-trivial user counts.

Locally validated: mailu variant=0 reinstall completes (23m, no rc=137).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 19:02:58 +02:00
kevinveenbirkenbachandClaude Opus 4.7 da829c5ed6 fix(sys-stk-full): delegate front-proxy include to openresty host with run_once
Resolves Cluster B (22 swarm jobs from CI run 27063963773): worker
hosts running sys-stk-full for their apps silently skipped the
sys-stk-front-proxy include (when clause required local openresty
membership). Workers' app vhosts were never created on the manager,
so openresty had no server_name for them. Playwright wait-for-url
then failed at https://<app>.infinito.example/ with TLSV1_UNRECOGNIZED_NAME.

Replace the membership when-clause with `apply: delegate_to:
<first-openresty-host> + run_once: true`. Now any host invoking
sys-stk-full produces the vhost on the openresty host exactly once,
regardless of where the caller lives.

Compose mode unchanged: openresty auto-placed on localhost, delegate
to localhost from localhost is a no-op.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 19:02:58 +02:00
kevinveenbirkenbachandClaude Opus 4.7 b18e939016 fix(sys-svc-compose): pre-create parent dir before delegated template render
Resolves Cluster D (4 swarm jobs from CI run 27063963773):
flowise, kix, mediawiki, nextcloud all failed with
"msg: Destination directory <path> does not exist" because their
templates render to non-standard subdirs (config/, volumes/mw-local/,
volumes/infinito/, volumes/) that aren't part of the default
sys-svc-compose dir-creation flow.

Pre-create task uses identical delegate_to: STACK_HOST + run_once + when
as the subsequent template task, so the parent always exists on the
correct host with no possible race.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 19:02:58 +02:00
kevinveenbirkenbachandClaude Opus 4.7 df01fd9b7b fix(deployment-mode): use groups['svc-swarm'] for host-stable detection
Resolves Cluster C (12 universal jobs) + Cluster F (4 swarm jobs)
from CI run 27063963773.

Cluster C: `groups[application_id]` raised "'application_id' is
undefined" in contexts without an active role (universal-deploy for
sys-ctl-alm-compose etc.), aborting the play at group_vars eval time.

Cluster F: per-role overrides of application_id (e.g. web-opt-rdr-
domains setting it to a domain string) silently flipped DEPLOYMENT_MODE
to 'compose' on every host. IS_STACK_HOST then evaluated to True on
swarm workers, defeating the stack_host_template skip-on-workers gate
and causing "Destination directory /etc/nginx/conf.d/servers/http
does not exist" failures on swarm-wrk-*.

`groups['svc-swarm']` is host-stable: depends only on inventory
membership, never on per-role context.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 19:02:58 +02:00
kevinveenbirkenbachandClaude Opus 4.7 59c221521c fix(cluster-a): NFS-everywhere for cluster-shared /var/lib/infinito state
Resolves CI swarm rc=69 ("msmtp: needs a password"): controller-side
Ansible lookups (lookup('email').password etc.) couldn't see the
tokens.yml that Mailu writes target-side, because the controller has
its own filesystem in swarm mode.

Mount NFS-shared /var/lib/infinito on every swarm host AND on the
controller during deploy (in-play delegate_to: localhost task at end
of svc-storage-nfs-server role). State subdir exported separately
with no_root_squash + wildcard CIDRs so root-owned writes succeed
and the controller can mount from outside the svc-swarm IP set.

Single-writer guarantee on FILE_TOKENS via delegate_to: STACK_HOST +
run_once on sys-token-store/{01_core,write}.yml. Backup roles
(svc-bkp-container-2-local, sys-ctl-cln-bkps, sys-ctl-cln-faild-bkps,
user-backup/03_permissions_folders, three direct callers of
04_seed-database-to-backup.yml) gated on IS_STACK_HOST to prevent
N-host write races on the shared NFS backup tree.

Fail-fast assertion in svc-storage-nfs-client + 05_controller_mount
when pre-existing local /var/lib/infinito would be hidden by mount-
over. Operator must migrate or wipe before re-deploying.

SPOTs: SWARM_LAB_NETWORK in workflow env block, storage.nfs.state_subdir
in group_vars/all/15_storage.yml, DIR_VAR_LIB unchanged. UPPER_CASE
constants in NFS roles. Cleanup script umounts controller view before
container teardown to prevent stale-mount leak between matrix jobs.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 19:02:57 +02:00
kevinveenbirkenbachandClaude Opus 4.7 0681c00c78 fix(web-app-matrix): drop iptables-legacy switch in DiD runner
Local V1 deploy diagnostic showed docker.service inside matrix-mdad
failing repeatedly:

  iptables -t nat -N DOCKER: iptables v1.8.11 (legacy):
  can't initialize iptables table 'nat': Table does not exist

The runner Dockerfile forced iptables-legacy via update-alternatives;
on a Debian 13 host with nf_tables as the kernel default the legacy
backend can't see the nat table, so the inner dockerd never starts,
matrix-mdad-bootstrap.service stays in 'dependency failed' state, and
the Synapse loopback probe times out for the full retry budget.

Drop the override and let Debian's default (iptables-nft, which talks
to nftables natively) be selected. The host's nf_tables stack is then
addressable from inside the DiD container.

Tests: 186/186 green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 15:44:43 +02:00
kevinveenbirkenbachandClaude Opus 4.7 81d6db9e07 fix(web-app-matrix): correct port forwards for synapse on host network
CI failure 27049736006 showed the probe at http://127.0.0.1:8021 timed
out (Connection reset by peer) for the full 15-min budget even though
matrix-mdad and its inner synapse were running.

Root cause: with matrix_synapse_container_network=host, MDAD's docker
run -p flag (rendered from matrix_synapse_container_client_api_host_bind_port)
is silently ignored — host networking does not use port mappings.
Synapse binds inside matrix-mdad's network namespace on its native
container port (8008 client, 8048 federation), not on the
externally-advertised host bind port. The outer compose service was
forwarding host 127.0.0.1:8021 -> matrix-mdad:8021, where nothing was
listening.

- services.yml.j2: outer compose port forwards now target the real
  internal synapse / federation / element ports (8008 / 8048 / 8080).
  Host ports stay aligned with MATRIX_SYNAPSE_PORT / MATRIX_ELEMENT_PORT
  / MATRIX_FEDERATION_PORT so the front-proxy targets a single 127.0.0.1
  port regardless of flavor.
- vars.yml.j2: drop the now-useless matrix_synapse_container_*_host_bind_port
  and matrix_client_element_container_http_host_bind_port settings —
  they only render a -p flag MDAD would ignore on --network=host.

Tests: 186/186 green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-09 13:37:42 +02:00
evangelostsak ae3eb76e6a add trailing new_line no meet CI tests 2026-06-06 17:33:51 +02:00
evangelostsak 771c449c8f feat(web-app-penpot) add missing requirement file used by the agent and its necessary documentation 2026-06-06 17:24:05 +02:00
evangelostsak a90fc621a8 revert(tests): remove unnecessary empty __init__.py files
Per reviewer feedback: the 59 __init__.py added by the earlier
'restore missing __init__.py' commit sit in test directories that are
empty on the rebased branch (no test files) and do not exist on
upstream. They were leftovers from the pre-rebase test layout. Removing
them; on a fresh checkout those empty dirs do not exist, so
tests/lint/filesystem/test_init_files.py has nothing to flag.
2026-06-06 17:10:52 +02:00
kevinveenbirkenbach dff531e5de Added guards for stack and deleted comments 2026-06-06 15:45:38 +02:00
kevinveenbirkenbachandClaude Opus 4.7 05f6a97bd8 refactor(stack_host_template): generalise IS_STACK_HOST template wrapper across the codebase
Renames the local action plugin from `conf_template` to
`stack_host_template` so the name carries the actual semantics
(IS_STACK_HOST gate, not nginx config), broadens the lint guard to
every bare `template:` task, and migrates every consumer found in the
audit.

Plugin / tests / lint:
  * plugins/action/conf_template.py -> plugins/action/stack_host_template.py
    (drop nginx-specific docstring; trust_as_template gate unchanged)
  * test_conf_template.py            -> test_stack_host_template.py
  * test_conf_template_migration.py  -> test_stack_host_template_migration.py
    Scope expanded from `.conf.j2` to every `template:` task; marker
    renamed to `nocheck: stack-host-template`.

Migrations (~50 tasks now using stack_host_template:):
  * sys-svc-compose/04_files.yml: compose.yml, .env, Dockerfile,
    compose.override (every per-app compose-tree file)
  * web-app-keycloak: sso_proxy upstream-config (fixes the friendica
    CI failure that prompted this refactor) + realm-import
  * web-app-taiga, web-app-matrix, web-app-opentalk, web-app-pgadmin,
    web-app-moodle, web-app-listmonk, web-app-littlejs, web-app-fediwall,
    web-app-wordpress, web-app-discourse, web-app-bigbluebutton,
    web-app-jenkins, web-app-jitsi, web-app-mobilizon, web-app-suitecrm,
    web-app-decidim, web-app-dashboard, web-app-nextcloud, web-app-xwiki,
    web-app-mailu, web-app-magento, web-app-odoo, web-app-prometheus,
    web-app-matrix, web-app-peertube, web-app-mailu (+ existing `conf_template:`
    callers updated to the new name)
  * sys-front-tls, svc-db-openldap, sys-svc-webserver-core,
    sys-svc-rdbms (dedicated DB env + initdb), sys-front-inj-css,
    sys-front-inj-dashboard, sys-front-inj-all, sys-stk-front-proxy,
    web-svc-cdn-css, web-svc-xmpp, web-svc-legal, web-svc-simpleicons,
    web-opt-rdr-domains

Nochecks (per-host or already-structurally-stack-host):
  * sys-svc-mail-smtp/postfix, sys-svc-sshd, sys-svc-container/daemon.json
  * sys-timer, sys-service, sys-svc-mail-msmtp, sys-svc-journalctl,
    sys-daemon, sys-svc-compose/utils/_render_one_template (delegate_to
    stack host with peer slurp dance)
  * dev-locales, dev-nix, desk-*, user, user-backup, user-workstation
  * svc-storage-nfs-server/exports, test-e2e-playwright stage dirs

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 15:45:38 +02:00
kevinveenbirkenbachandClaude Opus 4.7 37cb48a7e7 fix(container_depends_on): emit list form in swarm mode
`docker stack deploy` rejects compose map-form `depends_on` with
`services.<svc>.depends_on must be a list`; the syntax is only valid
for `docker compose v2`. Until now the lookup always emitted the
map form, so every swarm-deployed app with a local DB or Redis
aborted the stack deploy handler in sys-svc-compose with rc=1
(CI job 79876947270 on friendica).

Branch on `DEPLOYMENT_MODE`:

* compose: keep the map form so health-check ordering still applies.
* swarm:   emit the bare service-name list; swarm has no health-check
           gated ordering anyway.

Adds two regression tests pinning the emitted shape per mode.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-06 15:45:38 +02:00