8986b21270 wrote the patch expression from the OTRS convention, looking for
`$Self->{'DatabasePw'} = '...';`. The file does not use that form. Now that
kix_kix_config persists, the line can be read instead of guessed:
01main:13: 'DatabasePw' => 'oo4ca3...',
A perl hash entry, the same shape friendica's php array uses, so the expression
becomes friendica's with a different key and terminator.
The wrong expression surfaced because of 2c4e0e18b, on its first run: the deploy
stopped with
FAILED: no line in 01main matches the patch expression
Before that commit the same deploy would have printed PATCHED and gone green while
01main kept its pre-rotation password -- which is exactly how friendica looked fixed
for months.
Checked against the real line rather than a sample: sed -E with the new expression
returns `'DatabasePw' => 'NEWPW',` and rc 0, and grep finds DatabasePw on that
line only.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
compose gave up on `dependency failed to start: container kix-backend is unhealthy`,
and the backend's own log names the reason:
psql -q -h postgres -p 5432 -U kix kix -c 'CREATE EXTENSION ...' -> succeeds
/opt/kix/bin/kix.Console.pl Maint::Config::Rebuild --allow-root -> FATAL:
password authentication failed for user "kix"
Two calls in a row, one working and one not, with the same database. psql takes its
password from PGPASSWORD; the perl layer takes it from /opt/kix/config/01main, and the
entrypoint's own `[ ! -f /opt/kix/config/01main ]` test reports that file missing on
every start.
/opt/kix/config was on no volume. Every recreated container therefore loses 01main,
re-enters the first-install path, and runs it against a database that is already
installed. The credential rotation between the sync and the async pass is only what
made the container get recreated; any redeploy does this.
Everything else was ruled out from the run rather than assumed: the ALTER USER landed
(changed: true, 22:18:44), the rendered env carried the same value (22:18:57), there is
exactly one postgres container and 192.168.200.2 is its address, and it stayed up and
healthy across the whole window.
Persisting the directory alone would trade one failure for another: the entrypoint runs
`ln -s '/opt/kix/conf.d/*' /opt/kix/config/` on every start, unquoted-glob-style but with
the glob quoted, so it creates a single dangling link named `*` -- and the second start
would find it there. The link points at a path with a literal asterisk and nothing reads
it, so clear_stale_conf_link.yml removes it on every node before the stack comes up.
The password patch declaration follows the friendica pattern and needs 2c4e0e18b to work
at all: its groups are extended-regex, which basic-regex sed rejected outright.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>
A per-item boolean guard carried as a data field named 'when' must hold {{ EXPR }} so the consumer can template it to a real bool; but ansible-core 2.21+ then flags 'conditionals surrounded by templating delimiters'. Rename the data field to 'condition' (a data field may legitimately hold {{ }}) so no genuine conditional carries {{ }}. Also wraps the four previously-bare listmonk entries, which were silently always-False, so those settings now apply when enabled.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Introduce plugins/lookup/path_absolute.py as the single source for the
repo-root anchor: lookup('path_absolute', 'roles/...') returns an absolute
path under playbook_dir, joining every term. Replace all hand-built
playbook_dir path constructions across .yml/.yaml and .j2 templates with it:
[playbook_dir, '<rel>'] | path_join -> lookup('path_absolute', '<rel>')
playbook_dir ~ '/<rel>' -> lookup('path_absolute', '<rel>')
{{ playbook_dir }}/<rel> -> lookup('path_absolute', '<rel>')
Unrelated repo_path symbols in cli/meta/roles/applications/resolution and the
single bare discover_playwright_roles(playbook_dir) call are left untouched.
Add tests/lint/ansible/test_playbook_dir_via_lookup.py: fails the build if any
of the three construction forms reappears in .yml/.yaml/.j2, pointing callers
at the lookup. Unit test covers the plugin.
Touching these files pulled their pre-existing narration comments into
test_comments_valid scope; clean them (delete restatement/banners, keep real
trip-wires as Workaround/Upstream/Invariant markers). Drop caution, important
and pitfall from the comment-lint marker vocabulary and remove the comments
that used them.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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>
Variant 1 of web-app-kix disables every dynamic service flag
(css, dashboard, email, ldap, logout, matomo, oauth2, prometheus —
meta/variants.yml:32-56). With `services.ldap.enabled=false`,
svc-db-openldap is not in the deploy closure, so the top-level
`LDAP.SERVER.DOMAIN` chain in group_vars/all/12_ldap.yml:14-16
resolves via `lookup('config','svc-db-openldap', ...)` and raises
hard the moment any task touches it.
tasks/01_core.yml rendered `zzz_auth_ldap.xml.j2` unconditionally,
the template referenced `KIX_LDAP_HOST` etc., the lazy `LDAP.*`
chain fired, and the deploy aborted before any companion subapp
ever reached Playwright (job 77602110489 on run 26362676803 has
zero variant-1 artefacts across the entire 14-app bundle, while
gitlab's variant-1 produced at least three).
Introduce `KIX_LDAP_ENABLED` in vars/main.yml as the single source
of truth (matches OPENTALK_OIDC_ENABLED / OPENTALK_TURN_ENABLED in
web-app-opentalk), and gate both consumers on it:
* the LDAP-config render task — skipped when LDAP is off
* the compose mount of the LDAP config file — emitted only when
LDAP is on, so the backend container does not try to mount a
missing path
Matches the persisted operator preference: auth services stay
optional — fix code, not variants.
Static check only (per operator request). Confidence ~70% that
this clears the immediate variant-1 failure point; possible
follow-up issues (oauth2-proxy absence, etc.) would only surface
once the kix log reaches further than the previous abort.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Per-app nginx gateways used proxy_pass with a literal docker container
hostname. nginx OSS resolves that hostname once at startup; if the
upstream container later restarts and gets a new IP (e.g. a partial
compose-up after credential change), the gateway proxies onto a dead
address until nginx itself reloads — the 502 we hit on taiga after a
reuse-kept replaced the taiga-back container.
Switch every per-app docker-hostname proxy_pass to a variable form so
the embedded docker DNS re-resolves at request time:
set $upstream_<rolename>_<purpose> http://<host>:<port>;
proxy_pass $upstream_<rolename>_<purpose>;
The full `resolver` directive (TTL + ipv6 toggle inherited from
NETWORK_IPV6_ENABLED) lives in one SPOT — NETWORK_DOCKER_DNS_NGINX_DIRECTIVE
in group_vars/all/08_networks.yml — so cache TTL and ipv6 stay
consistent across the stack.
In taiga the upstream `set` declarations hoist to server scope because
taiga-back is reused across /api/, /oidc/ and /admin/; in nextcloud
and kix each upstream is used exactly once so the declaration stays
local. Locations whose original proxy_pass stripped the location
prefix (`proxy_pass http://.../;` on /media/, /whiteboard/,
/standalone-signaling/) keep that semantics via an explicit
`rewrite ^<loc>(.*)$ /\$1 break;`, because variable-proxy_pass drops
the implicit prefix-strip.
kix's static files/kix-proxy.default.conf moves to
templates/kix-proxy.default.conf.j2 so it can reference the new
constant; the deploy task switches from copy to template accordingly.
Drive-by: scripts/tests/e2e/rerun-spec.sh docstring refresh — the
"only overwrites tests/playwright.spec.js" claim went stale when the
rerunner started restaging every role-local playwright/*.js.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pure 1:1 rename (69 insertions / 69 deletions across 66 files) to
finish the ``docker_*`` → ``compose_*`` alignment started in
8373468e9. The flag configures whether the compose include flushes
handlers right after run; the ``docker_`` prefix was misleading
(handlers are Ansible-side, not docker-side) and the variable is part
of the same ``sys-svc-compose`` SPOT cluster as ``compose_repository_*``.
Touches the role default, the implementation
(``sys-svc-compose/tasks/utils/up.yml``), all 60+ consumer roles,
the network-utility wrapper + its README, and the
``test_network_create_via_util.py`` lint (both the docstring guidance
and the asserted diff).
Breaking change for any external override that still sets the old
name — no alias is provided.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* scripts/lint/autoformat.sh: rename AUTOFORMAT_PARALLEL to PARALLEL,
declare it at file start with default `true`; still env-overridable.
Reword the worker comment so the literal `# shellcheck` token no
longer appears at start-of-line and trips SC1072/SC1073.
* scripts/lint/galaxy.sh: drop the GALAXY_FATAL / GALAXY_NAMESPACE /
GALAXY_PARALLEL env vars (over-engineered for a single repo). Failure
is always fatal. NAMESPACE is an internal constant. Add the same
PARALLEL env var (default `true`, env-overridable); falsy demotes the
xargs fan-out to one worker.
* docs/contributing/environment/variables.md: collapse the four
AUTOFORMAT_*/GALAXY_* rows into one `PARALLEL` row covering both
consumers.
* docs/contributing/tools/make.md: refer to `PARALLEL=0` everywhere;
add the lint-galaxy parallelism note.
Plus working-tree drift across roles/, group_vars/, inventories/, and
tasks/ that accumulated from the make-test setup phase regenerating
generated artefacts during recent test runs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Deploys KIX Start as a first-class web-app-* role behind
sys-stk-front-proxy + web-app-oauth2-proxy + Keycloak realm-level
2FA at kix.<DOMAIN_PRIMARY>. The 4-container upstream stack
(proxy, backend, frontend, db) reuses the shared svc-db-redis for
OAuth2-proxy session storage and KIX cache.
Auth chain:
- OAuth2-proxy `allowed_groups` admits members of either
`/roles/web-app-kix/administrator` or `/roles/web-app-kix/user`
(the user role is declared via `meta/rbac.yml`).
- A custom kix-proxy nginx default.conf is bind-mounted (the
upstream image ships an empty one) and forwards everything
through the frontend Node server, plus copies the OAuth2-proxy
X-Forwarded-User into a Remote-User upstream header.
- KIX `Auth::LDAP` + `Auth::Sync::LDAP` against svc-db-openldap
with one backend per role group (admin GroupDN and user GroupDN).
KIX UsageContext must be a single-string value; comma-list
silently disables the backend.
Validated end-to-end via deploy-fresh-purged-apps APPS=web-app-kix
FULL_CYCLE=true (Pass 1 + Pass 2 both failed=0). Playwright 3/3
in both passes:
- TLS+HSTS at the canonical URL.
- administrator full flow: dashboard -> KIX card -> OAuth2-proxy
-> Keycloak -> KIX-LDAP login form -> KIX UI -> universal logout
-> re-anonymous redirect back to the OAuth2-proxy gate.
- biber full flow: same, with a Keycloak admin REST API fixture
that puts biber into /roles/web-app-kix/user (mirrors what an
operator does manually via the Keycloak UI).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>