feat(web-app-joomla): reliable first-run install, safe debug toggler, DB patching, LDAP scaffolding

Why
- Fix flaky first-run installs and make config edits idempotent.
- Prepare LDAP support and allow optional inline CSP for UI.
- Improve observability and guard against broken configuration.php.

What
- config/main.yml: enable features.ldap; add CSP flags (allow inline style/script elem); minor spacing.
- tasks/: split into 01_install (wait for core, absolute CLI path), 02_debug (toggle $debug/$error_reporting safely), 03_patch (patch DB creds in configuration.php), 04_ldap (configure plugin via helper), 05_assert (optional php -l).
- templates/Dockerfile.j2: conditionally install/compile php-ldap (fallback to docker-php-ext-install with libsasl2-dev).
- templates/cli-ldap.php.j2: idempotently enable & configure Authentication - LDAP from env.
- templates/docker-compose.yml.j2: build custom image when LDAP is enabled; mount cli-ldap.php; pull_policy: never.
- templates/env.j2: add site/admin vars, MariaDB connector/env, full LDAP env.
- vars/main.yml: default to MariaDB (mysqli), add JOOMLA_* vars incl. JOOMLA_CONFIG_FILE.

Notes
- LDAP path implemented but NOT yet tested end-to-end.
- Ref: https://chatgpt.com/share/68b068a8-2aa4-800f-8cd1-56383561a9a8.
This commit is contained in:
2025-08-28 16:33:45 +02:00
parent dece6228a4
commit 18f3b1042f
12 changed files with 351 additions and 16 deletions

View File

@@ -0,0 +1,53 @@
# Wait until the Joomla core is copied into the volume
- name: "Wait for Joomla files to exist"
command:
argv: [ docker, exec, "{{ JOOMLA_CONTAINER }}", test, -f, /var/www/html/index.php ]
register: joomla_files
changed_when: false
retries: 60
delay: 2
until: joomla_files.rc == 0
# (Optional) specifically wait for the CLI installer script
- name: "Check for CLI installer"
command:
argv: [ docker, exec, "{{ JOOMLA_CONTAINER }}", test, -f, /var/www/html/installation/joomla.php ]
register: has_installer
changed_when: false
failed_when: false
# Only if not already installed (no configuration.php)
- name: "Check if Joomla is already installed"
command:
argv: [ docker, exec, "{{ JOOMLA_CONTAINER }}", test, -f, "{{ JOOMLA_CONFIG_FILE }}" ]
register: joomla_installed
changed_when: false
failed_when: false
# Install (uses absolute path + argv)
- name: "Joomla CLI install (first run only)"
command:
argv:
- docker
- exec
- "{{ JOOMLA_CONTAINER }}"
- php
- /var/www/html/installation/joomla.php
- install
- "--db-type={{ JOOMLA_DB_CONNECTOR }}"
- "--db-host={{ database_host }}"
- "--db-user={{ database_username }}"
- "--db-pass={{ database_password }}"
- "--db-name={{ database_name }}"
- "--admin-user={{ JOOMLA_USER }}"
- "--admin-username={{ JOOMLA_USER_NAME }}"
- "--admin-password={{ JOOMLA_USER_PASSWORD }}"
- "--admin-email={{ JOOMLA_USER_EMAIL }}"
- "--no-interaction"
- "--site-name={{ JOOMLA_SITE_NAME }}"
register: j_install
changed_when: j_install.rc == 0
failed_when: j_install.rc != 0
when:
- joomla_installed.rc != 0
- has_installer.rc == 0

View File

@@ -0,0 +1,45 @@
- name: "Toggle Joomla debug flags safely (configuration.php)"
command:
argv:
- docker
- exec
- -e
- "J_MODE_DEBUG={{ MODE_DEBUG | default(false) | bool | ternary('1','0') }}"
- -e
- "J_ERR_LEVEL={{ MODE_DEBUG | default(false) | bool | ternary('maximum','default') }}"
- "{{ JOOMLA_CONTAINER }}"
- php
- -r
- |
$f = '{{ JOOMLA_CONFIG_FILE }}';
if (!file_exists($f)) { fwrite(STDERR, "configuration.php missing\n"); exit(1); }
$c = file_get_contents($f);
$changed = 0;
$debug = getenv('J_MODE_DEBUG') === '1';
$err = getenv('J_ERR_LEVEL') ?: 'default';
// Clean up previously broken lines
$c = preg_replace('/^\s*public\s+1\s*=.*?;$/m', '', $c, -1, $nBad1); $changed += $nBad1;
$c = preg_replace('/^\s*public\s*=\s*maximum;$/m', '', $c, -1, $nBad2); $changed += $nBad2;
// Ensure: public $debug = true|false;
$lineDebug = "public \$debug = " . ($debug ? 'true' : 'false') . ";";
if (preg_match('/public\s*\$debug\s*=\s*[^;]*;/', $c)) {
$c = preg_replace('/public\s*\$debug\s*=\s*[^;]*;/', $lineDebug, $c, 1, $n); $changed += $n;
} else {
$c = preg_replace("/\n\}\s*$/", "\n\t".$lineDebug."\n}\n", $c, 1, $n); $changed += $n;
}
// Ensure: public $error_reporting = 'maximum'|'default';
$lineErr = "public \$error_reporting = '" . str_replace("'", "\\'", $err) . "';";
if (preg_match('/public\s*\$error_reporting\s*=\s*[^;]*;/', $c)) {
$c = preg_replace('/public\s*\$error_reporting\s*=\s*[^;]*;/', $lineErr, $c, 1, $n); $changed += $n;
} else {
$c = preg_replace("/\n\}\s*$/", "\n\t".$lineErr."\n}\n", $c, 1, $n); $changed += $n;
}
if ($changed) { file_put_contents($f, $c); echo "changed"; } else { echo "ok"; }
register: j_cfg_debug
changed_when: (j_cfg_debug.stdout | trim) == "changed"
failed_when: j_cfg_debug.rc != 0

