Commit Graph
27 Commits
Author SHA1 Message Date
kevinveenbirkenbach d2abd332f0 fix(web-app-suitecrm): let the external-auth provisioning path create its user
SuiteCRM 8.10.2 tightened User::hasSaveAccess() from return true to is_admin($current_user) for a record without an id, and restrictAdminOnlyFields() now forces status Inactive on such a record. CreateUser::create() runs while the browser is still anonymous, because it is the LDAP and SAML auto-create path, so every FIRST login of a directory account ends in RuntimeException: Not authorized and HTTP 500 on POST /login. The pre-existing administrator is unaffected, which is why one persona passes and one fails in both auth modes. 451f63cbdd carried the role from 8.9.1 to 8.10.2; 8.9.1 exempted a new record from both guards.

The patch raises the admin context around that one save and restores it in a finally, rather than relaxing the guards, which stay closed for every request-driven save. create() has a single caller in its own file, createExternalAuthUser, which sets is_admin 0 and external_auth_only 1 before calling, so the raised context cannot mint an administrator. patch runs with -F0 so a hunk upstream has moved fails the build instead of landing somewhere else.

The patch path is derived once in vars/main.yml and reaches the Dockerfile as a build arg, following the role's existing SUITECRM_ENTRYPOINT_SCRIPT_* shape, so the file name is written once.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 495d23dbde)

# Conflicts:
#	roles/web-app-suitecrm/vars/main.yml
2026-10-16 13:29:07 +02:00
kevinveenbirkenbachandClaude Opus 5.5 ab9c23fa76 fix(web-app-suitecrm): run Symfony's session through the Redis handler too
3cefb01d71 moved PHP sessions to Redis by pointing session.save_path at
redis-central. SuiteCRM's own core_services.yaml wraps a
NativeFileSessionHandler around '%session.save_path%', and that handler
forces session.save_handler back to files and treats the Redis URL as a
directory ("is_dir(): Unable to find the wrapper "tcp""). Symfony's session
was empty on every request, so every POST /api/graphql answered 403
"Invalid CSRF token", the SPA logged the user out right after the SAML login
and the biber logout spec failed with "session was lost before the logout
attempt" (SuiteCRM#0 on PR #869, reproduced on main locally).

core_services.yaml is imported after every extension service file, so the
core handler cannot be redefined there. An extension packages file now
points framework.session at our own handler and storage factory, which wrap
PHP's built-in SessionHandler and therefore use phpredis from php.ini, with
its locking, for Symfony and the legacy app alike.

The entrypoint rebuilds the prod cache when an extension config is newer
than it, so an existing installation picks the wiring up instead of keeping
the compiled container.

Verified with a local compose deploy of variant 0: on main the SAML logout
spec failed with the 403 sequence above; after a mode=update redeploy the
cache was rebuilt, all three SAML specs passed, and the prod log shows no
tcp-wrapper warning or CSRF failure after the rebuild.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-12 18:35:22 +02:00
kevinveenbirkenbachandClaude Opus 5.5 52bfc2a476 fix(web-app-suitecrm): hand the log directory to the web user
The entrypoint runs the installer and the cache commands as root. They
create logs/prod, and the entrypoint handed cache and public/legacy back
to the web user but not logs. Apache could not write there, so SuiteCRM
wrote neither its auth.log nor its main log.

The entrypoint now hands logs to the web user after the root-run console
commands, on every boot that holds the boot lock, so volumes of earlier
deploys are repaired too. The deploy fails if a path under logs is not
writable for the web user.

Closes #854

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-08 00:16:14 +02:00
kevinveenbirkenbachandClaude Opus 5 53e62c8c8c fix(suitecrm,magento): realign the DB password each app persisted at install
The CI rotation gate moves every application credential between the two deploy passes. MariaDB takes the new value, but both apps baked the old one into a persisted config at install time and their entrypoints refuse to rewrite it once the install flag exists, so every request dies with 'Access denied for user' - a 500 under compose, and under swarm a healthcheck reap that swarm restarts forever.

suitecrm gets the tasks/utils/patch/db_credentials.yml hook that sys-svc-rdbms already reaches for on every deploy, patching db_password in public/legacy/config.php. magento cannot use that hook - app/etc/env.php may carry more than one 'password' line and the shared script is a line-wise sed - so it calls its own supported writer, bin/magento setup:config:set, unguarded after the creates:-guarded install.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-08 13:17:14 +02:00
kevinveenbirkenbachandClaude Opus 5 b53d207667 fix(suitecrm): fetch the IdP descriptor with curl, not wget
Every swarm task of the tor variant died 1.4s after start with exit 8, so the
stack never converged and the drill failed at sys-svc-compose's converge wait
(CI run 32119014832, job 95662863105). The task log held exactly one line: the
entrypoint's "Fetching the IdP signing certificate" - nothing after it.

