Backend/exporter/frontend services were listing environment variables
without values (e.g. - PENPOT_DATABASE_PASSWORD) which causes Docker
Compose to pass through from host environment. Since these aren't set
on the host, they become null and override the env_file values.
Fixed by removing environment sections - env_file already contains all
needed variables with correct values.
Changed from single-line array format to multiline YAML block style
for better readability and to avoid potential parsing issues with
complex shell commands containing comments and special characters.
The container-based SSL certificate import approach is correct since
keytool only exists inside the Java container, not on the host.
- Removed backend healthcheck that was blocking stack startup
- Backend depends_on redis (healthy), frontend waits for backend (started)
- Playwright test.beforeAll() already polls backend API for up to 16 minutes
- This matches pattern from commit 332ef7833 (other roles without healthchecks)
- Avoids Docker Compose blocking on backend health determination
- Simpler, more reliable: let application-level tests handle readiness
- Changed from wget/curl (not available in penpotapp/backend image) to bash TCP socket
- Uses: exec 3<>/dev/tcp/localhost/6060 with HTTP GET request
- Pure bash built-in, guaranteed to work since backend command uses /bin/bash
- Matches pattern used by other roles (e.g., sys-svc-container/templates/healthcheck/tcp.yml.j2)
- Add healthcheck to backend service (curl http://localhost:6060/)
* interval: 30s, timeout: 10s, retries: 10, start_period: 120s
* This ensures backend is truly ready before tests run
- Update depends_on to use health conditions instead of just service_started
* frontend waits for backend to be healthy
* backend and exporter wait for redis to be healthy
- Add unsafe-eval to script-src-elem and script-src-attr in CSP config
* Ensures CSP2 fallback merge includes unsafe-eval for Penpot's eval() usage
Fixes#204 - Resolves 502 Bad Gateway errors during Playwright tests by ensuring proper container startup ordering
The test.beforeAll() hook polls backend API for up to 15 minutes to wait
for Java backend startup. However, Playwright's default beforeAll timeout
is only 5 minutes (300s), causing 'beforeAll hook timeout exceeded' error.
Add test.setTimeout(960000) at start of beforeAll hook to set 16-minute
timeout (15min wait + 1min buffer), allowing sufficient time for backend
readiness check to complete in CI environment.
Fixes CI run 25076502349 failure where CSP test failed with:
'beforeAll hook timeout of 300000ms exceeded'
Ref: #204
- Define SYSTEM_EMAIL_DOMAIN variable (defaults to DOMAIN_PRIMARY)
- Add missing __init__.py in tests/integration/application_id/
- Add default_users and defaults_applications to ignored test keys
- All 165 tests now pass
- Backend startup can take 10-15 minutes (Java, SSL certs, DB migrations)
- Without wait, tests hit Bad Gateway error from frontend
- Poll /api/rpc/command/get-profile endpoint before tests run
- Accept 200/401/403 status codes (all mean backend is responding)
- 15-minute timeout with 5-second retry interval
- Replaces Ansible uri_retry with Playwright-based wait
Penpot frontend briefly shows black screen during JavaScript initialization.
Wait for actual login UI elements (main_ui_auth_login, auth, login classes)
instead of just body element to ensure frontend fully loaded before tests.
Increases timeout to 120s to handle slow CI initialization.
Removed both frontend and backend uri_retry waits from main.yml.
Penpot has no post-deploy Ansible configuration tasks, so these waits
serve no purpose and cause CI failures (502 Bad Gateway after 15min).
Pattern analysis of 10+ other roles shows uri_retry is only used when
post-deploy Ansible tasks need to configure the application (e.g., Gitea,
Odoo). Roles without such tasks (Taiga, Discourse, Matrix, Mattermost, etc.)
have no waits and rely on Playwright's generous built-in timeouts (60-120s).
Local test results: 4/4 Playwright tests passed in 1.3m
- CSP test: 3.7s
- Admin OIDC login/logout: 29.4s
- Biber OIDC login/logout: 18.8s
- Project creation: 24.0s
Fixes CI deployment timeout that occurred in runs 25045210187,
25053926236, and 25060875762 where backend API check failed with
502 after 181 retries (15 minutes).
After analyzing CI runs 25045210187 and 25053926236:
- Even with 180s pause, SPA still showing loading screen (3 img tags)
- Frontend HTTP 200 doesn't guarantee backend is ready
- CI artifacts show backend takes longer to initialize
Solution: Actively poll backend API endpoint until it responds.
Using /api/main/methods/get-profile with uri_retry, accepting
200/401/403 status codes (auth errors mean API is up).
Max wait: 180 retries × 5s = 15 minutes
After analyzing CI artifacts from run 25045210187:
- Admin test: SPA still showing loading screen (3 img tags)
- Biber test: SPA loaded but backend returned Bad Gateway
90s was insufficient for both SPA initialization and backend
readiness in CI's resource-constrained environment. Increasing to
180s to provide adequate buffer for full stack initialization.
Static analysis of MDAD's group_vars/matrix_servers shows that every
bridge (heisenbridge, hookshot, appservice-*, mautrix-*), element-call,
livekit-server, livekit-jwt-service, alertmanager-receiver and the
mautrix-bluesky/discord/signal/slack/googlechat/kakaotalk roles inherit
matrix_addons_container_network (default 'matrix-addons') — not
matrix_homeserver_container_network. Pinning only the homeserver
network to host therefore left all the addons stranded on an inner
bridge with no path to the outer postgres/openldap networks.
Set matrix_addons_container_network: host so the entire bridges/RTC
stack cascades to host networking and inherits matrix-mdad's outer
network attachments.
Jitsi's four components don't inherit either base — pin each
explicitly: jitsi_web/jvb/prosody/jicofo _container_network: host.
Tests: 186/186 green.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
MDAD upstream moved matrix-coturn to the MASH organisation
(https://github.com/mother-of-all-self-hosting/ansible-role-coturn)
and renamed every variable from the matrix_coturn_* prefix to coturn_*.
CI failed with:
msg: The matrix-coturn role in the playbook now lives under the MASH
organization. Please rename matrix_coturn_* -> coturn_* on your
configuration file (vars.yml). The following variables in your
configuration need to be renamed: matrix_coturn_docker_network,
matrix_coturn_enabled
Rename the two we set:
- matrix_coturn_docker_network -> coturn_container_network
- matrix_coturn_enabled -> coturn_enabled
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The swarm CI deploy invoked cli.administration.deploy.swarm with only
swarm.yml and the runtime extras as -e files. Default-inventory vars
(OIDC.CLIENT.SECRET, users, design, ...) were applied only to
host_vars/<MGR>.yml via the provision step's --vars-file argument, so
they reached mgr-01 but not the workers. Workers without OIDC.CLIENT
defaults rendered roles/web-app-keycloak/templates/import/clients/default.json.j2
into "object of type 'dict' has no attribute 'SECRET'" at the realm
import step (CI run 27044129926).
Load default.yml as inventory-wide extra-vars before swarm.yml, so the
two are merged uniformly across mgr-01 and workers. swarm.yml still
overrides any keys it sets (extra-vars merge left-to-right, last wins).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
CI run 27044129926 failed every nginx-templating job with
"The value '{{ IS_STACK_HOST | default(false) | bool }}' is not a
valid boolean". Action plugins must explicitly expose task_vars to
their templar before calling template(); without it the renderer has
no variable scope and returns the Jinja string verbatim, which
_to_bool then rejects.
Sets self._templar.available_variables = task_vars in run(), matching
the standard pattern for action plugins that template against play
vars. Adds a unit test that holds the FakeTemplar's available_variables
mirror in place so any future regression that drops the seed call
fails the suite.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Switch the ansible flavor over to a forked
github.com/kevinveenbirkenbach/matrix-docker-ansible-deploy that
supports container_network='host'. Inside the matrix-mdad DiD
container, every homeserver-stack service (synapse, element, coturn)
runs with --network=host so it shares matrix-mdad's network
namespace, which is attached to the outer compose's `postgres` and
`openldap` networks. Synapse/bridges therefore resolve `database`,
`openldap` via outer Docker DNS — no inner postgres or LDAP needed.
meta/services.yml:
- upstream.repo -> the fork; ref kept at `master` (the host-network
patches live there). The pinned-commit story comes once the fork
push lands.
- postgres.enabled stays `true` (matrix always needs a DB); postgres.shared
becomes `'svc-db-postgres' in group_names` again — central when the
DB role is up, dedicated per-app when it isn't. Both paths reach
matrix-mdad's NS via the outer Docker daemon's DNS now that we run
Synapse on --network=host.
- coturn becomes a first-class shared-service toggle: enabled+shared
follow `'web-svc-coturn' in group_names`. When the central role is
present, MDAD's bundled coturn stays off and Synapse's TURN URIs
point at the central coturn (`turn:HOST:STUN_PORT` + tls variant)
with the shared static_auth_secret pulled from web-svc-coturn
credentials.
meta/variants.yml:
- V1 (ansible all-on): postgres.shared=true, coturn.shared=true.
- V2 (ansible no-auth): postgres.shared=false (dedicated path),
coturn off — exercises both polarities for the variant-coverage
lint and the dedicated DB code path.
- V3 (compose all-on): postgres.shared=true, coturn.shared=true.
vars/main.yml:
- MATRIX_COTURN_SHARED + MATRIX_COTURN_{HOST,STUN_TURN_PORT,STUN_TURN_TLS_PORT,AUTH_SECRET}
lookups (gated on shared so dedicated runs don't try to read
credentials that are not in scope).
templates/flavor/ansible/vars.yml.j2:
- matrix_homeserver_container_network / matrix_synapse_container_network
/ matrix_client_element_container_network / matrix_coturn_docker_network
all pinned to `host`. Hits the fork's `when: var not in ['', 'host']`
task guards (no 403 on docker_network create) and the
`{% if var not in ['', 'host'] %}` systemd-template guards (no
ExecStartPre=docker network connect against an additional bridge,
which is invalid on host networking).
- When central coturn is shared: matrix_coturn_enabled=false +
matrix_synapse_turn_uris (udp/tcp + turns udp/tcp) +
matrix_synapse_turn_shared_secret wired to MATRIX_COTURN_AUTH_SECRET.
Tests: 186/186 green.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds plugins/action/conf_template.py, a drop-in `template:` replacement
that returns `skipped: true` whenever IS_STACK_HOST evaluates to false.
Routes the rendered Jinja through ansible.module_utils.parsing.convert_bool
so both Python bool and string "True"/"False" templar outputs resolve
correctly. Inherits every standard template module argument.
Migrates seven nginx-tree consumers that previously aborted on workers
with "Destination directory does not exist": sys-front-tls,
svc-db-openldap, sys-svc-webserver-core (nginx.conf + default_server.conf),
web-app-matrix (synapse vhost), web-app-peertube (per-domain vhost),
web-opt-rdr-domains (redirect vhost), and web-app-prometheus.
Tags twelve legitimate bare `template:` callers with
`# nocheck: conf-template`: per-host system configs (locale, nix, systemd
manager defaults, journald, msmtprc) and per-app compose-dir configs
(magento x2, mailu x2, moodle, odoo, taiga) that intentionally bypass the
project's nginx tree.
Adds tests/lint/ansible/modules/test_conf_template_migration.py as the
drift guard: any future `template:` task whose `src:` ends in `.conf.j2`
must adopt conf_template or carry the nocheck marker. Adds seven unit
tests for the plugin covering skip semantics, dispatch, mode defaults,
mode overrides, full-arg passthrough, and the string-bool templar path.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
On Frappe v16 the /login page is served from cache, so enabling the Keycloak Social Login Key without invalidating the cache leaves the OIDC button missing; the Playwright OIDC login tests then fall back to the login_via_keycloak callback and hit a 500 (run 27029748629, web-app-erpnext variant-0/sync).
apply_api_bot.py already calls frappe.clear_cache() after commit. Mirror it in the OIDC, LDAP and email apply scripts so every committed config change refreshes cached state (LDAP/email are preventive: same auth/config cache class).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The inventory-vars-file SPOT drift guard
(tests/unit/cli/administration/deploy/development/test_inventory_vars_file_spot.py)
flagged the literal "inventories/development/default.yml" in the
docstring as a missed SPOT migration. Refer to "the development
inventory" in prose instead — the env var name (INFINITO_DOMAIN) is the
contract; the consuming file's path is not.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>