View File

@@ -0,0 +1,52 @@
- name: "Ensure configuration.php DB settings match inventory"
command:
argv:
- docker
- exec
- -e
- J_DBTYPE={{ JOOMLA_DB_CONNECTOR }}
- -e
- J_DBHOST={{ database_host }}:{{ database_port }}
- -e
- J_DBUSER={{ database_username }}
- -e
- J_DBPASS={{ database_password }}
- -e
- J_DBNAME={{ database_name }}
- "{{ JOOMLA_CONTAINER }}"
- php
- -r
- |
$f = '{{ JOOMLA_CONFIG_FILE }}';
if (!file_exists($f)) { exit(0); }
$c = file_get_contents($f);
$changed = 0;
$map = [
'dbtype' => getenv('J_DBTYPE'),
'host' => getenv('J_DBHOST'),
'user' => getenv('J_DBUSER'),
'password' => getenv('J_DBPASS'),
'db' => getenv('J_DBNAME'),
];
foreach ($map as $k => $v) {
// Escape single quotes for safe embedding into the PHP source string
$vEsc = str_replace("'", "\\'", $v);
// Match current value in config: public $key = '...';
if (preg_match("/public \\$".$k."\\s*=\\s*'([^']*)';/", $c, $m) && $m[1] !== $v) {
$c = preg_replace(
"/public \\$".$k."\\s*=\\s*'[^']*';/",
"public $".$k." = '".$vEsc."';",
$c
);
$changed = 1;
}
}
if ($changed) { file_put_contents($f, $c); echo "changed"; } else { echo "ok"; }
register: cfg_patch
changed_when: cfg_patch.stdout == "changed"
failed_when: cfg_patch.rc != 0
when: joomla_installed.rc == 0

View File

@@ -0,0 +1,9 @@
- name: "Configure LDAP plugin params via helper"
command: >
docker exec {{ JOOMLA_CONTAINER }}
php cli/cli-ldap.php
register: ldap_conf
changed_when: "'configured' in ldap_conf.stdout | lower"
async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}"
poll: "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}"
when: JOOMLA_LDAP_ENABLED | bool

View File

@@ -0,0 +1,5 @@
- name: "PHP lint configuration.php"
command:
argv: [ docker, exec, "{{ JOOMLA_CONTAINER }}", php, "-l", "{{ JOOMLA_CONFIG_FILE }}" ]
changed_when: false
when: MODE_ASSERT | bool

View File

@@ -1,7 +1,34 @@
---
- name: "load docker, db and proxy for {{ application_id }}"
include_role:
name: cmp-db-docker-proxy
loop: "{{ domains }}"
- name: "Include role srv-domain-provision for {{ application_id }}"
include_role:
name: srv-domain-provision
loop: "{{ JOOMLA_DOMAINS }}"
loop_control:
loop_var: domain
loop_var: domain
vars:
http_port: "{{ ports.localhost.http[application_id] }}"
- name: "load docker and db for {{ application_id }}"
include_role:
name: cmp-db-docker
vars:
docker_compose_flush_handlers: false
- name: "Render LDAP CLI helper"
template:
src: cli-ldap.php.j2
dest: "{{ JOOMLA_LDAP_CONF_FILE }}"
mode: "0644"
when: JOOMLA_LDAP_ENABLED | bool
- name: "flush docker compose handlers"
meta: flush_handlers
- name: Include install routines
include_tasks: "{{ item }}"
loop:
- 01_install.yml
- 02_debug.yml
- 03_patch.yml
- 04_ldap.yml
- 05_assert.yml