GNU wget cannot parse a socks5h:// proxy. It aborts before sending anything
with "Error parsing proxy URL: Unsupported scheme.", which src/retr.c maps to
PROXERR and src/exits.c to WGET_EXIT_SERVER_ERROR, i.e. 8 - identical in 1.21
through 1.25. Under -qO- the message is suppressed, which is why the container
log ends mid-sentence. Reproduced locally: the same URL without a proxy exits
4, with the socks5h proxy exits 8.

The proxy was injected container-wide as http_proxy/https_proxy, gated on
Keycloak's domain but applied to every destination - and the CI Tor network
has no exit nodes, so that also dead-ended every clearnet call the app makes.
The three sibling roles that solved this (wordpress, nextcloud, joomla) each
gate per destination host instead. Here the only server-side onion call is the
descriptor fetch itself: the SAML bundle is configured statically, so it needs
no backchannel, and the healthcheck already forces --noproxy '*'. The proxy
now reaches exactly the one client that needs it.

curl's timeouts are pinned so a stalled onion cannot hang the boot: 21
attempts of at most 30s bound the fetch at ~10.5min, inside the 20m
start_period and the ~28min converge budget.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 23:28:15 +02:00
kevinveenbirkenbachandClaude Opus 5 0a41963db3 fix(suitecrm): render ldap.yaml only when LDAP is on, via stack_host_template
meta/volumes.yml mounts ldap.yaml only when services.ldap.enabled, and
compose_volumes drops the whole configs.ldap_config entry when no mount
survives - but the task rendered the file unconditionally, so the ldap-off
variant wrote a host file nothing references. Both sides now share the
condition.

The mechanism was stranded too. render_replicated_templates exists to copy a
rendered file to the swarm peers, and those branches hang off
`not (IS_STACK_HOST | bool)`. Since 1da3c2eda this task file is included only
`when: IS_STACK_HOST | bool`, and include_tasks is dynamic, so for every other
host the file never exists - the peer branches became unreachable. Nothing was
lost by that: type: config renders as configs.<name>.file in both modes, and in
swarm `docker stack deploy` reads it on STACK_HOST and distributes it over raft.
php.ini one task above is the identical case and already used plain
stack_host_template, so this makes the file internally consistent and drops an
include_tasks, a slurp and a loop over a one-element list.

The notify stays: role_templates_expanded defaulted it to compose-up, which is
now spelled out.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 18:02:27 +02:00
kevinveenbirkenbachandClaude Opus 5 a46348b6cf refactor(handlers): bind notify to a listen topic, not to a name
A handler name was doing two jobs at once: the line an operator reads in the
play output, and the identifier a `notify:` binds to. The two pull in opposite
directions -- the log wants "restart the SSH service, with diagnostics", the
binding wants a token that survives quoting, YAML flow style and a shell
round-trip -- and a name that changes for readability silently changes which
handler runs.

Every handler that a notify reached by name now carries a `listen:` with that
exact wording, so the binding is explicit before anything is renamed. The
topics themselves become identifiers: `compose up` reads `compose-up`,
`Restart NFS ganesha` reads `restart-nfs-ganesha`, 22 of them across both
sides of every notify. Where the topic is assembled at runtime, both branches
moved together: the conditional in sys-service carries two literals, and the
LDAP renderer interpolates `import-{{ folder }}-ldif-files`, which is what
makes those handlers reachable at all.

Freed from the binding, the names take the emoji prefix every other task line
already has.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 03:38:42 +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 a5994a9281 fix(web-app-suitecrm): deliver ldap.yaml as a swarm config
The manager-local ldap.yaml host bind was rejected on swarm workers. Declare it as a swarm type: config (mounted only when LDAP is enabled) and render it unconditionally so the config source always exists.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 14:28:41 +02:00
kevinveenbirkenbachandClaude Opus 4.8 23d1447566 refactor(ansible): add path_absolute lookup, replace playbook_dir joins
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>
2026-06-29 20:36:57 +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 3bfeba1243 fix(roles): set the run-once flag as the first 01_core task for shared providers
test_service_core_first_task_run_once requires tasks/01_core.yml to set the
run_once flag (include_tasks: utils/once/flag.yml) as its very first task so the
guard is in place before the role body runs and the variant-aware loader cannot
recurse. Move the flag include from the end to the top in the shared-provider
roles that had it last (bigbluebutton, flowise, gitlab, matrix, mattermost,
moodle, openproject, openwebui, peertube, suitecrm, xwiki).

