From 86f341bd722d64b8831682ce8319c483f5d8cb34 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 17:48:02 -0500 Subject: [PATCH 01/13] fix(script): remove faulty template-mokoonyx duplicate in postflight Adds cleanup for the template-mokoonyx extension entry created by a bad install. Removes the extension, its update sites link, and any template styles. Runs on every update via postflight. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- src/helper/migrate.php | 4 ++-- src/script.php | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/helper/migrate.php b/src/helper/migrate.php index b57d956..1c9e088 100644 --- a/src/helper/migrate.php +++ b/src/helper/migrate.php @@ -236,11 +236,11 @@ use Joomla\CMS\Log\Log; // Update the update server try { - $onyxUpdatesUrl = 'https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/updates.xml'; + $onyxUpdatesUrl = 'https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/updates.xml'; $query = $db->getQuery(true) ->update('#__update_sites') ->set($db->quoteName('location') . ' = ' . $db->quote($onyxUpdatesUrl)) - ->set($db->quoteName('name') . ' = ' . $db->quote($newDisplay)) + ->set($db->quoteName('name') . ' = ' . $db->quote('Template - MokoOnyx')) ->where($db->quoteName('location') . ' LIKE ' . $db->quote('%MokoCassiopeia%')); $db->setQuery($query)->execute(); $n = $db->getAffectedRows(); diff --git a/src/script.php b/src/script.php index 35f6641..839642f 100644 --- a/src/script.php +++ b/src/script.php @@ -533,6 +533,37 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface } } + // Remove faulty "template-mokoonyx" duplicate (wrong element name from bad install) + $faultyId = (int) $db->setQuery( + $db->getQuery(true) + ->select('extension_id') + ->from('#__extensions') + ->where($db->quoteName('element') . ' = ' . $db->quote('template-mokoonyx')) + ->where($db->quoteName('type') . ' = ' . $db->quote('template')) + )->loadResult(); + + if ($faultyId) { + $db->setQuery( + $db->getQuery(true) + ->delete('#__update_sites_extensions') + ->where('extension_id = ' . $faultyId) + )->execute(); + + $db->setQuery( + $db->getQuery(true) + ->delete('#__extensions') + ->where('extension_id = ' . $faultyId) + )->execute(); + + $db->setQuery( + $db->getQuery(true) + ->delete('#__template_styles') + ->where($db->quoteName('template') . ' = ' . $db->quote('template-mokoonyx')) + )->execute(); + + $this->logMessage('Removed faulty template-mokoonyx duplicate extension.'); + } + // Remove stale MokoCassiopeia if not set as default $oldExt = (int) $db->setQuery( $db->getQuery(true) -- 2.52.0 From 9f628201ada8f817f5d8be8bd2a7daea65465bc6 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 22:48:19 +0000 Subject: [PATCH 02/13] chore(version): auto-bump 02.19.06-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 4 ++-- SECURITY.md | 2 +- src/html/layouts/joomla/module/card.php | 2 +- src/html/layouts/mokoonyx/article-metadata.php | 2 +- src/media/css/a11y-high-contrast.css | 2 +- src/templateDetails.xml | 2 +- updates.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index 9a17434..dea8d4e 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -9,7 +9,7 @@ Template - MokoOnyx MokoConsulting MokoOnyx - Joomla site template (successor to MokoCassiopeia) - 02.19.05 + 02.19.06 GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 651b5ea..5906d98 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 02.19.05 +# VERSION: 02.19.06 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c493a4..6a0bcae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,11 @@ DEFGROUP: Joomla.Template.Site INGROUP: MokoOnyx.Documentation PATH: ./CHANGELOG.md - VERSION: 02.19.05 + VERSION: 02.19.06 BRIEF: Changelog file documenting version history of MokoOnyx --> -# Changelog — MokoOnyx (VERSION: 02.19.05) +# Changelog — MokoOnyx (VERSION: 02.19.06) ## [Unreleased] ### Fixed diff --git a/SECURITY.md b/SECURITY.md index 32c6bd9..325251f 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -10,7 +10,7 @@ INGROUP: MokoOnyx.Governance REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx FILE: SECURITY.md - VERSION: 02.19.05 + VERSION: 02.19.06 BRIEF: Security policy and vulnerability reporting process for MokoOnyx. PATH: /SECURITY.md NOTE: This policy is process oriented and does not replace secure engineering practices. diff --git a/src/html/layouts/joomla/module/card.php b/src/html/layouts/joomla/module/card.php index 44aa28a..cf2ec1d 100644 --- a/src/html/layouts/joomla/module/card.php +++ b/src/html/layouts/joomla/module/card.php @@ -10,7 +10,7 @@ * INGROUP: MokoOnyx * REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx * PATH: /html/layouts/joomla/module/card.php - * VERSION: 02.19.05 + * VERSION: 02.19.06 * BRIEF: Custom card module chrome — renders module titles for all modules */ diff --git a/src/html/layouts/mokoonyx/article-metadata.php b/src/html/layouts/mokoonyx/article-metadata.php index 6ec69b4..020bff6 100644 --- a/src/html/layouts/mokoonyx/article-metadata.php +++ b/src/html/layouts/mokoonyx/article-metadata.php @@ -11,7 +11,7 @@ * INGROUP: MokoOnyx.Layouts * REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx * PATH: /src/html/layouts/mokoonyx/article-metadata.php - * VERSION: 02.19.05 + * VERSION: 02.19.06 * BRIEF: Article metadata footer layout -- renders jcfields grouped by field group */ diff --git a/src/media/css/a11y-high-contrast.css b/src/media/css/a11y-high-contrast.css index b08c623..5c2b707 100644 --- a/src/media/css/a11y-high-contrast.css +++ b/src/media/css/a11y-high-contrast.css @@ -10,7 +10,7 @@ * INGROUP: MokoOnyx.Accessibility * REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx * PATH: ./media/css/a11y-high-contrast.css - * VERSION: 02.19.05 + * VERSION: 02.19.06 * BRIEF: High-contrast stylesheet for accessibility toolbar */ diff --git a/src/templateDetails.xml b/src/templateDetails.xml index 98443da..0517138 100644 --- a/src/templateDetails.xml +++ b/src/templateDetails.xml @@ -35,7 +35,7 @@ mokoonyx - 02.19.05-dev + 02.19.06-dev script.php 2026-05-16 Jonathan Miller || Moko Consulting diff --git a/updates.xml b/updates.xml index 0cf0a09..8e6e849 100644 --- a/updates.xml +++ b/updates.xml @@ -1,7 +1,7 @@ -- 2.52.0 From 7b9ea92faa1690b450a4c1c592daeff6dc5f3ee5 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 4 Jun 2026 22:48:21 +0000 Subject: [PATCH 03/13] chore: update development channel 02.19.06-dev [skip ci] --- updates.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/updates.xml b/updates.xml index 8e6e849..6fddaa8 100644 --- a/updates.xml +++ b/updates.xml @@ -11,13 +11,13 @@ mokoonyx template site - 02.19.05-dev + 02.19.06-dev 2026-06-04 https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/development - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/development/tpl_mokoonyx-02.19.05-dev.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/development/tpl_mokoonyx-02.19.06-dev.zip - b99f6f06ce7b1cf5fc7e7eafade4f63e366de026894aa29edfff8e00c9c4cf22 + 718dd24295a5c8e3a0c855b01f86bf619bae84a4f7ab6e037c5cd5bfaadb2e30 dev https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/CHANGELOG.md Moko Consulting -- 2.52.0 From 6802b256d0aeec97643f538644012739665c57b8 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Fri, 5 Jun 2026 00:07:21 +0000 Subject: [PATCH 04/13] chore: remove update-server workflow [skip ci] --- .mokogitea/workflows/update-server.yml | 312 ------------------------- 1 file changed, 312 deletions(-) delete mode 100644 .mokogitea/workflows/update-server.yml diff --git a/.mokogitea/workflows/update-server.yml b/.mokogitea/workflows/update-server.yml deleted file mode 100644 index 339d3f5..0000000 --- a/.mokogitea/workflows/update-server.yml +++ /dev/null @@ -1,312 +0,0 @@ -# Copyright (C) 2026 Moko Consulting -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: moko-platform.Universal -# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform -# PATH: /templates/workflows/update-server.yml -# VERSION: 05.00.00 -# BRIEF: Pre-release build + update server XML for dev/alpha/beta/rc branches -# -# Thin wrapper around moko-platform CLI tools. -# Builds packages, updates updates.xml, and optionally deploys via SFTP. -# -# Joomla filters update entries by the user's "Minimum Stability" setting. - -name: "Update Server" - -on: - push: - branches: - - 'dev' - - 'dev/**' - - 'alpha/**' - - 'beta/**' - - 'rc/**' - paths: - - 'src/**' - - 'htdocs/**' - pull_request: - types: [closed] - branches: - - 'dev' - - 'dev/**' - - 'alpha/**' - - 'beta/**' - - 'rc/**' - paths: - - 'src/**' - - 'htdocs/**' - workflow_dispatch: - inputs: - stability: - description: 'Stability tag' - required: true - default: 'development' - type: choice - options: - - development - - alpha - - beta - - rc - - stable - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} - GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} - -permissions: - contents: write - -jobs: - update-xml: - name: Update Server - runs-on: release - if: >- - github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' || github.event_name == 'push' - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - token: ${{ secrets.MOKOGITEA_TOKEN }} - fetch-depth: 0 - - - name: Setup moko-platform tools - env: - MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting - COMPOSER_AUTH: '{"http-basic":{"git.mokoconsulting.tech":{"username":"token","password":"${{ secrets.MOKOGITEA_TOKEN }}"}}}' - run: | - if ! command -v composer &> /dev/null; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 - fi - # Always fetch latest CLI tools — never use stale cache from previous runs - rm -rf /tmp/moko-platform - 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 || true - if [ -d "/tmp/moko-platform" ] && [ -f "/tmp/moko-platform/composer.json" ]; then - cd /tmp/moko-platform && composer install --no-dev --no-interaction --quiet 2>/dev/null || true - fi - echo "MOKO_CLI=/tmp/moko-platform/cli" >> "$GITHUB_ENV" - - - name: Detect platform - id: platform - run: php ${MOKO_CLI}/manifest_read.php --path . --github-output - - - name: Resolve stability and bump version - id: meta - run: | - BRANCH="${{ github.ref_name }}" - - # Configure git for bot pushes - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" - - # Auto-bump patch version - php ${MOKO_CLI}/version_bump.php --path . 2>/dev/null || true - - VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "0.0.0") - - # Strip any existing suffix before applying stability - VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//') - - # Determine stability from branch or manual input - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - STABILITY="${{ inputs.stability }}" - elif [[ "$BRANCH" == rc/* ]]; then - STABILITY="rc" - elif [[ "$BRANCH" == beta/* ]]; then - STABILITY="beta" - elif [[ "$BRANCH" == alpha/* ]]; then - STABILITY="alpha" - else - STABILITY="development" - fi - - # Version suffix per stability stream - case "$STABILITY" in - development) SUFFIX="-dev"; TAG="development" ;; - alpha) SUFFIX="-alpha"; TAG="alpha" ;; - beta) SUFFIX="-beta"; TAG="beta" ;; - rc) SUFFIX="-rc"; TAG="release-candidate" ;; - *) SUFFIX=""; TAG="stable" ;; - esac - - # Propagate version with stability suffix to all manifest files - php ${MOKO_CLI}/version_set_platform.php \ - --path . --version "$VERSION" --branch "$BRANCH" --stability "$STABILITY" 2>/dev/null || true - php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true - - # Re-read version (now includes suffix from version_set_platform) - if [ -n "$SUFFIX" ]; then - VERSION="${VERSION}${SUFFIX}" - fi - - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" - echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT" - echo "tag=${TAG}" >> "$GITHUB_OUTPUT" - echo "display_version=${VERSION}" >> "$GITHUB_OUTPUT" - - # Commit version bump if changed - git add -A - git diff --cached --quiet || { - git commit -m "chore(version): auto-bump ${VERSION} [skip ci]" \ - --author="gitea-actions[bot] " - git push - } - - - name: Create release and upload package - id: package - run: | - VERSION="${{ steps.meta.outputs.version }}" - TAG="${{ steps.meta.outputs.tag }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - - # Create or update Gitea release - php ${MOKO_CLI}/release_create.php \ - --path . --version "$VERSION" --tag "$TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease - - # Build package and upload - php ${MOKO_CLI}/release_package.php \ - --path . --version "$VERSION" --tag "$TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --output /tmp || true - - - name: Update updates.xml - if: steps.platform.outputs.platform == 'joomla' - run: | - VERSION="${{ steps.meta.outputs.version }}" - STABILITY="${{ steps.meta.outputs.stability }}" - SHA256="${{ steps.package.outputs.sha256_zip }}" - - if [ ! -f "updates.xml" ]; then - echo "No updates.xml — skipping" - exit 0 - fi - - SHA_FLAG="" - [ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}" - - php ${MOKO_CLI}/updates_xml_build.php \ - --path . --version "${VERSION}" --stability "${STABILITY}" \ - --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \ - ${SHA_FLAG} - - # Commit and push updates.xml - git add updates.xml - git diff --cached --quiet || { - git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]" - git push - } - - - name: Sync updates.xml to main - if: github.ref_name != 'main' && steps.platform.outputs.platform == 'joomla' - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - GITEA_TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - - FILE_SHA=$(curl -sf -H "Authorization: token ${GITEA_TOKEN}" \ - "${API_BASE}/contents/updates.xml?ref=main" | python3 -c "import sys,json; print(json.load(sys.stdin).get('sha',''))" 2>/dev/null || true) - - if [ -n "$FILE_SHA" ] && [ -f "updates.xml" ]; then - python3 -c " - import base64, json, urllib.request, sys - with open('updates.xml', 'rb') as f: - content = base64.b64encode(f.read()).decode() - payload = json.dumps({ - 'content': content, - 'sha': '${FILE_SHA}', - 'message': 'chore: sync updates.xml from ${{ steps.meta.outputs.stability }} [skip ci]', - 'branch': 'main' - }).encode() - req = urllib.request.Request( - '${API_BASE}/contents/updates.xml', - data=payload, method='PUT', - headers={ - 'Authorization': 'token ${GITEA_TOKEN}', - 'Content-Type': 'application/json' - }) - try: - urllib.request.urlopen(req) - print('updates.xml synced to main') - except Exception as e: - print(f'WARNING: sync to main failed: {e}', file=sys.stderr) - " - fi - - - name: SFTP deploy to dev server - if: contains(github.ref, 'dev/') || github.ref == 'refs/heads/dev' - env: - DEV_HOST: ${{ vars.DEV_FTP_HOST }} - DEV_PATH: ${{ vars.DEV_FTP_PATH }} - DEV_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }} - DEV_USER: ${{ vars.DEV_FTP_USERNAME }} - DEV_PORT: ${{ vars.DEV_FTP_PORT }} - DEV_KEY: ${{ secrets.DEV_FTP_KEY }} - DEV_PASS: ${{ secrets.DEV_FTP_PASSWORD }} - run: | - # Permission check: admin or maintain role required - ACTOR="${{ github.actor }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - - PERMISSION=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ - "${API_BASE}/collaborators/${ACTOR}/permission" 2>/dev/null | \ - python3 -c "import sys,json; print(json.load(sys.stdin).get('permission','read'))" 2>/dev/null || echo "read") - case "$PERMISSION" in - admin|maintain|write) ;; - *) - echo "Deploy denied: ${ACTOR} has '${PERMISSION}' — requires admin, maintain, or write" - exit 0 - ;; - esac - - [ -z "$DEV_HOST" ] || [ -z "$DEV_PATH" ] && { echo "DEV FTP not configured — skipping SFTP"; exit 0; } - - SOURCE_DIR="src" - [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" - [ ! -d "$SOURCE_DIR" ] && exit 0 - - PORT="${DEV_PORT:-22}" - REMOTE="${DEV_PATH%/}" - [ -n "$DEV_SUFFIX" ] && REMOTE="${REMOTE}/${DEV_SUFFIX#/}" - - printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \ - "$DEV_HOST" "$PORT" "$DEV_USER" "$REMOTE" > /tmp/sftp-config.json - if [ -n "$DEV_KEY" ]; then - echo "$DEV_KEY" > /tmp/deploy_key && chmod 600 /tmp/deploy_key - printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json - else - printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json - fi - - PLATFORM=$(php ${MOKO_CLI}/platform_detect.php --path . 2>/dev/null || true) - if [ "$PLATFORM" = "waas-component" ] && [ -f "${MOKO_CLI}/../deploy/deploy-joomla.php" ]; then - php ${MOKO_CLI}/../deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json - elif [ -f "${MOKO_CLI}/../deploy/deploy-sftp.php" ]; then - php ${MOKO_CLI}/../deploy/deploy-sftp.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json - fi - rm -f /tmp/deploy_key /tmp/sftp-config.json - echo "SFTP deploy to dev complete" >> $GITHUB_STEP_SUMMARY - - - name: Summary - if: always() - run: | - VERSION="${{ steps.meta.outputs.version }}" - STABILITY="${{ steps.meta.outputs.stability }}" - DISPLAY="${{ steps.meta.outputs.display_version }}" - echo "## Update Server" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY - echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY - echo "| Stability | \`${STABILITY}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Version | \`${DISPLAY}\` |" >> $GITHUB_STEP_SUMMARY -- 2.52.0 From 9968c81660fa49f9a4fcd45d70b8a0ae32f06d98 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 19:56:14 -0500 Subject: [PATCH 05/13] chore: remove updates.xml from dev branch [skip ci] Update server now served via Gitea Pages from main only. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- updates.xml | 48 ------------------------------------------------ 1 file changed, 48 deletions(-) delete mode 100644 updates.xml diff --git a/updates.xml b/updates.xml deleted file mode 100644 index 6fddaa8..0000000 --- a/updates.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - - Template - MokoOnyx - Template - MokoOnyx development build. - mokoonyx - template - site - 02.19.06-dev - 2026-06-04 - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/development - - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/development/tpl_mokoonyx-02.19.06-dev.zip - - 718dd24295a5c8e3a0c855b01f86bf619bae84a4f7ab6e037c5cd5bfaadb2e30 - dev - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/CHANGELOG.md - Moko Consulting - https://mokoconsulting.tech - - 8.1.0 - - - Template - MokoOnyx - Template - MokoOnyx stable build. - mokoonyx - template - site - 02.20.00 - 2026-06-04 - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable - - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.20.00.zip - - 7fe031b49d2908d161378213a9aa9bcf807ef0ee101d39bba077df4f73a60f30 - stable - https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/CHANGELOG.md - Moko Consulting - https://mokoconsulting.tech - - 8.1.0 - - -- 2.52.0 From d957022fc1e725b33870024d2a934408f0296549 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 4 Jun 2026 22:21:08 -0500 Subject: [PATCH 06/13] chore: remove submodule update workflow for MokoWaaS Submodule reference removed from MokoWaaS; this workflow is no longer needed. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .../workflows/update-submodule-waas.yml | 106 ------------------ 1 file changed, 106 deletions(-) delete mode 100644 .mokogitea/workflows/update-submodule-waas.yml diff --git a/.mokogitea/workflows/update-submodule-waas.yml b/.mokogitea/workflows/update-submodule-waas.yml deleted file mode 100644 index 39461ce..0000000 --- a/.mokogitea/workflows/update-submodule-waas.yml +++ /dev/null @@ -1,106 +0,0 @@ -# Copyright (C) 2026 Moko Consulting -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# BRIEF: Update MokoOnyx submodule in MokoWaas on main and dev branches - -name: "Update MokoWaas Submodule" - -on: - release: - types: [published] - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - WAAS_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} - WAAS_REPO: MokoWaaS - # Path where MokoOnyx lives as a submodule inside MokoWaaS - SUBMODULE_PATH: src/packages/tpl_mokoonyx - -permissions: - contents: read - -jobs: - update-submodule: - name: "Update submodule on ${{ matrix.branch }}" - runs-on: ubuntu-latest - strategy: - matrix: - branch: [main, dev] - fail-fast: false - - steps: - - name: Get release info - id: release - run: | - TAG="${{ github.event.release.tag_name }}" - IS_PRE="${{ github.event.release.prerelease }}" - echo "tag=${TAG}" >> $GITHUB_OUTPUT - echo "prerelease=${IS_PRE}" >> $GITHUB_OUTPUT - echo "Release tag: ${TAG} (prerelease: ${IS_PRE})" - - # Skip pre-releases (RC) — only update on stable releases - - name: Skip pre-releases - if: steps.release.outputs.prerelease == 'true' - run: | - echo "Skipping pre-release ${TAG} — only stable releases update MokoWaas" - exit 0 - - - name: Clone MokoWaas - if: steps.release.outputs.prerelease != 'true' - run: | - git clone --branch "${{ matrix.branch }}" --depth 1 --recurse-submodules \ - "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${WAAS_ORG}/${WAAS_REPO}.git" \ - waas - env: - GIT_TERMINAL_PROMPT: 0 - - - name: Configure git - if: steps.release.outputs.prerelease != 'true' - working-directory: waas - run: | - git config user.email "gitea-actions[bot]@mokoconsulting.tech" - git config user.name "gitea-actions[bot]" - git remote set-url origin \ - "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${WAAS_ORG}/${WAAS_REPO}.git" - - - name: Update submodule to release tag - if: steps.release.outputs.prerelease != 'true' - working-directory: waas - run: | - TAG="${{ steps.release.outputs.tag }}" - SUBPATH="${SUBMODULE_PATH}" - - # Verify submodule exists - if [ ! -f ".gitmodules" ] || ! grep -q "${SUBPATH}" .gitmodules; then - echo "::error::Submodule '${SUBPATH}' not found in .gitmodules on ${{ matrix.branch }}" - exit 1 - fi - - # Fetch the tag in the submodule and update to it - cd "${SUBPATH}" - git fetch origin "refs/tags/${TAG}:refs/tags/${TAG}" --depth 1 - git checkout "${TAG}" - cd - - - # Stage the submodule pointer change - git add "${SUBPATH}" - - # Only commit if there's actually a change - if git diff --cached --quiet; then - echo "Submodule already at ${TAG} on ${{ matrix.branch }} — nothing to do" - else - git commit -m "chore: update MokoOnyx submodule to ${TAG} - -Authored-by: Moko Consulting" - git push origin "${{ matrix.branch }}" - echo "Updated MokoOnyx to ${TAG} on ${{ matrix.branch }}" - fi - - - name: Summary - if: always() && steps.release.outputs.prerelease != 'true' - run: | - TAG="${{ steps.release.outputs.tag }}" - echo "## MokoWaas Submodule Update (${{ matrix.branch }})" >> $GITHUB_STEP_SUMMARY - echo "Updated \`${SUBMODULE_PATH}\` to \`${TAG}\`" >> $GITHUB_STEP_SUMMARY -- 2.52.0 From 24e42d9132951bb4fd2b8da1b5c570e439dcbf17 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 6 Jun 2026 12:31:36 +0000 Subject: [PATCH 07/13] chore: sync .mokogitea/workflows/pre-release.yml from moko-platform [skip ci] --- .mokogitea/workflows/pre-release.yml | 130 ++++++++++++--------------- 1 file changed, 56 insertions(+), 74 deletions(-) diff --git a/.mokogitea/workflows/pre-release.yml b/.mokogitea/workflows/pre-release.yml index 780150d..1a9eeef 100644 --- a/.mokogitea/workflows/pre-release.yml +++ b/.mokogitea/workflows/pre-release.yml @@ -63,16 +63,22 @@ jobs: MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting run: | - if ! command -v composer &> /dev/null; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 + # Use pre-installed /opt/moko-platform if available (updated by cron every 6h) + if [ -f “/opt/moko-platform/cli/version_bump.php” ] && [ -f “/opt/moko-platform/vendor/autoload.php” ]; then + echo “Using pre-installed /opt/moko-platform” + echo “MOKO_CLI=/opt/moko-platform/cli” >> “$GITHUB_ENV” + else + echo “Falling back to fresh clone” + if ! command -v composer &> /dev/null; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 + fi + rm -rf /tmp/moko-platform-api + git clone --depth 1 --branch main --quiet \ + “https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git” \ + /tmp/moko-platform-api + cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet + echo “MOKO_CLI=/tmp/moko-platform-api/cli” >> “$GITHUB_ENV” fi - # Always fetch latest CLI tools — never use stale cache from previous runs - rm -rf /tmp/moko-platform-api - git clone --depth 1 --branch main --quiet \ - "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ - /tmp/moko-platform-api - cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet - echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV" - name: Detect platform id: platform @@ -96,20 +102,23 @@ jobs: release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;; esac - # Read current version (bump already handled by push workflow) - VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null) - [ -z "$VERSION" ] && VERSION="00.00.01" + # Bump version via CLI: patch for dev/alpha/beta, minor for RC + case "$STABILITY" in + release-candidate) BUMP="minor" ;; + *) BUMP="patch" ;; + esac - # Strip any existing suffix from version before applying stability + php ${MOKO_CLI}/version_bump.php --path . $([ "$BUMP" = "minor" ] && echo "--minor") 2>/dev/null || true + + # Set stability suffix and verify consistency + VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01") VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//') php ${MOKO_CLI}/version_set_platform.php \ --path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true - - # Verify version consistency across all files php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true - # Update VERSION variable with suffix + # Append suffix for output if [ -n "$SUFFIX" ]; then VERSION="${VERSION}${SUFFIX}" fi @@ -155,19 +164,39 @@ jobs: --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ --repo "${GITEA_REPO}" --branch dev --prerelease - - name: Ensure prerelease flag + - name: Update release notes from CHANGELOG.md run: | TAG="${{ steps.meta.outputs.tag }}" + VERSION="${{ steps.meta.outputs.version }}" API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - # Get release ID by tag and force prerelease=true - RELEASE_ID=$(curl -s "${API_BASE}/releases/tags/${TAG}" \ - -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" | jq -r '.id // empty') + + # Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading) + if [ -f "CHANGELOG.md" ]; then + NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md) + [ -z "$NOTES" ] && NOTES="Release ${VERSION}" + else + NOTES="Release ${VERSION}" + fi + + # Update release body via API + RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ + "${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) + if [ -n "$RELEASE_ID" ]; then - curl -s -X PATCH "${API_BASE}/releases/${RELEASE_ID}" \ - -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ - -H "Content-Type: application/json" \ - -d '{"prerelease": true}' - echo "Marked release ${TAG} (id=${RELEASE_ID}) as prerelease" + python3 -c " + import json, urllib.request + body = open('/dev/stdin').read() + payload = json.dumps({'body': body}).encode() + req = urllib.request.Request( + '${API_BASE}/releases/${RELEASE_ID}', + data=payload, method='PATCH', + headers={ + 'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}', + 'Content-Type': 'application/json' + }) + urllib.request.urlopen(req) + " <<< "$NOTES" + echo "Release notes updated from CHANGELOG.md" fi - name: Build package and upload @@ -181,55 +210,8 @@ jobs: --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ --repo "${GITEA_REPO}" --output /tmp || true - - name: Update updates.xml - if: steps.platform.outputs.platform == 'joomla' - run: | - VERSION="${{ steps.meta.outputs.version }}" - STABILITY="${{ steps.meta.outputs.stability }}" - SHA256="${{ steps.package.outputs.sha256_zip }}" - - if [ ! -f "updates.xml" ]; then - echo "No updates.xml -- skipping" - exit 0 - fi - - SHA_FLAG="" - [ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}" - - php ${MOKO_CLI}/updates_xml_build.php \ - --path . --version "${VERSION}" --stability "${STABILITY}" \ - --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \ - ${SHA_FLAG} - - # Commit and push - if ! git diff --quiet updates.xml 2>/dev/null; then - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - git add updates.xml - git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]" - git push origin HEAD 2>&1 || echo "WARNING: push failed" - fi - - - name: "Sync updates.xml to all branches" - if: steps.platform.outputs.platform == 'joomla' - run: | - CURRENT_BRANCH="${{ github.ref_name }}" - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - - for BRANCH in main dev; do - [ "$BRANCH" = "$CURRENT_BRANCH" ] && continue - echo "Syncing updates.xml -> ${BRANCH}" - git fetch origin "${BRANCH}" 2>/dev/null || continue - git checkout "origin/${BRANCH}" -- updates.xml 2>/dev/null || continue - git checkout "${CURRENT_BRANCH}" -- updates.xml - if ! git diff --quiet updates.xml 2>/dev/null; then - git add updates.xml - git commit -m "chore: sync updates.xml from ${CURRENT_BRANCH} [skip ci]" - git push origin HEAD:refs/heads/${BRANCH} 2>&1 || echo "WARNING: push to ${BRANCH} failed" - fi - git checkout "${CURRENT_BRANCH}" 2>/dev/null - done + # updates.xml is generated dynamically by MokoGitea license server + # No need to build, commit, or sync updates.xml from workflows - name: "Delete lesser pre-release channels (cascade)" continue-on-error: true -- 2.52.0 From f573ac0a77b2b60015863c7c545e9c34d883a71c Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 09:31:23 -0500 Subject: [PATCH 08/13] chore: move CLAUDE.md to .mokogitea/ directory Relocate CLAUDE.md from repo root to .mokogitea/ per project convention. Content updated with focused, repo-specific architecture and rules. Co-Authored-By: Claude Opus 4.6 (1M context) --- .mokogitea/CLAUDE.md | 63 ++++++++++++++++++++++++++++++++++++++++++++ CLAUDE.md | 42 ----------------------------- 2 files changed, 63 insertions(+), 42 deletions(-) create mode 100644 .mokogitea/CLAUDE.md delete mode 100644 CLAUDE.md diff --git a/.mokogitea/CLAUDE.md b/.mokogitea/CLAUDE.md new file mode 100644 index 0000000..1384552 --- /dev/null +++ b/.mokogitea/CLAUDE.md @@ -0,0 +1,63 @@ +# MokoOnyx + +Joomla site template — successor to MokoCassiopeia. Base template for all WaaS client deployments. + +## Quick Reference + +| Field | Value | +|---|---| +| **Element** | `tpl_mokoonyx` | +| **Type** | Joomla site template | +| **Language** | PHP 8.1+ / CSS / JS | +| **Branch** | develop on `dev`, merge to `main` (protected) | +| **Wiki** | [MokoOnyx Wiki](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/wiki) | + +## Commands + +```bash +make build # Build template ZIP +make lint # Run linters +make validate # Validate structure +make release # Full release pipeline +make minify # Minify CSS/JS assets +make clean # Clean build artifacts +composer install # Install PHP dependencies +``` + +## Architecture + +Joomla **site template** — the base layer that client theme packages override: + +- `src/templateDetails.xml` — template manifest +- `src/index.php` — main template entry point +- `src/error.php` — error page +- `src/offline.php` — maintenance page +- `src/component.php` — print/component-only layout +- `src/html/` — template overrides for core components +- `src/media/css/` — base stylesheets +- `src/media/js/` — base scripts +- `src/media/images/` — template images +- `src/language/` — translations + +### Client Theme Packages + +Client repos (`client-clarksvillefurs`, `client-optainfunding`, etc.) install `type="file"` packages that overlay client-specific CSS, images, and JS into the MokoOnyx media directory. MokoOnyx provides the structure; client themes customize the appearance. + +### Minification + +`MokoMinifyHelper` handles runtime CSS/JS minification in Joomla. Build-time minification via `make minify`. Never commit `*.min.css`/`*.min.js` — they're generated. + +## Rules + +- **Never commit** `.claude/`, `.mcp.json`, `TODO.md`, `*.min.css`/`*.min.js` +- **Attribution**: `Authored-by: Moko Consulting` +- **Workflow directory**: `.mokogitea/` (not `.gitea/` or `.github/`) +- **Minification**: handled at build time (CI) and runtime (MokoMinifyHelper) +- **Wiki**: documentation lives in the Gitea wiki, not `docs/` files +- **Standards**: [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home) + +## Coding Standards + +- PHP 8.1+ minimum +- SPDX license headers on all PHP files +- `defined('_JEXEC') or die;` on all PHP files diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 6bf367c..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,42 +0,0 @@ -# CLAUDE.md - -This file provides guidance to Claude Code when working with this repository. - -## Project Overview - -**MokoOnyx** -- MokoOnyx - Joomla site template (successor to MokoCassiopeia) - -| Field | Value | -|---|---| -| **Platform** | joomla | -| **Language** | PHP | -| **Default branch** | main | -| **License** | GPL-3.0-or-later | -| **Wiki** | [MokoOnyx Wiki](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/wiki) | -| **Standards** | [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home) | - -## Common Commands - -```bash -composer install # Install PHP dependencies -``` - -## Architecture - -This is a Joomla extension. Key directories: -- `src/` -- extension source (deployed to Joomla) -- `src/*.xml` -- manifest file (version, files, params) -- `src/src/` or `src/services/` -- PHP classes -- `src/language/` -- translation strings -- `src/media/` -- CSS/JS/images - -## Rules - -- **Workflow directory**: `.mokogitea/` (not `.gitea/` or `.github/`) - -- **Never commit** `.claude/`, `.mcp.json`, `TODO.md`, or `*.min.css`/`*.min.js` -- **Attribution**: use `Authored-by: Moko Consulting` in commits -- **Branch strategy**: develop on `dev`, merge to `main` for release -- **Minification**: handled at build time (CI) and runtime (MokoMinifyHelper for Joomla templates) -- **Wiki**: documentation lives in the Gitea wiki, not in `docs/` files -- **Standards**: this repo follows [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home) -- 2.52.0 From 32cb7cecd6be072f473060d0ad4e9b4094fd8e0c Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 6 Jun 2026 19:48:12 +0000 Subject: [PATCH 09/13] chore: sync .mokogitea/workflows/pre-release.yml from moko-platform [skip ci] --- .mokogitea/workflows/pre-release.yml | 484 ++++++++++++++------------- 1 file changed, 243 insertions(+), 241 deletions(-) diff --git a/.mokogitea/workflows/pre-release.yml b/.mokogitea/workflows/pre-release.yml index 1a9eeef..9615a4e 100644 --- a/.mokogitea/workflows/pre-release.yml +++ b/.mokogitea/workflows/pre-release.yml @@ -1,241 +1,243 @@ -# Copyright (C) 2026 Moko Consulting -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: moko-platform.Release -# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform -# PATH: /templates/workflows/universal/pre-release.yml.template -# VERSION: 05.01.00 -# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch - -name: "Universal: Pre-Release" - -on: - pull_request: - types: [closed] - branches: - - dev - pull_request_target: - types: [synchronize, opened, reopened] - branches: - - main - workflow_dispatch: - inputs: - stability: - description: 'Pre-release channel' - required: true - type: choice - options: - - development - - alpha - - beta - - release-candidate - -permissions: - contents: write - -env: - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} - GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} - -jobs: - build: - name: "Build Pre-Release (${{ inputs.stability || 'development' }})" - runs-on: release - if: >- - github.event_name == 'workflow_dispatch' || - (github.event_name == 'pull_request' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev') || - (github.event_name == 'pull_request_target' && github.event.pull_request.base.ref == 'main') - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - token: ${{ secrets.MOKOGITEA_TOKEN }} - ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || '' }} - - - name: Setup moko-platform tools - env: - MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting - run: | - # Use pre-installed /opt/moko-platform if available (updated by cron every 6h) - if [ -f “/opt/moko-platform/cli/version_bump.php” ] && [ -f “/opt/moko-platform/vendor/autoload.php” ]; then - echo “Using pre-installed /opt/moko-platform” - echo “MOKO_CLI=/opt/moko-platform/cli” >> “$GITHUB_ENV” - else - echo “Falling back to fresh clone” - if ! command -v composer &> /dev/null; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 - fi - rm -rf /tmp/moko-platform-api - git clone --depth 1 --branch main --quiet \ - “https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git” \ - /tmp/moko-platform-api - cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet - echo “MOKO_CLI=/tmp/moko-platform-api/cli” >> “$GITHUB_ENV” - fi - - - name: Detect platform - id: platform - run: | - php ${MOKO_CLI}/manifest_read.php --path . --github-output - - - name: Resolve metadata and bump version - id: meta - run: | - # Auto-detect stability: RC for PRs targeting main, else use input or default to development - if [ "${{ github.event_name }}" = "pull_request_target" ] && [ "${{ github.event.pull_request.base.ref }}" = "main" ]; then - STABILITY="release-candidate" - else - STABILITY="${{ inputs.stability || 'development' }}" - fi - - case "$STABILITY" in - development) SUFFIX="-dev"; TAG="development" ;; - alpha) SUFFIX="-alpha"; TAG="alpha" ;; - beta) SUFFIX="-beta"; TAG="beta" ;; - release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;; - esac - - # Bump version via CLI: patch for dev/alpha/beta, minor for RC - case "$STABILITY" in - release-candidate) BUMP="minor" ;; - *) BUMP="patch" ;; - esac - - php ${MOKO_CLI}/version_bump.php --path . $([ "$BUMP" = "minor" ] && echo "--minor") 2>/dev/null || true - - # Set stability suffix and verify consistency - VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01") - VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//') - - php ${MOKO_CLI}/version_set_platform.php \ - --path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true - php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true - - # Append suffix for output - if [ -n "$SUFFIX" ]; then - VERSION="${VERSION}${SUFFIX}" - fi - - # Commit version bump - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" - git add -A - git diff --cached --quiet || { - git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]" - git push origin HEAD 2>&1 - } - - # Auto-detect element via manifest_element.php - php ${MOKO_CLI}/manifest_element.php \ - --path . --version "$VERSION" --stability "$STABILITY" \ - --repo "${GITEA_REPO}" --github-output - - # Read back element outputs - EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) - ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) - [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') - [ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip" - - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" - echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT" - echo "tag=${TAG}" >> "$GITHUB_OUTPUT" - echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT" - echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT" - - echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ===" - - - name: Create release - id: release - run: | - TAG="${{ steps.meta.outputs.tag }}" - VERSION="${{ steps.meta.outputs.version }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php ${MOKO_CLI}/release_create.php \ - --path . --version "$VERSION" --tag "$TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --branch dev --prerelease - - - name: Update release notes from CHANGELOG.md - run: | - TAG="${{ steps.meta.outputs.tag }}" - VERSION="${{ steps.meta.outputs.version }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - - # Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading) - if [ -f "CHANGELOG.md" ]; then - NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md) - [ -z "$NOTES" ] && NOTES="Release ${VERSION}" - else - NOTES="Release ${VERSION}" - fi - - # Update release body via API - RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ - "${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) - - if [ -n "$RELEASE_ID" ]; then - python3 -c " - import json, urllib.request - body = open('/dev/stdin').read() - payload = json.dumps({'body': body}).encode() - req = urllib.request.Request( - '${API_BASE}/releases/${RELEASE_ID}', - data=payload, method='PATCH', - headers={ - 'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}', - 'Content-Type': 'application/json' - }) - urllib.request.urlopen(req) - " <<< "$NOTES" - echo "Release notes updated from CHANGELOG.md" - fi - - - name: Build package and upload - id: package - run: | - VERSION="${{ steps.meta.outputs.version }}" - TAG="${{ steps.meta.outputs.tag }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php ${MOKO_CLI}/release_package.php \ - --path . --version "$VERSION" --tag "$TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --output /tmp || true - - # updates.xml is generated dynamically by MokoGitea license server - # No need to build, commit, or sync updates.xml from workflows - - - name: "Delete lesser pre-release channels (cascade)" - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - - php ${MOKO_CLI}/release_cascade.php \ - --stability "${{ steps.meta.outputs.stability }}" \ - --token "${TOKEN}" \ - --api-base "${API_BASE}" - - - name: Summary - if: always() - run: | - VERSION="${{ steps.meta.outputs.version }}" - STABILITY="${{ steps.meta.outputs.stability }}" - ZIP_NAME="${{ steps.meta.outputs.zip_name }}" - SHA256="${{ steps.package.outputs.sha256_zip }}" - echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY - echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY - echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY - echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY - echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY +# Copyright (C) 2026 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Gitea.Workflow +# INGROUP: moko-platform.Release +# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform +# PATH: /templates/workflows/universal/pre-release.yml.template +# VERSION: 05.01.00 +# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch + +name: "Universal: Pre-Release" + +on: + pull_request: + types: [closed] + branches: + - dev + pull_request_target: + types: [synchronize, opened, reopened] + branches: + - main + workflow_dispatch: + inputs: + stability: + description: 'Pre-release channel' + required: true + type: choice + options: + - development + - alpha + - beta + - release-candidate + +permissions: + contents: write + +env: + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} + GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} + +jobs: + build: + name: "Build Pre-Release (${{ inputs.stability || 'development' }})" + runs-on: release + if: >- + github.event_name == 'workflow_dispatch' || + (github.event_name == 'pull_request' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev') || + (github.event_name == 'pull_request_target' && github.event.pull_request.base.ref == 'main') + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.MOKOGITEA_TOKEN }} + ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || '' }} + + - name: Setup moko-platform tools + env: + MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting + run: | + # Use pre-installed /opt/moko-platform if available (updated by cron every 6h) + if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/cli/manifest_element.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then + echo Using pre-installed /opt/moko-platform + echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV + else + echo Falling back to fresh clone + if ! command -v composer > /dev/null 2>&1; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1 + fi + rm -rf /tmp/moko-platform-api + CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git + git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api + cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet + echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV + fi + + - name: Detect platform + id: platform + run: | + php ${MOKO_CLI}/manifest_read.php --path . --github-output + + - name: Resolve metadata and bump version + id: meta + run: | + # Auto-detect stability: RC for PRs targeting main, else use input or default to development + if [ "${{ github.event_name }}" = "pull_request_target" ] && [ "${{ github.event.pull_request.base.ref }}" = "main" ]; then + STABILITY="release-candidate" + else + STABILITY="${{ inputs.stability || 'development' }}" + fi + + case "$STABILITY" in + development) SUFFIX="-dev"; TAG="development" ;; + alpha) SUFFIX="-alpha"; TAG="alpha" ;; + beta) SUFFIX="-beta"; TAG="beta" ;; + release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;; + esac + + # Bump version via CLI: patch for dev/alpha/beta, minor for RC + case "$STABILITY" in + release-candidate) BUMP="minor" ;; + *) BUMP="patch" ;; + esac + + php ${MOKO_CLI}/version_bump.php --path . $([ "$BUMP" = "minor" ] && echo "--minor") 2>/dev/null || true + + # Set stability suffix and verify consistency + VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01") + VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//') + + php ${MOKO_CLI}/version_set_platform.php \ + --path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true + php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true + + # Ensure licensing tags (updateservers, dlid) if enabled in manifest.xml + php ${MOKO_CLI}/manifest_licensing.php --path . --fix 2>/dev/null || true + + # Append suffix for output + if [ -n "$SUFFIX" ]; then + VERSION="${VERSION}${SUFFIX}" + fi + + # Commit version bump + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" + git config --local user.name "gitea-actions[bot]" + git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" + git add -A + git diff --cached --quiet || { + git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]" + git push origin HEAD 2>&1 + } + + # Auto-detect element via manifest_element.php + php ${MOKO_CLI}/manifest_element.php \ + --path . --version "$VERSION" --stability "$STABILITY" \ + --repo "${GITEA_REPO}" --github-output + + # Read back element outputs + EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) + ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) + [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') + [ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip" + + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" + echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT" + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT" + echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT" + + echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ===" + + - name: Create release + id: release + run: | + TAG="${{ steps.meta.outputs.tag }}" + VERSION="${{ steps.meta.outputs.version }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php ${MOKO_CLI}/release_create.php \ + --path . --version "$VERSION" --tag "$TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --repo "${GITEA_REPO}" --branch dev --prerelease + + - name: Update release notes from CHANGELOG.md + run: | + TAG="${{ steps.meta.outputs.tag }}" + VERSION="${{ steps.meta.outputs.version }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + + # Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading) + if [ -f "CHANGELOG.md" ]; then + NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md) + [ -z "$NOTES" ] && NOTES="Release ${VERSION}" + else + NOTES="Release ${VERSION}" + fi + + # Update release body via API + RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ + "${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) + + if [ -n "$RELEASE_ID" ]; then + python3 -c " + import json, urllib.request + body = open('/dev/stdin').read() + payload = json.dumps({'body': body}).encode() + req = urllib.request.Request( + '${API_BASE}/releases/${RELEASE_ID}', + data=payload, method='PATCH', + headers={ + 'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}', + 'Content-Type': 'application/json' + }) + urllib.request.urlopen(req) + " <<< "$NOTES" + echo "Release notes updated from CHANGELOG.md" + fi + + - name: Build package and upload + id: package + run: | + VERSION="${{ steps.meta.outputs.version }}" + TAG="${{ steps.meta.outputs.tag }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php ${MOKO_CLI}/release_package.php \ + --path . --version "$VERSION" --tag "$TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --repo "${GITEA_REPO}" --output /tmp || true + + # updates.xml is generated dynamically by MokoGitea license server + # No need to build, commit, or sync updates.xml from workflows + + - name: "Delete lesser pre-release channels (cascade)" + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" + + php ${MOKO_CLI}/release_cascade.php \ + --stability "${{ steps.meta.outputs.stability }}" \ + --token "${TOKEN}" \ + --api-base "${API_BASE}" + + - name: Summary + if: always() + run: | + VERSION="${{ steps.meta.outputs.version }}" + STABILITY="${{ steps.meta.outputs.stability }}" + ZIP_NAME="${{ steps.meta.outputs.zip_name }}" + SHA256="${{ steps.package.outputs.sha256_zip }}" + echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY + echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY + echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY + echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY + echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY -- 2.52.0 From 59875466f4133de2957c620f4638303855950279 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 6 Jun 2026 19:50:39 +0000 Subject: [PATCH 10/13] chore: sync .mokogitea/workflows/auto-release.yml from moko-platform [skip ci] --- .mokogitea/workflows/auto-release.yml | 101 ++++++++++++++++++-------- 1 file changed, 70 insertions(+), 31 deletions(-) diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 44a2d64..ca40435 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -17,7 +17,7 @@ # | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. | # | | # | Platform-specific: | -# | joomla: XML manifest, updates.xml, type-prefixed packages | +# | joomla: XML manifest, type-prefixed packages | # | dolibarr: mod*.class.php, update.txt, dev version reset | # | generic: README-only, no update stream | # | | @@ -71,20 +71,25 @@ jobs: MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting run: | - if ! command -v composer &> /dev/null; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 + if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then + echo Using pre-installed /opt/moko-platform + echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV + else + echo Falling back to fresh clone + if ! command -v composer > /dev/null 2>&1; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1 + fi + rm -rf /tmp/moko-platform-api + CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git + git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api + cd /tmp/moko-platform-api + composer install --no-dev --no-interaction --quiet + echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV fi - # Always fetch latest CLI tools — never use stale cache from previous runs - rm -rf /tmp/moko-platform-api - git clone --depth 1 --branch main --quiet \ - "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ - /tmp/moko-platform-api - cd /tmp/moko-platform-api - composer install --no-dev --no-interaction --quiet - name: Rename branch to rc run: | - php /tmp/moko-platform-api/cli/branch_rename.php \ + php ${MOKO_CLI}/branch_rename.php \ --from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \ --token "${{ secrets.MOKOGITEA_TOKEN }}" \ --api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \ @@ -100,16 +105,15 @@ jobs: - name: Publish RC release run: | - php /tmp/moko-platform-api/cli/release_publish.php \ + php ${MOKO_CLI}/release_publish.php \ --path . --stability rc --bump minor --branch rc \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --skip-update-stream + --token "${{ secrets.MOKOGITEA_TOKEN }}" - name: Summary if: always() run: | echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY - echo "Branch renamed to rc, minor bump, RC release built (updates.xml managed by Gitea Pages)" >> $GITHUB_STEP_SUMMARY + echo "Branch renamed to rc, minor bump, RC release built" >> $GITHUB_STEP_SUMMARY # ── Merged PR → Build & Release (or promote RC to stable) ──────────────────── release: @@ -151,25 +155,60 @@ jobs: MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}' run: | - # Ensure PHP + Composer are available - if ! command -v composer &> /dev/null; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 + if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then + echo Using pre-installed /opt/moko-platform + echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV + else + echo Falling back to fresh clone + if ! command -v composer > /dev/null 2>&1; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1 + fi + rm -rf /tmp/moko-platform-api + CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git + git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api + cd /tmp/moko-platform-api + composer install --no-dev --no-interaction --quiet + echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV fi - # Always fetch latest CLI tools — never use stale cache from previous runs - rm -rf /tmp/moko-platform-api - git clone --depth 1 --branch main --quiet \ - "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ - /tmp/moko-platform-api - cd /tmp/moko-platform-api - composer install --no-dev --no-interaction --quiet - - name: "Publish stable release" run: | - php /tmp/moko-platform-api/cli/release_publish.php \ + php ${MOKO_CLI}/release_publish.php \ --path . --stability stable --bump minor --branch main \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --skip-update-stream + --token "${{ secrets.MOKOGITEA_TOKEN }}" + + - name: Update release notes from CHANGELOG.md + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + + # Extract [Unreleased] section from changelog + if [ -f "CHANGELOG.md" ]; then + NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md) + [ -z "$NOTES" ] && NOTES="Stable release" + else + NOTES="Stable release" + fi + + # Update release body via API + RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ + "${API_BASE}/releases/tags/stable" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) + + if [ -n "$RELEASE_ID" ]; then + python3 -c " + import json, urllib.request + body = open('/dev/stdin').read() + payload = json.dumps({'body': body}).encode() + req = urllib.request.Request( + '${API_BASE}/releases/${RELEASE_ID}', + data=payload, method='PATCH', + headers={ + 'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}', + 'Content-Type': 'application/json' + }) + urllib.request.urlopen(req) + " <<< "$NOTES" + echo "Release notes updated from CHANGELOG.md" + fi # -- STEP 9: Mirror to GitHub (stable only) -------------------------------- - name: "Step 9: Mirror release to GitHub" @@ -182,7 +221,7 @@ jobs: RELEASE_TAG="${{ steps.version.outputs.release_tag }}" GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/release_mirror.php \ + php ${MOKO_CLI}/release_mirror.php \ --version "$VERSION" --tag "$RELEASE_TAG" \ --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ --gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \ @@ -256,7 +295,7 @@ jobs: continue-on-error: true run: | API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/version_reset_dev.php \ + php ${MOKO_CLI}/version_reset_dev.php \ --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \ --branch dev --path . 2>&1 || true -- 2.52.0 From ce9b5f0e5c4dbc53261c95ce4439f7f0706ee6b0 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 17:21:27 -0500 Subject: [PATCH 11/13] feat: download key preservation + license key warning (MokoWaaS pattern) --- src/script.php | 107 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/src/script.php b/src/script.php index 839642f..17e6f1e 100644 --- a/src/script.php +++ b/src/script.php @@ -34,6 +34,9 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface private const OLD_DISPLAY = 'MokoCassiopeia'; private const NEW_DISPLAY = 'MokoOnyx'; + /** @var array Download keys saved before Joomla wipes update sites */ + private array $savedDownloadKeys = []; + public function preflight(string $type, InstallerAdapter $parent): bool { if (version_compare(PHP_VERSION, self::MIN_PHP, '<')) { @@ -83,11 +86,16 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface public function uninstall(InstallerAdapter $parent): bool { $this->logMessage('MokoOnyx template uninstalled.'); + $this->savedDownloadKeys = $this->backupDownloadKeys(); + return true; } public function postflight(string $type, InstallerAdapter $parent): bool { + $this->restoreDownloadKeys($this->savedDownloadKeys); + $this->warnMissingLicenseKey(); + if ($type === 'install' || $type === 'update') { $this->migrateFromCassiopeia(); $this->replaceCassiopeiaReferences(); @@ -684,4 +692,103 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface Log::add($message, $priorities[$priority] ?? Log::INFO, 'mokoonyx'); } + + private function backupDownloadKeys(): array + { + $keys = []; + + try + { + $db = Factory::getDbo(); + $db->setQuery( + $db->getQuery(true) + ->select(['us.' . $db->quoteName('update_site_id'), 'us.' . $db->quoteName('extra_query'), 'us.' . $db->quoteName('location'), 'e.' . $db->quoteName('element')]) + ->from($db->quoteName('#__update_sites', 'us')) + ->join('INNER', $db->quoteName('#__update_sites_extensions', 'use') . ' ON us.update_site_id = use.update_site_id') + ->join('INNER', $db->quoteName('#__extensions', 'e') . ' ON e.extension_id = use.extension_id') + ->where($db->quoteName('us.extra_query') . ' != ' . $db->quote('')) + ); + + foreach ($db->loadObjectList() ?: [] as $row) + { + $keys['elem_' . $row->element] = $row->extra_query; + $keys[$row->location] = $row->extra_query; + $keys['id_' . $row->update_site_id] = $row->extra_query; + } + } + catch (\Throwable $e) {} + + return $keys; + } + + private function restoreDownloadKeys(array $savedKeys): void + { + if (empty($savedKeys)) { return; } + + try + { + $db = Factory::getDbo(); + $db->setQuery( + $db->getQuery(true) + ->select(['us.' . $db->quoteName('update_site_id'), 'us.' . $db->quoteName('extra_query'), 'us.' . $db->quoteName('location'), 'e.' . $db->quoteName('element')]) + ->from($db->quoteName('#__update_sites', 'us')) + ->join('LEFT', $db->quoteName('#__update_sites_extensions', 'use') . ' ON us.update_site_id = use.update_site_id') + ->join('LEFT', $db->quoteName('#__extensions', 'e') . ' ON e.extension_id = use.extension_id') + ->where('(' . $db->quoteName('us.extra_query') . ' = ' . $db->quote('') . ' OR ' . $db->quoteName('us.extra_query') . ' NOT LIKE ' . $db->quote('%dlid=%') . ')') + ); + + $restored = 0; + + foreach ($db->loadObjectList() ?: [] as $site) + { + $element = (string) ($site->element ?? ''); + $key = ($element !== '') ? ($savedKeys['elem_' . $element] ?? '') : ''; + if (empty($key)) { $key = $savedKeys[$site->location] ?? $savedKeys['id_' . $site->update_site_id] ?? ''; } + + if (!empty($key)) + { + $db->setQuery($db->getQuery(true)->update($db->quoteName('#__update_sites'))->set($db->quoteName('extra_query') . ' = ' . $db->quote($key))->where($db->quoteName('update_site_id') . ' = ' . (int) $site->update_site_id))->execute(); + $restored++; + } + } + + if ($restored > 0) { Factory::getApplication()->enqueueMessage(sprintf('Restored %d download key(s).', $restored), 'message'); } + } + catch (\Throwable $e) {} + } + + private function warnMissingLicenseKey(): void + { + try + { + $db = Factory::getDbo(); + $db->setQuery( + $db->getQuery(true) + ->select([$db->quoteName('update_site_id'), $db->quoteName('extra_query')]) + ->from($db->quoteName('#__update_sites')) + ->where('(' . $db->quoteName('name') . ' LIKE ' . $db->quote('%MokoOnyx%') . ' OR ' . $db->quoteName('location') . ' LIKE ' . $db->quote('%MokoOnyx%') . ')') + ->setLimit(1) + ); + $site = $db->loadObject(); + + if ($site) + { + $eq = (string) ($site->extra_query ?? ''); + if (!empty($eq) && strpos($eq, 'dlid=') !== false) { parse_str($eq, $p); if (!empty($p['dlid'])) { return; } } + $editUrl = 'index.php?option=com_installer&task=updatesite.edit&update_site_id=' . (int) $site->update_site_id; + } + else + { + $editUrl = 'index.php?option=com_installer&view=updatesites'; + } + + Factory::getApplication()->enqueueMessage( + 'Moko Consulting License Key Required — ' + . 'No download key is configured. Updates will not be available until a valid license key is entered. ' + . 'Enter License Key', + 'warning' + ); + } + catch (\Throwable $e) {} + } } -- 2.52.0 From cebbb33d87d00b0e7d34845366c44a8abc343679 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 17:32:31 -0500 Subject: [PATCH 12/13] fix: use single-key pattern instead of universal backup --- src/script.php | 179 +++++++++++++++++++++++-------------------------- 1 file changed, 84 insertions(+), 95 deletions(-) diff --git a/src/script.php b/src/script.php index 17e6f1e..5496c17 100644 --- a/src/script.php +++ b/src/script.php @@ -34,8 +34,8 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface private const OLD_DISPLAY = 'MokoCassiopeia'; private const NEW_DISPLAY = 'MokoOnyx'; - /** @var array Download keys saved before Joomla wipes update sites */ - private array $savedDownloadKeys = []; + + public function preflight(string $type, InstallerAdapter $parent): bool { @@ -86,14 +86,14 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface public function uninstall(InstallerAdapter $parent): bool { $this->logMessage('MokoOnyx template uninstalled.'); - $this->savedDownloadKeys = $this->backupDownloadKeys(); + $this->saveDownloadKey(); return true; } public function postflight(string $type, InstallerAdapter $parent): bool { - $this->restoreDownloadKeys($this->savedDownloadKeys); + $this->restoreDownloadKey(); $this->warnMissingLicenseKey(); if ($type === 'install' || $type === 'update') { @@ -693,102 +693,91 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface Log::add($message, $priorities[$priority] ?? Log::INFO, 'mokoonyx'); } - private function backupDownloadKeys(): array - { - $keys = []; - try - { - $db = Factory::getDbo(); - $db->setQuery( - $db->getQuery(true) - ->select(['us.' . $db->quoteName('update_site_id'), 'us.' . $db->quoteName('extra_query'), 'us.' . $db->quoteName('location'), 'e.' . $db->quoteName('element')]) - ->from($db->quoteName('#__update_sites', 'us')) - ->join('INNER', $db->quoteName('#__update_sites_extensions', 'use') . ' ON us.update_site_id = use.update_site_id') - ->join('INNER', $db->quoteName('#__extensions', 'e') . ' ON e.extension_id = use.extension_id') - ->where($db->quoteName('us.extra_query') . ' != ' . $db->quote('')) - ); + private ?string $savedDownloadKey = null; - foreach ($db->loadObjectList() ?: [] as $row) - { - $keys['elem_' . $row->element] = $row->extra_query; - $keys[$row->location] = $row->extra_query; - $keys['id_' . $row->update_site_id] = $row->extra_query; - } - } - catch (\Throwable $e) {} + private function saveDownloadKey(): void + { + try + { + $db = \Joomla\CMS\Factory::getDbo(); + $db->setQuery( + $db->getQuery(true) + ->select($db->quoteName('us.extra_query')) + ->from($db->quoteName('#__update_sites', 'us')) + ->join('INNER', $db->quoteName('#__update_sites_extensions', 'use') . ' ON use.update_site_id = us.update_site_id') + ->join('INNER', $db->quoteName('#__extensions', 'e') . ' ON e.extension_id = use.extension_id') + ->where($db->quoteName('e.element') . ' = ' . $db->quote('mokoonyx')) + ->setLimit(1) + ); + $key = $db->loadResult(); + if (!empty($key)) { $this->savedDownloadKey = $key; } + } + catch (\Throwable $e) {} + } - return $keys; - } + private function restoreDownloadKey(): void + { + if ($this->savedDownloadKey === null) { return; } - private function restoreDownloadKeys(array $savedKeys): void - { - if (empty($savedKeys)) { return; } + try + { + $db = \Joomla\CMS\Factory::getDbo(); + $db->setQuery( + $db->getQuery(true) + ->select($db->quoteName('us.update_site_id')) + ->from($db->quoteName('#__update_sites', 'us')) + ->join('INNER', $db->quoteName('#__update_sites_extensions', 'use') . ' ON use.update_site_id = us.update_site_id') + ->join('INNER', $db->quoteName('#__extensions', 'e') . ' ON e.extension_id = use.extension_id') + ->where($db->quoteName('e.element') . ' = ' . $db->quote('mokoonyx')) + ->setLimit(1) + ); + $siteId = (int) $db->loadResult(); + if ($siteId > 0) + { + $db->setQuery( + $db->getQuery(true) + ->update($db->quoteName('#__update_sites')) + ->set($db->quoteName('extra_query') . ' = ' . $db->quote($this->savedDownloadKey)) + ->where($db->quoteName('update_site_id') . ' = ' . $siteId) + )->execute(); + } + } + catch (\Throwable $e) {} + } - try - { - $db = Factory::getDbo(); - $db->setQuery( - $db->getQuery(true) - ->select(['us.' . $db->quoteName('update_site_id'), 'us.' . $db->quoteName('extra_query'), 'us.' . $db->quoteName('location'), 'e.' . $db->quoteName('element')]) - ->from($db->quoteName('#__update_sites', 'us')) - ->join('LEFT', $db->quoteName('#__update_sites_extensions', 'use') . ' ON us.update_site_id = use.update_site_id') - ->join('LEFT', $db->quoteName('#__extensions', 'e') . ' ON e.extension_id = use.extension_id') - ->where('(' . $db->quoteName('us.extra_query') . ' = ' . $db->quote('') . ' OR ' . $db->quoteName('us.extra_query') . ' NOT LIKE ' . $db->quote('%dlid=%') . ')') - ); + private function warnMissingLicenseKey(): void + { + try + { + $db = \Joomla\CMS\Factory::getDbo(); + $db->setQuery( + $db->getQuery(true) + ->select([$db->quoteName('update_site_id'), $db->quoteName('extra_query')]) + ->from($db->quoteName('#__update_sites')) + ->where('(' . $db->quoteName('name') . ' LIKE ' . $db->quote('%MokoOnyx%') . ' OR ' . $db->quoteName('location') . ' LIKE ' . $db->quote('%MokoOnyx%') . ')') + ->setLimit(1) + ); + $site = $db->loadObject(); - $restored = 0; + if ($site) + { + $eq = (string) ($site->extra_query ?? ''); + if (!empty($eq) && strpos($eq, 'dlid=') !== false) { parse_str($eq, $p); if (!empty($p['dlid'])) { return; } } + $editUrl = 'index.php?option=com_installer&task=updatesite.edit&update_site_id=' . (int) $site->update_site_id; + } + else + { + $editUrl = 'index.php?option=com_installer&view=updatesites'; + } - foreach ($db->loadObjectList() ?: [] as $site) - { - $element = (string) ($site->element ?? ''); - $key = ($element !== '') ? ($savedKeys['elem_' . $element] ?? '') : ''; - if (empty($key)) { $key = $savedKeys[$site->location] ?? $savedKeys['id_' . $site->update_site_id] ?? ''; } - - if (!empty($key)) - { - $db->setQuery($db->getQuery(true)->update($db->quoteName('#__update_sites'))->set($db->quoteName('extra_query') . ' = ' . $db->quote($key))->where($db->quoteName('update_site_id') . ' = ' . (int) $site->update_site_id))->execute(); - $restored++; - } - } - - if ($restored > 0) { Factory::getApplication()->enqueueMessage(sprintf('Restored %d download key(s).', $restored), 'message'); } - } - catch (\Throwable $e) {} - } - - private function warnMissingLicenseKey(): void - { - try - { - $db = Factory::getDbo(); - $db->setQuery( - $db->getQuery(true) - ->select([$db->quoteName('update_site_id'), $db->quoteName('extra_query')]) - ->from($db->quoteName('#__update_sites')) - ->where('(' . $db->quoteName('name') . ' LIKE ' . $db->quote('%MokoOnyx%') . ' OR ' . $db->quoteName('location') . ' LIKE ' . $db->quote('%MokoOnyx%') . ')') - ->setLimit(1) - ); - $site = $db->loadObject(); - - if ($site) - { - $eq = (string) ($site->extra_query ?? ''); - if (!empty($eq) && strpos($eq, 'dlid=') !== false) { parse_str($eq, $p); if (!empty($p['dlid'])) { return; } } - $editUrl = 'index.php?option=com_installer&task=updatesite.edit&update_site_id=' . (int) $site->update_site_id; - } - else - { - $editUrl = 'index.php?option=com_installer&view=updatesites'; - } - - Factory::getApplication()->enqueueMessage( - 'Moko Consulting License Key Required — ' - . 'No download key is configured. Updates will not be available until a valid license key is entered. ' - . 'Enter License Key', - 'warning' - ); - } - catch (\Throwable $e) {} - } + \Joomla\CMS\Factory::getApplication()->enqueueMessage( + 'Moko Consulting License Key Required — ' + . 'No download key is configured. Updates will not be available until a valid license key is entered. ' + . 'Enter License Key', + 'warning' + ); + } + catch (\Throwable $e) {} + } } -- 2.52.0 From 6c6f813c26f7ca43b4ffd51c2cfc4a13e7972a97 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sun, 7 Jun 2026 01:10:24 +0000 Subject: [PATCH 13/13] chore(version): pre-release bump to 02.19.07-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 4 ++-- SECURITY.md | 2 +- src/html/layouts/joomla/module/card.php | 2 +- src/html/layouts/mokoonyx/article-metadata.php | 2 +- src/media/css/a11y-high-contrast.css | 2 +- src/templateDetails.xml | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index dea8d4e..d7e107c 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -9,7 +9,7 @@ Template - MokoOnyx MokoConsulting MokoOnyx - Joomla site template (successor to MokoCassiopeia) - 02.19.06 + 02.19.07 GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index 5906d98..3a44f6a 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 02.19.06 +# VERSION: 02.19.07 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a0bcae..ce16c15 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,11 +8,11 @@ DEFGROUP: Joomla.Template.Site INGROUP: MokoOnyx.Documentation PATH: ./CHANGELOG.md - VERSION: 02.19.06 + VERSION: 02.19.07 BRIEF: Changelog file documenting version history of MokoOnyx --> -# Changelog — MokoOnyx (VERSION: 02.19.06) +# Changelog — MokoOnyx (VERSION: 02.19.07) ## [Unreleased] ### Fixed diff --git a/SECURITY.md b/SECURITY.md index 325251f..343ccbc 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -10,7 +10,7 @@ INGROUP: MokoOnyx.Governance REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx FILE: SECURITY.md - VERSION: 02.19.06 + VERSION: 02.19.07 BRIEF: Security policy and vulnerability reporting process for MokoOnyx. PATH: /SECURITY.md NOTE: This policy is process oriented and does not replace secure engineering practices. diff --git a/src/html/layouts/joomla/module/card.php b/src/html/layouts/joomla/module/card.php index cf2ec1d..0c1a69c 100644 --- a/src/html/layouts/joomla/module/card.php +++ b/src/html/layouts/joomla/module/card.php @@ -10,7 +10,7 @@ * INGROUP: MokoOnyx * REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx * PATH: /html/layouts/joomla/module/card.php - * VERSION: 02.19.06 + * VERSION: 02.19.07 * BRIEF: Custom card module chrome — renders module titles for all modules */ diff --git a/src/html/layouts/mokoonyx/article-metadata.php b/src/html/layouts/mokoonyx/article-metadata.php index 020bff6..e80034c 100644 --- a/src/html/layouts/mokoonyx/article-metadata.php +++ b/src/html/layouts/mokoonyx/article-metadata.php @@ -11,7 +11,7 @@ * INGROUP: MokoOnyx.Layouts * REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx * PATH: /src/html/layouts/mokoonyx/article-metadata.php - * VERSION: 02.19.06 + * VERSION: 02.19.07 * BRIEF: Article metadata footer layout -- renders jcfields grouped by field group */ diff --git a/src/media/css/a11y-high-contrast.css b/src/media/css/a11y-high-contrast.css index 5c2b707..2bd790d 100644 --- a/src/media/css/a11y-high-contrast.css +++ b/src/media/css/a11y-high-contrast.css @@ -10,7 +10,7 @@ * INGROUP: MokoOnyx.Accessibility * REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx * PATH: ./media/css/a11y-high-contrast.css - * VERSION: 02.19.06 + * VERSION: 02.19.07 * BRIEF: High-contrast stylesheet for accessibility toolbar */ diff --git a/src/templateDetails.xml b/src/templateDetails.xml index 0517138..d2b770d 100644 --- a/src/templateDetails.xml +++ b/src/templateDetails.xml @@ -35,7 +35,7 @@ mokoonyx - 02.19.06-dev + 02.19.07-dev script.php 2026-05-16 Jonathan Miller || Moko Consulting -- 2.52.0