fix: installer postflight is honest about a partial/failed install #239

Merged
jmiller merged 2 commits from fix/installer-honesty into dev 2026-07-06 15:50:35 +00:00
Owner

The package postflight could print "MokoSuiteBackup installed successfully!" + next-steps even when the install actually failed or only partially completed — because Joomla logs a failed child extension but still runs the package postflight.

Fix (package installer — source/script.php, type="package")

Before the success/next-steps messages, the postflight now gates on two dynamically-derived checks:

  1. Bundled children registered — read the package manifest via $parent->getParent()->getManifest(), iterate <files><file>, and confirm each declared child exists in #__extensions, matched by element (= file id) + type, and for plugins also folder (= file group) since plugins are only unique that way.
  2. Component schema installed — the component has no installer script of its own, so this is the only place to catch a component whose extension row exists but whose CREATE TABLEs failed (exactly the class of bug the MySQL-8 PREPARE migration caused). Parse the installed administrator/components/com_mokosuitebackup/sql/install.mysql.sql with preg_match_all('/CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[\"']?(#[A-Za-z0-9_]+)/i', …), swap #for$db->getPrefix(), and verify each is in $db->getTableList()` (case-insensitive).

If anything is missing → enqueue an error listing it and RETURN before the success message.

Safety

  • Fail-open: the whole check is wrapped in try/catch; any manifest/DB/IO glitch returns "nothing missing" so a transient error never turns a good install into a false failure.
  • Only the success path is gated — unconditional housekeeping (download-key restore, menu/submenu fixes, orphan cleanup, legacy-column drop) runs regardless (it's all above the gate).
  • Extension names interpolated into HTML are escaped with htmlspecialchars.
  • No hard-coded table/child lists — everything is derived from the manifest / install SQL.
  • Modules & plugins otherwise unchanged (Joomla installs them atomically and reports their own pass/fail).

Verify

  • php -l clean.

https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i

The package postflight could print **"MokoSuiteBackup installed successfully!"** + next-steps even when the install actually failed or only partially completed — because Joomla logs a failed child extension but still runs the package postflight. ## Fix (package installer — `source/script.php`, `type="package"`) Before the success/next-steps messages, the postflight now gates on two dynamically-derived checks: 1. **Bundled children registered** — read the package manifest via `$parent->getParent()->getManifest()`, iterate `<files><file>`, and confirm each declared child exists in `#__extensions`, matched by `element` (= file `id`) + `type`, and for plugins also `folder` (= file `group`) since plugins are only unique that way. 2. **Component schema installed** — the component has no installer script of its own, so this is the only place to catch a component whose extension row exists but whose `CREATE TABLE`s failed (exactly the class of bug the MySQL-8 PREPARE migration caused). Parse the installed `administrator/components/com_mokosuitebackup/sql/install.mysql.sql` with `preg_match_all('/CREATE\s+TABLE\s+(?:IF\s+NOT\s+EXISTS\s+)?[\`"\']?(#__[A-Za-z0-9_]+)/i', …)`, swap `#__` for `$db->getPrefix()`, and verify each is in `$db->getTableList()` (case-insensitive). If anything is missing → enqueue an **error** listing it and **RETURN before the success message**. ## Safety - **Fail-open:** the whole check is wrapped in try/catch; any manifest/DB/IO glitch returns "nothing missing" so a transient error never turns a good install into a false failure. - Only the **success path** is gated — unconditional housekeeping (download-key restore, menu/submenu fixes, orphan cleanup, legacy-column drop) runs regardless (it's all above the gate). - Extension names interpolated into HTML are escaped with `htmlspecialchars`. - No hard-coded table/child lists — everything is derived from the manifest / install SQL. - Modules & plugins otherwise unchanged (Joomla installs them atomically and reports their own pass/fail). ## Verify - `php -l` clean. https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
jmiller added 1 commit 2026-07-06 15:50:16 +00:00
fix: installer postflight honest about partial/failed install
Universal: PR Check / Require Docs Update (pull_request) Has been skipped
Universal: PR Check / Wiki Update Reminder (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Secret Scan (pull_request) Successful in 8s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 37s
Generic: Project CI / Lint & Validate (pull_request) Successful in 33s
Universal: PR Check / Validate PR (pull_request) Failing after 28s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 32s
Generic: Project CI / Tests (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
6b4f98869a
Joomla logs a failed child extension but still runs the package
postflight, so the 'installed successfully' message could appear on a
broken install. In postflight, before the success/next-steps messages:

- verify every bundled child declared in the package manifest actually
  registered in #__extensions (match element+type, and folder=group for
  plugins), and
- verify the component's schema tables (parsed dynamically from the
  installed install.mysql.sql) exist in the DB.

If anything is missing, enqueue an error listing it and RETURN before the
success message. Fail-open (try/catch -> nothing missing) so a transient
manifest/DB/IO glitch can't fail a good install. Unconditional
housekeeping (download-key restore, menu fixes) still runs regardless.
Names escaped with htmlspecialchars; table/child lists derived
dynamically, never hard-coded.

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
jmiller added 1 commit 2026-07-06 15:50:21 +00:00
chore(version): pre-release bump to 02.58.15-dev [skip ci]
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
38160e9ae9
jmiller merged commit 8a4c4efaad into dev 2026-07-06 15:50:35 +00:00
jmiller deleted branch fix/installer-honesty 2026-07-06 15:50:36 +00:00
Sign in to join this conversation.