Also give the template-only providers svc-db-redis and svc-db-memcached the
run-once guard (main.yml includes 01_core.yml when run_once_<role> is undefined;
01_core.yml sets the flag) so they satisfy the shared-provider run_once guard.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 06:03:17 +02:00
kevinveenbirkenbachandClaude Opus 4.8 fc5ad87dc5 feat(roles): make referenced partner services proper shared providers
Declare the 14 services that consuming roles reference but no role provided
(bigbluebutton, xwiki, openproject, gitlab, mattermost, matrix, zammad,
openwebui, flowise, peertube, moodle, suitecrm, redis, memcached) as shared
providers: set enabled/shared/bond on each entity so the service registry
registers them and the dangling references resolve.

Add test_shared_service_provider.py (lint): fail when a referenced shared
service has no provider and its name matches a role's entity/provider that does
not register it (suppressable via '# nocheck: shared-provider').

Guard the 9 web-app shared services with the run-once 01_core.yml pattern
(main.yml includes 01_core.yml only when run_once_<role> is undefined; the core
flags itself via utils/once/flag.yml) so they deploy once when pulled in by
multiple consumers. svc-db-redis/memcached are template-only (no runtime tasks).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 06:03:17 +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
kevinveenbirkenbach 1da3c2eda7 fix(web-app): gate manager-only role bodies behind IS_STACK_HOST
Across roles/web-app-*, the typical body shape is:
  - include_role: sys-stk-full (or sys-svc-compose)   cluster prereqs
  - <host-fs writes, container exec, compose exec>    stack-host only

Under swarm-mode delegation the second group only makes sense on the
host that holds the compose stack (manager), but the role plays on
every host in groups[<role>] so workers abort on missing paths.

Two workflow sweeps (gate-manager-ops + gate-manager-ops-01core, 58
agents total, discover/plan/adversarial-verify per role) plus three
manual fixes surfaced 27 roles to refactor. Each role keeps
sys-stk-full ungated so worker-side prereqs (sys-svc-container, NFS
client) still bootstrap; the trailing manager-only block either moves
into a new tasks/02_manager_ops.yml included with
when: IS_STACK_HOST | bool (SUB_FILE pattern) or gets the gate
inlined on the one or two trailing tasks (INLINE pattern). In
compose mode IS_STACK_HOST stays true so single-host deploys are
unchanged.

Manual catches the workflow agents missed:
- web-app-fediwall: utils/once/flag.yml lives AT THE END of 01_core,
  not at the start; preserved verbatim outside the gate so workers
  still flip the run_once_<role> fact.
- web-app-xwiki: a pre-existing block(...) wraps sys-stk-full plus
  _flush_config.yml; gated only the _flush_config.yml include so
  sys-stk-full keeps running on workers.
- web-app-dashboard: 02_javascript.yml (creates /opt/compose/dashboard/
  JS dirs) ran BEFORE sys-stk-full and was ungated; added the gate.

Roles touched (INLINE): akaunting, jenkins, magento, mobilizon
Roles touched (SUB_FILE): baserow, dashboard, decidim, erpnext,
  fediwall, friendica, gitea, jitsi, keycloak, listmonk, littlejs,
  mailu, matomo, moodle, odoo, openproject, opentalk, pixelfed,
  shopware, suitecrm, taiga, xwiki, zammad
2026-06-05 12:18:32 +02:00
kevinveenbirkenbachandClaude Opus 4.7 5adf3011fe refactor(swarm-nfs): generalize swarm+NFS chain across all roles
Migrate the mediawiki-specific swarm patterns into shared sys-svc-compose
helpers so every role inherits swarm + NFS portability:

- meta/volumes.yml: dict schema with mandatory name|path + optional nfs
  block (uid/gid/mode); 47 roles migrated, 48 lookup consumers updated to
  volumes.<key>.{name,path}
- sys-svc-compose helpers: finalize.yml (flush + post_deploy),
  post_deploy.yml (swarm-resolve + DB-healthy-wait), nfs_prep.yml
  (pre-create NFS subdirs), swarm/resolve_self.yml (canonical
  <ENTITY>_CONTAINER fact), render_replicated_templates.yml +
  _render_one_template.yml (template render + swarm-peer replication,
  per-entry notify, fileglob support)
- 15 roles migrated to render_replicated_templates.yml helper
  (mediawiki, prometheus, kix, friendica, dashboard, nextcloud,
  suitecrm, matrix, flowise, openldap)
- 5 new lint tests pin the architecture: volumes schema, compose chain,
  volume-constants-are-lookups, template-render-centralization,
  meta-no-role-var-refs
