From 7d2ca3607bed640fec764645bf4ae3ea272950b3 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Thu, 28 May 2026 22:31:56 +0000 Subject: [PATCH 01/12] chore(version): auto-bump patch 02.20.01-dev [skip ci] --- README.md | 2 +- src/packages/com_mokowaas/mokowaas.xml | 2 +- src/packages/plg_system_mokowaas/mokowaas.xml | 2 +- src/packages/plg_webservices_mokowaas/mokowaas.xml | 2 +- .../plg_webservices_perfectpublisher/perfectpublisher.xml | 2 +- src/pkg_mokowaas.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 68c805c9..94620476 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS - VERSION: 02.20.00 + VERSION: 02.20.01 PATH: /README.md BRIEF: MokoWaaS platform plugin for Joomla --> diff --git a/src/packages/com_mokowaas/mokowaas.xml b/src/packages/com_mokowaas/mokowaas.xml index c17b0555..45bccc0a 100644 --- a/src/packages/com_mokowaas/mokowaas.xml +++ b/src/packages/com_mokowaas/mokowaas.xml @@ -7,7 +7,7 @@ GPL-3.0-or-later hello@mokoconsulting.tech https://mokoconsulting.tech - 02.18.01-dev + 02.20.01-dev Minimal API-only component for MokoWaaS. Provides REST endpoints for site health, cache, updates, and backups. Moko\Component\MokoWaaS\Api diff --git a/src/packages/plg_system_mokowaas/mokowaas.xml b/src/packages/plg_system_mokowaas/mokowaas.xml index ee97942c..f7287450 100644 --- a/src/packages/plg_system_mokowaas/mokowaas.xml +++ b/src/packages/plg_system_mokowaas/mokowaas.xml @@ -30,7 +30,7 @@ GNU General Public License version 3 or later; see LICENSE.md hello@mokoconsulting.tech https://mokoconsulting.tech - 02.18.01-dev + 02.20.01-dev This plugin rebrands the Joomla system interface with MokoWaaS identity. It applies language overrides and ensures consistent branding across the platform. Moko\Plugin\System\MokoWaaS script.php diff --git a/src/packages/plg_webservices_mokowaas/mokowaas.xml b/src/packages/plg_webservices_mokowaas/mokowaas.xml index c57e4374..3b111372 100644 --- a/src/packages/plg_webservices_mokowaas/mokowaas.xml +++ b/src/packages/plg_webservices_mokowaas/mokowaas.xml @@ -7,7 +7,7 @@ GPL-3.0-or-later hello@mokoconsulting.tech https://mokoconsulting.tech - 02.18.01-dev + 02.20.01-dev Joomla Web Services API routes for MokoWaaS site management — health checks, cache, updates, backups, and site info. Moko\Plugin\WebServices\MokoWaaS diff --git a/src/packages/plg_webservices_perfectpublisher/perfectpublisher.xml b/src/packages/plg_webservices_perfectpublisher/perfectpublisher.xml index 3dfb693a..fab07cde 100644 --- a/src/packages/plg_webservices_perfectpublisher/perfectpublisher.xml +++ b/src/packages/plg_webservices_perfectpublisher/perfectpublisher.xml @@ -7,7 +7,7 @@ GPL-3.0-or-later hello@mokoconsulting.tech https://mokoconsulting.tech - 02.18.01-dev + 02.20.01-dev Joomla Web Services API routes for Perfect Publisher (com_autotweet) — channels, posts, requests, rules, and feeds. Moko\Plugin\WebServices\PerfectPublisher diff --git a/src/pkg_mokowaas.xml b/src/pkg_mokowaas.xml index 9d220daf..01cf8855 100644 --- a/src/pkg_mokowaas.xml +++ b/src/pkg_mokowaas.xml @@ -2,7 +2,7 @@ MokoWaaS mokowaas - 02.18.01-dev + 02.20.01-dev 2026-05-23 Moko Consulting hello@mokoconsulting.tech -- 2.52.0 From 16943408b7875fcc3ae66301cf81f5b859c94f89 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Fri, 29 May 2026 10:31:53 +0000 Subject: [PATCH 02/12] chore: sync .mokogitea/workflows/auto-bump.yml from moko-platform [skip ci] --- .mokogitea/workflows/auto-bump.yml | 152 +++++++++++++---------------- 1 file changed, 67 insertions(+), 85 deletions(-) diff --git a/.mokogitea/workflows/auto-bump.yml b/.mokogitea/workflows/auto-bump.yml index dc760390..a397a9e5 100644 --- a/.mokogitea/workflows/auto-bump.yml +++ b/.mokogitea/workflows/auto-bump.yml @@ -1,85 +1,67 @@ -# 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: /.mokogitea/workflows/auto-bump.yml -# VERSION: 09.02.00 -# BRIEF: Auto patch-bump version on every push to dev (skips merge commits) - -name: "Universal: Auto Version Bump" - -on: - push: - branches: - - dev - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - -permissions: - contents: write - -jobs: - bump: - name: Version Bump - runs-on: release - if: >- - !contains(github.event.head_commit.message, '[skip ci]') && - !contains(github.event.head_commit.message, '[skip bump]') && - !startsWith(github.event.head_commit.message, 'Merge pull request') - - steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - token: ${{ secrets.MOKOGITEA_TOKEN }} - fetch-depth: 1 - - - name: Setup moko-platform tools - 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 - if [ -d "/opt/moko-platform/cli" ]; then - echo "MOKO_CLI=/opt/moko-platform/cli" >> "$GITHUB_ENV" - else - git clone --depth 1 --branch main --quiet \ - "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/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: Bump version - run: | - BUMP=$(php ${MOKO_CLI}/version_bump.php --path . 2>&1) || true - echo "$BUMP" - - VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null) || true - [ -z "$VERSION" ] && { echo "No version found — skipping"; exit 0; } - - # Propagate to platform manifests with -dev suffix - php ${MOKO_CLI}/version_set_platform.php \ - --path . --version "$VERSION" --branch dev --stability dev 2>/dev/null || true - php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true - VERSION="${VERSION}-dev" - - # Commit if anything changed - if git diff --quiet && git diff --cached --quiet; then - echo "No version changes to commit" - exit 0 - fi - - 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 commit -m "chore(version): auto-bump patch ${VERSION} [skip ci]" \ - --author="gitea-actions[bot] " - git push origin dev - echo "Bumped to ${VERSION}" >> $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: /.mokogitea/workflows/auto-bump.yml +# VERSION: 09.02.00 +# BRIEF: Auto patch-bump version on every push to dev (skips merge commits) + +name: "Universal: Auto Version Bump" + +on: + push: + branches: + - dev + - alpha + - beta + - rc + - 'feature/**' + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} + +permissions: + contents: write + +jobs: + bump: + name: Version Bump + runs-on: release + if: >- + !contains(github.event.head_commit.message, '[skip ci]') && + !contains(github.event.head_commit.message, '[skip bump]') && + !startsWith(github.event.head_commit.message, 'Merge pull request') + + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + token: ${{ secrets.MOKOGITEA_TOKEN }} + fetch-depth: 1 + + - name: Setup moko-platform tools + 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 + if [ -d "/opt/moko-platform/cli" ]; then + echo "MOKO_CLI=/opt/moko-platform/cli" >> "$GITHUB_ENV" + else + git clone --depth 1 --branch main --quiet \ + "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/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: Bump version + run: | + php ${MOKO_CLI}/version_auto_bump.php \ + --path . --branch "${GITHUB_REF_NAME}" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --repo-url "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" -- 2.52.0 From 0c964e84dcdfc6536eac769b0994b71142af94a1 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 30 May 2026 01:16:24 +0000 Subject: [PATCH 03/12] chore: sync .mokogitea/workflows/auto-release.yml from moko-platform [skip ci] --- .mokogitea/workflows/auto-release.yml | 1110 +++++++++++++------------ 1 file changed, 579 insertions(+), 531 deletions(-) diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 72ce95a6..04ec8179 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -1,531 +1,579 @@ -# 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-tech/moko-platform -# PATH: /templates/workflows/universal/auto-release.yml.template -# VERSION: 05.00.00 -# BRIEF: Universal build & release � detects platform from manifest.xml -# -# +========================================================================+ -# | UNIVERSAL BUILD & RELEASE PIPELINE | -# +========================================================================+ -# | | -# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. | -# | | -# | Platform-specific: | -# | joomla: XML manifest, updates.xml, type-prefixed packages | -# | dolibarr: mod*.class.php, update.txt, dev version reset | -# | generic: README-only, no update stream | -# | | -# +========================================================================+ - -name: "Universal: Build & Release" - -on: - pull_request: - types: [opened, closed] - branches: - - main - workflow_dispatch: - inputs: - action: - description: 'Action to perform' - required: false - type: choice - default: release - options: - - release - - promote-rc - -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: - # ── Draft PR → Promote highest pre-release to RC ───────────────────────────── - promote-rc: - name: Promote Pre-Release to RC - runs-on: release - if: >- - (github.event.action == 'opened' && github.event.pull_request.draft == true) || - (github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc') - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - token: ${{ secrets.MOKOGITEA_TOKEN }} - fetch-depth: 1 - - - name: Setup moko-platform tools - env: - 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 - 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: Promote to release-candidate - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/release_promote.php \ - --from auto --to release-candidate \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --api-base "${API_BASE}" \ - --branch "${{ github.event.pull_request.head.ref || 'dev' }}" - - - name: Cascade lesser channels - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/release_cascade.php \ - --stability release-candidate \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --api-base "${API_BASE}" - - - name: Summary - if: always() - run: | - echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY - echo "Draft PR opened — promoted highest pre-release to RC" >> $GITHUB_STEP_SUMMARY - - # ── Merged PR → Build & Release (or promote RC to stable) ──────────────────── - release: - name: Build & Release Pipeline - runs-on: release - if: >- - github.event.pull_request.merged == true || - (github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc') - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - token: ${{ secrets.MOKOGITEA_TOKEN }} - fetch-depth: 0 - - - name: Configure git for bot pushes - run: | - 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" - - - name: Setup moko-platform tools - env: - MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - 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 - 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 - - - # -- PLATFORM DETECTION --------------------------------------------------- - - name: Detect platform - id: platform - run: | - php /tmp/moko-platform-api/cli/manifest_read.php --path . --github-output - MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1 || true) - MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1 || true) - echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT" - echo "mod_file=${MOD_FILE}" >> "$GITHUB_OUTPUT" - - - name: "Step 1: Read version" - id: version - run: | - VERSION=$(php /tmp/moko-platform-api/cli/version_read.php --path .) - if [ -z "$VERSION" ]; then - echo "::error::No VERSION in README.md" - echo "skip=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - # Strip any pre-release suffix merged from dev (e.g. 01.02.20-dev → 01.02.20) - VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//') - MAJOR=$(echo "$VERSION" | cut -d. -f1) - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "release_tag=stable" >> "$GITHUB_OUTPUT" - echo "skip=false" >> "$GITHUB_OUTPUT" - echo "branch=main" >> "$GITHUB_OUTPUT" - - # -- CHECK FOR RC PROMOTION ------------------------------------------------ - - name: "Check for RC release" - id: rc - if: steps.version.outputs.skip != 'true' - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - RC_JSON=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ - "${API_BASE}/releases/tags/release-candidate" 2>/dev/null || echo "{}") - RC_ID=$(echo "$RC_JSON" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('id',''))" 2>/dev/null || true) - - if [ -n "$RC_ID" ] && [ "$RC_ID" != "None" ] && [ "$RC_ID" != "" ]; then - echo "promote=true" >> "$GITHUB_OUTPUT" - echo "release_id=${RC_ID}" >> "$GITHUB_OUTPUT" - echo "::notice::RC release found (id: ${RC_ID}) — will promote to stable" - else - echo "promote=false" >> "$GITHUB_OUTPUT" - echo "::notice::No RC release — full build pipeline" - fi - - - name: "Step 1b: Minor bump version" - id: bump - if: >- - steps.version.outputs.skip != 'true' && - steps.rc.outputs.promote != 'true' - run: | - MOKO_API="/tmp/moko-platform-api/cli" - php ${MOKO_API}/version_bump.php --path . --minor 2>&1 || true - VERSION=$(php ${MOKO_API}/version_read.php --path .) - # Strip any pre-release suffix — stable releases have no suffix - VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//') - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "Bumped to: ${VERSION}" - - - name: Check if already released - if: steps.version.outputs.skip != 'true' - id: check - run: | - TAG="${{ steps.version.outputs.release_tag }}" - BRANCH="${{ steps.version.outputs.branch }}" - - TAG_EXISTS=false - BRANCH_EXISTS=false - - git rev-parse "$TAG" >/dev/null 2>&1 && TAG_EXISTS=true - git ls-remote --heads origin "$BRANCH" 2>/dev/null | grep -q "$BRANCH" && BRANCH_EXISTS=true - - echo "tag_exists=$TAG_EXISTS" >> "$GITHUB_OUTPUT" - echo "branch_exists=$BRANCH_EXISTS" >> "$GITHUB_OUTPUT" - - # Tag and branch may persist across patch releases — never skip - echo "already_released=false" >> "$GITHUB_OUTPUT" - - # -- SANITY CHECKS ------------------------------------------------------- - - name: "Sanity: Pre-release validation" - if: >- - steps.version.outputs.skip != 'true' && - steps.check.outputs.already_released != 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - php /tmp/moko-platform-api/cli/release_validate.php \ - --path . --version "$VERSION" --output-summary --github-output || true - - # -- STEP 2: Create or update version/XX.YY archive branch --------------- - # Always runs — every version change on main archives to version/XX.YY - - name: "Step 2: Version archive branch" - if: steps.check.outputs.already_released != 'true' - run: | - BRANCH="${{ steps.version.outputs.branch }}" - IS_MINOR="${{ steps.version.outputs.is_minor }}" - PATCH="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - PATCH_NUM=$(echo "$PATCH" | awk -F. '{print $3}') - - # Check if branch exists - if git ls-remote --heads origin "$BRANCH" | grep -q "$BRANCH"; then - git push origin HEAD:"$BRANCH" --force - echo "Updated archive branch: ${BRANCH} (patch ${PATCH_NUM})" >> $GITHUB_STEP_SUMMARY - else - git checkout -b "$BRANCH" 2>/dev/null || git checkout "$BRANCH" - git push origin "$BRANCH" --force - echo "Created archive branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY - fi - - # -- STEP 3: Set platform version ---------------------------------------- - - name: "Step 3: Set platform version" - if: >- - steps.version.outputs.skip != 'true' && - steps.check.outputs.already_released != 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - php /tmp/moko-platform-api/cli/version_set_platform.php \ - --path . --version "$VERSION" --branch main - - # -- STEP 4: Update version badges ---------------------------------------- - - name: "Step 4: Update version badges" - if: steps.version.outputs.skip != 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - php /tmp/moko-platform-api/cli/badge_update.php --path . --version "${VERSION}" 2>/dev/null || true - php /tmp/moko-platform-api/cli/version_check.php --path . --fix 2>/dev/null || true - - # Step 5 (updates.xml) moved after Step 8 to include SHA-256 checksum - - - name: "Step 4b: Promote and prune CHANGELOG" - if: >- - steps.version.outputs.skip != 'true' && - steps.check.outputs.already_released != 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - MOKO_API="/tmp/moko-platform-api/cli" - if [ -f "CHANGELOG.md" ]; then - php ${MOKO_API}/changelog_promote.php --path . --version "$VERSION" 2>&1 || true - php ${MOKO_API}/changelog_prune.php --path . --keep 5 2>&1 || true - fi - - - name: Commit release changes - if: >- - steps.version.outputs.skip != 'true' && - steps.check.outputs.already_released != 'true' - run: | - if git diff --quiet && git diff --cached --quiet; then - echo "No changes to commit" - exit 0 - fi - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - git add -A - git commit -m "chore(release): build ${VERSION} [skip ci]" \ - --author="gitea-actions[bot] " - # Detached HEAD on PR merge — push explicitly to main - git push origin HEAD:refs/heads/main - - # -- STEP 6: Create tag --------------------------------------------------- - - name: "Step 6: Create git tag" - if: >- - steps.version.outputs.skip != 'true' - run: | - RELEASE_TAG="${{ steps.version.outputs.release_tag }}" - # Only create the major release tag if it doesn't exist yet - if ! git rev-parse "$RELEASE_TAG" >/dev/null 2>&1; then - git tag "$RELEASE_TAG" - git push origin "$RELEASE_TAG" - echo "Tag created: ${RELEASE_TAG}" >> $GITHUB_STEP_SUMMARY - else - echo "Tag ${RELEASE_TAG} already exists" >> $GITHUB_STEP_SUMMARY - fi - echo "Tag: ${TAG}" >> $GITHUB_STEP_SUMMARY - - # -- STEP 7a: Promote RC to stable (skip build) ---------------------------- - - name: "Step 7a: Promote RC to stable" - if: >- - steps.version.outputs.skip != 'true' && - steps.rc.outputs.promote == 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/release_promote.php \ - --from release-candidate --to stable \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --api-base "${API_BASE}" \ - --path . --branch main - echo "Promoted RC → stable (${VERSION})" >> $GITHUB_STEP_SUMMARY - - # -- STEP 7b: Create or update Gitea Release (full build path) ------------- - - name: "Step 7b: Gitea Release" - if: >- - steps.version.outputs.skip != 'true' && - steps.rc.outputs.promote != 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - RELEASE_TAG="${{ steps.version.outputs.release_tag }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/release_create.php \ - --path . --version "$VERSION" --tag "$RELEASE_TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --branch main - echo "Release created: ${VERSION}" >> $GITHUB_STEP_SUMMARY - - # -- STEP 8: Build packages and upload to release ---------------------------- - - name: "Step 8: Build package and upload" - id: package - if: >- - steps.version.outputs.skip != 'true' && - steps.rc.outputs.promote != 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - RELEASE_TAG="${{ steps.version.outputs.release_tag }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/release_package.php \ - --path . --version "$VERSION" --tag "$RELEASE_TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --output /tmp || true - - # -- STEP 5: Write update stream (after build so SHA-256 is available) ----- - - name: "Step 5: Write update stream" - if: steps.version.outputs.skip != 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - SHA256="${{ steps.package.outputs.sha256_zip }}" - - # Fetch latest updates.xml from main so preserve logic has all channels - GITEA_TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - API="${GITEA_URL}/api/v1/repos/${{ github.repository }}" - curl -sf -H "Authorization: token ${GITEA_TOKEN}" \ - "${API}/contents/updates.xml?ref=main" 2>/dev/null | \ - python3 -c "import sys,json,base64; print(base64.b64decode(json.load(sys.stdin)['content']).decode())" \ - > updates.xml 2>/dev/null || true - - SHA_FLAG="" - [ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}" - - php /tmp/moko-platform-api/cli/updates_xml_build.php \ - --path . --version "${VERSION}" --stability stable \ - --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \ - ${SHA_FLAG} --github-output - - # Commit updates.xml if changed - if ! git diff --quiet updates.xml 2>/dev/null; then - git add updates.xml - git commit -m "chore: update stable channel ${VERSION} [skip ci]" \ - --author="gitea-actions[bot] " - git push origin HEAD:refs/heads/main 2>&1 || true - fi - - # -- STEP 8b: Update release description with changelog ---------------------- - - name: "Step 8b: Update release body" - if: steps.version.outputs.skip != 'true' - continue-on-error: true - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - RELEASE_TAG="${{ steps.version.outputs.release_tag }}" - php /tmp/moko-platform-api/cli/release_body_update.php \ - --path . --version "${VERSION}" --tag "${RELEASE_TAG}" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \ - 2>&1 || true - echo "Release body updated" >> $GITHUB_STEP_SUMMARY - - # -- STEP 9: Mirror to GitHub (stable only) -------------------------------- - - name: "Step 9: Mirror release to GitHub" - if: >- - steps.version.outputs.skip != 'true' && - secrets.GH_MIRROR_TOKEN != '' - continue-on-error: true - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - 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 \ - --version "$VERSION" --tag "$RELEASE_TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \ - --branch main 2>&1 || true - echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY - - # -- STEP 10: Sync main branch to GitHub mirror ---------------------------- - - name: "Step 10: Push main to GitHub mirror" - if: >- - steps.version.outputs.skip != 'true' && - secrets.GH_MIRROR_TOKEN != '' - continue-on-error: true - run: | - GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" - GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1) - GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2) - git remote add github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \ - git remote set-url github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" - git fetch origin main --depth=1 - git push github origin/main:refs/heads/main --force 2>/dev/null \ - && echo "main branch pushed to GitHub mirror" \ - || echo "WARNING: GitHub mirror push failed" - - # -- Clean up lesser pre-releases (cascade) --------------------------------- - # stable → deletes all | rc → beta,alpha,dev | beta → alpha,dev | alpha → dev - - name: "Delete lesser pre-release channels" - continue-on-error: true - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/release_cascade.php \ - --stability stable \ - --version "${VERSION}" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --api-base "${API_BASE}" 2>/dev/null || true - - - name: "Step 11: Delete and recreate dev branch from main" - if: steps.version.outputs.skip != 'true' - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - - # Delete dev branch - curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ - "${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch" - - # Recreate dev from main (now includes version bump + changelog promotion) - curl -sf -X POST -H "Authorization: token ${TOKEN}" \ - -H "Content-Type: application/json" \ - "${API_BASE}/branches" \ - -d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main" - - echo "Dev branch reset from main (keeps dev ahead after release)" >> $GITHUB_STEP_SUMMARY - - - name: "Step 12: Create version branch from main" - if: steps.version.outputs.skip != 'true' - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - BRANCH_NAME="version/${VERSION}" - MAIN_SHA=$(git rev-parse HEAD) - - # Delete old version branch if it exists (same version re-release) - curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${BRANCH_NAME}" 2>/dev/null && echo "Deleted old ${BRANCH_NAME}" - - # Create version/XX.YY.ZZ from main - curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/branches" -d "{\"new_branch_name\":\"${BRANCH_NAME}\",\"old_branch_name\":\"main\"}" 2>/dev/null && echo "Created ${BRANCH_NAME} from main (${MAIN_SHA})" || echo "WARNING: ${BRANCH_NAME} creation failed" - - echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY - - - - # -- Dolibarr post-release: Reset dev version ----------------------------- - - name: "Post-release: Reset dev version" - if: steps.version.outputs.skip != 'true' - 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 \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \ - --branch dev --path . 2>&1 || true - - # -- Summary -------------------------------------------------------------- - - name: Pipeline Summary - if: always() - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - PLATFORM="${{ steps.platform.outputs.platform }}" - if [ "${{ steps.version.outputs.skip }}" = "true" ]; then - echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY - echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY - elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then - echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY - else - echo "" >> $GITHUB_STEP_SUMMARY - echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY - echo "|------|--------|" >> $GITHUB_STEP_SUMMARY - echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY - fi +# 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-tech/moko-platform +# PATH: /templates/workflows/universal/auto-release.yml.template +# VERSION: 05.00.00 +# BRIEF: Universal build & release � detects platform from manifest.xml +# +# +========================================================================+ +# | UNIVERSAL BUILD & RELEASE PIPELINE | +# +========================================================================+ +# | | +# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. | +# | | +# | Platform-specific: | +# | joomla: XML manifest, updates.xml, type-prefixed packages | +# | dolibarr: mod*.class.php, update.txt, dev version reset | +# | generic: README-only, no update stream | +# | | +# +========================================================================+ + +name: "Universal: Build & Release" + +on: + pull_request: + types: [opened, closed] + branches: + - main + workflow_dispatch: + inputs: + action: + description: 'Action to perform' + required: false + type: choice + default: release + options: + - release + - promote-rc + +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: + # ── PR Opened → Rename branch to RC and build RC release ───────────────────── + promote-rc: + name: Promote to RC + runs-on: release + if: >- + (github.event.action == 'opened' && github.event.pull_request.merged != true) || + (github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc') + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + token: ${{ secrets.MOKOGITEA_TOKEN }} + fetch-depth: 1 + + - name: Setup moko-platform tools + env: + 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 + 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 source branch to rc + run: | + SOURCE_BRANCH="${{ github.event.pull_request.head.ref || 'dev' }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + PR_NUM="${{ github.event.pull_request.number }}" + php /tmp/moko-platform-api/cli/branch_rename.php \ + --from "$SOURCE_BRANCH" --to rc \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --api-base "${API_BASE}" \ + --pr "$PR_NUM" + + - name: Set RC version on renamed branch + run: | + # Checkout the new rc branch + git fetch origin rc + git checkout rc + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + MOKO_CLI="/tmp/moko-platform-api/cli" + + VERSION=$(php ${MOKO_CLI}/version_read.php --path .) || true + [ -z "$VERSION" ] && { echo "No version — skipping"; exit 0; } + + php ${MOKO_CLI}/version_set_platform.php \ + --path . --version "$VERSION" --branch rc --stability rc 2>/dev/null || true + php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true + + if ! git diff --quiet || ! git diff --cached --quiet; then + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" + git config --local user.name "gitea-actions[bot]" + git add -A + git commit -m "chore(version): set RC stability suffix [skip ci]" \ + --author="gitea-actions[bot] " + git push origin rc + fi + + - name: Build RC release + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + MOKO_CLI="/tmp/moko-platform-api/cli" + VERSION=$(php ${MOKO_CLI}/version_read.php --path .) || true + + php ${MOKO_CLI}/release_create.php \ + --path . --version "$VERSION" --tag "release-candidate" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --repo "${GITEA_REPO}" --branch rc 2>&1 || true + + php ${MOKO_CLI}/release_package.php \ + --path . --version "$VERSION" --tag "release-candidate" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --repo "${GITEA_REPO}" --output /tmp 2>&1 || true + + - name: Cascade lesser channels + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php /tmp/moko-platform-api/cli/release_cascade.php \ + --stability release-candidate \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --api-base "${API_BASE}" + + - name: Summary + if: always() + run: | + echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY + echo "Draft PR opened — branch renamed to rc, RC release built" >> $GITHUB_STEP_SUMMARY + + # ── Merged PR → Build & Release (or promote RC to stable) ──────────────────── + release: + name: Build & Release Pipeline + runs-on: release + if: >- + github.event.pull_request.merged == true || + (github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc') + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + token: ${{ secrets.MOKOGITEA_TOKEN }} + fetch-depth: 0 + + - name: Configure git for bot pushes + run: | + 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" + + - name: Setup moko-platform tools + env: + MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + 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 + 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 + + + # -- PLATFORM DETECTION --------------------------------------------------- + - name: Detect platform + id: platform + run: | + php /tmp/moko-platform-api/cli/manifest_read.php --path . --github-output + MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1 || true) + MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1 || true) + echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT" + echo "mod_file=${MOD_FILE}" >> "$GITHUB_OUTPUT" + + - name: "Step 1: Read version" + id: version + run: | + VERSION=$(php /tmp/moko-platform-api/cli/version_read.php --path .) + if [ -z "$VERSION" ]; then + echo "::error::No VERSION in README.md" + echo "skip=true" >> "$GITHUB_OUTPUT" + exit 0 + fi + # version_set_platform strips suffixes internally when --stability stable + MAJOR=$(echo "$VERSION" | cut -d. -f1 | sed 's/-.*//') + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "release_tag=stable" >> "$GITHUB_OUTPUT" + echo "skip=false" >> "$GITHUB_OUTPUT" + echo "branch=main" >> "$GITHUB_OUTPUT" + + # -- CHECK FOR RC PROMOTION ------------------------------------------------ + - name: "Check for RC release" + id: rc + if: steps.version.outputs.skip != 'true' + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + RC_JSON=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ + "${API_BASE}/releases/tags/release-candidate" 2>/dev/null || echo "{}") + RC_ID=$(echo "$RC_JSON" | php -r "\$d=json_decode(file_get_contents('php://stdin'),true); echo \$d['id'] ?? '';" 2>/dev/null || true) + + if [ -n "$RC_ID" ] && [ "$RC_ID" != "None" ] && [ "$RC_ID" != "" ]; then + echo "promote=true" >> "$GITHUB_OUTPUT" + echo "release_id=${RC_ID}" >> "$GITHUB_OUTPUT" + echo "::notice::RC release found (id: ${RC_ID}) — will promote to stable" + else + echo "promote=false" >> "$GITHUB_OUTPUT" + echo "::notice::No RC release — full build pipeline" + fi + + - name: "Step 1b: Minor bump version" + id: bump + if: >- + steps.version.outputs.skip != 'true' && + steps.rc.outputs.promote != 'true' + run: | + MOKO_API="/tmp/moko-platform-api/cli" + php ${MOKO_API}/version_bump.php --path . --minor 2>&1 || true + VERSION=$(php ${MOKO_API}/version_read.php --path .) + # version_set_platform handles suffix stripping — just pass clean base version + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "Bumped to: ${VERSION}" + + - name: Check if already released + if: steps.version.outputs.skip != 'true' + id: check + run: | + TAG="${{ steps.version.outputs.release_tag }}" + BRANCH="${{ steps.version.outputs.branch }}" + + TAG_EXISTS=false + BRANCH_EXISTS=false + + git rev-parse "$TAG" >/dev/null 2>&1 && TAG_EXISTS=true + git ls-remote --heads origin "$BRANCH" 2>/dev/null | grep -q "$BRANCH" && BRANCH_EXISTS=true + + echo "tag_exists=$TAG_EXISTS" >> "$GITHUB_OUTPUT" + echo "branch_exists=$BRANCH_EXISTS" >> "$GITHUB_OUTPUT" + + # Tag and branch may persist across patch releases — never skip + echo "already_released=false" >> "$GITHUB_OUTPUT" + + # -- SANITY CHECKS ------------------------------------------------------- + - name: "Sanity: Pre-release validation" + if: >- + steps.version.outputs.skip != 'true' && + steps.check.outputs.already_released != 'true' + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + php /tmp/moko-platform-api/cli/release_validate.php \ + --path . --version "$VERSION" --output-summary --github-output || true + + # -- STEP 2: Create or update version/XX.YY archive branch --------------- + # Always runs — every version change on main archives to version/XX.YY + - name: "Step 2: Version archive branch" + if: steps.check.outputs.already_released != 'true' + run: | + BRANCH="${{ steps.version.outputs.branch }}" + IS_MINOR="${{ steps.version.outputs.is_minor }}" + PATCH="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + PATCH_NUM=$(echo "$PATCH" | awk -F. '{print $3}') + + # Check if branch exists + if git ls-remote --heads origin "$BRANCH" | grep -q "$BRANCH"; then + git push origin HEAD:"$BRANCH" --force + echo "Updated archive branch: ${BRANCH} (patch ${PATCH_NUM})" >> $GITHUB_STEP_SUMMARY + else + git checkout -b "$BRANCH" 2>/dev/null || git checkout "$BRANCH" + git push origin "$BRANCH" --force + echo "Created archive branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY + fi + + # -- STEP 3: Set platform version ---------------------------------------- + - name: "Step 3: Set platform version" + if: >- + steps.version.outputs.skip != 'true' && + steps.check.outputs.already_released != 'true' + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + php /tmp/moko-platform-api/cli/version_set_platform.php \ + --path . --version "$VERSION" --branch main + + # -- STEP 4: Update version badges ---------------------------------------- + - name: "Step 4: Update version badges" + if: steps.version.outputs.skip != 'true' + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + php /tmp/moko-platform-api/cli/badge_update.php --path . --version "${VERSION}" 2>/dev/null || true + php /tmp/moko-platform-api/cli/version_check.php --path . --fix 2>/dev/null || true + + # Step 5 (updates.xml) moved after Step 8 to include SHA-256 checksum + + - name: "Step 4b: Promote and prune CHANGELOG" + if: >- + steps.version.outputs.skip != 'true' && + steps.check.outputs.already_released != 'true' + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + MOKO_API="/tmp/moko-platform-api/cli" + if [ -f "CHANGELOG.md" ]; then + php ${MOKO_API}/changelog_promote.php --path . --version "$VERSION" 2>&1 || true + php ${MOKO_API}/changelog_prune.php --path . --keep 5 2>&1 || true + fi + + - name: Commit release changes + if: >- + steps.version.outputs.skip != 'true' && + steps.check.outputs.already_released != 'true' + run: | + if git diff --quiet && git diff --cached --quiet; then + echo "No changes to commit" + exit 0 + fi + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + git add -A + git commit -m "chore(release): build ${VERSION} [skip ci]" \ + --author="gitea-actions[bot] " + # Detached HEAD on PR merge — push explicitly to main + git push origin HEAD:refs/heads/main + + # -- STEP 6: Create tag --------------------------------------------------- + - name: "Step 6: Create git tag" + if: >- + steps.version.outputs.skip != 'true' + run: | + RELEASE_TAG="${{ steps.version.outputs.release_tag }}" + # Only create the major release tag if it doesn't exist yet + if ! git rev-parse "$RELEASE_TAG" >/dev/null 2>&1; then + git tag "$RELEASE_TAG" + git push origin "$RELEASE_TAG" + echo "Tag created: ${RELEASE_TAG}" >> $GITHUB_STEP_SUMMARY + else + echo "Tag ${RELEASE_TAG} already exists" >> $GITHUB_STEP_SUMMARY + fi + echo "Tag: ${TAG}" >> $GITHUB_STEP_SUMMARY + + # -- STEP 7a: Promote RC to stable (skip build) ---------------------------- + - name: "Step 7a: Promote RC to stable" + if: >- + steps.version.outputs.skip != 'true' && + steps.rc.outputs.promote == 'true' + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php /tmp/moko-platform-api/cli/release_promote.php \ + --from release-candidate --to stable \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --api-base "${API_BASE}" \ + --path . --branch main + echo "Promoted RC → stable (${VERSION})" >> $GITHUB_STEP_SUMMARY + + # -- STEP 7b: Create or update Gitea Release (full build path) ------------- + - name: "Step 7b: Gitea Release" + if: >- + steps.version.outputs.skip != 'true' && + steps.rc.outputs.promote != 'true' + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + RELEASE_TAG="${{ steps.version.outputs.release_tag }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php /tmp/moko-platform-api/cli/release_create.php \ + --path . --version "$VERSION" --tag "$RELEASE_TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --repo "${GITEA_REPO}" --branch main + echo "Release created: ${VERSION}" >> $GITHUB_STEP_SUMMARY + + # -- STEP 8: Build packages and upload to release ---------------------------- + - name: "Step 8: Build package and upload" + id: package + if: >- + steps.version.outputs.skip != 'true' && + steps.rc.outputs.promote != 'true' + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + RELEASE_TAG="${{ steps.version.outputs.release_tag }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php /tmp/moko-platform-api/cli/release_package.php \ + --path . --version "$VERSION" --tag "$RELEASE_TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --repo "${GITEA_REPO}" --output /tmp || true + + # -- STEP 5: Write update stream (after build so SHA-256 is available) ----- + - name: "Step 5: Write update stream" + if: steps.version.outputs.skip != 'true' + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + SHA256="${{ steps.package.outputs.sha256_zip }}" + + # Fetch latest updates.xml from main so preserve logic has all channels + GITEA_TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" + API="${GITEA_URL}/api/v1/repos/${{ github.repository }}" + curl -sf -H "Authorization: token ${GITEA_TOKEN}" \ + "${API}/contents/updates.xml?ref=main" 2>/dev/null | \ + php -r "\$d=json_decode(file_get_contents('php://stdin'),true); echo base64_decode(\$d['content'] ?? '');" \ + > updates.xml 2>/dev/null || true + + SHA_FLAG="" + [ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}" + + php /tmp/moko-platform-api/cli/updates_xml_build.php \ + --path . --version "${VERSION}" --stability stable \ + --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \ + ${SHA_FLAG} --github-output + + # Commit updates.xml if changed + if ! git diff --quiet updates.xml 2>/dev/null; then + git add updates.xml + git commit -m "chore: update stable channel ${VERSION} [skip ci]" \ + --author="gitea-actions[bot] " + git push origin HEAD:refs/heads/main 2>&1 || true + fi + + # -- STEP 8b: Update release description with changelog ---------------------- + - name: "Step 8b: Update release body" + if: steps.version.outputs.skip != 'true' + continue-on-error: true + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + RELEASE_TAG="${{ steps.version.outputs.release_tag }}" + php /tmp/moko-platform-api/cli/release_body_update.php \ + --path . --version "${VERSION}" --tag "${RELEASE_TAG}" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \ + 2>&1 || true + echo "Release body updated" >> $GITHUB_STEP_SUMMARY + + # -- STEP 9: Mirror to GitHub (stable only) -------------------------------- + - name: "Step 9: Mirror release to GitHub" + if: >- + steps.version.outputs.skip != 'true' && + secrets.GH_MIRROR_TOKEN != '' + continue-on-error: true + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + 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 \ + --version "$VERSION" --tag "$RELEASE_TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \ + --branch main 2>&1 || true + echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY + + # -- STEP 10: Sync main branch to GitHub mirror ---------------------------- + - name: "Step 10: Push main to GitHub mirror" + if: >- + steps.version.outputs.skip != 'true' && + secrets.GH_MIRROR_TOKEN != '' + continue-on-error: true + run: | + GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" + GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1) + GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2) + git remote add github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \ + git remote set-url github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" + git fetch origin main --depth=1 + git push github origin/main:refs/heads/main --force 2>/dev/null \ + && echo "main branch pushed to GitHub mirror" \ + || echo "WARNING: GitHub mirror push failed" + + # -- Clean up lesser pre-releases (cascade) --------------------------------- + # stable → deletes all | rc → beta,alpha,dev | beta → alpha,dev | alpha → dev + - name: "Delete lesser pre-release channels" + continue-on-error: true + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php /tmp/moko-platform-api/cli/release_cascade.php \ + --stability stable \ + --version "${VERSION}" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --api-base "${API_BASE}" 2>/dev/null || true + + - name: "Step 11: Clean up pre-release branches and recreate dev from main" + if: steps.version.outputs.skip != 'true' + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" + + # Delete ephemeral pre-release branches (rc, alpha, beta) + for EPHEMERAL in rc alpha beta; do + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ + "${API_BASE}/branches/${EPHEMERAL}" 2>/dev/null \ + && echo "Deleted ${EPHEMERAL} branch" \ + || echo "${EPHEMERAL} branch not found" + done + + # Delete dev branch + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ + "${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch" + + # Recreate dev from main (now includes version bump + changelog promotion) + curl -sf -X POST -H "Authorization: token ${TOKEN}" \ + -H "Content-Type: application/json" \ + "${API_BASE}/branches" \ + -d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main" + + echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY + + - name: "Step 12: Create version branch from main" + if: steps.version.outputs.skip != 'true' + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + BRANCH_NAME="version/${VERSION}" + MAIN_SHA=$(git rev-parse HEAD) + + # Delete old version branch if it exists (same version re-release) + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${BRANCH_NAME}" 2>/dev/null && echo "Deleted old ${BRANCH_NAME}" + + # Create version/XX.YY.ZZ from main + curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/branches" -d "{\"new_branch_name\":\"${BRANCH_NAME}\",\"old_branch_name\":\"main\"}" 2>/dev/null && echo "Created ${BRANCH_NAME} from main (${MAIN_SHA})" || echo "WARNING: ${BRANCH_NAME} creation failed" + + echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY + + + + # -- Dolibarr post-release: Reset dev version ----------------------------- + - name: "Post-release: Reset dev version" + if: steps.version.outputs.skip != 'true' + 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 \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \ + --branch dev --path . 2>&1 || true + + # -- Summary -------------------------------------------------------------- + - name: Pipeline Summary + if: always() + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + PLATFORM="${{ steps.platform.outputs.platform }}" + if [ "${{ steps.version.outputs.skip }}" = "true" ]; then + echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY + echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY + elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then + echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY + else + echo "" >> $GITHUB_STEP_SUMMARY + echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY + echo "|------|--------|" >> $GITHUB_STEP_SUMMARY + echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY + fi -- 2.52.0 From 80326664c90a24cb83179dfc65b66e70523a5012 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 30 May 2026 15:01:26 +0000 Subject: [PATCH 04/12] chore: sync .mokogitea/workflows/auto-bump.yml from moko-platform [skip ci] --- .mokogitea/workflows/auto-bump.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.mokogitea/workflows/auto-bump.yml b/.mokogitea/workflows/auto-bump.yml index a397a9e5..fb9dc827 100644 --- a/.mokogitea/workflows/auto-bump.yml +++ b/.mokogitea/workflows/auto-bump.yml @@ -16,10 +16,9 @@ on: push: branches: - dev - - alpha - - beta - rc - 'feature/**' + - 'patch/**' env: FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true -- 2.52.0 From d93da0b1b715721bc4c78705d3cbe2f1066c6543 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 30 May 2026 15:03:54 +0000 Subject: [PATCH 05/12] chore: sync .mokogitea/workflows/auto-release.yml from moko-platform [skip ci] --- .mokogitea/workflows/auto-release.yml | 353 ++------------------------ 1 file changed, 22 insertions(+), 331 deletions(-) diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 04ec8179..1227ff8d 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -82,71 +82,33 @@ jobs: cd /tmp/moko-platform-api composer install --no-dev --no-interaction --quiet - - name: Rename source branch to rc + - name: Rename branch to rc run: | - SOURCE_BRANCH="${{ github.event.pull_request.head.ref || 'dev' }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - PR_NUM="${{ github.event.pull_request.number }}" php /tmp/moko-platform-api/cli/branch_rename.php \ - --from "$SOURCE_BRANCH" --to rc \ + --from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \ --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --api-base "${API_BASE}" \ - --pr "$PR_NUM" + --api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \ + --pr "${{ github.event.pull_request.number }}" - - name: Set RC version on renamed branch + - name: Checkout rc and configure git run: | - # Checkout the new rc branch git fetch origin rc git checkout rc - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - MOKO_CLI="/tmp/moko-platform-api/cli" + 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" - VERSION=$(php ${MOKO_CLI}/version_read.php --path .) || true - [ -z "$VERSION" ] && { echo "No version — skipping"; exit 0; } - - php ${MOKO_CLI}/version_set_platform.php \ - --path . --version "$VERSION" --branch rc --stability rc 2>/dev/null || true - php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true - - if ! git diff --quiet || ! git diff --cached --quiet; then - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - git add -A - git commit -m "chore(version): set RC stability suffix [skip ci]" \ - --author="gitea-actions[bot] " - git push origin rc - fi - - - name: Build RC release + - name: Publish RC release run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - MOKO_CLI="/tmp/moko-platform-api/cli" - VERSION=$(php ${MOKO_CLI}/version_read.php --path .) || true - - php ${MOKO_CLI}/release_create.php \ - --path . --version "$VERSION" --tag "release-candidate" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --branch rc 2>&1 || true - - php ${MOKO_CLI}/release_package.php \ - --path . --version "$VERSION" --tag "release-candidate" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --output /tmp 2>&1 || true - - - name: Cascade lesser channels - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/release_cascade.php \ - --stability release-candidate \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --api-base "${API_BASE}" + php /tmp/moko-platform-api/cli/release_publish.php \ + --path . --stability rc --bump minor --branch rc \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" - name: Summary if: always() run: | echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY - echo "Draft PR opened — branch renamed to rc, RC release built" >> $GITHUB_STEP_SUMMARY + echo "Branch renamed to rc, minor bump, RC + lesser stream releases built, updates.xml synced" >> $GITHUB_STEP_SUMMARY # ── Merged PR → Build & Release (or promote RC to stable) ──────────────────── release: @@ -188,266 +150,11 @@ jobs: composer install --no-dev --no-interaction --quiet - # -- PLATFORM DETECTION --------------------------------------------------- - - name: Detect platform - id: platform + - name: "Publish stable release" run: | - php /tmp/moko-platform-api/cli/manifest_read.php --path . --github-output - MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1 || true) - MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1 || true) - echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT" - echo "mod_file=${MOD_FILE}" >> "$GITHUB_OUTPUT" - - - name: "Step 1: Read version" - id: version - run: | - VERSION=$(php /tmp/moko-platform-api/cli/version_read.php --path .) - if [ -z "$VERSION" ]; then - echo "::error::No VERSION in README.md" - echo "skip=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - # version_set_platform strips suffixes internally when --stability stable - MAJOR=$(echo "$VERSION" | cut -d. -f1 | sed 's/-.*//') - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "release_tag=stable" >> "$GITHUB_OUTPUT" - echo "skip=false" >> "$GITHUB_OUTPUT" - echo "branch=main" >> "$GITHUB_OUTPUT" - - # -- CHECK FOR RC PROMOTION ------------------------------------------------ - - name: "Check for RC release" - id: rc - if: steps.version.outputs.skip != 'true' - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - RC_JSON=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ - "${API_BASE}/releases/tags/release-candidate" 2>/dev/null || echo "{}") - RC_ID=$(echo "$RC_JSON" | php -r "\$d=json_decode(file_get_contents('php://stdin'),true); echo \$d['id'] ?? '';" 2>/dev/null || true) - - if [ -n "$RC_ID" ] && [ "$RC_ID" != "None" ] && [ "$RC_ID" != "" ]; then - echo "promote=true" >> "$GITHUB_OUTPUT" - echo "release_id=${RC_ID}" >> "$GITHUB_OUTPUT" - echo "::notice::RC release found (id: ${RC_ID}) — will promote to stable" - else - echo "promote=false" >> "$GITHUB_OUTPUT" - echo "::notice::No RC release — full build pipeline" - fi - - - name: "Step 1b: Minor bump version" - id: bump - if: >- - steps.version.outputs.skip != 'true' && - steps.rc.outputs.promote != 'true' - run: | - MOKO_API="/tmp/moko-platform-api/cli" - php ${MOKO_API}/version_bump.php --path . --minor 2>&1 || true - VERSION=$(php ${MOKO_API}/version_read.php --path .) - # version_set_platform handles suffix stripping — just pass clean base version - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "Bumped to: ${VERSION}" - - - name: Check if already released - if: steps.version.outputs.skip != 'true' - id: check - run: | - TAG="${{ steps.version.outputs.release_tag }}" - BRANCH="${{ steps.version.outputs.branch }}" - - TAG_EXISTS=false - BRANCH_EXISTS=false - - git rev-parse "$TAG" >/dev/null 2>&1 && TAG_EXISTS=true - git ls-remote --heads origin "$BRANCH" 2>/dev/null | grep -q "$BRANCH" && BRANCH_EXISTS=true - - echo "tag_exists=$TAG_EXISTS" >> "$GITHUB_OUTPUT" - echo "branch_exists=$BRANCH_EXISTS" >> "$GITHUB_OUTPUT" - - # Tag and branch may persist across patch releases — never skip - echo "already_released=false" >> "$GITHUB_OUTPUT" - - # -- SANITY CHECKS ------------------------------------------------------- - - name: "Sanity: Pre-release validation" - if: >- - steps.version.outputs.skip != 'true' && - steps.check.outputs.already_released != 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - php /tmp/moko-platform-api/cli/release_validate.php \ - --path . --version "$VERSION" --output-summary --github-output || true - - # -- STEP 2: Create or update version/XX.YY archive branch --------------- - # Always runs — every version change on main archives to version/XX.YY - - name: "Step 2: Version archive branch" - if: steps.check.outputs.already_released != 'true' - run: | - BRANCH="${{ steps.version.outputs.branch }}" - IS_MINOR="${{ steps.version.outputs.is_minor }}" - PATCH="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - PATCH_NUM=$(echo "$PATCH" | awk -F. '{print $3}') - - # Check if branch exists - if git ls-remote --heads origin "$BRANCH" | grep -q "$BRANCH"; then - git push origin HEAD:"$BRANCH" --force - echo "Updated archive branch: ${BRANCH} (patch ${PATCH_NUM})" >> $GITHUB_STEP_SUMMARY - else - git checkout -b "$BRANCH" 2>/dev/null || git checkout "$BRANCH" - git push origin "$BRANCH" --force - echo "Created archive branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY - fi - - # -- STEP 3: Set platform version ---------------------------------------- - - name: "Step 3: Set platform version" - if: >- - steps.version.outputs.skip != 'true' && - steps.check.outputs.already_released != 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - php /tmp/moko-platform-api/cli/version_set_platform.php \ - --path . --version "$VERSION" --branch main - - # -- STEP 4: Update version badges ---------------------------------------- - - name: "Step 4: Update version badges" - if: steps.version.outputs.skip != 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - php /tmp/moko-platform-api/cli/badge_update.php --path . --version "${VERSION}" 2>/dev/null || true - php /tmp/moko-platform-api/cli/version_check.php --path . --fix 2>/dev/null || true - - # Step 5 (updates.xml) moved after Step 8 to include SHA-256 checksum - - - name: "Step 4b: Promote and prune CHANGELOG" - if: >- - steps.version.outputs.skip != 'true' && - steps.check.outputs.already_released != 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - MOKO_API="/tmp/moko-platform-api/cli" - if [ -f "CHANGELOG.md" ]; then - php ${MOKO_API}/changelog_promote.php --path . --version "$VERSION" 2>&1 || true - php ${MOKO_API}/changelog_prune.php --path . --keep 5 2>&1 || true - fi - - - name: Commit release changes - if: >- - steps.version.outputs.skip != 'true' && - steps.check.outputs.already_released != 'true' - run: | - if git diff --quiet && git diff --cached --quiet; then - echo "No changes to commit" - exit 0 - fi - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - git add -A - git commit -m "chore(release): build ${VERSION} [skip ci]" \ - --author="gitea-actions[bot] " - # Detached HEAD on PR merge — push explicitly to main - git push origin HEAD:refs/heads/main - - # -- STEP 6: Create tag --------------------------------------------------- - - name: "Step 6: Create git tag" - if: >- - steps.version.outputs.skip != 'true' - run: | - RELEASE_TAG="${{ steps.version.outputs.release_tag }}" - # Only create the major release tag if it doesn't exist yet - if ! git rev-parse "$RELEASE_TAG" >/dev/null 2>&1; then - git tag "$RELEASE_TAG" - git push origin "$RELEASE_TAG" - echo "Tag created: ${RELEASE_TAG}" >> $GITHUB_STEP_SUMMARY - else - echo "Tag ${RELEASE_TAG} already exists" >> $GITHUB_STEP_SUMMARY - fi - echo "Tag: ${TAG}" >> $GITHUB_STEP_SUMMARY - - # -- STEP 7a: Promote RC to stable (skip build) ---------------------------- - - name: "Step 7a: Promote RC to stable" - if: >- - steps.version.outputs.skip != 'true' && - steps.rc.outputs.promote == 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/release_promote.php \ - --from release-candidate --to stable \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --api-base "${API_BASE}" \ - --path . --branch main - echo "Promoted RC → stable (${VERSION})" >> $GITHUB_STEP_SUMMARY - - # -- STEP 7b: Create or update Gitea Release (full build path) ------------- - - name: "Step 7b: Gitea Release" - if: >- - steps.version.outputs.skip != 'true' && - steps.rc.outputs.promote != 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - RELEASE_TAG="${{ steps.version.outputs.release_tag }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/release_create.php \ - --path . --version "$VERSION" --tag "$RELEASE_TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --branch main - echo "Release created: ${VERSION}" >> $GITHUB_STEP_SUMMARY - - # -- STEP 8: Build packages and upload to release ---------------------------- - - name: "Step 8: Build package and upload" - id: package - if: >- - steps.version.outputs.skip != 'true' && - steps.rc.outputs.promote != 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - RELEASE_TAG="${{ steps.version.outputs.release_tag }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/release_package.php \ - --path . --version "$VERSION" --tag "$RELEASE_TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --output /tmp || true - - # -- STEP 5: Write update stream (after build so SHA-256 is available) ----- - - name: "Step 5: Write update stream" - if: steps.version.outputs.skip != 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - SHA256="${{ steps.package.outputs.sha256_zip }}" - - # Fetch latest updates.xml from main so preserve logic has all channels - GITEA_TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - API="${GITEA_URL}/api/v1/repos/${{ github.repository }}" - curl -sf -H "Authorization: token ${GITEA_TOKEN}" \ - "${API}/contents/updates.xml?ref=main" 2>/dev/null | \ - php -r "\$d=json_decode(file_get_contents('php://stdin'),true); echo base64_decode(\$d['content'] ?? '');" \ - > updates.xml 2>/dev/null || true - - SHA_FLAG="" - [ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}" - - php /tmp/moko-platform-api/cli/updates_xml_build.php \ - --path . --version "${VERSION}" --stability stable \ - --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \ - ${SHA_FLAG} --github-output - - # Commit updates.xml if changed - if ! git diff --quiet updates.xml 2>/dev/null; then - git add updates.xml - git commit -m "chore: update stable channel ${VERSION} [skip ci]" \ - --author="gitea-actions[bot] " - git push origin HEAD:refs/heads/main 2>&1 || true - fi - - # -- STEP 8b: Update release description with changelog ---------------------- - - name: "Step 8b: Update release body" - if: steps.version.outputs.skip != 'true' - continue-on-error: true - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - RELEASE_TAG="${{ steps.version.outputs.release_tag }}" - php /tmp/moko-platform-api/cli/release_body_update.php \ - --path . --version "${VERSION}" --tag "${RELEASE_TAG}" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \ - 2>&1 || true - echo "Release body updated" >> $GITHUB_STEP_SUMMARY + php /tmp/moko-platform-api/cli/release_publish.php \ + --path . --stability stable --bump minor --branch main \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" # -- STEP 9: Mirror to GitHub (stable only) -------------------------------- - name: "Step 9: Mirror release to GitHub" @@ -484,33 +191,17 @@ jobs: && echo "main branch pushed to GitHub mirror" \ || echo "WARNING: GitHub mirror push failed" - # -- Clean up lesser pre-releases (cascade) --------------------------------- - # stable → deletes all | rc → beta,alpha,dev | beta → alpha,dev | alpha → dev - - name: "Delete lesser pre-release channels" - continue-on-error: true - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php /tmp/moko-platform-api/cli/release_cascade.php \ - --stability stable \ - --version "${VERSION}" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" \ - --api-base "${API_BASE}" 2>/dev/null || true - - - name: "Step 11: Clean up pre-release branches and recreate dev from main" + - name: "Step 11: Delete rc branch and recreate dev from main" if: steps.version.outputs.skip != 'true' continue-on-error: true run: | API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - # Delete ephemeral pre-release branches (rc, alpha, beta) - for EPHEMERAL in rc alpha beta; do - curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ - "${API_BASE}/branches/${EPHEMERAL}" 2>/dev/null \ - && echo "Deleted ${EPHEMERAL} branch" \ - || echo "${EPHEMERAL} branch not found" - done + # Delete rc branch (ephemeral — created by promote-rc) + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ + "${API_BASE}/branches/rc" 2>/dev/null \ + && echo "Deleted rc branch" || echo "rc branch not found" # Delete dev branch curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ -- 2.52.0 From 9eaf2baea6bf0d6b555eec107c1eb21b29fb0e36 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Sat, 30 May 2026 16:03:07 +0000 Subject: [PATCH 06/12] chore: sync .mokogitea/workflows/pr-check.yml from moko-platform [skip ci] --- .mokogitea/workflows/pr-check.yml | 38 ++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 8 deletions(-) diff --git a/.mokogitea/workflows/pr-check.yml b/.mokogitea/workflows/pr-check.yml index df065238..ce64a271 100644 --- a/.mokogitea/workflows/pr-check.yml +++ b/.mokogitea/workflows/pr-check.yml @@ -52,22 +52,22 @@ jobs: REASON="Fix branches must target 'dev', not '${BASE}'" fi ;; + patch/*) + if [ "$BASE" != "dev" ] && [ "$BASE" != "rc" ]; then + ALLOWED=false + REASON="Patch branches must target 'dev' or 'rc', not '${BASE}'" + fi + ;; hotfix/*) if [ "$BASE" != "dev" ] && [ "$BASE" != "main" ]; then ALLOWED=false REASON="Hotfix branches can only target 'dev' or 'main', not '${BASE}'" fi ;; - alpha/*|beta/*) - if [ "$BASE" != "dev" ]; then - ALLOWED=false - REASON="Pre-release branches must target 'dev', not '${BASE}'" - fi - ;; - rc/*) + rc) if [ "$BASE" != "main" ]; then ALLOWED=false - REASON="Release candidate branches must target 'main', not '${BASE}'" + REASON="RC branch can only merge into 'main', not '${BASE}'" fi ;; dev) @@ -183,6 +183,28 @@ jobs: ;; esac + - name: Check changelog has unreleased entry + run: | + if [ ! -f "CHANGELOG.md" ]; then + echo "::warning::No CHANGELOG.md found" + exit 0 + fi + # Check for content under [Unreleased] section + if ! grep -q "## \[Unreleased\]" CHANGELOG.md; then + echo "::error::CHANGELOG.md missing [Unreleased] section" + exit 1 + fi + # Check there's at least one entry (Added/Changed/Fixed/Removed) under Unreleased + UNRELEASED_CONTENT=$(sed -n '/## \[Unreleased\]/,/## \[/p' CHANGELOG.md | grep -cE '^\s*-\s' || true) + if [ "$UNRELEASED_CONTENT" -eq 0 ]; then + echo "::error::CHANGELOG.md [Unreleased] section has no entries. Add a changelog entry describing your changes." + echo "## Changelog Check: Failed" >> $GITHUB_STEP_SUMMARY + echo "The \`[Unreleased]\` section in CHANGELOG.md has no entries." >> $GITHUB_STEP_SUMMARY + echo "Add a line like \`- Description of your change\` under a heading (\`### Added\`, \`### Changed\`, \`### Fixed\`, etc.)" >> $GITHUB_STEP_SUMMARY + exit 1 + fi + echo "Changelog: ${UNRELEASED_CONTENT} entry/entries in [Unreleased]" + - name: Verify package source run: | SOURCE_DIR="src" -- 2.52.0 From 7a9e3da17411c250efc1a45de2d37aa895d960b2 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 30 May 2026 11:41:20 -0500 Subject: [PATCH 07/12] feat(api): add install-from-URL endpoint for remote extension deployment Adds POST /api/v1/mokowaas/install to install Joomla extensions from a remote ZIP URL via the REST API, enabling push-deploy from CI pipelines. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 2 + .../api/src/Controller/InstallController.php | 283 ++++++++++++++++++ .../src/Extension/MokoWaaSApi.php | 6 + wiki/api-endpoints.md | 52 +++- 4 files changed, 338 insertions(+), 5 deletions(-) create mode 100644 src/packages/com_mokowaas/api/src/Controller/InstallController.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 14b46695..92134fc5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ # Changelog ## [Unreleased] +### Added +- API endpoint `POST /api/index.php/v1/mokowaas/install` — install extensions from a remote ZIP URL ## [02.20.00] --- 2026-05-28 diff --git a/src/packages/com_mokowaas/api/src/Controller/InstallController.php b/src/packages/com_mokowaas/api/src/Controller/InstallController.php new file mode 100644 index 00000000..8b36c42a --- /dev/null +++ b/src/packages/com_mokowaas/api/src/Controller/InstallController.php @@ -0,0 +1,283 @@ +input->getMethod() !== 'POST') + { + $this->sendJson(405, ['error' => 'POST required']); + return; + } + + $user = $app->getIdentity(); + + if (!$user->authorise('core.manage', 'com_installer')) + { + $this->sendJson(403, ['error' => 'Not authorized — requires core.manage on com_installer']); + return; + } + + // Parse JSON body + $body = json_decode($app->input->json->getRaw(), true); + $url = $body['url'] ?? ''; + + if ($url === '') + { + $this->sendJson(400, ['error' => 'Missing "url" in request body']); + return; + } + + // Validate URL scheme + if (!preg_match('#^https?://#i', $url)) + { + $this->sendJson(400, ['error' => 'URL must use http or https scheme']); + return; + } + + // Must point to a .zip file + $path = parse_url($url, PHP_URL_PATH); + + if (!$path || !str_ends_with(strtolower($path), '.zip')) + { + $this->sendJson(400, ['error' => 'URL must point to a .zip file']); + return; + } + + try + { + $result = $this->downloadAndInstall($url); + $this->sendJson(200, $result); + } + catch (\Throwable $e) + { + $this->sendJson(500, [ + 'error' => 'Installation failed', + 'message' => $e->getMessage(), + ]); + } + } + + /** + * Download ZIP from URL, extract, and install via Joomla Installer. + * + * @param string $url The remote ZIP URL + * + * @return array Result payload + * + * @throws \RuntimeException on failure + * + * @since 02.21.00 + */ + private function downloadAndInstall(string $url): array + { + $config = Factory::getConfig(); + $tmpPath = $config->get('tmp_path', JPATH_ROOT . '/tmp'); + $zipFile = $tmpPath . '/mokowaas_install_' . bin2hex(random_bytes(8)) . '.zip'; + + // Download + $this->downloadFile($url, $zipFile); + + try + { + // Extract + $extractDir = $tmpPath . '/mokowaas_extract_' . bin2hex(random_bytes(8)); + + if (!mkdir($extractDir, 0755, true)) + { + throw new \RuntimeException('Failed to create extraction directory'); + } + + $archive = new \Joomla\Archive\Archive; + $archive->extract($zipFile, $extractDir); + + // Install + $installer = Installer::getInstance(); + $result = $installer->install($extractDir); + + if (!$result) + { + throw new \RuntimeException('Joomla Installer returned failure — check server logs for details'); + } + + // Read installed extension info from the installer + $manifest = $installer->getManifest(); + $name = $manifest ? (string) $manifest->name : 'Unknown'; + $version = $manifest ? (string) $manifest->version : 'Unknown'; + $type = $installer->get('extension.type', 'Unknown'); + + return [ + 'status' => 'ok', + 'message' => 'Extension installed successfully', + 'extension' => [ + 'name' => $name, + 'version' => $version, + 'type' => $type, + ], + 'source_url' => $url, + ]; + } + finally + { + // Clean up temp files + @unlink($zipFile); + + if (isset($extractDir) && is_dir($extractDir)) + { + $this->removeDirectory($extractDir); + } + } + } + + /** + * Download a file from a URL with size limit enforcement. + * + * @param string $url Remote URL + * @param string $destPath Local destination path + * + * @return void + * + * @throws \RuntimeException on failure + * + * @since 02.21.00 + */ + private function downloadFile(string $url, string $destPath): void + { + $ch = curl_init($url); + + if ($ch === false) + { + throw new \RuntimeException('Failed to initialise cURL'); + } + + $fp = fopen($destPath, 'wb'); + + if ($fp === false) + { + curl_close($ch); + throw new \RuntimeException('Failed to open temp file for writing'); + } + + curl_setopt_array($ch, [ + CURLOPT_FILE => $fp, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_MAXREDIRS => 5, + CURLOPT_TIMEOUT => 120, + CURLOPT_CONNECTTIMEOUT => 15, + CURLOPT_FAILONERROR => true, + CURLOPT_USERAGENT => 'MokoWaaS-Installer/1.0', + ]); + + $success = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + $error = curl_error($ch); + $fileSize = curl_getinfo($ch, CURLINFO_SIZE_DOWNLOAD); + + curl_close($ch); + fclose($fp); + + if (!$success) + { + @unlink($destPath); + throw new \RuntimeException('Download failed (HTTP ' . $httpCode . '): ' . $error); + } + + if ($fileSize > self::MAX_DOWNLOAD_BYTES) + { + @unlink($destPath); + throw new \RuntimeException('Download exceeds maximum size of ' . (self::MAX_DOWNLOAD_BYTES / 1048576) . ' MB'); + } + } + + /** + * Recursively remove a directory and its contents. + * + * @param string $dir Directory path + * + * @return void + * + * @since 02.21.00 + */ + private function removeDirectory(string $dir): void + { + $items = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS), + \RecursiveIteratorIterator::CHILD_FIRST + ); + + foreach ($items as $item) + { + if ($item->isDir()) + { + @rmdir($item->getPathname()); + } + else + { + @unlink($item->getPathname()); + } + } + + @rmdir($dir); + } + + /** + * Send a JSON response and close. + * + * @param int $code HTTP status code + * @param array $payload Response data + * + * @return void + * + * @since 02.21.00 + */ + private function sendJson(int $code, array $payload): void + { + $app = Factory::getApplication(); + $app->setHeader('Content-Type', 'application/json', true); + $app->setHeader('Status', (string) $code, true); + echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); + $app->close(); + } +} diff --git a/src/packages/plg_webservices_mokowaas/src/Extension/MokoWaaSApi.php b/src/packages/plg_webservices_mokowaas/src/Extension/MokoWaaSApi.php index b6e2e405..79558888 100644 --- a/src/packages/plg_webservices_mokowaas/src/Extension/MokoWaaSApi.php +++ b/src/packages/plg_webservices_mokowaas/src/Extension/MokoWaaSApi.php @@ -64,5 +64,11 @@ final class MokoWaaSApi extends CMSPlugin implements SubscriberInterface 'update', ['component' => 'com_mokowaas'] ); + + $router->createCRUDRoutes( + 'v1/mokowaas/install', + 'install', + ['component' => 'com_mokowaas'] + ); } } diff --git a/wiki/api-endpoints.md b/wiki/api-endpoints.md index 310eed25..f1fd79ea 100644 --- a/wiki/api-endpoints.md +++ b/wiki/api-endpoints.md @@ -172,10 +172,52 @@ Requesting an unknown action returns HTTP 400 with the list of available actions In addition to the query-string endpoints above, MokoWaaS registers standard Joomla API routes via the `plg_webservices_mokowaas` plugin: -| Route | Controller | -|---|---| -| `GET /api/v1/mokowaas/health` | HealthController | -| `POST /api/v1/mokowaas/cache` | CacheController | -| `POST /api/v1/mokowaas/update` | UpdateController | +| Route | Controller | Description | +|---|---|---| +| `GET /api/v1/mokowaas/health` | HealthController | Full site health diagnostics | +| `POST /api/v1/mokowaas/cache` | CacheController | Clear all caches | +| `POST /api/v1/mokowaas/update` | UpdateController | Trigger update check | +| `POST /api/v1/mokowaas/install` | InstallController | Install extension from ZIP URL | These routes use Joomla's standard API authentication (API token in `X-Joomla-Token` header) and are useful for integrations that already use the Joomla API framework. + +### Install Endpoint (REST API) + +``` +POST /api/index.php/v1/mokowaas/install +X-Joomla-Token: +Content-Type: application/json + +{"url": "https://git.mokoconsulting.tech/.../plg_system_mokowaas-02.20.00.zip"} +``` + +Downloads the ZIP from the given URL and installs it via Joomla's Installer. Requires a Joomla API token with `core.manage` permission on `com_installer`. + +**Constraints:** +- URL must use `http` or `https` scheme +- URL path must end in `.zip` +- Maximum download size: 64 MB + +**Success Response** (HTTP 200): + +```json +{ + "status": "ok", + "message": "Extension installed successfully", + "extension": { + "name": "MokoWaaS", + "version": "02.20.00", + "type": "plugin" + }, + "source_url": "https://git.mokoconsulting.tech/.../plg_system_mokowaas-02.20.00.zip" +} +``` + +**Error Responses:** + +| HTTP Status | Condition | +|---|---| +| 400 | Missing `url` field, non-HTTPS URL, or URL not ending in `.zip` | +| 403 | API token lacks `core.manage` on `com_installer` | +| 405 | Request method is not POST | +| 500 | Download or installation failed | -- 2.52.0 From 4190e0e9ea4aa4264bef68228e3750338a0c1545 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 30 May 2026 16:46:46 +0000 Subject: [PATCH 08/12] chore(version): auto-bump 02.20.02-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- CODE_OF_CONDUCT.md | 2 +- CONTRIBUTING.md | 4 ++-- GOVERNANCE.md | 2 +- LICENSE.md | 2 +- README.md | 2 +- SECURITY.md | 2 +- docs/guides/build-guide.md | 4 ++-- docs/guides/configuration-guide.md | 4 ++-- docs/guides/installation-guide.md | 4 ++-- docs/guides/operations-guide.md | 4 ++-- docs/guides/rollback-and-recovery-guide.md | 4 ++-- docs/guides/testing-guide.md | 4 ++-- docs/guides/troubleshooting-guide.md | 4 ++-- docs/guides/upgrade-and-versioning-guide.md | 4 ++-- docs/index.md | 4 ++-- docs/plugin-basic.md | 4 ++-- docs/update-server.md | 2 +- src/packages/com_mokowaas/mokowaas.xml | 2 +- src/packages/plg_system_mokowaas/Extension/MokoWaaS.php | 2 +- src/packages/plg_system_mokowaas/Field/AllowedIpsField.php | 2 +- src/packages/plg_system_mokowaas/Field/CurrentIpField.php | 2 +- src/packages/plg_system_mokowaas/mokowaas.xml | 2 +- src/packages/plg_system_mokowaas/script.php | 2 +- src/packages/plg_system_mokowaas/services/provider.php | 2 +- src/packages/plg_webservices_mokowaas/mokowaas.xml | 2 +- .../plg_webservices_perfectpublisher/perfectpublisher.xml | 2 +- .../plg_webservices_perfectpublisher/services/provider.php | 2 +- .../src/Extension/PerfectPublisherApi.php | 2 +- src/pkg_mokowaas.xml | 2 +- updates.xml | 2 +- 33 files changed, 44 insertions(+), 44 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index d01fd65f..3e2b93a2 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -8,7 +8,7 @@ MokoWaaS MokoConsulting White-label identity, security hardening, and tenant restriction layer for WaaS-managed Joomla environments - 02.20.00 + 02.20.02 GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index f084fe1b..d939708e 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: 01.00.00 +# VERSION: 02.20.02 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 92134fc5..9d9ddb7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ INGROUP: MokoWaaS.Documentation REPO: https://github.com/mokoconsulting-tech/mokowaas PATH: ./CHANGELOG.md - VERSION: 02.01.08 + VERSION: 02.20.02 BRIEF: Version history using `Keep a Changelog` --> diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 8930b430..29473c98 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -14,7 +14,7 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Documentation REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.01.08 + VERSION: 02.20.02 PATH: ./CODE_OF_CONDUCT.md BRIEF: Reference + packaging repo for Moko Consulting Developer GPT Other Default --> diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 5669099c..2c2f699f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,12 +10,12 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Contributing REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.01.08 + VERSION: 02.20.02 PATH: /CONTRIBUTING.md BRIEF: Contribution guidelines for the MokoWaaS plugin --> -# Contributing to MokoWaaS (VERSION: 02.01.08) +# Contributing to MokoWaaS (VERSION: 02.20.02) ## Overview Contributions to the MokoWaaS plugin follow standardized development, governance, and quality control expectations defined by Moko Consulting. This document outlines contribution requirements, acceptable change types, branch management, testing expectations, and release readiness standards. diff --git a/GOVERNANCE.md b/GOVERNANCE.md index ffd817a5..9d1dee6a 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -19,7 +19,7 @@ DEFGROUP: mokoconsulting-tech.MokoWaaSBrand INGROUP: MokoStandards.Governance REPO: https://github.com/mokoconsulting-tech/MokoWaaSBrand - VERSION: 02.01.08 + VERSION: 02.20.02 PATH: /GOVERNANCE.md BRIEF: Project governance rules, roles, and decision process for MokoWaaSBrand --> diff --git a/LICENSE.md b/LICENSE.md index 25285186..01c832c6 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -15,7 +15,7 @@ INGROUP: MokoWaaS.Documentation REPO: https://github.com/mokoconsulting-tech/mokowaas PATH: ./LICENSE.md - VERSION: 02.01.08 + VERSION: 02.20.02 BRIEF: Project license (GPL-3.0-or-later) --> GNU GENERAL PUBLIC LICENSE diff --git a/README.md b/README.md index 94620476..ef57e0eb 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS - VERSION: 02.20.01 + VERSION: 02.20.02 PATH: /README.md BRIEF: MokoWaaS platform plugin for Joomla --> diff --git a/SECURITY.md b/SECURITY.md index 6568c9a0..ecec6962 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: [PROJECT_NAME] INGROUP: [PROJECT_NAME].Documentation REPO: [REPOSITORY_URL] PATH: /SECURITY.md -VERSION: 02.01.08 +VERSION: 02.20.02 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/docs/guides/build-guide.md b/docs/guides/build-guide.md index 8f129653..5e66d529 100644 --- a/docs/guides/build-guide.md +++ b/docs/guides/build-guide.md @@ -11,13 +11,13 @@ INGROUP: MokoWaaS.Build REPO: https://github.com/mokoconsulting-tech/mokowaas FILE: build-guide.md - VERSION: 02.01.08 + VERSION: 02.20.02 PATH: /docs/guides/ BRIEF: Build and packaging guide for the MokoWaaS system plugin NOTE: Defines environment setup, repository layout, packaging rules, and release preparation --> -# MokoWaaS Build Guide (VERSION: 02.01.08) +# MokoWaaS Build Guide (VERSION: 02.20.02) ## 1. Purpose diff --git a/docs/guides/configuration-guide.md b/docs/guides/configuration-guide.md index 2bcb5dc1..5d7d649d 100644 --- a/docs/guides/configuration-guide.md +++ b/docs/guides/configuration-guide.md @@ -10,13 +10,13 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Guides REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.01.08 + VERSION: 02.20.02 PATH: /docs/guides/configuration-guide.md BRIEF: Configuration guide for the MokoWaaS system plugin NOTE: Defines plugin parameters, expected behaviors, and recommended defaults --> -# MokoWaaS Configuration Guide (VERSION: 02.01.08) +# MokoWaaS Configuration Guide (VERSION: 02.20.02) ## 1. Objective diff --git a/docs/guides/installation-guide.md b/docs/guides/installation-guide.md index 2e3d7dfe..eca34f4a 100644 --- a/docs/guides/installation-guide.md +++ b/docs/guides/installation-guide.md @@ -10,13 +10,13 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Guides REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.01.08 + VERSION: 02.20.02 PATH: /docs/guides/installation-guide.md BRIEF: Installation guide for the MokoWaaS system plugin NOTE: First document in the guide set --> -# MokoWaaS Installation Guide (VERSION: 02.01.08) +# MokoWaaS Installation Guide (VERSION: 02.20.02) ## Introduction diff --git a/docs/guides/operations-guide.md b/docs/guides/operations-guide.md index 4aca9b32..45c0a8b6 100644 --- a/docs/guides/operations-guide.md +++ b/docs/guides/operations-guide.md @@ -10,13 +10,13 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Guides REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.01.08 + VERSION: 02.20.02 PATH: /docs/guides/operations-guide.md BRIEF: Operational guide for administering and managing the MokoWaaS system plugin NOTE: Defines lifecycle, responsibilities, and operational behaviors --> -# MokoWaaS Operations Guide (VERSION: 02.01.08) +# MokoWaaS Operations Guide (VERSION: 02.20.02) ## Introduction diff --git a/docs/guides/rollback-and-recovery-guide.md b/docs/guides/rollback-and-recovery-guide.md index 754d4b48..9e73a42c 100644 --- a/docs/guides/rollback-and-recovery-guide.md +++ b/docs/guides/rollback-and-recovery-guide.md @@ -10,13 +10,13 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Guides REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.01.08 + VERSION: 02.20.02 PATH: /docs/guides/rollback-and-recovery-guide.md BRIEF: Rollback and recovery guide for restoring stable operation after plugin related incidents NOTE: Completes the core guide set for WaaS plugin governance --> -# MokoWaaS Rollback and Recovery Guide (VERSION: 02.01.08) +# MokoWaaS Rollback and Recovery Guide (VERSION: 02.20.02) ## Introduction diff --git a/docs/guides/testing-guide.md b/docs/guides/testing-guide.md index 8252d0dc..b64104ac 100644 --- a/docs/guides/testing-guide.md +++ b/docs/guides/testing-guide.md @@ -7,13 +7,13 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Guides REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.01.08 + VERSION: 02.20.02 PATH: /docs/guides/testing-guide.md BRIEF: Testing guide for MokoWaaS v02.01.08 NOTE: Covers manual test procedures for language overrides, install/uninstall, and configuration --> -# MokoWaaS Testing Guide (VERSION: 02.01.08) +# MokoWaaS Testing Guide (VERSION: 02.20.02) ## 1. Prerequisites diff --git a/docs/guides/troubleshooting-guide.md b/docs/guides/troubleshooting-guide.md index 451f438d..2e9a8ad4 100644 --- a/docs/guides/troubleshooting-guide.md +++ b/docs/guides/troubleshooting-guide.md @@ -10,13 +10,13 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Guides REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.01.08 + VERSION: 02.20.02 PATH: /docs/guides/troubleshooting-guide.md BRIEF: Troubleshooting guide for diagnosing and resolving issues related to the MokoWaaS plugin NOTE: Designed for administrators and WaaS operations teams --> -# MokoWaaS Troubleshooting Guide (VERSION: 02.01.08) +# MokoWaaS Troubleshooting Guide (VERSION: 02.20.02) ## Introduction diff --git a/docs/guides/upgrade-and-versioning-guide.md b/docs/guides/upgrade-and-versioning-guide.md index 13a440e9..3caa14d1 100644 --- a/docs/guides/upgrade-and-versioning-guide.md +++ b/docs/guides/upgrade-and-versioning-guide.md @@ -10,13 +10,13 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Guides REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.01.08 + VERSION: 02.20.02 PATH: /docs/guides/upgrade-and-versioning-guide.md BRIEF: Guide for updating, versioning, and maintaining the MokoWaaS plugin NOTE: Defines release flow, version rules, and upgrade validation --> -# MokoWaaS Upgrade and Versioning Guide (VERSION: 02.01.08) +# MokoWaaS Upgrade and Versioning Guide (VERSION: 02.20.02) ## Introduction diff --git a/docs/index.md b/docs/index.md index 50f7d918..1fc3209f 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,13 +10,13 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Documentation REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.01.08 + VERSION: 02.20.02 PATH: /docs/index.md BRIEF: Master index of all documentation for the MokoWaaS plugin NOTE: Automatically maintained index for all guide canvases --> -# MokoWaaS Documentation Index (VERSION: 02.01.08) +# MokoWaaS Documentation Index (VERSION: 02.20.02) ## Introduction diff --git a/docs/plugin-basic.md b/docs/plugin-basic.md index 249b7b8a..e23d4098 100644 --- a/docs/plugin-basic.md +++ b/docs/plugin-basic.md @@ -11,12 +11,12 @@ INGROUP: MokoWaaS REPO: https://github.com/mokoconsulting-tech/mokowaas PATH: /docs/plugin-basic.md - VERSION: 02.01.08 + VERSION: 02.20.02 BRIEF: Baseline documentation for the MokoWaaS system plugin NOTE: Foundational reference for internal and external stakeholders --> -# MokoWaaS Plugin Overview (VERSION: 02.01.08) +# MokoWaaS Plugin Overview (VERSION: 02.20.02) ## Introduction diff --git a/docs/update-server.md b/docs/update-server.md index 5bfe0327..8ff06123 100644 --- a/docs/update-server.md +++ b/docs/update-server.md @@ -10,7 +10,7 @@ DEFGROUP: MokoWaaS.Documentation INGROUP: MokoStandards.Templates REPO: https://github.com/mokoconsulting-tech/MokoWaaS PATH: /docs/update-server.md -VERSION: 02.01.08 +VERSION: 02.20.02 BRIEF: How this extension's Joomla update server file (update.xml) is managed --> diff --git a/src/packages/com_mokowaas/mokowaas.xml b/src/packages/com_mokowaas/mokowaas.xml index 45bccc0a..ad652fff 100644 --- a/src/packages/com_mokowaas/mokowaas.xml +++ b/src/packages/com_mokowaas/mokowaas.xml @@ -7,7 +7,7 @@ GPL-3.0-or-later hello@mokoconsulting.tech https://mokoconsulting.tech - 02.20.01-dev + 02.20.02-dev Minimal API-only component for MokoWaaS. Provides REST endpoints for site health, cache, updates, and backups. Moko\Component\MokoWaaS\Api diff --git a/src/packages/plg_system_mokowaas/Extension/MokoWaaS.php b/src/packages/plg_system_mokowaas/Extension/MokoWaaS.php index 0886fb27..7528f4f2 100644 --- a/src/packages/plg_system_mokowaas/Extension/MokoWaaS.php +++ b/src/packages/plg_system_mokowaas/Extension/MokoWaaS.php @@ -22,7 +22,7 @@ * DEFGROUP: Joomla.Plugin * INGROUP: MokoWaaS * REPO: https://github.com/mokoconsulting-tech/mokowaas - * VERSION: 02.01.08 + * VERSION: 02.20.02 * PATH: /src/Extension/MokoWaaS.php * NOTE: Handles Joomla system events for rebranding functionality */ diff --git a/src/packages/plg_system_mokowaas/Field/AllowedIpsField.php b/src/packages/plg_system_mokowaas/Field/AllowedIpsField.php index 65bdef20..e91218af 100644 --- a/src/packages/plg_system_mokowaas/Field/AllowedIpsField.php +++ b/src/packages/plg_system_mokowaas/Field/AllowedIpsField.php @@ -7,7 +7,7 @@ * FILE INFORMATION * DEFGROUP: Joomla.Plugin * INGROUP: MokoWaaS - * VERSION: 02.01.08 + * VERSION: 02.20.02 * PATH: /src/Field/AllowedIpsField.php * BRIEF: Custom form field that displays the current IP whitelist */ diff --git a/src/packages/plg_system_mokowaas/Field/CurrentIpField.php b/src/packages/plg_system_mokowaas/Field/CurrentIpField.php index 6de6e00f..264bd474 100644 --- a/src/packages/plg_system_mokowaas/Field/CurrentIpField.php +++ b/src/packages/plg_system_mokowaas/Field/CurrentIpField.php @@ -7,7 +7,7 @@ * FILE INFORMATION * DEFGROUP: Joomla.Plugin * INGROUP: MokoWaaS - * VERSION: 02.11.00 + * VERSION: 02.20.02 * PATH: /src/Field/CurrentIpField.php * BRIEF: Read-only field that displays the current user's IP address */ diff --git a/src/packages/plg_system_mokowaas/mokowaas.xml b/src/packages/plg_system_mokowaas/mokowaas.xml index f7287450..f55d8875 100644 --- a/src/packages/plg_system_mokowaas/mokowaas.xml +++ b/src/packages/plg_system_mokowaas/mokowaas.xml @@ -30,7 +30,7 @@ GNU General Public License version 3 or later; see LICENSE.md hello@mokoconsulting.tech https://mokoconsulting.tech - 02.20.01-dev + 02.20.02-dev This plugin rebrands the Joomla system interface with MokoWaaS identity. It applies language overrides and ensures consistent branding across the platform. Moko\Plugin\System\MokoWaaS script.php diff --git a/src/packages/plg_system_mokowaas/script.php b/src/packages/plg_system_mokowaas/script.php index 65ec942b..43cb58ca 100644 --- a/src/packages/plg_system_mokowaas/script.php +++ b/src/packages/plg_system_mokowaas/script.php @@ -22,7 +22,7 @@ * DEFGROUP: Joomla.Plugin * INGROUP: MokoWaaS * REPO: https://github.com/mokoconsulting-tech/mokowaas - * VERSION: 02.01.08 + * VERSION: 02.20.02 * PATH: /src/script.php * BRIEF: Installation script for MokoWaaS plugin * NOTE: Handles installation, update, and uninstallation tasks including language override deployment diff --git a/src/packages/plg_system_mokowaas/services/provider.php b/src/packages/plg_system_mokowaas/services/provider.php index 4f501fe8..f7007931 100644 --- a/src/packages/plg_system_mokowaas/services/provider.php +++ b/src/packages/plg_system_mokowaas/services/provider.php @@ -22,7 +22,7 @@ * DEFGROUP: Joomla.Plugin * INGROUP: MokoWaaS * REPO: https://github.com/mokoconsulting-tech/mokowaas - * VERSION: 02.01.08 + * VERSION: 02.20.02 * PATH: /src/services/provider.php * BRIEF: Service provider for dependency injection in Joomla 5.x * NOTE: Registers the plugin with Joomla's DI container diff --git a/src/packages/plg_webservices_mokowaas/mokowaas.xml b/src/packages/plg_webservices_mokowaas/mokowaas.xml index 3b111372..65de22bb 100644 --- a/src/packages/plg_webservices_mokowaas/mokowaas.xml +++ b/src/packages/plg_webservices_mokowaas/mokowaas.xml @@ -7,7 +7,7 @@ GPL-3.0-or-later hello@mokoconsulting.tech https://mokoconsulting.tech - 02.20.01-dev + 02.20.02-dev Joomla Web Services API routes for MokoWaaS site management — health checks, cache, updates, backups, and site info. Moko\Plugin\WebServices\MokoWaaS diff --git a/src/packages/plg_webservices_perfectpublisher/perfectpublisher.xml b/src/packages/plg_webservices_perfectpublisher/perfectpublisher.xml index fab07cde..0c9ffea7 100644 --- a/src/packages/plg_webservices_perfectpublisher/perfectpublisher.xml +++ b/src/packages/plg_webservices_perfectpublisher/perfectpublisher.xml @@ -7,7 +7,7 @@ GPL-3.0-or-later hello@mokoconsulting.tech https://mokoconsulting.tech - 02.20.01-dev + 02.20.02-dev Joomla Web Services API routes for Perfect Publisher (com_autotweet) — channels, posts, requests, rules, and feeds. Moko\Plugin\WebServices\PerfectPublisher diff --git a/src/packages/plg_webservices_perfectpublisher/services/provider.php b/src/packages/plg_webservices_perfectpublisher/services/provider.php index dd88ea91..ac0a9655 100644 --- a/src/packages/plg_webservices_perfectpublisher/services/provider.php +++ b/src/packages/plg_webservices_perfectpublisher/services/provider.php @@ -8,7 +8,7 @@ * INGROUP: MokoWaaS * REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS * PATH: /src/packages/plg_webservices_perfectpublisher/services/provider.php - * VERSION: 02.13.01 + * VERSION: 02.20.02 * BRIEF: DI service provider for Perfect Publisher Web Services plugin */ diff --git a/src/packages/plg_webservices_perfectpublisher/src/Extension/PerfectPublisherApi.php b/src/packages/plg_webservices_perfectpublisher/src/Extension/PerfectPublisherApi.php index a0543cff..fd5ff661 100644 --- a/src/packages/plg_webservices_perfectpublisher/src/Extension/PerfectPublisherApi.php +++ b/src/packages/plg_webservices_perfectpublisher/src/Extension/PerfectPublisherApi.php @@ -8,7 +8,7 @@ * INGROUP: MokoWaaS * REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS * PATH: /src/packages/plg_webservices_perfectpublisher/src/Extension/PerfectPublisherApi.php - * VERSION: 02.13.01 + * VERSION: 02.20.02 * BRIEF: Web Services API plugin for Perfect Publisher (com_autotweet) */ diff --git a/src/pkg_mokowaas.xml b/src/pkg_mokowaas.xml index 01cf8855..1943c439 100644 --- a/src/pkg_mokowaas.xml +++ b/src/pkg_mokowaas.xml @@ -2,7 +2,7 @@ MokoWaaS mokowaas - 02.20.01-dev + 02.20.02-dev 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/updates.xml b/updates.xml index 8828784a..8eb1e4cb 100644 --- a/updates.xml +++ b/updates.xml @@ -1,7 +1,7 @@ -- 2.52.0 From c4b8aa0fbb049d3e616a991da2f3663b3e63642b Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 30 May 2026 16:46:47 +0000 Subject: [PATCH 09/12] chore: update development channel 02.20.02-dev [skip ci] --- updates.xml | 61 +++++++++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 30 deletions(-) diff --git a/updates.xml b/updates.xml index 8eb1e4cb..e47e4707 100644 --- a/updates.xml +++ b/updates.xml @@ -1,28 +1,10 @@ - - Package - MokoWaaS - Package - MokoWaaS development build. - pkg_mokowaas - package - site - 02.20.00 - 2026-05-28 - https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/stable - - https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/stable/pkg_mokowaas-02.20.00.zip - - 71a9f9d03810499992771fc505b9caf08191e8d9596963ab135d92abf836d6cc - dev - Moko Consulting - https://mokoconsulting.tech - - Package - MokoWaaS Package - MokoWaaS alpha build. @@ -31,15 +13,15 @@ site 02.20.00 2026-05-28 - https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/stable + https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/stable - https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/stable/pkg_mokowaas-02.20.00.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/stable/pkg_mokowaas-02.20.00.zip 71a9f9d03810499992771fc505b9caf08191e8d9596963ab135d92abf836d6cc alpha Moko Consulting https://mokoconsulting.tech - + Package - MokoWaaS @@ -49,15 +31,15 @@ site 02.20.00 2026-05-28 - https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/stable + https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/stable - https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/stable/pkg_mokowaas-02.20.00.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/stable/pkg_mokowaas-02.20.00.zip 71a9f9d03810499992771fc505b9caf08191e8d9596963ab135d92abf836d6cc beta Moko Consulting https://mokoconsulting.tech - + Package - MokoWaaS @@ -67,15 +49,15 @@ site 02.20.00 2026-05-28 - https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/stable + https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/stable - https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/stable/pkg_mokowaas-02.20.00.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/stable/pkg_mokowaas-02.20.00.zip 71a9f9d03810499992771fc505b9caf08191e8d9596963ab135d92abf836d6cc rc Moko Consulting https://mokoconsulting.tech - + Package - MokoWaaS @@ -85,14 +67,33 @@ site 02.20.00 2026-05-28 - https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/stable + https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/stable - https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/stable/pkg_mokowaas-02.20.00.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/stable/pkg_mokowaas-02.20.00.zip 71a9f9d03810499992771fc505b9caf08191e8d9596963ab135d92abf836d6cc stable Moko Consulting https://mokoconsulting.tech + + + + Package - MokoWaaS + Package - MokoWaaS development build. + pkg_mokowaas + package + site + 02.20.02-dev + 2026-05-30 + https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/development + + https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.20.02-dev.zip + + 548ba5286120f81f682a388630336eadd3728ba9c6dee6873de32de2fea7f2ee + dev + https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/raw/branch/main/CHANGELOG.md + Moko Consulting + https://mokoconsulting.tech -- 2.52.0 From 72308fee538eabc3e0507a81ea7bc1ebd6306484 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 30 May 2026 12:50:11 -0500 Subject: [PATCH 10/12] feat(demo): add demo site reset with baseline snapshots and warning banner Adds Demo Mode feature for configuring sites as demo instances with automatic periodic reset to a saved baseline. Includes DemoResetService for DB table + media snapshots, frontend warning banner, REST and query-string API endpoints, admin toggles, and a Joomla Scheduled Task plugin (plg_task_mokowaasdemo) for automatic reset. Closes #88 Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 6 + .../api/src/Controller/ResetController.php | 126 ++++ .../api/src/Controller/SnapshotController.php | 153 ++++ .../Extension/MokoWaaS.php | 238 +++++- .../Service/DemoResetService.php | 714 ++++++++++++++++++ .../plg_system_mokowaas/Service/index.html | 1 + .../language/en-GB/plg_system_mokowaas.ini | 25 + .../language/en-US/plg_system_mokowaas.ini | 25 + src/packages/plg_system_mokowaas/mokowaas.xml | 64 +- .../plg_task_mokowaas/forms/reset_params.xml | 9 + .../language/en-GB/plg_task_mokowaasdemo.ini | 10 + .../en-GB/plg_task_mokowaasdemo.sys.ini | 6 + .../plg_task_mokowaas/mokowaasdemo.xml | 30 + .../plg_task_mokowaas/services/provider.php | 37 + .../src/Extension/DemoReset.php | 125 +++ .../src/Extension/MokoWaaSApi.php | 12 + src/pkg_mokowaas.xml | 1 + src/script.php | 1 + wiki/api-endpoints.md | 63 +- 19 files changed, 1643 insertions(+), 3 deletions(-) create mode 100644 src/packages/com_mokowaas/api/src/Controller/ResetController.php create mode 100644 src/packages/com_mokowaas/api/src/Controller/SnapshotController.php create mode 100644 src/packages/plg_system_mokowaas/Service/DemoResetService.php create mode 100644 src/packages/plg_system_mokowaas/Service/index.html create mode 100644 src/packages/plg_task_mokowaas/forms/reset_params.xml create mode 100644 src/packages/plg_task_mokowaas/language/en-GB/plg_task_mokowaasdemo.ini create mode 100644 src/packages/plg_task_mokowaas/language/en-GB/plg_task_mokowaasdemo.sys.ini create mode 100644 src/packages/plg_task_mokowaas/mokowaasdemo.xml create mode 100644 src/packages/plg_task_mokowaas/services/provider.php create mode 100644 src/packages/plg_task_mokowaas/src/Extension/DemoReset.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d9ddb7d..104e3b72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,12 @@ ## [Unreleased] ### Added - API endpoint `POST /api/index.php/v1/mokowaas/install` — install extensions from a remote ZIP URL +- Demo Mode with configurable warning banner on frontend when enabled +- `DemoResetService` — baseline snapshot and restore for DB tables + media files +- API endpoints `POST /?mokowaas=reset` and `POST /?mokowaas=snapshot` (query-string) +- REST endpoints `POST /api/v1/mokowaas/reset` and `GET/POST /api/v1/mokowaas/snapshot` +- `plg_task_mokowaasdemo` — Joomla Scheduled Task plugin for automatic demo site reset +- Admin toggles: Take Snapshot Now and Restore Baseline Now in plugin config ## [02.20.00] --- 2026-05-28 diff --git a/src/packages/com_mokowaas/api/src/Controller/ResetController.php b/src/packages/com_mokowaas/api/src/Controller/ResetController.php new file mode 100644 index 00000000..671332f9 --- /dev/null +++ b/src/packages/com_mokowaas/api/src/Controller/ResetController.php @@ -0,0 +1,126 @@ +input->getMethod() !== 'POST') + { + $this->sendJson(405, ['error' => 'POST required']); + return; + } + + $user = $app->getIdentity(); + + if (!$user->authorise('core.manage', 'com_plugins')) + { + $this->sendJson(403, ['error' => 'Not authorized']); + return; + } + + $plugin = PluginHelper::getPlugin('system', 'mokowaas'); + + if (!$plugin) + { + $this->sendJson(503, ['error' => 'MokoWaaS system plugin not enabled']); + return; + } + + $params = new Registry($plugin->params); + + try + { + $body = json_decode($app->input->json->getRaw(), true); + $baseline = $body['baseline'] + ?? $params->get('demo_active_baseline', 'default'); + + $service = $this->createService($params); + $result = $service->restoreSnapshot($baseline); + + $this->sendJson(200, $result); + } + catch (\Throwable $e) + { + $this->sendJson(500, [ + 'error' => 'Reset failed', + 'message' => $e->getMessage(), + ]); + } + } + + /** + * Create DemoResetService from plugin params. + * + * @param Registry $params Plugin parameters + * + * @return \Moko\Plugin\System\MokoWaaS\Service\DemoResetService + * + * @since 02.21.00 + */ + private function createService(Registry $params) + { + $serviceFile = JPATH_PLUGINS . '/system/mokowaas/Service/DemoResetService.php'; + + if (!file_exists($serviceFile)) + { + throw new \RuntimeException('DemoResetService not found — is the MokoWaaS plugin installed?'); + } + + require_once $serviceFile; + + $tablesRaw = $params->get('demo_snapshot_tables', ''); + $tables = array_filter(array_map('trim', explode("\n", $tablesRaw))); + $media = (bool) $params->get('demo_snapshot_include_media', 1); + + return new \Moko\Plugin\System\MokoWaaS\Service\DemoResetService($tables, $media); + } + + /** + * @param int $code HTTP status code + * @param array $payload Response data + * @return void + */ + private function sendJson(int $code, array $payload): void + { + $app = Factory::getApplication(); + $app->setHeader('Content-Type', 'application/json', true); + $app->setHeader('Status', (string) $code, true); + echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); + $app->close(); + } +} diff --git a/src/packages/com_mokowaas/api/src/Controller/SnapshotController.php b/src/packages/com_mokowaas/api/src/Controller/SnapshotController.php new file mode 100644 index 00000000..2577cb7f --- /dev/null +++ b/src/packages/com_mokowaas/api/src/Controller/SnapshotController.php @@ -0,0 +1,153 @@ +getIdentity(); + + if (!$user->authorise('core.manage', 'com_plugins')) + { + $this->sendJson(403, ['error' => 'Not authorized']); + return; + } + + try + { + $service = $this->createService(); + + $this->sendJson(200, [ + 'status' => 'ok', + 'snapshots' => $service->listSnapshots(), + ]); + } + catch (\Throwable $e) + { + $this->sendJson(500, [ + 'error' => 'Failed to list snapshots', + 'message' => $e->getMessage(), + ]); + } + } + + /** + * Create a new snapshot. + * + * @return void + * + * @since 02.21.00 + */ + public function execute(): void + { + $app = Factory::getApplication(); + + if ($app->input->getMethod() !== 'POST') + { + $this->sendJson(405, ['error' => 'POST required']); + return; + } + + $user = $app->getIdentity(); + + if (!$user->authorise('core.manage', 'com_plugins')) + { + $this->sendJson(403, ['error' => 'Not authorized']); + return; + } + + try + { + $plugin = PluginHelper::getPlugin('system', 'mokowaas'); + $params = $plugin ? new Registry($plugin->params) : new Registry; + + $body = json_decode($app->input->json->getRaw(), true); + $name = $body['name'] + ?? $params->get('demo_active_baseline', 'default'); + + $service = $this->createService(); + $result = $service->createSnapshot($name); + + $this->sendJson(200, $result); + } + catch (\Throwable $e) + { + $this->sendJson(500, [ + 'error' => 'Snapshot failed', + 'message' => $e->getMessage(), + ]); + } + } + + /** + * Create DemoResetService from plugin params. + * + * @return \Moko\Plugin\System\MokoWaaS\Service\DemoResetService + * + * @since 02.21.00 + */ + private function createService() + { + $serviceFile = JPATH_PLUGINS . '/system/mokowaas/Service/DemoResetService.php'; + + if (!file_exists($serviceFile)) + { + throw new \RuntimeException('DemoResetService not found'); + } + + require_once $serviceFile; + + $plugin = PluginHelper::getPlugin('system', 'mokowaas'); + $params = $plugin ? new Registry($plugin->params) : new Registry; + + $tablesRaw = $params->get('demo_snapshot_tables', ''); + $tables = array_filter(array_map('trim', explode("\n", $tablesRaw))); + $media = (bool) $params->get('demo_snapshot_include_media', 1); + + return new \Moko\Plugin\System\MokoWaaS\Service\DemoResetService($tables, $media); + } + + /** + * @param int $code HTTP status code + * @param array $payload Response data + * @return void + */ + private function sendJson(int $code, array $payload): void + { + $app = Factory::getApplication(); + $app->setHeader('Content-Type', 'application/json', true); + $app->setHeader('Status', (string) $code, true); + echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); + $app->close(); + } +} diff --git a/src/packages/plg_system_mokowaas/Extension/MokoWaaS.php b/src/packages/plg_system_mokowaas/Extension/MokoWaaS.php index 7528f4f2..62648f4b 100644 --- a/src/packages/plg_system_mokowaas/Extension/MokoWaaS.php +++ b/src/packages/plg_system_mokowaas/Extension/MokoWaaS.php @@ -862,6 +862,60 @@ class MokoWaaS extends CMSPlugin implements BootableExtensionInterface ); } + // Demo Mode: Take Snapshot Now + if ((int) $params->get('demo_take_snapshot_now', 0) === 1) + { + $params->set('demo_take_snapshot_now', '0'); + $changed = true; + + try + { + $this->params = $params; + $service = $this->createDemoResetService(); + $baseline = $params->get('demo_active_baseline', 'default'); + $result = $service->createSnapshot($baseline); + + $app->enqueueMessage( + sprintf('Demo snapshot "%s" created (%d tables).', $baseline, $result['tables']), + 'message' + ); + } + catch (\Throwable $e) + { + $app->enqueueMessage( + 'Snapshot failed: ' . $e->getMessage(), + 'error' + ); + } + } + + // Demo Mode: Restore Baseline Now + if ((int) $params->get('demo_restore_now', 0) === 1) + { + $params->set('demo_restore_now', '0'); + $changed = true; + + try + { + $this->params = $params; + $service = $this->createDemoResetService(); + $baseline = $params->get('demo_active_baseline', 'default'); + $result = $service->restoreSnapshot($baseline); + + $app->enqueueMessage( + sprintf('Site restored to baseline "%s" (%d tables).', $baseline, $result['restored_tables']), + 'message' + ); + } + catch (\Throwable $e) + { + $app->enqueueMessage( + 'Restore failed: ' . $e->getMessage(), + 'error' + ); + } + } + if ($changed) { $db = Factory::getDbo(); @@ -965,6 +1019,12 @@ class MokoWaaS extends CMSPlugin implements BootableExtensionInterface $this->injectAliasRobots($doc); } + // Demo mode banner (frontend only) + if ($this->app->isClient('site') && (int) $this->params->get('demo_mode_enabled', 0)) + { + $this->injectDemoBanner($doc); + } + if (!$this->app->isClient('administrator')) { return; @@ -980,6 +1040,65 @@ class MokoWaaS extends CMSPlugin implements BootableExtensionInterface } } + /** + * Inject demo mode warning banner into the frontend site. + * + * Renders a fixed-position bar at the top of the page with a configurable + * message, color, optional countdown, and session-dismissable behavior. + * + * @param \Joomla\CMS\Document\HtmlDocument $doc Document object + * + * @return void + * + * @since 02.21.00 + */ + protected function injectDemoBanner($doc) + { + $message = htmlspecialchars($this->params->get('demo_banner_message', 'This is a demo site. All changes will be reset periodically.'), ENT_QUOTES, 'UTF-8'); + $bgColor = htmlspecialchars($this->params->get('demo_banner_color', '#d9534f'), ENT_QUOTES, 'UTF-8'); + $showCountdown = (int) $this->params->get('demo_banner_show_countdown', 0); + $intervalHours = (int) $this->params->get('demo_reset_interval_hours', 24); + $resetAt = time() + ($intervalHours * 3600); + + $countdownJs = ''; + + if ($showCountdown) + { + $countdownJs = " + var resetAt = {$resetAt} * 1000; + var cdSpan = document.getElementById('mokowaas-demo-countdown'); + if (cdSpan) { + setInterval(function() { + var now = Date.now(); + var diff = Math.max(0, Math.floor((resetAt - now) / 1000)); + var h = Math.floor(diff / 3600); + var m = Math.floor((diff % 3600) / 60); + var s = diff % 60; + cdSpan.textContent = ' — Resets in ' + h + 'h ' + m + 'm ' + s + 's'; + }, 1000); + } + "; + } + + $doc->addScriptDeclaration(" + document.addEventListener('DOMContentLoaded', function() { + if (sessionStorage.getItem('mokowaas_banner_dismissed') === '1') return; + + var bar = document.createElement('div'); + bar.id = 'mokowaas-demo-banner'; + bar.style.cssText = 'position:fixed;top:0;left:0;right:0;z-index:999999;background:{$bgColor};color:#fff;padding:10px 40px 10px 20px;font-family:-apple-system,BlinkMacSystemFont,sans-serif;font-size:14px;text-align:center;box-shadow:0 2px 8px rgba(0,0,0,.3);'; + bar.innerHTML = '{$message}' + + '" . ($showCountdown ? "" : "") . "' + + ''; + + document.body.insertBefore(bar, document.body.firstChild); + document.body.style.paddingTop = bar.offsetHeight + 'px'; + + {$countdownJs} + }); + "); + } + /** * Hide MokoWaaS plugin and package from the extensions list via JS. * @@ -1407,11 +1526,17 @@ class MokoWaaS extends CMSPlugin implements BootableExtensionInterface case 'info': $this->handleInfoAction(); break; + case 'reset': + $this->handleDemoResetAction(); + break; + case 'snapshot': + $this->handleSnapshotAction(); + break; default: $this->sendHealthResponse(400, [ 'error' => 'Unknown action', 'action' => $action, - 'available' => ['health', 'install', 'update', 'cache', 'backup', 'info'], + 'available' => ['health', 'install', 'update', 'cache', 'backup', 'info', 'reset', 'snapshot'], ]); break; } @@ -1421,6 +1546,117 @@ class MokoWaaS extends CMSPlugin implements BootableExtensionInterface // API Actions // ------------------------------------------------------------------ + /** + * Handle demo site reset via API. + * + * POST /?mokowaas=reset + * Body: {"baseline": "default"} (optional, defaults to active baseline) + * + * @return void + * @since 02.21.00 + */ + protected function handleDemoResetAction() + { + if ($this->app->input->getMethod() !== 'POST') + { + $this->sendHealthResponse(405, ['error' => 'POST required']); + + return; + } + + try + { + $body = json_decode(file_get_contents('php://input'), true); + $baseline = $body['baseline'] + ?? $this->params->get('demo_active_baseline', 'default'); + + $service = $this->createDemoResetService(); + $result = $service->restoreSnapshot($baseline); + + $this->sendHealthResponse(200, $result); + } + catch (\Throwable $e) + { + $this->sendHealthResponse(500, [ + 'error' => 'Reset failed', + 'message' => $e->getMessage(), + ]); + } + } + + /** + * Handle snapshot create/list via API. + * + * GET /?mokowaas=snapshot — list snapshots + * POST /?mokowaas=snapshot — create snapshot + * Body: {"name": "my-baseline"} (optional, defaults to active baseline) + * + * @return void + * @since 02.21.00 + */ + protected function handleSnapshotAction() + { + $service = $this->createDemoResetService(); + + if ($this->app->input->getMethod() === 'GET') + { + $this->sendHealthResponse(200, [ + 'status' => 'ok', + 'snapshots' => $service->listSnapshots(), + ]); + + return; + } + + if ($this->app->input->getMethod() !== 'POST') + { + $this->sendHealthResponse(405, ['error' => 'GET or POST required']); + + return; + } + + try + { + $body = json_decode(file_get_contents('php://input'), true); + $name = $body['name'] + ?? $this->params->get('demo_active_baseline', 'default'); + + $result = $service->createSnapshot($name); + + $this->sendHealthResponse(200, $result); + } + catch (\Throwable $e) + { + $this->sendHealthResponse(500, [ + 'error' => 'Snapshot failed', + 'message' => $e->getMessage(), + ]); + } + } + + /** + * Create a DemoResetService instance from current plugin params. + * + * @return \Moko\Plugin\System\MokoWaaS\Service\DemoResetService + * @since 02.21.00 + */ + protected function createDemoResetService() + { + require_once __DIR__ . '/../Service/DemoResetService.php'; + + $tablesRaw = $this->params->get('demo_snapshot_tables', ''); + $tables = array_filter( + array_map('trim', explode("\n", $tablesRaw)) + ); + + $includeMedia = (bool) $this->params->get('demo_snapshot_include_media', 1); + + return new \Moko\Plugin\System\MokoWaaS\Service\DemoResetService( + $tables, + $includeMedia + ); + } + /** * Trigger Joomla update finder check. * diff --git a/src/packages/plg_system_mokowaas/Service/DemoResetService.php b/src/packages/plg_system_mokowaas/Service/DemoResetService.php new file mode 100644 index 00000000..bb0bb596 --- /dev/null +++ b/src/packages/plg_system_mokowaas/Service/DemoResetService.php @@ -0,0 +1,714 @@ +tables = !empty($tables) ? $tables : self::DEFAULT_TABLES; + $this->includeMedia = $includeMedia; + $this->snapshotDir = $baseDir ?: JPATH_ROOT . '/mokowaas-snapshots'; + } + + /** + * List all available snapshots. + * + * @return array Array of manifest data keyed by snapshot name + * + * @since 02.21.00 + */ + public function listSnapshots(): array + { + $snapshots = []; + + if (!is_dir($this->snapshotDir)) + { + return $snapshots; + } + + $dirs = glob($this->snapshotDir . '/*/manifest.json'); + + foreach ($dirs as $manifestPath) + { + $data = json_decode(file_get_contents($manifestPath), true); + + if ($data && isset($data['name'])) + { + $snapshots[$data['name']] = $data; + } + } + + return $snapshots; + } + + /** + * Create a named snapshot of the current site state. + * + * @param string $name Snapshot name (alphanumeric, hyphens, underscores) + * + * @return array Result payload with status, tables count, size info + * + * @throws \InvalidArgumentException On invalid snapshot name + * @throws \RuntimeException On filesystem/database failures + * + * @since 02.21.00 + */ + public function createSnapshot(string $name): array + { + $this->validateSnapshotName($name); + $this->ensureSnapshotDir(); + + $path = $this->getSnapshotPath($name); + + // Remove existing snapshot with the same name + if (is_dir($path)) + { + $this->removeDirectory($path); + } + + if (!mkdir($path, 0755, true)) + { + throw new \RuntimeException('Failed to create snapshot directory: ' . $path); + } + + $db = Factory::getDbo(); + $prefix = $db->getPrefix(); + $tables = $db->getTableList(); + $dumped = 0; + + foreach ($this->tables as $tableName) + { + $realTable = str_replace('#__', $prefix, $tableName); + + if (!in_array($realTable, $tables)) + { + continue; + } + + $this->dumpTable($tableName, $realTable, $path, $db); + $dumped++; + } + + // Media snapshot + $hasMedia = false; + + if ($this->includeMedia) + { + $hasMedia = $this->snapshotMedia($path); + } + + // Write manifest + $manifest = [ + 'name' => $name, + 'created_at' => gmdate('Y-m-d\TH:i:s\Z'), + 'tables' => $dumped, + 'table_list' => $this->tables, + 'has_media' => $hasMedia, + 'joomla_version' => JVERSION, + ]; + + file_put_contents( + $path . '/manifest.json', + json_encode($manifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) + ); + + Log::add( + sprintf('Demo snapshot "%s" created (%d tables, media=%s)', $name, $dumped, $hasMedia ? 'yes' : 'no'), + Log::INFO, + 'mokowaas' + ); + + return [ + 'status' => 'ok', + 'message' => 'Snapshot created', + 'name' => $name, + 'tables' => $dumped, + 'has_media' => $hasMedia, + ]; + } + + /** + * Restore the site to a named snapshot. + * + * @param string $name Snapshot name to restore + * + * @return array Result payload + * + * @throws \InvalidArgumentException On invalid name + * @throws \RuntimeException On missing snapshot or restore failure + * + * @since 02.21.00 + */ + public function restoreSnapshot(string $name): array + { + $this->validateSnapshotName($name); + + $path = $this->getSnapshotPath($name); + $manifestFile = $path . '/manifest.json'; + + if (!file_exists($manifestFile)) + { + throw new \RuntimeException('Snapshot not found: ' . $name); + } + + $manifest = json_decode(file_get_contents($manifestFile), true); + + if (!$manifest) + { + throw new \RuntimeException('Invalid manifest for snapshot: ' . $name); + } + + // Clear Joomla cache before restore + try + { + $cache = Factory::getCache(''); + $cache->clean(''); + } + catch (\Throwable $e) + { + // Cache clear is best-effort + } + + $db = Factory::getDbo(); + $prefix = $db->getPrefix(); + $restored = 0; + + // Restore tables — assets first for ACL integrity + $sqlFiles = glob($path . '/*.sql'); + + // Sort: #__assets first + usort($sqlFiles, function ($a, $b) { + $aIsAssets = str_contains(basename($a), '__assets'); + $bIsAssets = str_contains(basename($b), '__assets'); + + if ($aIsAssets) return -1; + if ($bIsAssets) return 1; + + return strcmp($a, $b); + }); + + foreach ($sqlFiles as $sqlFile) + { + try + { + $this->restoreTable($sqlFile, $db, $prefix); + $restored++; + } + catch (\Throwable $e) + { + Log::add( + sprintf('Demo reset: failed to restore %s: %s', basename($sqlFile), $e->getMessage()), + Log::ERROR, + 'mokowaas' + ); + } + } + + // Restore media + $mediaRestored = false; + + if ($manifest['has_media'] ?? false) + { + $mediaRestored = $this->restoreMedia($path); + } + + Log::add( + sprintf('Demo site reset to baseline "%s" (%d tables, media=%s)', $name, $restored, $mediaRestored ? 'yes' : 'no'), + Log::WARNING, + 'mokowaas' + ); + + return [ + 'status' => 'ok', + 'message' => 'Site restored to baseline: ' . $name, + 'baseline' => $name, + 'restored_tables' => $restored, + 'media_restored' => $mediaRestored, + ]; + } + + /** + * Delete a named snapshot. + * + * @param string $name Snapshot name + * + * @return bool True on success + * + * @since 02.21.00 + */ + public function deleteSnapshot(string $name): bool + { + $this->validateSnapshotName($name); + + $path = $this->getSnapshotPath($name); + + if (!is_dir($path)) + { + return false; + } + + $this->removeDirectory($path); + + Log::add( + sprintf('Demo snapshot "%s" deleted', $name), + Log::INFO, + 'mokowaas' + ); + + return true; + } + + /** + * Dump a single table to a SQL file using paginated reads. + * + * @param string $logicalName Table name with #__ prefix + * @param string $realName Actual table name with prefix + * @param string $dir Snapshot directory + * @param \Joomla\Database\DatabaseInterface $db Database driver + * + * @return void + * + * @since 02.21.00 + */ + private function dumpTable(string $logicalName, string $realName, string $dir, $db): void + { + $safeFileName = str_replace('#__', 'jml__', $logicalName); + $fp = fopen($dir . '/' . $safeFileName . '.sql', 'w'); + + if ($fp === false) + { + throw new \RuntimeException('Cannot write dump file for: ' . $logicalName); + } + + // Get column names for consistent INSERT statements + $columns = $db->getTableColumns($realName, false); + $colNames = array_keys($columns); + $quotedCols = array_map([$db, 'quoteName'], $colNames); + $colList = implode(', ', $quotedCols); + + $offset = 0; + + while (true) + { + $query = $db->getQuery(true) + ->select('*') + ->from($db->quoteName($realName)) + ->setLimit(self::BATCH_SIZE, $offset); + + $db->setQuery($query); + $rows = $db->loadAssocList(); + + if (empty($rows)) + { + break; + } + + // Build multi-value INSERT + $values = []; + + foreach ($rows as $row) + { + $vals = []; + + foreach ($colNames as $col) + { + $val = $row[$col]; + + if ($val === null) + { + $vals[] = 'NULL'; + } + else + { + $vals[] = $db->quote($val); + } + } + + $values[] = '(' . implode(', ', $vals) . ')'; + } + + fwrite($fp, 'INSERT INTO ' . $db->quoteName($realName) + . ' (' . $colList . ') VALUES ' . "\n" + . implode(",\n", $values) . ";\n\n"); + + $offset += self::BATCH_SIZE; + + if (count($rows) < self::BATCH_SIZE) + { + break; + } + } + + fclose($fp); + } + + /** + * Restore a table from a SQL dump file. + * + * @param string $sqlFile Path to the .sql file + * @param \Joomla\Database\DatabaseInterface $db Database driver + * @param string $prefix Table prefix + * + * @return void + * + * @since 02.21.00 + */ + private function restoreTable(string $sqlFile, $db, string $prefix): void + { + // Derive table name from filename: jml__content.sql -> {prefix}content + $baseName = basename($sqlFile, '.sql'); + $realTable = str_replace('jml__', $prefix, $baseName); + + // Truncate the table first + $db->setQuery('TRUNCATE TABLE ' . $db->quoteName($realTable)); + $db->execute(); + + $sql = file_get_contents($sqlFile); + + if (empty(trim($sql))) + { + return; + } + + // Split by semicolons and execute each statement + $statements = array_filter( + array_map('trim', explode(";\n", $sql)), + function ($s) { return !empty($s) && $s !== ';'; } + ); + + foreach ($statements as $statement) + { + $statement = rtrim($statement, ';'); + + if (empty($statement)) + { + continue; + } + + $db->setQuery($statement); + $db->execute(); + } + } + + /** + * Create a ZIP archive of the /images/ directory. + * + * @param string $snapshotDir Snapshot directory path + * + * @return bool True if media was archived + * + * @since 02.21.00 + */ + private function snapshotMedia(string $snapshotDir): bool + { + $imagesDir = JPATH_ROOT . '/images'; + + if (!is_dir($imagesDir)) + { + return false; + } + + $zipPath = $snapshotDir . '/media.zip'; + $zip = new \ZipArchive(); + + if ($zip->open($zipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) + { + return false; + } + + $iterator = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($imagesDir, \RecursiveDirectoryIterator::SKIP_DOTS), + \RecursiveIteratorIterator::SELF_FIRST + ); + + foreach ($iterator as $item) + { + $relativePath = substr($item->getPathname(), strlen($imagesDir) + 1); + $relativePath = str_replace('\\', '/', $relativePath); + + if ($item->isDir()) + { + $zip->addEmptyDir($relativePath); + } + else + { + $zip->addFile($item->getPathname(), $relativePath); + } + } + + $zip->close(); + + return true; + } + + /** + * Restore media files from a snapshot ZIP. + * + * @param string $snapshotDir Snapshot directory path + * + * @return bool True if media was restored + * + * @since 02.21.00 + */ + private function restoreMedia(string $snapshotDir): bool + { + $zipPath = $snapshotDir . '/media.zip'; + $imagesDir = JPATH_ROOT . '/images'; + + if (!file_exists($zipPath)) + { + return false; + } + + // Clear existing images directory contents (keep the directory itself) + $this->clearDirectory($imagesDir); + + $zip = new \ZipArchive(); + + if ($zip->open($zipPath) !== true) + { + return false; + } + + $zip->extractTo($imagesDir); + $zip->close(); + + return true; + } + + /** + * Ensure the snapshot root directory exists with .htaccess protection. + * + * @return void + * + * @since 02.21.00 + */ + private function ensureSnapshotDir(): void + { + if (!is_dir($this->snapshotDir)) + { + if (!mkdir($this->snapshotDir, 0755, true)) + { + throw new \RuntimeException('Cannot create snapshot directory: ' . $this->snapshotDir); + } + } + + $htaccess = $this->snapshotDir . '/.htaccess'; + + if (!file_exists($htaccess)) + { + file_put_contents($htaccess, "Deny from all\n"); + } + } + + /** + * Get the full path for a named snapshot. + * + * @param string $name Snapshot name + * + * @return string Full directory path + * + * @since 02.21.00 + */ + private function getSnapshotPath(string $name): string + { + return $this->snapshotDir . '/' . $name; + } + + /** + * Validate a snapshot name to prevent path traversal. + * + * @param string $name Snapshot name to validate + * + * @return void + * + * @throws \InvalidArgumentException On invalid name + * + * @since 02.21.00 + */ + private function validateSnapshotName(string $name): void + { + if ($name === '' || strlen($name) > self::MAX_NAME_LENGTH) + { + throw new \InvalidArgumentException( + 'Snapshot name must be 1-' . self::MAX_NAME_LENGTH . ' characters' + ); + } + + if (!preg_match('/^[a-zA-Z0-9_-]+$/', $name)) + { + throw new \InvalidArgumentException( + 'Snapshot name must contain only letters, numbers, hyphens, and underscores' + ); + } + } + + /** + * Recursively remove a directory and all contents. + * + * @param string $dir Directory to remove + * + * @return void + * + * @since 02.21.00 + */ + private function removeDirectory(string $dir): void + { + $items = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS), + \RecursiveIteratorIterator::CHILD_FIRST + ); + + foreach ($items as $item) + { + if ($item->isDir()) + { + @rmdir($item->getPathname()); + } + else + { + @unlink($item->getPathname()); + } + } + + @rmdir($dir); + } + + /** + * Clear all contents of a directory without removing the directory itself. + * + * @param string $dir Directory to clear + * + * @return void + * + * @since 02.21.00 + */ + private function clearDirectory(string $dir): void + { + if (!is_dir($dir)) + { + return; + } + + $items = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS), + \RecursiveIteratorIterator::CHILD_FIRST + ); + + foreach ($items as $item) + { + if ($item->isDir()) + { + @rmdir($item->getPathname()); + } + else + { + @unlink($item->getPathname()); + } + } + } +} diff --git a/src/packages/plg_system_mokowaas/Service/index.html b/src/packages/plg_system_mokowaas/Service/index.html new file mode 100644 index 00000000..2efb97f3 --- /dev/null +++ b/src/packages/plg_system_mokowaas/Service/index.html @@ -0,0 +1 @@ + diff --git a/src/packages/plg_system_mokowaas/language/en-GB/plg_system_mokowaas.ini b/src/packages/plg_system_mokowaas/language/en-GB/plg_system_mokowaas.ini index 538d0cf8..81098e29 100644 --- a/src/packages/plg_system_mokowaas/language/en-GB/plg_system_mokowaas.ini +++ b/src/packages/plg_system_mokowaas/language/en-GB/plg_system_mokowaas.ini @@ -137,6 +137,31 @@ PLG_SYSTEM_MOKOWAAS_UPLOAD_TYPES_DESC="Comma-separated list of allowed file exte PLG_SYSTEM_MOKOWAAS_UPLOAD_SIZE_LABEL="Max Upload Size (MB)" PLG_SYSTEM_MOKOWAAS_UPLOAD_SIZE_DESC="Maximum file upload size in megabytes." +; ===== Demo Mode fieldset ===== +PLG_SYSTEM_MOKOWAAS_FIELDSET_DEMO_LABEL="Demo Mode" +PLG_SYSTEM_MOKOWAAS_FIELDSET_DEMO_DESC="Configure demo site behavior with baseline snapshots and automatic periodic reset. When enabled, a warning banner is shown on the frontend." + +PLG_SYSTEM_MOKOWAAS_DEMO_ENABLED_LABEL="Enable Demo Mode" +PLG_SYSTEM_MOKOWAAS_DEMO_ENABLED_DESC="When enabled, shows a warning banner on the frontend and enables snapshot/restore functionality." +PLG_SYSTEM_MOKOWAAS_DEMO_BANNER_MSG_LABEL="Banner Message" +PLG_SYSTEM_MOKOWAAS_DEMO_BANNER_MSG_DESC="Message displayed in the demo warning banner on the frontend." +PLG_SYSTEM_MOKOWAAS_DEMO_BANNER_COLOR_LABEL="Banner Color" +PLG_SYSTEM_MOKOWAAS_DEMO_BANNER_COLOR_DESC="Background color for the demo warning banner." +PLG_SYSTEM_MOKOWAAS_DEMO_COUNTDOWN_LABEL="Show Reset Countdown" +PLG_SYSTEM_MOKOWAAS_DEMO_COUNTDOWN_DESC="Display a countdown timer in the banner showing time until the next scheduled reset." +PLG_SYSTEM_MOKOWAAS_DEMO_INTERVAL_LABEL="Reset Interval (Hours)" +PLG_SYSTEM_MOKOWAAS_DEMO_INTERVAL_DESC="Hours between scheduled demo resets. Used for countdown display and scheduled task interval." +PLG_SYSTEM_MOKOWAAS_DEMO_TABLES_LABEL="Snapshot Tables" +PLG_SYSTEM_MOKOWAAS_DEMO_TABLES_DESC="Database tables to include in snapshots. One per line, using #__ prefix. These tables will be truncated and restored during a reset." +PLG_SYSTEM_MOKOWAAS_DEMO_MEDIA_LABEL="Include Media Files" +PLG_SYSTEM_MOKOWAAS_DEMO_MEDIA_DESC="Include the /images/ directory in snapshots. Disabling this speeds up snapshot/restore for sites with large media libraries." +PLG_SYSTEM_MOKOWAAS_DEMO_ACTIVE_BASELINE_LABEL="Active Baseline Name" +PLG_SYSTEM_MOKOWAAS_DEMO_ACTIVE_BASELINE_DESC="Name of the baseline snapshot used by admin toggles and scheduled tasks. Alphanumeric, hyphens, and underscores only." +PLG_SYSTEM_MOKOWAAS_DEMO_TAKE_SNAPSHOT_LABEL="Take Snapshot Now" +PLG_SYSTEM_MOKOWAAS_DEMO_TAKE_SNAPSHOT_DESC="Save the current site state as a baseline snapshot. Uses the Active Baseline Name above. Resets to No after execution." +PLG_SYSTEM_MOKOWAAS_DEMO_RESTORE_NOW_LABEL="Restore Baseline Now" +PLG_SYSTEM_MOKOWAAS_DEMO_RESTORE_NOW_DESC="Immediately restore the site to the active baseline snapshot. WARNING: This will overwrite current content. Resets to No after execution." + ; ===== Site Aliases fieldset ===== PLG_SYSTEM_MOKOWAAS_FIELDSET_ALIASES_LABEL="Site Aliases" PLG_SYSTEM_MOKOWAAS_FIELDSET_ALIASES_DESC="Configure additional domains that mirror this site. Each alias can have its own offline status, robots directive, and backend redirect behavior." diff --git a/src/packages/plg_system_mokowaas/language/en-US/plg_system_mokowaas.ini b/src/packages/plg_system_mokowaas/language/en-US/plg_system_mokowaas.ini index 4adb06cf..b26a7b6a 100644 --- a/src/packages/plg_system_mokowaas/language/en-US/plg_system_mokowaas.ini +++ b/src/packages/plg_system_mokowaas/language/en-US/plg_system_mokowaas.ini @@ -137,6 +137,31 @@ PLG_SYSTEM_MOKOWAAS_UPLOAD_TYPES_DESC="Comma-separated list of allowed file exte PLG_SYSTEM_MOKOWAAS_UPLOAD_SIZE_LABEL="Max Upload Size (MB)" PLG_SYSTEM_MOKOWAAS_UPLOAD_SIZE_DESC="Maximum file upload size in megabytes." +; ===== Demo Mode fieldset ===== +PLG_SYSTEM_MOKOWAAS_FIELDSET_DEMO_LABEL="Demo Mode" +PLG_SYSTEM_MOKOWAAS_FIELDSET_DEMO_DESC="Configure demo site behavior with baseline snapshots and automatic periodic reset. When enabled, a warning banner is shown on the frontend." + +PLG_SYSTEM_MOKOWAAS_DEMO_ENABLED_LABEL="Enable Demo Mode" +PLG_SYSTEM_MOKOWAAS_DEMO_ENABLED_DESC="When enabled, shows a warning banner on the frontend and enables snapshot/restore functionality." +PLG_SYSTEM_MOKOWAAS_DEMO_BANNER_MSG_LABEL="Banner Message" +PLG_SYSTEM_MOKOWAAS_DEMO_BANNER_MSG_DESC="Message displayed in the demo warning banner on the frontend." +PLG_SYSTEM_MOKOWAAS_DEMO_BANNER_COLOR_LABEL="Banner Color" +PLG_SYSTEM_MOKOWAAS_DEMO_BANNER_COLOR_DESC="Background color for the demo warning banner." +PLG_SYSTEM_MOKOWAAS_DEMO_COUNTDOWN_LABEL="Show Reset Countdown" +PLG_SYSTEM_MOKOWAAS_DEMO_COUNTDOWN_DESC="Display a countdown timer in the banner showing time until the next scheduled reset." +PLG_SYSTEM_MOKOWAAS_DEMO_INTERVAL_LABEL="Reset Interval (Hours)" +PLG_SYSTEM_MOKOWAAS_DEMO_INTERVAL_DESC="Hours between scheduled demo resets. Used for countdown display and scheduled task interval." +PLG_SYSTEM_MOKOWAAS_DEMO_TABLES_LABEL="Snapshot Tables" +PLG_SYSTEM_MOKOWAAS_DEMO_TABLES_DESC="Database tables to include in snapshots. One per line, using #__ prefix. These tables will be truncated and restored during a reset." +PLG_SYSTEM_MOKOWAAS_DEMO_MEDIA_LABEL="Include Media Files" +PLG_SYSTEM_MOKOWAAS_DEMO_MEDIA_DESC="Include the /images/ directory in snapshots. Disabling this speeds up snapshot/restore for sites with large media libraries." +PLG_SYSTEM_MOKOWAAS_DEMO_ACTIVE_BASELINE_LABEL="Active Baseline Name" +PLG_SYSTEM_MOKOWAAS_DEMO_ACTIVE_BASELINE_DESC="Name of the baseline snapshot used by admin toggles and scheduled tasks. Alphanumeric, hyphens, and underscores only." +PLG_SYSTEM_MOKOWAAS_DEMO_TAKE_SNAPSHOT_LABEL="Take Snapshot Now" +PLG_SYSTEM_MOKOWAAS_DEMO_TAKE_SNAPSHOT_DESC="Save the current site state as a baseline snapshot. Uses the Active Baseline Name above. Resets to No after execution." +PLG_SYSTEM_MOKOWAAS_DEMO_RESTORE_NOW_LABEL="Restore Baseline Now" +PLG_SYSTEM_MOKOWAAS_DEMO_RESTORE_NOW_DESC="Immediately restore the site to the active baseline snapshot. WARNING: This will overwrite current content. Resets to No after execution." + ; ===== Site Aliases fieldset ===== PLG_SYSTEM_MOKOWAAS_FIELDSET_ALIASES_LABEL="Site Aliases" PLG_SYSTEM_MOKOWAAS_FIELDSET_ALIASES_DESC="Configure additional domains that mirror this site. Each alias can have its own offline status, robots directive, and backend redirect behavior." diff --git a/src/packages/plg_system_mokowaas/mokowaas.xml b/src/packages/plg_system_mokowaas/mokowaas.xml index f55d8875..458ca50f 100644 --- a/src/packages/plg_system_mokowaas/mokowaas.xml +++ b/src/packages/plg_system_mokowaas/mokowaas.xml @@ -39,6 +39,7 @@ script.php Extension Field + Service forms payload services @@ -264,7 +265,68 @@ description="PLG_SYSTEM_MOKOWAAS_HIDDEN_MENUS_DESC" rows="5" filter="raw" /> -
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
diff --git a/src/packages/plg_task_mokowaas/forms/reset_params.xml b/src/packages/plg_task_mokowaas/forms/reset_params.xml new file mode 100644 index 00000000..f2844933 --- /dev/null +++ b/src/packages/plg_task_mokowaas/forms/reset_params.xml @@ -0,0 +1,9 @@ + +
+
+ +
+
diff --git a/src/packages/plg_task_mokowaas/language/en-GB/plg_task_mokowaasdemo.ini b/src/packages/plg_task_mokowaas/language/en-GB/plg_task_mokowaasdemo.ini new file mode 100644 index 00000000..112d51fb --- /dev/null +++ b/src/packages/plg_task_mokowaas/language/en-GB/plg_task_mokowaasdemo.ini @@ -0,0 +1,10 @@ +; MokoWaaS Demo Reset Task Plugin +; Copyright (C) 2026 Moko Consulting +; SPDX-License-Identifier: GPL-3.0-or-later + +PLG_TASK_MOKOWAASDEMO="Task - MokoWaaS Demo Reset" +PLG_TASK_MOKOWAASDEMO_DESC="Scheduled task to periodically reset a demo site to a saved baseline snapshot." +PLG_TASK_MOKOWAASDEMO_RESET_TITLE="MokoWaaS Demo Reset" +PLG_TASK_MOKOWAASDEMO_RESET_DESC="Restore the site to a named baseline snapshot. Configure the baseline name and schedule interval." +PLG_TASK_MOKOWAASDEMO_BASELINE_LABEL="Baseline Name" +PLG_TASK_MOKOWAASDEMO_BASELINE_DESC="Name of the snapshot to restore. Must match a snapshot created via the MokoWaaS Demo Mode settings." diff --git a/src/packages/plg_task_mokowaas/language/en-GB/plg_task_mokowaasdemo.sys.ini b/src/packages/plg_task_mokowaas/language/en-GB/plg_task_mokowaasdemo.sys.ini new file mode 100644 index 00000000..12c08f89 --- /dev/null +++ b/src/packages/plg_task_mokowaas/language/en-GB/plg_task_mokowaasdemo.sys.ini @@ -0,0 +1,6 @@ +; MokoWaaS Demo Reset Task Plugin (sys) +; Copyright (C) 2026 Moko Consulting +; SPDX-License-Identifier: GPL-3.0-or-later + +PLG_TASK_MOKOWAASDEMO="Task - MokoWaaS Demo Reset" +PLG_TASK_MOKOWAASDEMO_DESC="Scheduled task to periodically reset a demo site to a saved baseline snapshot." diff --git a/src/packages/plg_task_mokowaas/mokowaasdemo.xml b/src/packages/plg_task_mokowaas/mokowaasdemo.xml new file mode 100644 index 00000000..cb008be2 --- /dev/null +++ b/src/packages/plg_task_mokowaas/mokowaasdemo.xml @@ -0,0 +1,30 @@ + + + + Task - MokoWaaS Demo Reset + mokowaasdemo + Moko Consulting + 2026-05-30 + Copyright (C) 2026 Moko Consulting. All rights reserved. + GNU General Public License version 3 or later; see LICENSE + hello@mokoconsulting.tech + https://mokoconsulting.tech + 02.21.00 + PLG_TASK_MOKOWAASDEMO_DESC + Moko\Plugin\Task\MokoWaaSDemo + + + src + services + forms + language + + + + en-GB/plg_task_mokowaasdemo.ini + en-GB/plg_task_mokowaasdemo.sys.ini + + diff --git a/src/packages/plg_task_mokowaas/services/provider.php b/src/packages/plg_task_mokowaas/services/provider.php new file mode 100644 index 00000000..b8af1d3f --- /dev/null +++ b/src/packages/plg_task_mokowaas/services/provider.php @@ -0,0 +1,37 @@ +set( + PluginInterface::class, + function (Container $container) { + $dispatcher = $container->get(DispatcherInterface::class); + $plugin = new DemoReset( + $dispatcher, + (array) PluginHelper::getPlugin('task', 'mokowaasdemo') + ); + $plugin->setApplication(Factory::getApplication()); + + return $plugin; + } + ); + } +}; diff --git a/src/packages/plg_task_mokowaas/src/Extension/DemoReset.php b/src/packages/plg_task_mokowaas/src/Extension/DemoReset.php new file mode 100644 index 00000000..24d2f2fc --- /dev/null +++ b/src/packages/plg_task_mokowaas/src/Extension/DemoReset.php @@ -0,0 +1,125 @@ + [ + 'langConstPrefix' => 'PLG_TASK_MOKOWAASDEMO_RESET', + 'method' => 'resetDemoSite', + 'form' => 'reset_params', + ], + ]; + + /** + * @return array + * + * @since 02.21.00 + */ + public static function getSubscribedEvents(): array + { + return [ + 'onTaskOptionsList' => 'advertiseRoutines', + 'onExecuteTask' => 'standardRoutineHandler', + 'onContentPrepareForm' => 'enhanceTaskItemForm', + ]; + } + + /** + * Reset the demo site to a baseline snapshot. + * + * @param ExecuteTaskEvent $event The task event + * + * @return int Status::OK or Status::KNOCKOUT + * + * @since 02.21.00 + */ + private function resetDemoSite(ExecuteTaskEvent $event): int + { + $params = $event->getArgument('params'); + $baseline = $params->baseline ?? 'default'; + + // Load system plugin params for table list and media setting + $sysPlugin = PluginHelper::getPlugin('system', 'mokowaas'); + + if (!$sysPlugin) + { + $this->logTask('MokoWaaS system plugin not enabled — cannot reset'); + + return Status::KNOCKOUT; + } + + $sysParams = new Registry($sysPlugin->params); + + // Load the service + $serviceFile = JPATH_PLUGINS . '/system/mokowaas/Service/DemoResetService.php'; + + if (!file_exists($serviceFile)) + { + $this->logTask('DemoResetService.php not found'); + + return Status::KNOCKOUT; + } + + require_once $serviceFile; + + $tablesRaw = $sysParams->get('demo_snapshot_tables', ''); + $tables = array_filter(array_map('trim', explode("\n", $tablesRaw))); + $media = (bool) $sysParams->get('demo_snapshot_include_media', 1); + + $service = new \Moko\Plugin\System\MokoWaaS\Service\DemoResetService($tables, $media); + + try + { + $result = $service->restoreSnapshot($baseline); + $this->logTask(sprintf( + 'Demo site reset to "%s" — %d tables restored, media=%s', + $baseline, + $result['restored_tables'], + $result['media_restored'] ? 'yes' : 'no' + )); + + return Status::OK; + } + catch (\Throwable $e) + { + $this->logTask('Demo reset failed: ' . $e->getMessage()); + + return Status::KNOCKOUT; + } + } +} diff --git a/src/packages/plg_webservices_mokowaas/src/Extension/MokoWaaSApi.php b/src/packages/plg_webservices_mokowaas/src/Extension/MokoWaaSApi.php index 79558888..74523396 100644 --- a/src/packages/plg_webservices_mokowaas/src/Extension/MokoWaaSApi.php +++ b/src/packages/plg_webservices_mokowaas/src/Extension/MokoWaaSApi.php @@ -70,5 +70,17 @@ final class MokoWaaSApi extends CMSPlugin implements SubscriberInterface 'install', ['component' => 'com_mokowaas'] ); + + $router->createCRUDRoutes( + 'v1/mokowaas/reset', + 'reset', + ['component' => 'com_mokowaas'] + ); + + $router->createCRUDRoutes( + 'v1/mokowaas/snapshot', + 'snapshot', + ['component' => 'com_mokowaas'] + ); } } diff --git a/src/pkg_mokowaas.xml b/src/pkg_mokowaas.xml index 1943c439..d5de3461 100644 --- a/src/pkg_mokowaas.xml +++ b/src/pkg_mokowaas.xml @@ -17,6 +17,7 @@ com_mokowaas.zip plg_webservices_mokowaas.zip plg_webservices_perfectpublisher.zip + plg_task_mokowaasdemo.zip diff --git a/src/script.php b/src/script.php index cc58abf5..8bf5daaa 100644 --- a/src/script.php +++ b/src/script.php @@ -36,6 +36,7 @@ class Pkg_MokowaasInstallerScript { $this->enablePlugin('system', 'mokowaas'); $this->enablePlugin('webservices', 'mokowaas'); + $this->enablePlugin('task', 'mokowaasdemo'); // Mark MokoWaaS extensions as protected (prevents disable/uninstall at framework level) $this->protectExtensions(); diff --git a/wiki/api-endpoints.md b/wiki/api-endpoints.md index f1fd79ea..aeab2a15 100644 --- a/wiki/api-endpoints.md +++ b/wiki/api-endpoints.md @@ -164,7 +164,7 @@ Requesting an unknown action returns HTTP 400 with the list of available actions { "error": "Unknown action", "action": "invalid", - "available": ["health", "install", "update", "cache", "backup", "info"] + "available": ["health", "install", "update", "cache", "backup", "info", "reset", "snapshot"] } ``` @@ -178,6 +178,8 @@ In addition to the query-string endpoints above, MokoWaaS registers standard Joo | `POST /api/v1/mokowaas/cache` | CacheController | Clear all caches | | `POST /api/v1/mokowaas/update` | UpdateController | Trigger update check | | `POST /api/v1/mokowaas/install` | InstallController | Install extension from ZIP URL | +| `POST /api/v1/mokowaas/reset` | ResetController | Restore site to baseline snapshot | +| `GET/POST /api/v1/mokowaas/snapshot` | SnapshotController | List or create snapshots | These routes use Joomla's standard API authentication (API token in `X-Joomla-Token` header) and are useful for integrations that already use the Joomla API framework. @@ -221,3 +223,62 @@ Downloads the ZIP from the given URL and installs it via Joomla's Installer. Req | 403 | API token lacks `core.manage` on `com_installer` | | 405 | Request method is not POST | | 500 | Download or installation failed | + +### Reset Endpoint (REST API) + +``` +POST /api/index.php/v1/mokowaas/reset +X-Joomla-Token: +Content-Type: application/json + +{"baseline": "default"} +``` + +Restores the site to a named baseline snapshot. The `baseline` field is optional and defaults to the active baseline configured in the plugin. + +Requires `core.manage` permission on `com_plugins`. + +**Success Response** (HTTP 200): + +```json +{ + "status": "ok", + "message": "Site restored to baseline: default", + "baseline": "default", + "restored_tables": 15, + "media_restored": true +} +``` + +### Snapshot Endpoint (REST API) + +**List snapshots:** + +``` +GET /api/index.php/v1/mokowaas/snapshot +X-Joomla-Token: +``` + +**Create snapshot:** + +``` +POST /api/index.php/v1/mokowaas/snapshot +X-Joomla-Token: +Content-Type: application/json + +{"name": "my-baseline"} +``` + +The `name` field is optional and defaults to the active baseline name. + +**Success Response** (HTTP 200): + +```json +{ + "status": "ok", + "message": "Snapshot created", + "name": "my-baseline", + "tables": 15, + "has_media": true +} +``` -- 2.52.0 From 06f149a07302408048d51eaa9bd36bf10c739f73 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 30 May 2026 17:51:10 +0000 Subject: [PATCH 11/12] chore(version): auto-bump 02.21.01-dev [skip ci] --- .mokogitea/manifest.xml | 2 +- .mokogitea/workflows/issue-branch.yml | 2 +- CHANGELOG.md | 2 +- CODE_OF_CONDUCT.md | 2 +- CONTRIBUTING.md | 4 ++-- GOVERNANCE.md | 2 +- LICENSE.md | 2 +- README.md | 2 +- SECURITY.md | 2 +- docs/guides/build-guide.md | 4 ++-- docs/guides/configuration-guide.md | 4 ++-- docs/guides/installation-guide.md | 4 ++-- docs/guides/operations-guide.md | 4 ++-- docs/guides/rollback-and-recovery-guide.md | 4 ++-- docs/guides/testing-guide.md | 4 ++-- docs/guides/troubleshooting-guide.md | 4 ++-- docs/guides/upgrade-and-versioning-guide.md | 4 ++-- docs/index.md | 4 ++-- docs/plugin-basic.md | 4 ++-- docs/update-server.md | 2 +- src/packages/com_mokowaas/mokowaas.xml | 2 +- src/packages/plg_system_mokowaas/Extension/MokoWaaS.php | 2 +- src/packages/plg_system_mokowaas/Field/AllowedIpsField.php | 2 +- src/packages/plg_system_mokowaas/Field/CurrentIpField.php | 2 +- src/packages/plg_system_mokowaas/Service/DemoResetService.php | 2 +- src/packages/plg_system_mokowaas/mokowaas.xml | 2 +- src/packages/plg_system_mokowaas/script.php | 2 +- src/packages/plg_system_mokowaas/services/provider.php | 2 +- src/packages/plg_task_mokowaas/mokowaasdemo.xml | 2 +- src/packages/plg_webservices_mokowaas/mokowaas.xml | 2 +- .../plg_webservices_perfectpublisher/perfectpublisher.xml | 2 +- .../plg_webservices_perfectpublisher/services/provider.php | 2 +- .../src/Extension/PerfectPublisherApi.php | 2 +- src/pkg_mokowaas.xml | 2 +- updates.xml | 2 +- 35 files changed, 46 insertions(+), 46 deletions(-) diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index 3e2b93a2..144acd5b 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -8,7 +8,7 @@ MokoWaaS MokoConsulting White-label identity, security hardening, and tenant restriction layer for WaaS-managed Joomla environments - 02.20.02 + 02.21.01 GNU General Public License v3 diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index d939708e..f18bc373 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.20.02 +# VERSION: 02.21.01 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/CHANGELOG.md b/CHANGELOG.md index 104e3b72..95e6bb4b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,7 +14,7 @@ INGROUP: MokoWaaS.Documentation REPO: https://github.com/mokoconsulting-tech/mokowaas PATH: ./CHANGELOG.md - VERSION: 02.20.02 + VERSION: 02.21.01 BRIEF: Version history using `Keep a Changelog` --> diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 29473c98..72a81fd2 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -14,7 +14,7 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Documentation REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.20.02 + VERSION: 02.21.01 PATH: ./CODE_OF_CONDUCT.md BRIEF: Reference + packaging repo for Moko Consulting Developer GPT Other Default --> diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2c2f699f..9b16c315 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,12 +10,12 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Contributing REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.20.02 + VERSION: 02.21.01 PATH: /CONTRIBUTING.md BRIEF: Contribution guidelines for the MokoWaaS plugin --> -# Contributing to MokoWaaS (VERSION: 02.20.02) +# Contributing to MokoWaaS (VERSION: 02.21.01) ## Overview Contributions to the MokoWaaS plugin follow standardized development, governance, and quality control expectations defined by Moko Consulting. This document outlines contribution requirements, acceptable change types, branch management, testing expectations, and release readiness standards. diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 9d1dee6a..a0355f0f 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -19,7 +19,7 @@ DEFGROUP: mokoconsulting-tech.MokoWaaSBrand INGROUP: MokoStandards.Governance REPO: https://github.com/mokoconsulting-tech/MokoWaaSBrand - VERSION: 02.20.02 + VERSION: 02.21.01 PATH: /GOVERNANCE.md BRIEF: Project governance rules, roles, and decision process for MokoWaaSBrand --> diff --git a/LICENSE.md b/LICENSE.md index 01c832c6..1a2e15af 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -15,7 +15,7 @@ INGROUP: MokoWaaS.Documentation REPO: https://github.com/mokoconsulting-tech/mokowaas PATH: ./LICENSE.md - VERSION: 02.20.02 + VERSION: 02.21.01 BRIEF: Project license (GPL-3.0-or-later) --> GNU GENERAL PUBLIC LICENSE diff --git a/README.md b/README.md index ef57e0eb..70b81191 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS - VERSION: 02.20.02 + VERSION: 02.21.01 PATH: /README.md BRIEF: MokoWaaS platform plugin for Joomla --> diff --git a/SECURITY.md b/SECURITY.md index ecec6962..43c4652e 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -23,7 +23,7 @@ DEFGROUP: [PROJECT_NAME] INGROUP: [PROJECT_NAME].Documentation REPO: [REPOSITORY_URL] PATH: /SECURITY.md -VERSION: 02.20.02 +VERSION: 02.21.01 BRIEF: Security vulnerability reporting and handling policy --> diff --git a/docs/guides/build-guide.md b/docs/guides/build-guide.md index 5e66d529..db27c3d7 100644 --- a/docs/guides/build-guide.md +++ b/docs/guides/build-guide.md @@ -11,13 +11,13 @@ INGROUP: MokoWaaS.Build REPO: https://github.com/mokoconsulting-tech/mokowaas FILE: build-guide.md - VERSION: 02.20.02 + VERSION: 02.21.01 PATH: /docs/guides/ BRIEF: Build and packaging guide for the MokoWaaS system plugin NOTE: Defines environment setup, repository layout, packaging rules, and release preparation --> -# MokoWaaS Build Guide (VERSION: 02.20.02) +# MokoWaaS Build Guide (VERSION: 02.21.01) ## 1. Purpose diff --git a/docs/guides/configuration-guide.md b/docs/guides/configuration-guide.md index 5d7d649d..6f58a5ed 100644 --- a/docs/guides/configuration-guide.md +++ b/docs/guides/configuration-guide.md @@ -10,13 +10,13 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Guides REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.20.02 + VERSION: 02.21.01 PATH: /docs/guides/configuration-guide.md BRIEF: Configuration guide for the MokoWaaS system plugin NOTE: Defines plugin parameters, expected behaviors, and recommended defaults --> -# MokoWaaS Configuration Guide (VERSION: 02.20.02) +# MokoWaaS Configuration Guide (VERSION: 02.21.01) ## 1. Objective diff --git a/docs/guides/installation-guide.md b/docs/guides/installation-guide.md index eca34f4a..710cf2fd 100644 --- a/docs/guides/installation-guide.md +++ b/docs/guides/installation-guide.md @@ -10,13 +10,13 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Guides REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.20.02 + VERSION: 02.21.01 PATH: /docs/guides/installation-guide.md BRIEF: Installation guide for the MokoWaaS system plugin NOTE: First document in the guide set --> -# MokoWaaS Installation Guide (VERSION: 02.20.02) +# MokoWaaS Installation Guide (VERSION: 02.21.01) ## Introduction diff --git a/docs/guides/operations-guide.md b/docs/guides/operations-guide.md index 45c0a8b6..3e14978e 100644 --- a/docs/guides/operations-guide.md +++ b/docs/guides/operations-guide.md @@ -10,13 +10,13 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Guides REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.20.02 + VERSION: 02.21.01 PATH: /docs/guides/operations-guide.md BRIEF: Operational guide for administering and managing the MokoWaaS system plugin NOTE: Defines lifecycle, responsibilities, and operational behaviors --> -# MokoWaaS Operations Guide (VERSION: 02.20.02) +# MokoWaaS Operations Guide (VERSION: 02.21.01) ## Introduction diff --git a/docs/guides/rollback-and-recovery-guide.md b/docs/guides/rollback-and-recovery-guide.md index 9e73a42c..8b01d7ae 100644 --- a/docs/guides/rollback-and-recovery-guide.md +++ b/docs/guides/rollback-and-recovery-guide.md @@ -10,13 +10,13 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Guides REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.20.02 + VERSION: 02.21.01 PATH: /docs/guides/rollback-and-recovery-guide.md BRIEF: Rollback and recovery guide for restoring stable operation after plugin related incidents NOTE: Completes the core guide set for WaaS plugin governance --> -# MokoWaaS Rollback and Recovery Guide (VERSION: 02.20.02) +# MokoWaaS Rollback and Recovery Guide (VERSION: 02.21.01) ## Introduction diff --git a/docs/guides/testing-guide.md b/docs/guides/testing-guide.md index b64104ac..49c88c70 100644 --- a/docs/guides/testing-guide.md +++ b/docs/guides/testing-guide.md @@ -7,13 +7,13 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Guides REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.20.02 + VERSION: 02.21.01 PATH: /docs/guides/testing-guide.md BRIEF: Testing guide for MokoWaaS v02.01.08 NOTE: Covers manual test procedures for language overrides, install/uninstall, and configuration --> -# MokoWaaS Testing Guide (VERSION: 02.20.02) +# MokoWaaS Testing Guide (VERSION: 02.21.01) ## 1. Prerequisites diff --git a/docs/guides/troubleshooting-guide.md b/docs/guides/troubleshooting-guide.md index 2e9a8ad4..54c1554f 100644 --- a/docs/guides/troubleshooting-guide.md +++ b/docs/guides/troubleshooting-guide.md @@ -10,13 +10,13 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Guides REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.20.02 + VERSION: 02.21.01 PATH: /docs/guides/troubleshooting-guide.md BRIEF: Troubleshooting guide for diagnosing and resolving issues related to the MokoWaaS plugin NOTE: Designed for administrators and WaaS operations teams --> -# MokoWaaS Troubleshooting Guide (VERSION: 02.20.02) +# MokoWaaS Troubleshooting Guide (VERSION: 02.21.01) ## Introduction diff --git a/docs/guides/upgrade-and-versioning-guide.md b/docs/guides/upgrade-and-versioning-guide.md index 3caa14d1..e3e5b413 100644 --- a/docs/guides/upgrade-and-versioning-guide.md +++ b/docs/guides/upgrade-and-versioning-guide.md @@ -10,13 +10,13 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Guides REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.20.02 + VERSION: 02.21.01 PATH: /docs/guides/upgrade-and-versioning-guide.md BRIEF: Guide for updating, versioning, and maintaining the MokoWaaS plugin NOTE: Defines release flow, version rules, and upgrade validation --> -# MokoWaaS Upgrade and Versioning Guide (VERSION: 02.20.02) +# MokoWaaS Upgrade and Versioning Guide (VERSION: 02.21.01) ## Introduction diff --git a/docs/index.md b/docs/index.md index 1fc3209f..36c8e541 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,13 +10,13 @@ DEFGROUP: Joomla.Plugin INGROUP: MokoWaaS.Documentation REPO: https://github.com/mokoconsulting-tech/mokowaas - VERSION: 02.20.02 + VERSION: 02.21.01 PATH: /docs/index.md BRIEF: Master index of all documentation for the MokoWaaS plugin NOTE: Automatically maintained index for all guide canvases --> -# MokoWaaS Documentation Index (VERSION: 02.20.02) +# MokoWaaS Documentation Index (VERSION: 02.21.01) ## Introduction diff --git a/docs/plugin-basic.md b/docs/plugin-basic.md index e23d4098..57b8804c 100644 --- a/docs/plugin-basic.md +++ b/docs/plugin-basic.md @@ -11,12 +11,12 @@ INGROUP: MokoWaaS REPO: https://github.com/mokoconsulting-tech/mokowaas PATH: /docs/plugin-basic.md - VERSION: 02.20.02 + VERSION: 02.21.01 BRIEF: Baseline documentation for the MokoWaaS system plugin NOTE: Foundational reference for internal and external stakeholders --> -# MokoWaaS Plugin Overview (VERSION: 02.20.02) +# MokoWaaS Plugin Overview (VERSION: 02.21.01) ## Introduction diff --git a/docs/update-server.md b/docs/update-server.md index 8ff06123..0442b3ea 100644 --- a/docs/update-server.md +++ b/docs/update-server.md @@ -10,7 +10,7 @@ DEFGROUP: MokoWaaS.Documentation INGROUP: MokoStandards.Templates REPO: https://github.com/mokoconsulting-tech/MokoWaaS PATH: /docs/update-server.md -VERSION: 02.20.02 +VERSION: 02.21.01 BRIEF: How this extension's Joomla update server file (update.xml) is managed --> diff --git a/src/packages/com_mokowaas/mokowaas.xml b/src/packages/com_mokowaas/mokowaas.xml index ad652fff..da81ed2f 100644 --- a/src/packages/com_mokowaas/mokowaas.xml +++ b/src/packages/com_mokowaas/mokowaas.xml @@ -7,7 +7,7 @@ GPL-3.0-or-later hello@mokoconsulting.tech https://mokoconsulting.tech - 02.20.02-dev + 02.21.01-dev Minimal API-only component for MokoWaaS. Provides REST endpoints for site health, cache, updates, and backups. Moko\Component\MokoWaaS\Api diff --git a/src/packages/plg_system_mokowaas/Extension/MokoWaaS.php b/src/packages/plg_system_mokowaas/Extension/MokoWaaS.php index 62648f4b..d8bb0c0c 100644 --- a/src/packages/plg_system_mokowaas/Extension/MokoWaaS.php +++ b/src/packages/plg_system_mokowaas/Extension/MokoWaaS.php @@ -22,7 +22,7 @@ * DEFGROUP: Joomla.Plugin * INGROUP: MokoWaaS * REPO: https://github.com/mokoconsulting-tech/mokowaas - * VERSION: 02.20.02 + * VERSION: 02.21.01 * PATH: /src/Extension/MokoWaaS.php * NOTE: Handles Joomla system events for rebranding functionality */ diff --git a/src/packages/plg_system_mokowaas/Field/AllowedIpsField.php b/src/packages/plg_system_mokowaas/Field/AllowedIpsField.php index e91218af..3fd8ef3a 100644 --- a/src/packages/plg_system_mokowaas/Field/AllowedIpsField.php +++ b/src/packages/plg_system_mokowaas/Field/AllowedIpsField.php @@ -7,7 +7,7 @@ * FILE INFORMATION * DEFGROUP: Joomla.Plugin * INGROUP: MokoWaaS - * VERSION: 02.20.02 + * VERSION: 02.21.01 * PATH: /src/Field/AllowedIpsField.php * BRIEF: Custom form field that displays the current IP whitelist */ diff --git a/src/packages/plg_system_mokowaas/Field/CurrentIpField.php b/src/packages/plg_system_mokowaas/Field/CurrentIpField.php index 264bd474..2a1a6b10 100644 --- a/src/packages/plg_system_mokowaas/Field/CurrentIpField.php +++ b/src/packages/plg_system_mokowaas/Field/CurrentIpField.php @@ -7,7 +7,7 @@ * FILE INFORMATION * DEFGROUP: Joomla.Plugin * INGROUP: MokoWaaS - * VERSION: 02.20.02 + * VERSION: 02.21.01 * PATH: /src/Field/CurrentIpField.php * BRIEF: Read-only field that displays the current user's IP address */ diff --git a/src/packages/plg_system_mokowaas/Service/DemoResetService.php b/src/packages/plg_system_mokowaas/Service/DemoResetService.php index bb0bb596..600ada42 100644 --- a/src/packages/plg_system_mokowaas/Service/DemoResetService.php +++ b/src/packages/plg_system_mokowaas/Service/DemoResetService.php @@ -10,7 +10,7 @@ * INGROUP: MokoWaaS * REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS * PATH: /src/packages/plg_system_mokowaas/Service/DemoResetService.php - * VERSION: 02.21.00 + * VERSION: 02.21.01 * BRIEF: Core snapshot/restore service for demo site reset */ diff --git a/src/packages/plg_system_mokowaas/mokowaas.xml b/src/packages/plg_system_mokowaas/mokowaas.xml index 458ca50f..94b8dba3 100644 --- a/src/packages/plg_system_mokowaas/mokowaas.xml +++ b/src/packages/plg_system_mokowaas/mokowaas.xml @@ -30,7 +30,7 @@ GNU General Public License version 3 or later; see LICENSE.md hello@mokoconsulting.tech https://mokoconsulting.tech - 02.20.02-dev + 02.21.01-dev This plugin rebrands the Joomla system interface with MokoWaaS identity. It applies language overrides and ensures consistent branding across the platform. Moko\Plugin\System\MokoWaaS script.php diff --git a/src/packages/plg_system_mokowaas/script.php b/src/packages/plg_system_mokowaas/script.php index 43cb58ca..db106eda 100644 --- a/src/packages/plg_system_mokowaas/script.php +++ b/src/packages/plg_system_mokowaas/script.php @@ -22,7 +22,7 @@ * DEFGROUP: Joomla.Plugin * INGROUP: MokoWaaS * REPO: https://github.com/mokoconsulting-tech/mokowaas - * VERSION: 02.20.02 + * VERSION: 02.21.01 * PATH: /src/script.php * BRIEF: Installation script for MokoWaaS plugin * NOTE: Handles installation, update, and uninstallation tasks including language override deployment diff --git a/src/packages/plg_system_mokowaas/services/provider.php b/src/packages/plg_system_mokowaas/services/provider.php index f7007931..a43208a7 100644 --- a/src/packages/plg_system_mokowaas/services/provider.php +++ b/src/packages/plg_system_mokowaas/services/provider.php @@ -22,7 +22,7 @@ * DEFGROUP: Joomla.Plugin * INGROUP: MokoWaaS * REPO: https://github.com/mokoconsulting-tech/mokowaas - * VERSION: 02.20.02 + * VERSION: 02.21.01 * PATH: /src/services/provider.php * BRIEF: Service provider for dependency injection in Joomla 5.x * NOTE: Registers the plugin with Joomla's DI container diff --git a/src/packages/plg_task_mokowaas/mokowaasdemo.xml b/src/packages/plg_task_mokowaas/mokowaasdemo.xml index cb008be2..722ae923 100644 --- a/src/packages/plg_task_mokowaas/mokowaasdemo.xml +++ b/src/packages/plg_task_mokowaas/mokowaasdemo.xml @@ -12,7 +12,7 @@ GNU General Public License version 3 or later; see LICENSE hello@mokoconsulting.tech https://mokoconsulting.tech - 02.21.00 + 02.21.01-dev PLG_TASK_MOKOWAASDEMO_DESC Moko\Plugin\Task\MokoWaaSDemo diff --git a/src/packages/plg_webservices_mokowaas/mokowaas.xml b/src/packages/plg_webservices_mokowaas/mokowaas.xml index 65de22bb..e9c130bf 100644 --- a/src/packages/plg_webservices_mokowaas/mokowaas.xml +++ b/src/packages/plg_webservices_mokowaas/mokowaas.xml @@ -7,7 +7,7 @@ GPL-3.0-or-later hello@mokoconsulting.tech https://mokoconsulting.tech - 02.20.02-dev + 02.21.01-dev Joomla Web Services API routes for MokoWaaS site management — health checks, cache, updates, backups, and site info. Moko\Plugin\WebServices\MokoWaaS diff --git a/src/packages/plg_webservices_perfectpublisher/perfectpublisher.xml b/src/packages/plg_webservices_perfectpublisher/perfectpublisher.xml index 0c9ffea7..d6cb8ca3 100644 --- a/src/packages/plg_webservices_perfectpublisher/perfectpublisher.xml +++ b/src/packages/plg_webservices_perfectpublisher/perfectpublisher.xml @@ -7,7 +7,7 @@ GPL-3.0-or-later hello@mokoconsulting.tech https://mokoconsulting.tech - 02.20.02-dev + 02.21.01-dev Joomla Web Services API routes for Perfect Publisher (com_autotweet) — channels, posts, requests, rules, and feeds. Moko\Plugin\WebServices\PerfectPublisher diff --git a/src/packages/plg_webservices_perfectpublisher/services/provider.php b/src/packages/plg_webservices_perfectpublisher/services/provider.php index ac0a9655..7a52a056 100644 --- a/src/packages/plg_webservices_perfectpublisher/services/provider.php +++ b/src/packages/plg_webservices_perfectpublisher/services/provider.php @@ -8,7 +8,7 @@ * INGROUP: MokoWaaS * REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS * PATH: /src/packages/plg_webservices_perfectpublisher/services/provider.php - * VERSION: 02.20.02 + * VERSION: 02.21.01 * BRIEF: DI service provider for Perfect Publisher Web Services plugin */ diff --git a/src/packages/plg_webservices_perfectpublisher/src/Extension/PerfectPublisherApi.php b/src/packages/plg_webservices_perfectpublisher/src/Extension/PerfectPublisherApi.php index fd5ff661..5c9ac28b 100644 --- a/src/packages/plg_webservices_perfectpublisher/src/Extension/PerfectPublisherApi.php +++ b/src/packages/plg_webservices_perfectpublisher/src/Extension/PerfectPublisherApi.php @@ -8,7 +8,7 @@ * INGROUP: MokoWaaS * REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS * PATH: /src/packages/plg_webservices_perfectpublisher/src/Extension/PerfectPublisherApi.php - * VERSION: 02.20.02 + * VERSION: 02.21.01 * BRIEF: Web Services API plugin for Perfect Publisher (com_autotweet) */ diff --git a/src/pkg_mokowaas.xml b/src/pkg_mokowaas.xml index d5de3461..2bf509dc 100644 --- a/src/pkg_mokowaas.xml +++ b/src/pkg_mokowaas.xml @@ -2,7 +2,7 @@ MokoWaaS mokowaas - 02.20.02-dev + 02.21.01-dev 2026-05-23 Moko Consulting hello@mokoconsulting.tech diff --git a/updates.xml b/updates.xml index e47e4707..1ba82dfd 100644 --- a/updates.xml +++ b/updates.xml @@ -1,7 +1,7 @@ -- 2.52.0 From fc30ee6fb26ccb539ad76a75c36e41145a74a3b2 Mon Sep 17 00:00:00 2001 From: "gitea-actions[bot]" Date: Sat, 30 May 2026 17:51:12 +0000 Subject: [PATCH 12/12] chore: update development channel 02.21.01-dev [skip ci] --- updates.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/updates.xml b/updates.xml index 1ba82dfd..405d9de7 100644 --- a/updates.xml +++ b/updates.xml @@ -1,7 +1,7 @@ @@ -83,13 +83,13 @@ pkg_mokowaas package site - 02.20.02-dev + 02.21.01-dev 2026-05-30 https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/development - https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.20.02-dev.zip + https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.21.01-dev.zip - 548ba5286120f81f682a388630336eadd3728ba9c6dee6873de32de2fea7f2ee + 964cf7ae175f0b54818ba28cbeb9843742c5c4c7aadee5c79f9d732e70656dc8 dev https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/raw/branch/main/CHANGELOG.md Moko Consulting -- 2.52.0