diff --git a/.gitignore b/.gitignore
index 391f47d..44a63a2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -113,7 +113,7 @@ releases/
build/
dist/
out/
-site/
+/site/
*.map
*.css.map
*.js.map
diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml
deleted file mode 100644
index 23beccb..0000000
--- a/.mokogitea/manifest.xml
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
-
-
- Template-Joomla
- MokoConsulting
- Template repository for Joomla extensions (plugins, modules, components, templates)
- GNU General Public License v3
-
-
- joomla
- 05.00.00
- https://git.mokoconsulting.tech/MokoConsulting/moko-platform
-
-
- PHP
- joomla-extension
- src/
-
-
diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml
index 5865324..4489ae0 100644
--- a/.mokogitea/workflows/auto-release.yml
+++ b/.mokogitea/workflows/auto-release.yml
@@ -7,7 +7,7 @@
# INGROUP: mokocli.Release
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli
# PATH: /templates/workflows/universal/auto-release.yml.template
-# VERSION: 05.00.00
+# VERSION: 05.01.00
# BRIEF: Universal build & release � detects platform from manifest.xml
#
# +=======================================================================+
@@ -75,6 +75,7 @@ jobs:
with:
token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 1
+ submodules: recursive
- name: Setup mokocli tools
env:
@@ -173,6 +174,7 @@ jobs:
with:
token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 0
+ submodules: recursive
- name: Configure git for bot pushes
run: |
diff --git a/.mokogitea/workflows/ci-joomla.yml b/.mokogitea/workflows/ci-joomla.yml
index 0c6f5ea..9d5a1c9 100644
--- a/.mokogitea/workflows/ci-joomla.yml
+++ b/.mokogitea/workflows/ci-joomla.yml
@@ -45,22 +45,22 @@ jobs:
fi
php -v && composer --version
- - name: Setup moko-platform tools
+ - name: Setup mokocli tools
env:
- MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN || github.token }}
- MOKO_CLONE_HOST: ${{ secrets.GA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }}
+ MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN || github.token }}
+ MOKO_CLONE_HOST: ${{ secrets.MOKOGITEA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }}
run: |
- if [ -d "/tmp/moko-platform" ] || [ -d "/opt/moko-platform" ]; then
- echo "moko-platform already available on runner — skipping clone"
+ if [ -d "/opt/mokocli" ] || [ -d "/tmp/mokocli" ]; then
+ echo "mokocli already available on runner — skipping clone"
else
git clone --depth 1 --branch main --quiet \
- "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
- /tmp/moko-platform 2>/dev/null || echo "moko-platform clone skipped — continuing without it"
+ "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git" \
+ /tmp/mokocli 2>/dev/null || echo "mokocli clone skipped — continuing without it"
fi
- name: Install dependencies
env:
- COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || secrets.GA_TOKEN || github.token }}"}}'
+ COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || secrets.MOKOGITEA_TOKEN || github.token }}"}}'
run: |
if [ -f "composer.json" ]; then
composer install \
@@ -164,6 +164,75 @@ jobs:
echo "**Manifest validation passed.**" >> $GITHUB_STEP_SUMMARY
fi
+ - name: Update server & packaging checks
+ continue-on-error: true
+ run: |
+ echo "### Update Server & Packaging" >> $GITHUB_STEP_SUMMARY
+ WARNINGS=0
+
+ # Find the extension manifest
+ MANIFEST=""
+ for XML_FILE in $(find . -maxdepth 2 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do
+ if grep -q "/dev/null; then
+ MANIFEST="$XML_FILE"
+ break
+ fi
+ done
+
+ if [ -z "$MANIFEST" ]; then
+ echo "No manifest found — skipping." >> $GITHUB_STEP_SUMMARY
+ else
+ EXT_TYPE=$(grep -oP ']*\btype="\K[^"]+' "$MANIFEST" | head -1)
+
+ # 1. Check exists and uses MokoGitea update server
+ if ! grep -q '' "$MANIFEST" 2>/dev/null; then
+ echo "::warning file=${MANIFEST}::Missing \`\` tag — extension will not receive OTA updates"
+ echo "- **Missing** \`\` — extension will not receive OTA updates" >> $GITHUB_STEP_SUMMARY
+ WARNINGS=$((WARNINGS + 1))
+ else
+ SERVER_URL=$(grep -oP ']*>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1)
+ if [ -z "$SERVER_URL" ]; then
+ echo "::warning file=${MANIFEST}::\`\` is empty — no server URL defined"
+ echo "- **Empty** \`\` — no server URL defined" >> $GITHUB_STEP_SUMMARY
+ WARNINGS=$((WARNINGS + 1))
+ elif ! echo "$SERVER_URL" | grep -q 'git\.mokoconsulting\.tech'; then
+ echo "::warning file=${MANIFEST}::Update server does not use MokoGitea engine: ${SERVER_URL}"
+ echo "- **Non-MokoGitea update server:** \`${SERVER_URL}\`" >> $GITHUB_STEP_SUMMARY
+ echo " Expected: \`https://git.mokoconsulting.tech/{org}/{repo}/updates.xml\`" >> $GITHUB_STEP_SUMMARY
+ WARNINGS=$((WARNINGS + 1))
+ else
+ echo "- \`\`: MokoGitea engine ✓" >> $GITHUB_STEP_SUMMARY
+ fi
+ fi
+
+ # 2. Check tag exists
+ if ! grep -q '/dev/null; then
+ echo "::warning file=${MANIFEST}::Missing \`\` tag — download ID authentication is not configured"
+ echo "- **Missing** \`\` — download ID authentication not configured" >> $GITHUB_STEP_SUMMARY
+ WARNINGS=$((WARNINGS + 1))
+ else
+ echo "- \`\`: present ✓" >> $GITHUB_STEP_SUMMARY
+ fi
+
+ # 3. For packages: check tag
+ if [ "$EXT_TYPE" = "package" ]; then
+ if ! grep -q '' "$MANIFEST" 2>/dev/null; then
+ echo "::warning file=${MANIFEST}::Package is missing \`\` — child extensions will not be removed on uninstall"
+ echo "- **Missing** \`\` — child extensions will remain when package is uninstalled" >> $GITHUB_STEP_SUMMARY
+ WARNINGS=$((WARNINGS + 1))
+ else
+ echo "- \`\`: present ✓" >> $GITHUB_STEP_SUMMARY
+ fi
+ fi
+ fi
+
+ echo "" >> $GITHUB_STEP_SUMMARY
+ if [ "$WARNINGS" -gt 0 ]; then
+ echo "**${WARNINGS} packaging warning(s).** These won't block CI but should be addressed." >> $GITHUB_STEP_SUMMARY
+ else
+ echo "**Update server & packaging checks passed.**" >> $GITHUB_STEP_SUMMARY
+ fi
+
- name: Check language files referenced in manifest
run: |
echo "### Language File Check" >> $GITHUB_STEP_SUMMARY
@@ -245,10 +314,676 @@ jobs:
echo "All ${CHECKED} directories contain index.html." >> $GITHUB_STEP_SUMMARY
fi
+ - name: Check config.xml and access.xml for components
+ run: |
+ echo "### Component Config & ACL Check" >> $GITHUB_STEP_SUMMARY
+ ERRORS=0
+
+ # Find all component manifests (XML with type="component")
+ # Uses maxdepth 10 to reach into nested package repos (packages/*/source/packages/com_*/...)
+ COMP_MANIFESTS=$(find . -maxdepth 10 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*" -not -path "./.claude/*" -exec grep -l ']*type="component"' {} \; 2>/dev/null || true)
+
+ if [ -z "$COMP_MANIFESTS" ]; then
+ echo "No component extensions found — skipping." >> $GITHUB_STEP_SUMMARY
+ else
+ for MANIFEST in $COMP_MANIFESTS; do
+ COMP_DIR=$(dirname "$MANIFEST")
+ COMP_NAME=$(basename "$COMP_DIR")
+ echo "Component: `${COMP_NAME}` (manifest: `${MANIFEST}`)" >> $GITHUB_STEP_SUMMARY
+
+ # Check access.xml exists
+ ACCESS_FILE=$(find "$COMP_DIR" -name "access.xml" -not -path "./.git/*" 2>/dev/null | head -1)
+ if [ -z "$ACCESS_FILE" ]; then
+ echo "- Missing `access.xml` — ACL permissions will not work." >> $GITHUB_STEP_SUMMARY
+ ERRORS=$((ERRORS + 1))
+ else
+ if command -v php &> /dev/null; then
+ if ! php -r "@simplexml_load_file('$ACCESS_FILE') ?: exit(1);" 2>/dev/null; then
+ echo "- `access.xml` is not well-formed XML." >> $GITHUB_STEP_SUMMARY
+ ERRORS=$((ERRORS + 1))
+ else
+ for ACTION in core.admin core.manage; do
+ if ! grep -q "name=\"${ACTION}\"" "$ACCESS_FILE" 2>/dev/null; then
+ echo "- `access.xml` missing required action: `${ACTION}`" >> $GITHUB_STEP_SUMMARY
+ ERRORS=$((ERRORS + 1))
+ fi
+ done
+ echo "- `access.xml`: valid" >> $GITHUB_STEP_SUMMARY
+ fi
+ fi
+ fi
+
+ # Check config.xml exists
+ CONFIG_FILE=$(find "$COMP_DIR" -name "config.xml" -not -path "./.git/*" 2>/dev/null | head -1)
+ if [ -z "$CONFIG_FILE" ]; then
+ echo "- Missing `config.xml` — component Options page will be empty." >> $GITHUB_STEP_SUMMARY
+ ERRORS=$((ERRORS + 1))
+ else
+ if command -v php &> /dev/null; then
+ if ! php -r "@simplexml_load_file('$CONFIG_FILE') ?: exit(1);" 2>/dev/null; then
+ echo "- `config.xml` is not well-formed XML." >> $GITHUB_STEP_SUMMARY
+ ERRORS=$((ERRORS + 1))
+ else
+ echo "- `config.xml`: valid" >> $GITHUB_STEP_SUMMARY
+ fi
+ fi
+ fi
+ done
+ fi
+
+ echo "" >> $GITHUB_STEP_SUMMARY
+ if [ "${ERRORS}" -gt 0 ]; then
+ echo "**${ERRORS} config/ACL issue(s) found.**" >> $GITHUB_STEP_SUMMARY
+ exit 1
+ else
+ echo "**Component config & ACL check passed.**" >> $GITHUB_STEP_SUMMARY
+ fi
+
+ - name: SQL schema validation
+ run: |
+ echo "### SQL Schema Validation" >> $GITHUB_STEP_SUMMARY
+ ERRORS=0
+
+ # Find SQL files in source/htdocs
+ SQL_FILES=$(find . -name "*.sql" -path "*/sql/*" -not -path "./.git/*" -not -path "./vendor/*" 2>/dev/null)
+ if [ -z "$SQL_FILES" ]; then
+ echo "No SQL files found — skipping." >> $GITHUB_STEP_SUMMARY
+ else
+ echo "Found $(echo "$SQL_FILES" | wc -l) SQL file(s)" >> $GITHUB_STEP_SUMMARY
+
+ for FILE in $SQL_FILES; do
+ # Basic syntax check: balanced parentheses, no empty files
+ SIZE=$(wc -c < "$FILE" | tr -d ' ')
+ if [ "$SIZE" -eq 0 ]; then
+ echo "- Empty SQL file: \`${FILE}\`" >> $GITHUB_STEP_SUMMARY
+ ERRORS=$((ERRORS + 1))
+ continue
+ fi
+
+ # Check for common SQL errors
+ if grep -qP '^\s*$' "$FILE" && [ "$SIZE" -lt 5 ]; then
+ echo "- Whitespace-only SQL file: \`${FILE}\`" >> $GITHUB_STEP_SUMMARY
+ ERRORS=$((ERRORS + 1))
+ continue
+ fi
+
+ echo "- \`${FILE}\`: ${SIZE} bytes" >> $GITHUB_STEP_SUMMARY
+ done
+
+ # Check update SQL files follow version numbering pattern
+ UPDATE_DIR=$(find . -path "*/sql/updates/mysql" -type d -not -path "./.git/*" 2>/dev/null | head -1)
+ if [ -n "$UPDATE_DIR" ]; then
+ BAD_NAMES=0
+ for UFILE in "$UPDATE_DIR"/*.sql; do
+ [ ! -f "$UFILE" ] && continue
+ BASENAME=$(basename "$UFILE" .sql)
+ if ! echo "$BASENAME" | grep -qP '^\d+\.\d+\.\d+'; then
+ echo "- Update file \`${UFILE}\` does not follow version naming (expected X.Y.Z.sql)" >> $GITHUB_STEP_SUMMARY
+ BAD_NAMES=$((BAD_NAMES + 1))
+ fi
+ done
+ if [ "$BAD_NAMES" -gt 0 ]; then
+ ERRORS=$((ERRORS + BAD_NAMES))
+ fi
+ fi
+ fi
+
+ echo "" >> $GITHUB_STEP_SUMMARY
+ if [ "${ERRORS}" -gt 0 ]; then
+ echo "**${ERRORS} SQL issue(s) found.**" >> $GITHUB_STEP_SUMMARY
+ exit 1
+ else
+ echo "**SQL schema validation passed.**" >> $GITHUB_STEP_SUMMARY
+ fi
+
+ - name: Manifest file references check
+ run: |
+ echo "### Manifest File References" >> $GITHUB_STEP_SUMMARY
+ ERRORS=0
+
+ MANIFEST=""
+ for XML_FILE in $(find . -maxdepth 2 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do
+ if grep -q "/dev/null; then
+ MANIFEST="$XML_FILE"
+ break
+ fi
+ done
+
+ if [ -z "$MANIFEST" ]; then
+ echo "No manifest found — skipping." >> $GITHUB_STEP_SUMMARY
+ else
+ MANIFEST_DIR=$(dirname "$MANIFEST")
+
+ # Check references
+ FILENAMES=$(grep -oP ']*>\K[^<]+' "$MANIFEST" 2>/dev/null || true)
+ for F in $FILENAMES; do
+ if [ ! -f "${MANIFEST_DIR}/${F}" ] && [ ! -d "${MANIFEST_DIR}/${F}" ]; then
+ echo "- Missing: \`${F}\` (referenced in manifest)" >> $GITHUB_STEP_SUMMARY
+ ERRORS=$((ERRORS + 1))
+ fi
+ done
+
+ # Check references
+ FOLDERS=$(grep -oP ']*>\K[^<]+' "$MANIFEST" 2>/dev/null || true)
+ for F in $FOLDERS; do
+ if [ ! -d "${MANIFEST_DIR}/${F}" ]; then
+ echo "- Missing folder: \`${F}\` (referenced in manifest)" >> $GITHUB_STEP_SUMMARY
+ ERRORS=$((ERRORS + 1))
+ fi
+ done
+
+ # Check references in package manifests (ZIP files won't exist in source)
+ EXT_TYPE=$(grep -oP ']*\btype="\K[^"]+' "$MANIFEST" | head -1)
+ if [ "$EXT_TYPE" != "package" ]; then
+ FILES=$(grep -oP ']*>\K[^<]+' "$MANIFEST" 2>/dev/null || true)
+ for F in $FILES; do
+ if [ ! -f "${MANIFEST_DIR}/${F}" ]; then
+ echo "- Missing file: \`${F}\` (referenced in manifest)" >> $GITHUB_STEP_SUMMARY
+ ERRORS=$((ERRORS + 1))
+ fi
+ done
+ fi
+ fi
+
+ echo "" >> $GITHUB_STEP_SUMMARY
+ if [ "${ERRORS}" -gt 0 ]; then
+ echo "**${ERRORS} missing file reference(s).**" >> $GITHUB_STEP_SUMMARY
+ exit 1
+ else
+ echo "**Manifest file references check passed.**" >> $GITHUB_STEP_SUMMARY
+ fi
+
+ - name: Form XML validation
+ run: |
+ echo "### Form XML Validation" >> $GITHUB_STEP_SUMMARY
+ ERRORS=0
+
+ FORM_FILES=$(find . -name "*.xml" -path "*/forms/*" -not -path "./.git/*" -not -path "./vendor/*" 2>/dev/null)
+ if [ -z "$FORM_FILES" ]; then
+ echo "No form XML files found — skipping." >> $GITHUB_STEP_SUMMARY
+ else
+ echo "Found $(echo "$FORM_FILES" | wc -l) form file(s)" >> $GITHUB_STEP_SUMMARY
+ for FILE in $FORM_FILES; do
+ if command -v php &> /dev/null; then
+ if ! php -r "@simplexml_load_file('$FILE') ?: exit(1);" 2>/dev/null; then
+ echo "- \`${FILE}\`: malformed XML" >> $GITHUB_STEP_SUMMARY
+ ERRORS=$((ERRORS + 1))
+ else
+ # Check for valid Joomla form structure
+ if ! grep -qE '