kevinveenbirkenbach and Claude Opus 4.8
15e7647d2f
refactor(env): central TZ+LANG base included by every role env.j2
...
New roles/sys-svc-container/templates/env.j2 sets TZ={{ HOST_TIMEZONE }} and LANG=C.UTF-8. Every role env.j2 includes it at the top; it renders via stack_host_template, the same engine that resolves the includes in compose.yml.j2, so it works in both compose and swarm. TZ/LANG are removed from the individual role env.j2 so each value stays single-source.
New lint tests/lint/ansible/test_env_base_include.py verifies every role env.j2 includes the base and never re-sets a base-owned key (keys derived from the base file).
mattermost and gitea meta/volumes.yml drop the /etc/timezone host-path bind: swarm hard-rejects a task whose bind source is missing on the node (Debian-trixie nodes have no /etc/timezone). TZ now comes from the base env. /etc/localtime bind also dropped; the container timezone follows TZ.
Non-directive comments were removed from the touched env.j2 (banners, restatements, reference URLs, narration) so the changed files pass the comment-lint; only tool directives remain.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com >
2026-07-15 22:13:57 +02:00
kevinveenbirkenbach and Claude Opus 4.7
39c990a7ad
feat(web-app-erpnext): ship ERPNext role with OIDC SSO, LDAP, outbound mail
...
Implements requirement docs/requirements/024-web-app-erpnext.md.
Stack (per frappe_docker v15 single-image multi-role pattern):
- configurator (one-shot bench set-config: db_host/port, three redis URLs
split by DB number 0/1/2 for cache/queue/socketio, socketio_port, host_name
HTTPS)
- backend (gunicorn), frontend (nginx-entrypoint.sh), websocket (socketio),
scheduler, queue-short (--queue short,default), queue-long
(--queue long,default,short)
Central-service consumer pattern (Decision #7 ):
- ERPNEXT_DB_ROOT_PASSWORD branches on services.mariadb.shared: central
svc-db-mariadb root pw when true, per-role MariaDB consumer pw when false
- Redis via in-compose `redis` alias, 3-DB-number split for Frappe's logical
buses
- Explicit erpnext_assets volume mount on every container (image's
VOLUME ['sites/assets'] otherwise creates anonymous volumes that fail the
hlth-volumes check)
Bench bootstrap (Decision #9 ):
- bench new-site --install-app erpnext with 30-min hard timeout
- Post-install restart of all long-lived Frappe containers (otherwise the
cached app set serves HTTP 500 on /login)
- TCP-port readiness probe (HTTP would 404 pre-site)
- API-bot user seed + setup_complete=1 via apply_api_bot.py
OIDC (Decisions #4 + #5 ):
- redirect_uris filter auto-includes ERPNext in the shared Keycloak client
- Social Login Key wired via apply_oidc_settings.py with
social_login_provider=Keycloak (delete+recreate due to set_only_once),
custom_base_url=1, relative authorize/token/userinfo paths,
auth_url_data={response_type:code,scope:openid}, login_via_keycloak
redirect path
- Group-to-Frappe-role mapping persisted as a frappe.db default; per-login
reconciliation hook deferred to follow-up (documented in README)
LDAP (V3): LDAP Settings doctype applied via apply_ldap_source.py. End-to-end
LDAP login (auto-create Frappe user from LDAP bind) deferred to follow-up;
LDAP-login Playwright specs intentionally absent in v1 (documented).
Email (Decision #8 , outbound only): outbound Email Account applied via
apply_email_account.py using db_insert/db_update to bypass Frappe's
deploy-time SMTP socket validation. IMAP inbound deferred to follow-up.
Variant matrix (Decision #11 ): V1 sso+ldap, V2 all-false, V3 ldap-only.
Three variants verified end-to-end via
`make compose-deploy mode=reinstall apps=web-app-erpnext full_cycle=true
purge=true` — 6 PLAY RECAPs, all failed=0, all Playwright runs
passed-or-skipped with no failures.
Playwright specs: test-landing, test-csp-headers, test-login-administrator
(OIDC), test-login-biber (OIDC), test-login-native-administrator
(break-glass local login, always runs), test-guest-persona. _shared.js
exposes signInViaErpnextOidc + signInViaErpnextLocal.
Repo-wide lint addition: tests/lint/ansible/modules/test_task_name_length.py
caps Ansible task `name:` strings at 120 chars (opt-out via
`# nocheck: name-length`). Fixed two pre-existing violations in
web-app-keycloak/tasks/update/ldap/per_app_mapper_one.yml.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com >
2026-06-03 09:53:56 +02:00