diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml
index 614bebd..f9ed2ac 100644
--- a/.mokogitea/manifest.xml
+++ b/.mokogitea/manifest.xml
@@ -8,7 +8,7 @@
MokoOnyx
MokoConsulting
MokoOnyx - Joomla site template (successor to MokoCassiopeia)
- 02.07.02
+ 02.07.03-dev
GNU General Public License v3
diff --git a/.mokogitea/workflows/auto-bump.yml b/.mokogitea/workflows/auto-bump.yml
index 572facd..dc76039 100644
--- a/.mokogitea/workflows/auto-bump.yml
+++ b/.mokogitea/workflows/auto-bump.yml
@@ -1,85 +1,85 @@
-# 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
+
+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
diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml
index 8be9b71..8d0ac29 100644
--- a/.mokogitea/workflows/auto-release.yml
+++ b/.mokogitea/workflows/auto-release.yml
@@ -1,524 +1,524 @@
-# 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
- 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.GITHUB_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
- 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 .)
- 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] "
- git push -u origin HEAD
-
- # -- 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 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.GITHUB_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.GITHUB_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.GITHUB_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.GITHUB_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \
- git remote set-url github "https://x-access-token:${{ secrets.GITHUB_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:
+ # ── 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
+ 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
+ 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 .)
+ 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] "
+ git push -u origin HEAD
+
+ # -- 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 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
diff --git a/.mokogitea/workflows/branch-cleanup.yml b/.mokogitea/workflows/branch-cleanup.yml
index 484ad72..d45f1fe 100644
--- a/.mokogitea/workflows/branch-cleanup.yml
+++ b/.mokogitea/workflows/branch-cleanup.yml
@@ -1,48 +1,48 @@
-# Copyright (C) 2026 Moko Consulting
-#
-# SPDX-License-Identifier: GPL-3.0-or-later
-#
-# FILE INFORMATION
-# DEFGROUP: Gitea.Workflow
-# INGROUP: MokoStandards.Universal
-# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
-# PATH: /.mokogitea/workflows/branch-cleanup.yml
-# VERSION: 01.00.00
-# BRIEF: Delete feature branches after PR merge
-
-name: "Branch Cleanup"
-
-on:
- pull_request:
- types: [closed]
-
-env:
- FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
-
-jobs:
- cleanup:
- name: Delete merged branch
- runs-on: ubuntu-latest
- if: >-
- github.event.pull_request.merged == true &&
- github.event.pull_request.head.ref != 'dev' &&
- github.event.pull_request.head.ref != 'main'
-
- steps:
- - name: Delete source branch
- run: |
- BRANCH="${{ github.event.pull_request.head.ref }}"
- API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches"
- ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${BRANCH}', safe=''))")
-
- STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
- -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
- "${API}/${ENCODED}" 2>/dev/null || true)
-
- if [ "$STATUS" = "204" ]; then
- echo "Deleted branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
- elif [ "$STATUS" = "404" ]; then
- echo "Branch already deleted: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
- else
- echo "::warning::Failed to delete branch ${BRANCH} (HTTP ${STATUS})"
- fi
+# Copyright (C) 2026 Moko Consulting
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# FILE INFORMATION
+# DEFGROUP: Gitea.Workflow
+# INGROUP: MokoStandards.Universal
+# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
+# PATH: /.mokogitea/workflows/branch-cleanup.yml
+# VERSION: 01.00.00
+# BRIEF: Delete feature branches after PR merge
+
+name: "Branch Cleanup"
+
+on:
+ pull_request:
+ types: [closed]
+
+env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+
+jobs:
+ cleanup:
+ name: Delete merged branch
+ runs-on: ubuntu-latest
+ if: >-
+ github.event.pull_request.merged == true &&
+ github.event.pull_request.head.ref != 'dev' &&
+ github.event.pull_request.head.ref != 'main'
+
+ steps:
+ - name: Delete source branch
+ run: |
+ BRANCH="${{ github.event.pull_request.head.ref }}"
+ API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches"
+ ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('${BRANCH}', safe=''))")
+
+ STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
+ -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
+ "${API}/${ENCODED}" 2>/dev/null || true)
+
+ if [ "$STATUS" = "204" ]; then
+ echo "Deleted branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
+ elif [ "$STATUS" = "404" ]; then
+ echo "Branch already deleted: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
+ else
+ echo "::warning::Failed to delete branch ${BRANCH} (HTTP ${STATUS})"
+ fi
diff --git a/.mokogitea/workflows/pre-release.yml b/.mokogitea/workflows/pre-release.yml
index 7920f53..0f0d000 100644
--- a/.mokogitea/workflows/pre-release.yml
+++ b/.mokogitea/workflows/pre-release.yml
@@ -1,231 +1,231 @@
-# Copyright (C) 2026 Moko Consulting
-#
-# SPDX-License-Identifier: GPL-3.0-or-later
-#
-# FILE INFORMATION
-# DEFGROUP: Gitea.Workflow
-# INGROUP: moko-platform.Release
-# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
-# PATH: /templates/workflows/universal/pre-release.yml.template
-# VERSION: 05.01.00
-# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch
-
-name: "Universal: Pre-Release"
-
-on:
- pull_request:
- types: [closed]
- branches:
- - dev
- workflow_dispatch:
- inputs:
- stability:
- description: 'Pre-release channel'
- required: true
- type: choice
- options:
- - development
- - alpha
- - beta
- - release-candidate
-
-permissions:
- contents: write
-
-env:
- GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
- GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
- GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
-
-jobs:
- build:
- name: "Build Pre-Release (${{ inputs.stability || 'development' }})"
- runs-on: release
- if: >-
- github.event_name == 'workflow_dispatch' ||
- (github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev')
-
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- fetch-depth: 0
- token: ${{ secrets.MOKOGITEA_TOKEN }}
-
- - 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
- git clone --depth 1 --branch main --quiet \
- "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
- /tmp/moko-platform-api
- cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
- echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
-
- - name: Detect platform
- id: platform
- run: |
- php ${MOKO_CLI}/manifest_read.php --path . --github-output
-
- - name: Resolve metadata and bump version
- id: meta
- run: |
- STABILITY="${{ inputs.stability || 'development' }}"
-
- case "$STABILITY" in
- development) SUFFIX="-dev"; TAG="development" ;;
- alpha) SUFFIX="-alpha"; TAG="alpha" ;;
- beta) SUFFIX="-beta"; TAG="beta" ;;
- release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
- esac
-
- # Read current version (bump already handled by push workflow)
- VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null)
- [ -z "$VERSION" ] && VERSION="00.00.01"
-
- # Strip any existing suffix from version before applying stability
- VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
-
- php ${MOKO_CLI}/version_set_platform.php \
- --path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true
-
- # Verify version consistency across all files
- php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
-
- # Update VERSION variable with suffix
- if [ -n "$SUFFIX" ]; then
- VERSION="${VERSION}${SUFFIX}"
- fi
-
- # Commit version bump
- git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
- git config --local user.name "gitea-actions[bot]"
- git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
- git add -A
- git diff --cached --quiet || {
- git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
- git push origin HEAD 2>&1
- }
-
- # Auto-detect element via manifest_element.php
- php ${MOKO_CLI}/manifest_element.php \
- --path . --version "$VERSION" --stability "$STABILITY" \
- --repo "${GITEA_REPO}" --github-output
-
- # Read back element outputs
- EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
- ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
- [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
- [ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip"
-
- echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
- echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
- echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
- echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
- echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
-
- echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
-
- - name: Create release
- id: release
- run: |
- TAG="${{ steps.meta.outputs.tag }}"
- VERSION="${{ steps.meta.outputs.version }}"
- API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
- php ${MOKO_CLI}/release_create.php \
- --path . --version "$VERSION" --tag "$TAG" \
- --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
- --repo "${GITEA_REPO}" --branch dev --prerelease
-
- - name: Build package and upload
- id: package
- run: |
- VERSION="${{ steps.meta.outputs.version }}"
- TAG="${{ steps.meta.outputs.tag }}"
- API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
- php ${MOKO_CLI}/release_package.php \
- --path . --version "$VERSION" --tag "$TAG" \
- --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
- --repo "${GITEA_REPO}" --output /tmp || true
-
- - name: Update updates.xml
- if: steps.platform.outputs.platform == 'joomla'
- run: |
- VERSION="${{ steps.meta.outputs.version }}"
- STABILITY="${{ steps.meta.outputs.stability }}"
- SHA256="${{ steps.package.outputs.sha256_zip }}"
-
- if [ ! -f "updates.xml" ]; then
- echo "No updates.xml -- skipping"
- exit 0
- fi
-
- SHA_FLAG=""
- [ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}"
-
- php ${MOKO_CLI}/updates_xml_build.php \
- --path . --version "${VERSION}" --stability "${STABILITY}" \
- --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
- ${SHA_FLAG}
-
- # Commit and push
- if ! git diff --quiet updates.xml 2>/dev/null; then
- git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
- git config --local user.name "gitea-actions[bot]"
- git add updates.xml
- git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]"
- git push origin HEAD 2>&1 || echo "WARNING: push failed"
- fi
-
- - name: "Sync updates.xml to all branches"
- if: steps.platform.outputs.platform == 'joomla'
- run: |
- CURRENT_BRANCH="${{ github.ref_name }}"
- git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
- git config --local user.name "gitea-actions[bot]"
-
- for BRANCH in main dev; do
- [ "$BRANCH" = "$CURRENT_BRANCH" ] && continue
- echo "Syncing updates.xml -> ${BRANCH}"
- git fetch origin "${BRANCH}" 2>/dev/null || continue
- git checkout "origin/${BRANCH}" -- updates.xml 2>/dev/null || continue
- git checkout "${CURRENT_BRANCH}" -- updates.xml
- if ! git diff --quiet updates.xml 2>/dev/null; then
- git add updates.xml
- git commit -m "chore: sync updates.xml from ${CURRENT_BRANCH} [skip ci]"
- git push origin HEAD:refs/heads/${BRANCH} 2>&1 || echo "WARNING: push to ${BRANCH} failed"
- fi
- git checkout "${CURRENT_BRANCH}" 2>/dev/null
- done
-
- - name: "Delete lesser pre-release channels (cascade)"
- continue-on-error: true
- run: |
- API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
- TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
-
- php ${MOKO_CLI}/release_cascade.php \
- --stability "${{ steps.meta.outputs.stability }}" \
- --token "${TOKEN}" \
- --api-base "${API_BASE}"
-
- - name: Summary
- if: always()
- run: |
- VERSION="${{ steps.meta.outputs.version }}"
- STABILITY="${{ steps.meta.outputs.stability }}"
- ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
- SHA256="${{ steps.package.outputs.sha256_zip }}"
- echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY
- echo "" >> $GITHUB_STEP_SUMMARY
- echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
- echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
- echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
- echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY
- echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY
- echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY
+# Copyright (C) 2026 Moko Consulting
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# FILE INFORMATION
+# DEFGROUP: Gitea.Workflow
+# INGROUP: moko-platform.Release
+# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
+# PATH: /templates/workflows/universal/pre-release.yml.template
+# VERSION: 05.01.00
+# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch
+
+name: "Universal: Pre-Release"
+
+on:
+ pull_request:
+ types: [closed]
+ branches:
+ - dev
+ workflow_dispatch:
+ inputs:
+ stability:
+ description: 'Pre-release channel'
+ required: true
+ type: choice
+ options:
+ - development
+ - alpha
+ - beta
+ - release-candidate
+
+permissions:
+ contents: write
+
+env:
+ GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
+ GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
+ GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
+
+jobs:
+ build:
+ name: "Build Pre-Release (${{ inputs.stability || 'development' }})"
+ runs-on: release
+ if: >-
+ github.event_name == 'workflow_dispatch' ||
+ (github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev')
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ token: ${{ secrets.MOKOGITEA_TOKEN }}
+
+ - 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
+ git clone --depth 1 --branch main --quiet \
+ "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
+ /tmp/moko-platform-api
+ cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
+ echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
+
+ - name: Detect platform
+ id: platform
+ run: |
+ php ${MOKO_CLI}/manifest_read.php --path . --github-output
+
+ - name: Resolve metadata and bump version
+ id: meta
+ run: |
+ STABILITY="${{ inputs.stability || 'development' }}"
+
+ case "$STABILITY" in
+ development) SUFFIX="-dev"; TAG="development" ;;
+ alpha) SUFFIX="-alpha"; TAG="alpha" ;;
+ beta) SUFFIX="-beta"; TAG="beta" ;;
+ release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
+ esac
+
+ # Read current version (bump already handled by push workflow)
+ VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null)
+ [ -z "$VERSION" ] && VERSION="00.00.01"
+
+ # Strip any existing suffix from version before applying stability
+ VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
+
+ php ${MOKO_CLI}/version_set_platform.php \
+ --path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true
+
+ # Verify version consistency across all files
+ php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
+
+ # Update VERSION variable with suffix
+ if [ -n "$SUFFIX" ]; then
+ VERSION="${VERSION}${SUFFIX}"
+ fi
+
+ # Commit version bump
+ git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
+ git config --local user.name "gitea-actions[bot]"
+ git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
+ git add -A
+ git diff --cached --quiet || {
+ git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
+ git push origin HEAD 2>&1
+ }
+
+ # Auto-detect element via manifest_element.php
+ php ${MOKO_CLI}/manifest_element.php \
+ --path . --version "$VERSION" --stability "$STABILITY" \
+ --repo "${GITEA_REPO}" --github-output
+
+ # Read back element outputs
+ EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
+ ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
+ [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
+ [ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip"
+
+ echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
+ echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
+ echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
+ echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
+ echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
+ echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
+
+ echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
+
+ - name: Create release
+ id: release
+ run: |
+ TAG="${{ steps.meta.outputs.tag }}"
+ VERSION="${{ steps.meta.outputs.version }}"
+ API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
+ php ${MOKO_CLI}/release_create.php \
+ --path . --version "$VERSION" --tag "$TAG" \
+ --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
+ --repo "${GITEA_REPO}" --branch dev --prerelease
+
+ - name: Build package and upload
+ id: package
+ run: |
+ VERSION="${{ steps.meta.outputs.version }}"
+ TAG="${{ steps.meta.outputs.tag }}"
+ API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
+ php ${MOKO_CLI}/release_package.php \
+ --path . --version "$VERSION" --tag "$TAG" \
+ --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
+ --repo "${GITEA_REPO}" --output /tmp || true
+
+ - name: Update updates.xml
+ if: steps.platform.outputs.platform == 'joomla'
+ run: |
+ VERSION="${{ steps.meta.outputs.version }}"
+ STABILITY="${{ steps.meta.outputs.stability }}"
+ SHA256="${{ steps.package.outputs.sha256_zip }}"
+
+ if [ ! -f "updates.xml" ]; then
+ echo "No updates.xml -- skipping"
+ exit 0
+ fi
+
+ SHA_FLAG=""
+ [ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}"
+
+ php ${MOKO_CLI}/updates_xml_build.php \
+ --path . --version "${VERSION}" --stability "${STABILITY}" \
+ --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
+ ${SHA_FLAG}
+
+ # Commit and push
+ if ! git diff --quiet updates.xml 2>/dev/null; then
+ git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
+ git config --local user.name "gitea-actions[bot]"
+ git add updates.xml
+ git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]"
+ git push origin HEAD 2>&1 || echo "WARNING: push failed"
+ fi
+
+ - name: "Sync updates.xml to all branches"
+ if: steps.platform.outputs.platform == 'joomla'
+ run: |
+ CURRENT_BRANCH="${{ github.ref_name }}"
+ git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
+ git config --local user.name "gitea-actions[bot]"
+
+ for BRANCH in main dev; do
+ [ "$BRANCH" = "$CURRENT_BRANCH" ] && continue
+ echo "Syncing updates.xml -> ${BRANCH}"
+ git fetch origin "${BRANCH}" 2>/dev/null || continue
+ git checkout "origin/${BRANCH}" -- updates.xml 2>/dev/null || continue
+ git checkout "${CURRENT_BRANCH}" -- updates.xml
+ if ! git diff --quiet updates.xml 2>/dev/null; then
+ git add updates.xml
+ git commit -m "chore: sync updates.xml from ${CURRENT_BRANCH} [skip ci]"
+ git push origin HEAD:refs/heads/${BRANCH} 2>&1 || echo "WARNING: push to ${BRANCH} failed"
+ fi
+ git checkout "${CURRENT_BRANCH}" 2>/dev/null
+ done
+
+ - name: "Delete lesser pre-release channels (cascade)"
+ continue-on-error: true
+ run: |
+ API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
+ TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
+
+ php ${MOKO_CLI}/release_cascade.php \
+ --stability "${{ steps.meta.outputs.stability }}" \
+ --token "${TOKEN}" \
+ --api-base "${API_BASE}"
+
+ - name: Summary
+ if: always()
+ run: |
+ VERSION="${{ steps.meta.outputs.version }}"
+ STABILITY="${{ steps.meta.outputs.stability }}"
+ ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
+ SHA256="${{ steps.package.outputs.sha256_zip }}"
+ echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
+ echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
+ echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
+ echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY
+ echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY
+ echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY
diff --git a/.mokogitea/workflows/update-server.yml b/.mokogitea/workflows/update-server.yml
index cd2eff0..061476f 100644
--- a/.mokogitea/workflows/update-server.yml
+++ b/.mokogitea/workflows/update-server.yml
@@ -1,311 +1,311 @@
-# Copyright (C) 2026 Moko Consulting
-#
-# SPDX-License-Identifier: GPL-3.0-or-later
-#
-# FILE INFORMATION
-# DEFGROUP: Gitea.Workflow
-# INGROUP: moko-platform.Universal
-# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
-# PATH: /templates/workflows/update-server.yml
-# VERSION: 05.00.00
-# BRIEF: Pre-release build + update server XML for dev/alpha/beta/rc branches
-#
-# Thin wrapper around moko-platform CLI tools.
-# Builds packages, updates updates.xml, and optionally deploys via SFTP.
-#
-# Joomla filters update entries by the user's "Minimum Stability" setting.
-
-name: "Update Server"
-
-on:
- push:
- branches:
- - 'dev'
- - 'dev/**'
- - 'alpha/**'
- - 'beta/**'
- - 'rc/**'
- paths:
- - 'src/**'
- - 'htdocs/**'
- pull_request:
- types: [closed]
- branches:
- - 'dev'
- - 'dev/**'
- - 'alpha/**'
- - 'beta/**'
- - 'rc/**'
- paths:
- - 'src/**'
- - 'htdocs/**'
- workflow_dispatch:
- inputs:
- stability:
- description: 'Stability tag'
- required: true
- default: 'development'
- type: choice
- options:
- - development
- - alpha
- - beta
- - rc
- - stable
-
-env:
- FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
- GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
- GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
- GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
-
-permissions:
- contents: write
-
-jobs:
- update-xml:
- name: Update Server
- runs-on: release
- if: >-
- github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' || github.event_name == 'push'
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
- with:
- token: ${{ secrets.MOKOGITEA_TOKEN }}
- fetch-depth: 0
-
- - name: Setup moko-platform tools
- env:
- MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
- MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
- COMPOSER_AUTH: '{"http-basic":{"git.mokoconsulting.tech":{"username":"token","password":"${{ secrets.MOKOGITEA_TOKEN }}"}}}'
- run: |
- if ! command -v composer &> /dev/null; then
- sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1
- fi
- if [ -d "/tmp/moko-platform" ]; then
- echo "moko-platform already available — skipping clone"
- else
- git clone --depth 1 --branch main --quiet \
- "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
- /tmp/moko-platform 2>/dev/null || true
- fi
- if [ -d "/tmp/moko-platform" ] && [ -f "/tmp/moko-platform/composer.json" ]; then
- cd /tmp/moko-platform && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
- fi
- echo "MOKO_CLI=/tmp/moko-platform/cli" >> "$GITHUB_ENV"
-
- - name: Detect platform
- id: platform
- run: php ${MOKO_CLI}/manifest_read.php --path . --github-output
-
- - name: Resolve stability and bump version
- id: meta
- run: |
- BRANCH="${{ github.ref_name }}"
-
- # Configure git for bot pushes
- git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
- git config --local user.name "gitea-actions[bot]"
- git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
-
- # Auto-bump patch version
- php ${MOKO_CLI}/version_bump.php --path . 2>/dev/null || true
-
- VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "0.0.0")
-
- # Determine stability from branch or manual input
- if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
- STABILITY="${{ inputs.stability }}"
- elif [[ "$BRANCH" == rc/* ]]; then
- STABILITY="rc"
- elif [[ "$BRANCH" == beta/* ]]; then
- STABILITY="beta"
- elif [[ "$BRANCH" == alpha/* ]]; then
- STABILITY="alpha"
- else
- STABILITY="development"
- fi
-
- # Version suffix per stability stream
- case "$STABILITY" in
- development) SUFFIX="-dev"; TAG="development" ;;
- alpha) SUFFIX="-alpha"; TAG="alpha" ;;
- beta) SUFFIX="-beta"; TAG="beta" ;;
- rc) SUFFIX="-rc"; TAG="release-candidate" ;;
- *) SUFFIX=""; TAG="stable" ;;
- esac
-
- # Propagate version with stability suffix to all manifest files
- php ${MOKO_CLI}/version_set_platform.php \
- --path . --version "$VERSION" --branch "$BRANCH" --stability "$STABILITY" 2>/dev/null || true
- php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
-
- # Re-read version (now includes suffix from version_set_platform)
- if [ -n "$SUFFIX" ]; then
- VERSION="${VERSION}${SUFFIX}"
- fi
-
- echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
- echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
- echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
- echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
- echo "display_version=${VERSION}" >> "$GITHUB_OUTPUT"
-
- # Commit version bump if changed
- git add -A
- git diff --cached --quiet || {
- git commit -m "chore(version): auto-bump ${VERSION} [skip ci]" \
- --author="gitea-actions[bot] "
- git push
- }
-
- - name: Create release and upload package
- id: package
- run: |
- VERSION="${{ steps.meta.outputs.version }}"
- TAG="${{ steps.meta.outputs.tag }}"
- API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
-
- # Create or update Gitea release
- php ${MOKO_CLI}/release_create.php \
- --path . --version "$VERSION" --tag "$TAG" \
- --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
- --repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease
-
- # Build package and upload
- php ${MOKO_CLI}/release_package.php \
- --path . --version "$VERSION" --tag "$TAG" \
- --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
- --repo "${GITEA_REPO}" --output /tmp || true
-
- - name: Update updates.xml
- if: steps.platform.outputs.platform == 'joomla'
- run: |
- VERSION="${{ steps.meta.outputs.version }}"
- STABILITY="${{ steps.meta.outputs.stability }}"
- SHA256="${{ steps.package.outputs.sha256_zip }}"
-
- if [ ! -f "updates.xml" ]; then
- echo "No updates.xml — skipping"
- exit 0
- fi
-
- SHA_FLAG=""
- [ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}"
-
- php ${MOKO_CLI}/updates_xml_build.php \
- --path . --version "${VERSION}" --stability "${STABILITY}" \
- --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
- ${SHA_FLAG}
-
- # Commit and push updates.xml
- git add updates.xml
- git diff --cached --quiet || {
- git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]"
- git push
- }
-
- - name: Sync updates.xml to main
- if: github.ref_name != 'main' && steps.platform.outputs.platform == 'joomla'
- run: |
- API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
- GITEA_TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
-
- FILE_SHA=$(curl -sf -H "Authorization: token ${GITEA_TOKEN}" \
- "${API_BASE}/contents/updates.xml?ref=main" | python3 -c "import sys,json; print(json.load(sys.stdin).get('sha',''))" 2>/dev/null || true)
-
- if [ -n "$FILE_SHA" ] && [ -f "updates.xml" ]; then
- python3 -c "
- import base64, json, urllib.request, sys
- with open('updates.xml', 'rb') as f:
- content = base64.b64encode(f.read()).decode()
- payload = json.dumps({
- 'content': content,
- 'sha': '${FILE_SHA}',
- 'message': 'chore: sync updates.xml from ${{ steps.meta.outputs.stability }} [skip ci]',
- 'branch': 'main'
- }).encode()
- req = urllib.request.Request(
- '${API_BASE}/contents/updates.xml',
- data=payload, method='PUT',
- headers={
- 'Authorization': 'token ${GITEA_TOKEN}',
- 'Content-Type': 'application/json'
- })
- try:
- urllib.request.urlopen(req)
- print('updates.xml synced to main')
- except Exception as e:
- print(f'WARNING: sync to main failed: {e}', file=sys.stderr)
- "
- fi
-
- - name: SFTP deploy to dev server
- if: contains(github.ref, 'dev/') || github.ref == 'refs/heads/dev'
- env:
- DEV_HOST: ${{ vars.DEV_FTP_HOST }}
- DEV_PATH: ${{ vars.DEV_FTP_PATH }}
- DEV_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }}
- DEV_USER: ${{ vars.DEV_FTP_USERNAME }}
- DEV_PORT: ${{ vars.DEV_FTP_PORT }}
- DEV_KEY: ${{ secrets.DEV_FTP_KEY }}
- DEV_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
- run: |
- # Permission check: admin or maintain role required
- ACTOR="${{ github.actor }}"
- API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
-
- PERMISSION=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
- "${API_BASE}/collaborators/${ACTOR}/permission" 2>/dev/null | \
- python3 -c "import sys,json; print(json.load(sys.stdin).get('permission','read'))" 2>/dev/null || echo "read")
- case "$PERMISSION" in
- admin|maintain|write) ;;
- *)
- echo "Deploy denied: ${ACTOR} has '${PERMISSION}' — requires admin, maintain, or write"
- exit 0
- ;;
- esac
-
- [ -z "$DEV_HOST" ] || [ -z "$DEV_PATH" ] && { echo "DEV FTP not configured — skipping SFTP"; exit 0; }
-
- SOURCE_DIR="src"
- [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
- [ ! -d "$SOURCE_DIR" ] && exit 0
-
- PORT="${DEV_PORT:-22}"
- REMOTE="${DEV_PATH%/}"
- [ -n "$DEV_SUFFIX" ] && REMOTE="${REMOTE}/${DEV_SUFFIX#/}"
-
- printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \
- "$DEV_HOST" "$PORT" "$DEV_USER" "$REMOTE" > /tmp/sftp-config.json
- if [ -n "$DEV_KEY" ]; then
- echo "$DEV_KEY" > /tmp/deploy_key && chmod 600 /tmp/deploy_key
- printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json
- else
- printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json
- fi
-
- PLATFORM=$(php ${MOKO_CLI}/platform_detect.php --path . 2>/dev/null || true)
- if [ "$PLATFORM" = "waas-component" ] && [ -f "${MOKO_CLI}/../deploy/deploy-joomla.php" ]; then
- php ${MOKO_CLI}/../deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
- elif [ -f "${MOKO_CLI}/../deploy/deploy-sftp.php" ]; then
- php ${MOKO_CLI}/../deploy/deploy-sftp.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
- fi
- rm -f /tmp/deploy_key /tmp/sftp-config.json
- echo "SFTP deploy to dev complete" >> $GITHUB_STEP_SUMMARY
-
- - name: Summary
- if: always()
- run: |
- VERSION="${{ steps.meta.outputs.version }}"
- STABILITY="${{ steps.meta.outputs.stability }}"
- DISPLAY="${{ steps.meta.outputs.display_version }}"
- echo "## Update Server" >> $GITHUB_STEP_SUMMARY
- echo "" >> $GITHUB_STEP_SUMMARY
- echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
- echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
- echo "| Stability | \`${STABILITY}\` |" >> $GITHUB_STEP_SUMMARY
- echo "| Version | \`${DISPLAY}\` |" >> $GITHUB_STEP_SUMMARY
+# Copyright (C) 2026 Moko Consulting
+#
+# SPDX-License-Identifier: GPL-3.0-or-later
+#
+# FILE INFORMATION
+# DEFGROUP: Gitea.Workflow
+# INGROUP: moko-platform.Universal
+# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
+# PATH: /templates/workflows/update-server.yml
+# VERSION: 05.00.00
+# BRIEF: Pre-release build + update server XML for dev/alpha/beta/rc branches
+#
+# Thin wrapper around moko-platform CLI tools.
+# Builds packages, updates updates.xml, and optionally deploys via SFTP.
+#
+# Joomla filters update entries by the user's "Minimum Stability" setting.
+
+name: "Update Server"
+
+on:
+ push:
+ branches:
+ - 'dev'
+ - 'dev/**'
+ - 'alpha/**'
+ - 'beta/**'
+ - 'rc/**'
+ paths:
+ - 'src/**'
+ - 'htdocs/**'
+ pull_request:
+ types: [closed]
+ branches:
+ - 'dev'
+ - 'dev/**'
+ - 'alpha/**'
+ - 'beta/**'
+ - 'rc/**'
+ paths:
+ - 'src/**'
+ - 'htdocs/**'
+ workflow_dispatch:
+ inputs:
+ stability:
+ description: 'Stability tag'
+ required: true
+ default: 'development'
+ type: choice
+ options:
+ - development
+ - alpha
+ - beta
+ - rc
+ - stable
+
+env:
+ FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
+ GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
+ GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
+ GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
+
+permissions:
+ contents: write
+
+jobs:
+ update-xml:
+ name: Update Server
+ runs-on: release
+ if: >-
+ github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' || github.event_name == 'push'
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ token: ${{ secrets.MOKOGITEA_TOKEN }}
+ fetch-depth: 0
+
+ - name: Setup moko-platform tools
+ env:
+ MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
+ MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
+ COMPOSER_AUTH: '{"http-basic":{"git.mokoconsulting.tech":{"username":"token","password":"${{ secrets.MOKOGITEA_TOKEN }}"}}}'
+ run: |
+ if ! command -v composer &> /dev/null; then
+ sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1
+ fi
+ if [ -d "/tmp/moko-platform" ]; then
+ echo "moko-platform already available — skipping clone"
+ else
+ git clone --depth 1 --branch main --quiet \
+ "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
+ /tmp/moko-platform 2>/dev/null || true
+ fi
+ if [ -d "/tmp/moko-platform" ] && [ -f "/tmp/moko-platform/composer.json" ]; then
+ cd /tmp/moko-platform && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
+ fi
+ echo "MOKO_CLI=/tmp/moko-platform/cli" >> "$GITHUB_ENV"
+
+ - name: Detect platform
+ id: platform
+ run: php ${MOKO_CLI}/manifest_read.php --path . --github-output
+
+ - name: Resolve stability and bump version
+ id: meta
+ run: |
+ BRANCH="${{ github.ref_name }}"
+
+ # Configure git for bot pushes
+ git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
+ git config --local user.name "gitea-actions[bot]"
+ git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
+
+ # Auto-bump patch version
+ php ${MOKO_CLI}/version_bump.php --path . 2>/dev/null || true
+
+ VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "0.0.0")
+
+ # Determine stability from branch or manual input
+ if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
+ STABILITY="${{ inputs.stability }}"
+ elif [[ "$BRANCH" == rc/* ]]; then
+ STABILITY="rc"
+ elif [[ "$BRANCH" == beta/* ]]; then
+ STABILITY="beta"
+ elif [[ "$BRANCH" == alpha/* ]]; then
+ STABILITY="alpha"
+ else
+ STABILITY="development"
+ fi
+
+ # Version suffix per stability stream
+ case "$STABILITY" in
+ development) SUFFIX="-dev"; TAG="development" ;;
+ alpha) SUFFIX="-alpha"; TAG="alpha" ;;
+ beta) SUFFIX="-beta"; TAG="beta" ;;
+ rc) SUFFIX="-rc"; TAG="release-candidate" ;;
+ *) SUFFIX=""; TAG="stable" ;;
+ esac
+
+ # Propagate version with stability suffix to all manifest files
+ php ${MOKO_CLI}/version_set_platform.php \
+ --path . --version "$VERSION" --branch "$BRANCH" --stability "$STABILITY" 2>/dev/null || true
+ php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
+
+ # Re-read version (now includes suffix from version_set_platform)
+ if [ -n "$SUFFIX" ]; then
+ VERSION="${VERSION}${SUFFIX}"
+ fi
+
+ echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
+ echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
+ echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
+ echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
+ echo "display_version=${VERSION}" >> "$GITHUB_OUTPUT"
+
+ # Commit version bump if changed
+ git add -A
+ git diff --cached --quiet || {
+ git commit -m "chore(version): auto-bump ${VERSION} [skip ci]" \
+ --author="gitea-actions[bot] "
+ git push
+ }
+
+ - name: Create release and upload package
+ id: package
+ run: |
+ VERSION="${{ steps.meta.outputs.version }}"
+ TAG="${{ steps.meta.outputs.tag }}"
+ API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
+
+ # Create or update Gitea release
+ php ${MOKO_CLI}/release_create.php \
+ --path . --version "$VERSION" --tag "$TAG" \
+ --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
+ --repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease
+
+ # Build package and upload
+ php ${MOKO_CLI}/release_package.php \
+ --path . --version "$VERSION" --tag "$TAG" \
+ --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
+ --repo "${GITEA_REPO}" --output /tmp || true
+
+ - name: Update updates.xml
+ if: steps.platform.outputs.platform == 'joomla'
+ run: |
+ VERSION="${{ steps.meta.outputs.version }}"
+ STABILITY="${{ steps.meta.outputs.stability }}"
+ SHA256="${{ steps.package.outputs.sha256_zip }}"
+
+ if [ ! -f "updates.xml" ]; then
+ echo "No updates.xml — skipping"
+ exit 0
+ fi
+
+ SHA_FLAG=""
+ [ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}"
+
+ php ${MOKO_CLI}/updates_xml_build.php \
+ --path . --version "${VERSION}" --stability "${STABILITY}" \
+ --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
+ ${SHA_FLAG}
+
+ # Commit and push updates.xml
+ git add updates.xml
+ git diff --cached --quiet || {
+ git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]"
+ git push
+ }
+
+ - name: Sync updates.xml to main
+ if: github.ref_name != 'main' && steps.platform.outputs.platform == 'joomla'
+ run: |
+ API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
+ GITEA_TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
+
+ FILE_SHA=$(curl -sf -H "Authorization: token ${GITEA_TOKEN}" \
+ "${API_BASE}/contents/updates.xml?ref=main" | python3 -c "import sys,json; print(json.load(sys.stdin).get('sha',''))" 2>/dev/null || true)
+
+ if [ -n "$FILE_SHA" ] && [ -f "updates.xml" ]; then
+ python3 -c "
+ import base64, json, urllib.request, sys
+ with open('updates.xml', 'rb') as f:
+ content = base64.b64encode(f.read()).decode()
+ payload = json.dumps({
+ 'content': content,
+ 'sha': '${FILE_SHA}',
+ 'message': 'chore: sync updates.xml from ${{ steps.meta.outputs.stability }} [skip ci]',
+ 'branch': 'main'
+ }).encode()
+ req = urllib.request.Request(
+ '${API_BASE}/contents/updates.xml',
+ data=payload, method='PUT',
+ headers={
+ 'Authorization': 'token ${GITEA_TOKEN}',
+ 'Content-Type': 'application/json'
+ })
+ try:
+ urllib.request.urlopen(req)
+ print('updates.xml synced to main')
+ except Exception as e:
+ print(f'WARNING: sync to main failed: {e}', file=sys.stderr)
+ "
+ fi
+
+ - name: SFTP deploy to dev server
+ if: contains(github.ref, 'dev/') || github.ref == 'refs/heads/dev'
+ env:
+ DEV_HOST: ${{ vars.DEV_FTP_HOST }}
+ DEV_PATH: ${{ vars.DEV_FTP_PATH }}
+ DEV_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }}
+ DEV_USER: ${{ vars.DEV_FTP_USERNAME }}
+ DEV_PORT: ${{ vars.DEV_FTP_PORT }}
+ DEV_KEY: ${{ secrets.DEV_FTP_KEY }}
+ DEV_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
+ run: |
+ # Permission check: admin or maintain role required
+ ACTOR="${{ github.actor }}"
+ API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
+
+ PERMISSION=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
+ "${API_BASE}/collaborators/${ACTOR}/permission" 2>/dev/null | \
+ python3 -c "import sys,json; print(json.load(sys.stdin).get('permission','read'))" 2>/dev/null || echo "read")
+ case "$PERMISSION" in
+ admin|maintain|write) ;;
+ *)
+ echo "Deploy denied: ${ACTOR} has '${PERMISSION}' — requires admin, maintain, or write"
+ exit 0
+ ;;
+ esac
+
+ [ -z "$DEV_HOST" ] || [ -z "$DEV_PATH" ] && { echo "DEV FTP not configured — skipping SFTP"; exit 0; }
+
+ SOURCE_DIR="src"
+ [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
+ [ ! -d "$SOURCE_DIR" ] && exit 0
+
+ PORT="${DEV_PORT:-22}"
+ REMOTE="${DEV_PATH%/}"
+ [ -n "$DEV_SUFFIX" ] && REMOTE="${REMOTE}/${DEV_SUFFIX#/}"
+
+ printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \
+ "$DEV_HOST" "$PORT" "$DEV_USER" "$REMOTE" > /tmp/sftp-config.json
+ if [ -n "$DEV_KEY" ]; then
+ echo "$DEV_KEY" > /tmp/deploy_key && chmod 600 /tmp/deploy_key
+ printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json
+ else
+ printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json
+ fi
+
+ PLATFORM=$(php ${MOKO_CLI}/platform_detect.php --path . 2>/dev/null || true)
+ if [ "$PLATFORM" = "waas-component" ] && [ -f "${MOKO_CLI}/../deploy/deploy-joomla.php" ]; then
+ php ${MOKO_CLI}/../deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
+ elif [ -f "${MOKO_CLI}/../deploy/deploy-sftp.php" ]; then
+ php ${MOKO_CLI}/../deploy/deploy-sftp.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
+ fi
+ rm -f /tmp/deploy_key /tmp/sftp-config.json
+ echo "SFTP deploy to dev complete" >> $GITHUB_STEP_SUMMARY
+
+ - name: Summary
+ if: always()
+ run: |
+ VERSION="${{ steps.meta.outputs.version }}"
+ STABILITY="${{ steps.meta.outputs.stability }}"
+ DISPLAY="${{ steps.meta.outputs.display_version }}"
+ echo "## Update Server" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
+ echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
+ echo "| Stability | \`${STABILITY}\` |" >> $GITHUB_STEP_SUMMARY
+ echo "| Version | \`${DISPLAY}\` |" >> $GITHUB_STEP_SUMMARY
diff --git a/src/language/en-GB/tpl_mokoonyx.sys.ini b/src/language/en-GB/tpl_mokoonyx.sys.ini
index 3038e6e..3657ef8 100644
--- a/src/language/en-GB/tpl_mokoonyx.sys.ini
+++ b/src/language/en-GB/tpl_mokoonyx.sys.ini
@@ -4,7 +4,7 @@
;
; SPDX-License-Identifier: GPL-3.0-or-later
;
-TPL_MOKOONYX="MokoOnyx"
+TPL_MOKOONYX="Template - MokoOnyx"
TPL_MOKOONYX_GOOGLE_FIELDSET_LABEL="Google"
TPL_MOKOONYX_DRAWERS_FIELDSET_LABEL="Drawers"
TPL_MOKOONYX_MOD_MENU_LAYOUT_COLLAPSE_METISMENU="Collapsible Dropdown"
diff --git a/src/language/en-US/tpl_mokoonyx.sys.ini b/src/language/en-US/tpl_mokoonyx.sys.ini
index d9714af..505a048 100644
--- a/src/language/en-US/tpl_mokoonyx.sys.ini
+++ b/src/language/en-US/tpl_mokoonyx.sys.ini
@@ -4,7 +4,7 @@
;
; SPDX-License-Identifier: GPL-3.0-or-later
;
-TPL_MOKOONYX="MokoOnyx"
+TPL_MOKOONYX="Template - MokoOnyx"
TPL_MOKOONYX_GOOGLE_FIELDSET_LABEL="Google"
TPL_MOKOONYX_DRAWERS_FIELDSET_LABEL="Drawers"
TPL_MOKOONYX_MOD_MENU_LAYOUT_COLLAPSE_METISMENU="Collapsible Dropdown"
diff --git a/src/templateDetails.xml b/src/templateDetails.xml
index ba69ce9..3e2beab 100644
--- a/src/templateDetails.xml
+++ b/src/templateDetails.xml
@@ -35,8 +35,8 @@
https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/updates.xml
- MokoOnyx
- 02.07.02-dev
+ Template - MokoOnyx
+ 02.07.03-dev
script.php
2026-05-16
Jonathan Miller || Moko Consulting
diff --git a/updates.xml b/updates.xml
index 72c543e..851e37e 100644
--- a/updates.xml
+++ b/updates.xml
@@ -1,27 +1,10 @@
-
- MokoOnyx
- MokoOnyx development build.
- mokoonyx
- template
- site
- 02.07.02
- 2026-05-28
- https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/development
-
- https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/development/mokoonyx-02.07.02-dev.zip
-
- development
- Moko Consulting
- https://mokoconsulting.tech
-
-
MokoOnyx
MokoOnyx update
@@ -35,7 +18,7 @@
https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.07.00.zip
d2046ccbec46e85f378448e266a9f33b79bebd5730919c03efabeadb871e83b8
-
+
Moko Consulting
https://mokoconsulting.tech
@@ -52,7 +35,7 @@
https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.07.00.zip
d2046ccbec46e85f378448e266a9f33b79bebd5730919c03efabeadb871e83b8
-
+
Moko Consulting
https://mokoconsulting.tech
@@ -69,7 +52,7 @@
https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.07.00.zip
d2046ccbec46e85f378448e266a9f33b79bebd5730919c03efabeadb871e83b8
-
+
Moko Consulting
https://mokoconsulting.tech
@@ -86,8 +69,26 @@
https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.07.00.zip
d2046ccbec46e85f378448e266a9f33b79bebd5730919c03efabeadb871e83b8
-
+
Moko Consulting
https://mokoconsulting.tech
+
+ Template - Template - MokoOnyx
+ Template - Template - MokoOnyx development build.
+ mokoonyx
+ template
+ site
+ 02.07.03-dev-dev-dev
+ 2026-05-28
+ https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/development
+
+ https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/development/tpl_mokoonyx-02.07.03-dev-dev-dev.zip
+
+ 1ff8d11d9515e18d9afe56d525e04b9df28f45a645d70a7cda2f33d0957ddc53
+ dev
+ Moko Consulting
+ https://mokoconsulting.tech
+
+