10 Commits

Author SHA1 Message Date
jmiller d0179fd6e3 refactor(installer): replace hardcoded extension list with dynamic manifest verification
Universal: PR Check / Branch Policy (pull_request) Successful in 3s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: PR Check / Secret Scan (pull_request) Successful in 8s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 48s
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Read package manifest via $adapter->getParent()->getManifest() to discover
children dynamically instead of maintaining a hardcoded EXPECTED_EXTENSIONS
array. Fail-open pattern wraps verification in try/catch. Only the success
path (plugin enables, license warning) is gated; unconditional housekeeping
(DLID restore) runs regardless.

Claude-Session: https://claude.ai/code/session_01CwLGvFJPjoPTp9BEnSjtJf
2026-07-06 10:45:42 -05:00
jmiller 5544a0812f feat: add post-install verification to detect failed sub-extensions
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Universal: PR Check / Secret Scan (pull_request) Successful in 9s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 12s
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Checks that all expected component and plugins registered in
#__extensions after install. Disables the package entry and shows
an error if any sub-extension failed to install.

Claude-Session: https://claude.ai/code/session_01CwLGvFJPjoPTp9BEnSjtJf
2026-07-05 22:32:14 -05:00
gitea-actions[bot] 17e02657a4 chore(version): pre-release bump to 01.00.37-dev [skip ci] 2026-07-05 22:55:07 +00:00
gitea-actions[bot] d038adb9c3 chore(version): auto-bump patch 01.00.36-dev [skip ci] 2026-07-05 22:54:47 +00:00
jmiller 7f6d80d20a Merge chore/normalize-docs: normalize doc/config refs
Universal: Auto Version Bump / Version Bump (push) Successful in 17s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 21s
2026-07-05 22:50:46 +00:00
jmiller d7421b3368 chore(docs): normalize refs — MokoStandards->org wiki, moko-platform->mokocli, residual MokoGitea/MokoSuite
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 20s
Non-workflow docs/config only (CLAUDE.md, README, branch-protection, ISSUE_TEMPLATE/config.yml,
ci-issue-reporter). Workflow files, manifest schema, and Makefile install paths left for the
forge-rebrand gate. Authored-by: Moko Consulting
2026-07-05 22:50:44 +00:00
gitea-actions[bot] a4ed8cf7a0 chore(version): pre-release bump to 01.00.35-dev [skip ci] 2026-06-28 18:57:53 +00:00
gitea-actions[bot] f07e7fe17f chore(version): auto-bump patch 01.00.34-dev [skip ci] 2026-06-28 18:57:41 +00:00
jmiller 9be313464a feat: add license key warning on install/update
Universal: Auto Version Bump / Version Bump (push) Successful in 9s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 17s
2026-06-28 18:57:26 +00:00
gitea-actions[bot] e75bcc5b1c chore(version): pre-release bump to 01.00.33-dev [skip ci] 2026-06-28 08:07:07 +00:00
15 changed files with 240 additions and 22 deletions
+1 -1
View File
@@ -8,7 +8,7 @@ contact_links:
url: https://mokoconsulting.tech/ url: https://mokoconsulting.tech/
about: Get help or ask questions through our website about: Get help or ask questions through our website
- name: 📚 MokoStandards Documentation - name: 📚 MokoStandards Documentation
url: https://git.mokoconsulting.tech/MokoConsulting/moko-platform url: https://git.mokoconsulting.tech/MokoConsulting/.mokogitea/wiki
about: View our coding standards and best practices about: View our coding standards and best practices
- name: 🔒 Report a Security Vulnerability - name: 🔒 Report a Security Vulnerability
url: https://git.mokoconsulting.tech/mokoconsulting-tech/.github-private/security/advisories/new url: https://git.mokoconsulting.tech/mokoconsulting-tech/.github-private/security/advisories/new
+1 -1
View File
@@ -37,7 +37,7 @@ If you have ideas about how this could be implemented, share them here:
Add any other context, mockups, or screenshots about the feature request here. Add any other context, mockups, or screenshots about the feature request here.
## Relevant Standards ## Relevant Standards
Does this relate to any standards in [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/MokoStandards)? Does this relate to any standards in [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/.mokogitea/wiki)?
- [ ] Accessibility (WCAG 2.1 AA) - [ ] Accessibility (WCAG 2.1 AA)
- [ ] Localization (en_US/en_GB) - [ ] Localization (en_US/en_GB)
- [ ] Security best practices - [ ] Security best practices
+1 -1
View File
@@ -35,7 +35,7 @@ Use this template only for:
<!-- Describe how this could be addressed --> <!-- Describe how this could be addressed -->
## Standards Reference ## Standards Reference
Does this relate to security standards in [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/MokoStandards)? Does this relate to security standards in [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/.mokogitea/wiki)?
- [ ] SPDX license identifiers - [ ] SPDX license identifiers
- [ ] Secret management - [ ] Secret management
- [ ] Dependency security - [ ] Dependency security
+1 -1
View File
@@ -5,7 +5,7 @@
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Automation # INGROUP: mokocli.Automation
# VERSION: 01.00.32 # VERSION: 01.00.37
# BRIEF: Auto-create feature branch when an issue is opened # BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch" name: "Universal: Issue Branch"
+2 -2
View File
@@ -46,8 +46,8 @@ Joomla **package** (`pkg_mokosuiteauto`) — Layer 4 add-on extending MokoSuiteP
- **Never commit** `.claude/`, `.mcp.json`, `TODO.md`, `*.min.css`/`*.min.js` - **Never commit** `.claude/`, `.mcp.json`, `TODO.md`, `*.min.css`/`*.min.js`
- **Attribution**: `Authored-by: Moko Consulting` - **Attribution**: `Authored-by: Moko Consulting`
- **Workflow directory**: `.mokogitea/` - **Workflow directory**: `.mokogitea/`
- **Wiki**: documentation lives in the Gitea wiki, not `docs/` files - **Wiki**: documentation lives in the MokoGitea wiki, not `docs/` files
- **Standards**: [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/MokoCLI/wiki) - **Standards**: [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/.mokogitea/wiki)
- **Changelog**: `[Unreleased]` only — release system assigns versions - **Changelog**: `[Unreleased]` only — release system assigns versions
- **No upstream references**: never reference competitor products - **No upstream references**: never reference competitor products
+1 -1
View File
@@ -14,7 +14,7 @@
DEFGROUP: Template-Joomla DEFGROUP: Template-Joomla
INGROUP: Template-Joomla.Documentation INGROUP: Template-Joomla.Documentation
REPO: https://github.com/mokoconsulting-tech/Template-Joomla/ REPO: https://github.com/mokoconsulting-tech/Template-Joomla/
VERSION: 01.00.32 VERSION: 01.00.37
PATH: ./CODE_OF_CONDUCT.md PATH: ./CODE_OF_CONDUCT.md
BRIEF: Community expectations and enforcement guidelines BRIEF: Community expectations and enforcement guidelines
NOTE: Adapted with attribution from the Contributor Covenant v2.1 NOTE: Adapted with attribution from the Contributor Covenant v2.1
+1 -1
View File
@@ -34,7 +34,7 @@ feature/* ──PR──> dev ──draft PR──> (renamed to rc) ──merge
7. **Merging to main** triggers the stable release pipeline: 7. **Merging to main** triggers the stable release pipeline:
- Minor version bump (e.g., `02.09.xx` → `02.10.00`) - Minor version bump (e.g., `02.09.xx` → `02.10.00`)
- Stability suffix stripped (clean version) - Stability suffix stripped (clean version)
- Gitea release created with ZIP/tar.gz packages - MokoGitea release created with ZIP/tar.gz packages
- `updates.xml` updated (Joomla extensions) - `updates.xml` updated (Joomla extensions)
- `dev` branch recreated from `main` - `dev` branch recreated from `main`
+3 -3
View File
@@ -19,12 +19,12 @@
DEFGROUP: mokoconsulting-tech.Template-Joomla DEFGROUP: mokoconsulting-tech.Template-Joomla
INGROUP: MokoStandards.Governance INGROUP: MokoStandards.Governance
REPO: https://github.com/mokoconsulting-tech/Template-Joomla REPO: https://github.com/mokoconsulting-tech/Template-Joomla
VERSION: 01.00.32 VERSION: 01.00.37
PATH: /GOVERNANCE.md PATH: /GOVERNANCE.md
BRIEF: Project governance rules, roles, and decision process for Template-Joomla BRIEF: Project governance rules, roles, and decision process for Template-Joomla
--> -->
[![MokoStandards](https://img.shields.io/badge/MokoStandards-04.00.04-blue)](https://github.com/mokoconsulting-tech/MokoStandards) [![MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/.mokogitea/wiki)](https://github.com/mokoconsulting-tech/MokoStandards)
# Project Governance # Project Governance
@@ -32,7 +32,7 @@
This document defines the governance model for the `Template-Joomla` repository within the This document defines the governance model for the `Template-Joomla` repository within the
`mokoconsulting-tech` organization. It is automatically maintained by `mokoconsulting-tech` organization. It is automatically maintained by
[MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards) v04.00.04. [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/.mokogitea/wiki) v04.00.04.
Full governance policy is defined in the MokoStandards source repository: Full governance policy is defined in the MokoStandards source repository:
[docs/policy/GOVERNANCE.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/GOVERNANCE.md) [docs/policy/GOVERNANCE.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/GOVERNANCE.md)
+1 -1
View File
@@ -23,7 +23,7 @@ DEFGROUP: Template-Joomla
INGROUP: Template-Joomla.Documentation INGROUP: Template-Joomla.Documentation
REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla
PATH: /SECURITY.md PATH: /SECURITY.md
VERSION: 01.00.32 VERSION: 01.00.37
BRIEF: Security vulnerability reporting and handling policy BRIEF: Security vulnerability reporting and handling policy
--> -->
+6 -6
View File
@@ -6,11 +6,11 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Automation.CI # DEFGROUP: Automation.CI
# INGROUP: moko-platform.Automation # INGROUP: mokocli.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /automation/ci-issue-reporter.sh # PATH: /automation/ci-issue-reporter.sh
# VERSION: 09.23.00 # VERSION: 09.23.00
# BRIEF: Creates or updates a Gitea issue when a CI gate fails. # BRIEF: Creates or updates a MokoGitea issue when a CI gate fails.
# Deduplicates by searching open issues with the "ci-auto" label # Deduplicates by searching open issues with the "ci-auto" label
# whose title matches the gate. If a matching issue exists, a comment # whose title matches the gate. If a matching issue exists, a comment
# is appended instead of opening a duplicate. # is appended instead of opening a duplicate.
@@ -45,8 +45,8 @@ Optional:
--workflow Workflow name for the issue title --workflow Workflow name for the issue title
--repo owner/repo (default: \$GITHUB_REPOSITORY) --repo owner/repo (default: \$GITHUB_REPOSITORY)
--run-url URL to the CI run (auto-detected from env) --run-url URL to the CI run (auto-detected from env)
--token Gitea API token (default: \$GITEA_TOKEN) --token MokoGitea API token (default: \$GITEA_TOKEN)
--url Gitea base URL (default: \$GITEA_URL) --url MokoGitea base URL (default: \$GITEA_URL)
EOF EOF
exit 1 exit 1
} }
@@ -213,7 +213,7 @@ print(json.dumps({
ISSUE_NUM=$(echo "$RESPONSE" | grep -oP '"number":\s*\K[0-9]+' | head -1) ISSUE_NUM=$(echo "$RESPONSE" | grep -oP '"number":\s*\K[0-9]+' | head -1)
if [[ -n "$ISSUE_NUM" ]]; then if [[ -n "$ISSUE_NUM" ]]; then
# Apply label (separate call — more reliable across Gitea versions) # Apply label (separate call — more reliable across MokoGitea versions)
LABEL_ID=$(curl -sf \ LABEL_ID=$(curl -sf \
-H "Authorization: token ${GITEA_TOKEN}" \ -H "Authorization: token ${GITEA_TOKEN}" \
"${API}/labels" 2>/dev/null \ "${API}/labels" 2>/dev/null \
@@ -5,7 +5,7 @@
<creationDate>2026-06-23</creationDate> <creationDate>2026-06-23</creationDate>
<copyright>Copyright (C) 2026 Moko Consulting.</copyright> <copyright>Copyright (C) 2026 Moko Consulting.</copyright>
<license>GPL-3.0-or-later</license> <license>GPL-3.0-or-later</license>
<version>01.00.32</version> <version>01.00.37</version>
<namespace path="src">Moko\Component\MokoSuiteAuto</namespace> <namespace path="src">Moko\Component\MokoSuiteAuto</namespace>
<administration> <administration>
<files folder="admin"><folder>services</folder><folder>src</folder><folder>tmpl</folder></files> <files folder="admin"><folder>services</folder><folder>src</folder><folder>tmpl</folder></files>
@@ -8,7 +8,7 @@
<license>GPL-3.0-or-later</license> <license>GPL-3.0-or-later</license>
<authorEmail>hello@mokoconsulting.tech</authorEmail> <authorEmail>hello@mokoconsulting.tech</authorEmail>
<authorUrl>https://mokoconsulting.tech</authorUrl> <authorUrl>https://mokoconsulting.tech</authorUrl>
<version>01.00.32</version> <version>01.00.37</version>
<php_minimum>8.3</php_minimum> <php_minimum>8.3</php_minimum>
<description>PLG_SYSTEM_MOKOSUITEAUTO_DESC</description> <description>PLG_SYSTEM_MOKOSUITEAUTO_DESC</description>
<namespace path="src">Moko\Plugin\System\MokoSuiteAuto</namespace> <namespace path="src">Moko\Plugin\System\MokoSuiteAuto</namespace>
@@ -3,7 +3,7 @@
<name>Web Services - MokoSuite Auto</name> <name>Web Services - MokoSuite Auto</name>
<element>mokosuiteauto</element> <element>mokosuiteauto</element>
<author>Moko Consulting</author> <author>Moko Consulting</author>
<version>01.00.32</version> <version>01.00.37</version>
<license>GPL-3.0-or-later</license> <license>GPL-3.0-or-later</license>
<namespace path="src">Moko\Plugin\WebServices\MokoSuiteAuto</namespace> <namespace path="src">Moko\Plugin\WebServices\MokoSuiteAuto</namespace>
<files><folder>src</folder><folder>services</folder></files> <files><folder>src</folder><folder>services</folder></files>
+1 -1
View File
@@ -2,7 +2,7 @@
<extension type="package" method="upgrade"> <extension type="package" method="upgrade">
<name>Package - MokoSuite Auto</name> <name>Package - MokoSuite Auto</name>
<packagename>mokosuiteauto</packagename> <packagename>mokosuiteauto</packagename>
<version>01.00.32</version> <version>01.00.37</version>
<creationDate>2026-06-21</creationDate> <creationDate>2026-06-21</creationDate>
<author>Moko Consulting</author> <author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail> <authorEmail>hello@mokoconsulting.tech</authorEmail>
+218
View File
@@ -0,0 +1,218 @@
<?php
/**
* MokoSuite Auto Package Install Script
*
* @package MokoSuite Auto
* @copyright Copyright (C) 2026 Moko Consulting. All rights reserved.
* @license GPL-3.0-or-later
*/
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Installer\InstallerAdapter;
use Joomla\CMS\Log\Log;
use Joomla\Database\DatabaseInterface;
class Pkg_mokosuiteautoInstallerScript
{
private string $savedDlid = '';
public function preflight(string $type, InstallerAdapter $adapter): bool
{
if ($type === 'update')
{
$this->savedDlid = $this->getExistingDlid();
}
return true;
}
public function postflight(string $type, InstallerAdapter $adapter): void
{
if ($this->savedDlid)
{
$this->restoreDlid($this->savedDlid);
}
if ($type === 'install')
{
$missing = $this->verifyChildren($adapter);
if (!empty($missing))
{
Factory::getApplication()->enqueueMessage(
'<strong>MokoSuite Auto — Incomplete Installation</strong><br>'
. 'The following extensions failed to install: '
. implode(', ', $missing)
. '.<br>Please check file permissions and server logs, then reinstall the package.',
'error'
);
return;
}
$this->enablePlugin('system', 'mokosuiteauto');
$this->enablePlugin('webservices', 'mokosuiteauto');
}
$dlid = $this->getExistingDlid();
if (empty($dlid))
{
$app = Factory::getApplication();
try
{
$db = Factory::getContainer()->get(DatabaseInterface::class);
$db->setQuery(
$db->getQuery(true)
->select($db->quoteName('update_site_id'))
->from($db->quoteName('#__update_sites'))
->where($db->quoteName('name') . ' LIKE ' . $db->quote('%MokoSuite Auto%'))
->setLimit(1)
);
$siteId = (int) $db->loadResult();
$editUrl = $siteId
? 'index.php?option=com_installer&task=updatesite.edit&update_site_id=' . $siteId
: 'index.php?option=com_installer&view=updatesites';
}
catch (\Throwable $e)
{
$editUrl = 'index.php?option=com_installer&view=updatesites';
}
$app->enqueueMessage(
'<strong>Moko Consulting License Key Required</strong> — '
. 'No download key is configured. Updates will not be available until a valid license key is entered. '
. '<a href="' . $editUrl . '" class="btn btn-sm btn-warning ms-2">Enter License Key</a>',
'warning'
);
}
}
private function verifyChildren(InstallerAdapter $adapter): array
{
try
{
$manifest = $adapter->getParent()->getManifest();
$files = $manifest->files->file ?? [];
if (!$files)
{
return [];
}
$db = Factory::getContainer()->get(DatabaseInterface::class);
$missing = [];
foreach ($files as $file)
{
$type = (string) ($file['type'] ?? '');
$id = (string) ($file['id'] ?? '');
$group = (string) ($file['group'] ?? '');
if ($type === '' || $id === '')
{
continue;
}
$element = ($type === 'plugin' && $group !== '')
? preg_replace('/^plg_' . preg_quote($group, '/') . '_/', '', $id)
: $id;
$query = $db->getQuery(true)
->select('COUNT(*)')
->from($db->quoteName('#__extensions'))
->where($db->quoteName('element') . ' = ' . $db->quote($element))
->where($db->quoteName('type') . ' = ' . $db->quote($type));
if ($type === 'plugin' && $group !== '')
{
$query->where($db->quoteName('folder') . ' = ' . $db->quote($group));
}
$db->setQuery($query);
if ((int) $db->loadResult() === 0)
{
$missing[] = htmlspecialchars($type . ': ' . $id, ENT_QUOTES, 'UTF-8');
}
}
return $missing;
}
catch (\Throwable $e)
{
return [];
}
}
private function getExistingDlid(): string
{
try
{
$db = Factory::getContainer()->get(DatabaseInterface::class);
$db->setQuery(
$db->getQuery(true)
->select($db->quoteName('extra_query'))
->from($db->quoteName('#__update_sites'))
->where($db->quoteName('name') . ' LIKE ' . $db->quote('%MokoSuite Auto%'))
->setLimit(1)
);
$extraQuery = $db->loadResult() ?? '';
if (preg_match('/dlid=([a-zA-Z0-9]+)/', $extraQuery, $matches))
{
return $matches[1];
}
}
catch (\Throwable $e)
{
Log::add('Auto install: failed to read DLID — ' . $e->getMessage(), Log::WARNING, 'mokosuite.auto');
}
return '';
}
private function restoreDlid(string $dlid): void
{
try
{
$db = Factory::getContainer()->get(DatabaseInterface::class);
$db->setQuery(
$db->getQuery(true)
->update($db->quoteName('#__update_sites'))
->set($db->quoteName('extra_query') . ' = ' . $db->quote('dlid=' . $dlid))
->where($db->quoteName('name') . ' LIKE ' . $db->quote('%MokoSuite Auto%'))
);
$db->execute();
}
catch (\Throwable $e)
{
Log::add('Auto install: failed to restore DLID — ' . $e->getMessage(), Log::WARNING, 'mokosuite.auto');
}
}
private function enablePlugin(string $group, string $element): void
{
try
{
$db = Factory::getContainer()->get(DatabaseInterface::class);
$db->setQuery(
$db->getQuery(true)
->update($db->quoteName('#__extensions'))
->set($db->quoteName('enabled') . ' = 1')
->where($db->quoteName('type') . ' = ' . $db->quote('plugin'))
->where($db->quoteName('folder') . ' = ' . $db->quote($group))
->where($db->quoteName('element') . ' = ' . $db->quote($element))
);
$db->execute();
}
catch (\Throwable $e)
{
Log::add('Auto install: failed to enable ' . $group . '/' . $element . ' — ' . $e->getMessage(), Log::WARNING, 'mokosuite.auto');
}
}
}