- ROLE_FILE_TEMPL_COMPOSE SPOT constant + 7 tests migrated to use it
- Handler test scanner extended to detect nested notify in
  vars: role_templates: lists

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 18:57:35 +02:00
kevinveenbirkenbachandClaude Opus 4.7 19254a23b4 refactor(sys-svc-compose): rename docker_compose_flush_handlers → compose_handlers_flush
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>
2026-05-18 13:43:43 +02:00
kevinveenbirkenbachandClaude Opus 4.7 2a023ca6a1 chore(lint): autoformat strict-failure semantics + claude.sh rename
* scripts/lint/autoformat.sh:
  - rename run_sort_claude_settings worker to run_claude (matches the
    renamed scripts/lint/claude.sh).
  - drop the EXIT trap that wiped status_dir; cleanup happens explicitly
    after `wait` returns, so a parallel worker that interrupts the
    parent never races a vanished status dir.
  - each worker now captures its tool's exit code and writes one of
    OK / SKIP / FAIL. Tolerated `expected violations remain`-style exits
    (markdownlint-cli2 rc≤1, ansible-lint rc≤2) still count as OK; only
    real failures count as FAIL.
  - all workers run to completion regardless of individual tool failures
    (so the user always sees the full picture); after the summary, the
    script exits 1 if any worker reported FAIL.
* scripts/lint/sort_claude_settings.sh -> scripts/lint/claude.sh
  (history preserved via git mv); update test_claude_settings_sorted.py
  fix-hint accordingly.

Plus working-tree drift across roles/, group_vars/, inventories/,
requirements/, and .pre-commit-config.yaml 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>
2026-05-09 01:30:49 +02:00
kevinveenbirkenbach 5ad0ecfd99 refactor(compose): switch build calls to compose wrapper
Replace all `docker compose build` invocations with the `compose` wrapper
to ensure automatic env-file detection and override handling. Update
handlers, notify targets, direct commands, and log labels consistently
across roles.

https://chatgpt.com/share/69866b76-ce6c-800f-ac9e-dba6bba27008
2026-02-06 23:30:04 +01:00
kevinveenbirkenbach 0a7b4efa22 Replaced docker compose pull by compose pull 2026-02-06 23:26:20 +01:00
kevinveenbirkenbach 8a81630391 Added newline at file and POSIX compatible 2026-02-04 19:49:56 +01:00
kevinveenbirkenbach 989c0c0fa2 refactor(stacks): merge stateful/stateless backend+full stacks into sys-stk-backend/sys-stk-full
- Replace sys-stk-back-stateful + sys-stk-back-stateless with unified sys-stk-backend role
- Replace sys-stk-full-stateful + sys-stk-full-stateless with unified sys-stk-full role
- Add reusable task helpers: _load_service.yml (optional deps) and 01_database.yml (DB bootstrap)
- Switch optional dependency loading (OpenLDAP, Ollama) to service_load_enabled (enabled && shared) + run_once guard
- Remove legacy OLLAMA_LOCAL_ENABLED flag; rely on docker.services.ollama.enabled/shared config
- Update all affected app/service roles and web-app template to use new stack role names

https://chatgpt.com/share/69685fb2-cb1c-800f-a9e8-2f5f765a1a53
2026-01-15 04:31:54 +01:00
kevinveenbirkenbach 4f48cf633b Add dynamic PHP error handling config for SuiteCRM:
- Introduce env-aware php.ini.j2 template
- Mount PHP config into container via docker-compose
- Add SUITECRM_PHP_CONFIG_HOST and SUITECRM_WELL_SOFTWARE_NAME variables
- Refactor LDAP extension path using normalized software name

https://chatgpt.com/share/69284194-cf6c-800f-b138-eb6ddf25592c
2025-11-27 13:18:50 +01:00
kevinveenbirkenbach d7e0123af2 SuiteCRM LDAP integration fixes:
- Correct LDAP attribute FIRSTNAME changed to givenName
- Add automated ldap.yaml rendering and bind mount
- Create LDAP extension directory inside container
- Adjust docker-compose.yml for proper LDAP config mount
- Add strict formatting template for ldap.yaml
- Update paths for SUITECRM_LDAP_CONFIG_HOST and SUITECRM_LDAP_EXTENSION_DIR

Details: https://chatgpt.com/share/6928375d-1458-800f-bcc3-b3364ea69078
2025-11-27 12:35:07 +01:00
kevinveenbirkenbach 4b2c7eef88 Finished SuiteCRM install mask - See https://chatgpt.com/share/69274924-9dec-800f-8653-345aa2c25664 2025-11-26 22:21:20 +01:00
kevinveenbirkenbach e6cb0cbed9 Added suitecrm draft - See https://chatgpt.com/share/69274924-9dec-800f-8653-345aa2c25664 2025-11-26 19:38:37 +01:00