diff --git a/.mokogitea/workflows/auto-bump.yml b/.mokogitea/workflows/auto-bump.yml index 9de5276..dd6e867 100644 --- a/.mokogitea/workflows/auto-bump.yml +++ b/.mokogitea/workflows/auto-bump.yml @@ -4,8 +4,8 @@ # # FILE INFORMATION # DEFGROUP: Gitea.Workflow -# INGROUP: moko-platform.Release -# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform +# INGROUP: mokoplatform.Release +# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform # PATH: /.mokogitea/workflows/auto-bump.yml # VERSION: 09.23.00 # BRIEF: Auto patch-bump version on every push to dev (skips merge commits) @@ -43,21 +43,21 @@ jobs: token: ${{ secrets.MOKOGITEA_TOKEN }} fetch-depth: 1 - - name: Setup moko-platform tools + - name: Setup mokoplatform tools run: | - if [ -f "/opt/moko-platform/cli/version_bump.php" ] && [ -f "/opt/moko-platform/vendor/autoload.php" ]; then - echo "Using pre-installed /opt/moko-platform" - echo "MOKO_CLI=/opt/moko-platform/cli" >> "$GITHUB_ENV" + if [ -f "/opt/mokoplatform/cli/version_bump.php" ] && [ -f "/opt/mokoplatform/vendor/autoload.php" ]; then + echo "Using pre-installed /opt/mokoplatform" + echo "MOKO_CLI=/opt/mokoplatform/cli" >> "$GITHUB_ENV" else if ! command -v composer &> /dev/null; then sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 fi - rm -rf /tmp/moko-platform-api + rm -rf /tmp/mokoplatform-api 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" + "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/mokoplatform.git" \ + /tmp/mokoplatform-api + cd /tmp/mokoplatform-api && composer install --no-dev --no-interaction --quiet + echo "MOKO_CLI=/tmp/mokoplatform-api/cli" >> "$GITHUB_ENV" fi - name: Bump version diff --git a/.mokogitea/workflows/issue-branch.yml b/.mokogitea/workflows/issue-branch.yml index e849522..3819480 100644 --- a/.mokogitea/workflows/issue-branch.yml +++ b/.mokogitea/workflows/issue-branch.yml @@ -5,7 +5,7 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Automation -# VERSION: 09.25.02 +# VERSION: 09.25.04 # BRIEF: Auto-create feature branch when an issue is opened name: "Universal: Issue Branch" diff --git a/.mokogitea/workflows/pre-release.yml b/.mokogitea/workflows/pre-release.yml index 9615a4e..24c47e0 100644 --- a/.mokogitea/workflows/pre-release.yml +++ b/.mokogitea/workflows/pre-release.yml @@ -1,243 +1,252 @@ -# Copyright (C) 2026 Moko Consulting -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: moko-platform.Release -# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform -# PATH: /templates/workflows/universal/pre-release.yml.template -# VERSION: 05.01.00 -# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch - -name: "Universal: Pre-Release" - -on: - pull_request: - types: [closed] - branches: - - dev - pull_request_target: - types: [synchronize, opened, reopened] - branches: - - main - workflow_dispatch: - inputs: - stability: - description: 'Pre-release channel' - required: true - type: choice - options: - - development - - alpha - - beta - - release-candidate - -permissions: - contents: write - -env: - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} - GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} - -jobs: - build: - name: "Build Pre-Release (${{ inputs.stability || 'development' }})" - runs-on: release - if: >- - github.event_name == 'workflow_dispatch' || - (github.event_name == 'pull_request' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev') || - (github.event_name == 'pull_request_target' && github.event.pull_request.base.ref == 'main') - - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 0 - token: ${{ secrets.MOKOGITEA_TOKEN }} - ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || '' }} - - - name: Setup moko-platform tools - env: - MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting - run: | - # Use pre-installed /opt/moko-platform if available (updated by cron every 6h) - if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/cli/manifest_element.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then - echo Using pre-installed /opt/moko-platform - echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV - else - echo Falling back to fresh clone - if ! command -v composer > /dev/null 2>&1; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1 - fi - rm -rf /tmp/moko-platform-api - CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git - git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api - cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet - echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV - fi - - - name: Detect platform - id: platform - run: | - php ${MOKO_CLI}/manifest_read.php --path . --github-output - - - name: Resolve metadata and bump version - id: meta - run: | - # Auto-detect stability: RC for PRs targeting main, else use input or default to development - if [ "${{ github.event_name }}" = "pull_request_target" ] && [ "${{ github.event.pull_request.base.ref }}" = "main" ]; then - STABILITY="release-candidate" - else - STABILITY="${{ inputs.stability || 'development' }}" - fi - - case "$STABILITY" in - development) SUFFIX="-dev"; TAG="development" ;; - alpha) SUFFIX="-alpha"; TAG="alpha" ;; - beta) SUFFIX="-beta"; TAG="beta" ;; - release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;; - esac - - # Bump version via CLI: patch for dev/alpha/beta, minor for RC - case "$STABILITY" in - release-candidate) BUMP="minor" ;; - *) BUMP="patch" ;; - esac - - php ${MOKO_CLI}/version_bump.php --path . $([ "$BUMP" = "minor" ] && echo "--minor") 2>/dev/null || true - - # Set stability suffix and verify consistency - VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01") - VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//') - - php ${MOKO_CLI}/version_set_platform.php \ - --path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true - php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true - - # Ensure licensing tags (updateservers, dlid) if enabled in manifest.xml - php ${MOKO_CLI}/manifest_licensing.php --path . --fix 2>/dev/null || true - - # Append suffix for output - if [ -n "$SUFFIX" ]; then - VERSION="${VERSION}${SUFFIX}" - fi - - # Commit version bump - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" - git add -A - git diff --cached --quiet || { - git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]" - git push origin HEAD 2>&1 - } - - # Auto-detect element via manifest_element.php - php ${MOKO_CLI}/manifest_element.php \ - --path . --version "$VERSION" --stability "$STABILITY" \ - --repo "${GITEA_REPO}" --github-output - - # Read back element outputs - EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) - ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) - [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') - [ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip" - - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" - echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT" - echo "tag=${TAG}" >> "$GITHUB_OUTPUT" - echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT" - echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT" - - echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ===" - - - name: Create release - id: release - run: | - TAG="${{ steps.meta.outputs.tag }}" - VERSION="${{ steps.meta.outputs.version }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php ${MOKO_CLI}/release_create.php \ - --path . --version "$VERSION" --tag "$TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --branch dev --prerelease - - - name: Update release notes from CHANGELOG.md - run: | - TAG="${{ steps.meta.outputs.tag }}" - VERSION="${{ steps.meta.outputs.version }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - - # Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading) - if [ -f "CHANGELOG.md" ]; then - NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md) - [ -z "$NOTES" ] && NOTES="Release ${VERSION}" - else - NOTES="Release ${VERSION}" - fi - - # Update release body via API - RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ - "${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) - - if [ -n "$RELEASE_ID" ]; then - python3 -c " - import json, urllib.request - body = open('/dev/stdin').read() - payload = json.dumps({'body': body}).encode() - req = urllib.request.Request( - '${API_BASE}/releases/${RELEASE_ID}', - data=payload, method='PATCH', - headers={ - 'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}', - 'Content-Type': 'application/json' - }) - urllib.request.urlopen(req) - " <<< "$NOTES" - echo "Release notes updated from CHANGELOG.md" - fi - - - name: Build package and upload - id: package - run: | - VERSION="${{ steps.meta.outputs.version }}" - TAG="${{ steps.meta.outputs.tag }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php ${MOKO_CLI}/release_package.php \ - --path . --version "$VERSION" --tag "$TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --output /tmp || true - - # updates.xml is generated dynamically by MokoGitea license server - # No need to build, commit, or sync updates.xml from workflows - - - name: "Delete lesser pre-release channels (cascade)" - continue-on-error: true - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - - php ${MOKO_CLI}/release_cascade.php \ - --stability "${{ steps.meta.outputs.stability }}" \ - --token "${TOKEN}" \ - --api-base "${API_BASE}" - - - name: Summary - if: always() - run: | - VERSION="${{ steps.meta.outputs.version }}" - STABILITY="${{ steps.meta.outputs.stability }}" - ZIP_NAME="${{ steps.meta.outputs.zip_name }}" - SHA256="${{ steps.package.outputs.sha256_zip }}" - echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY - echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY - echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY - echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY - echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY +# 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: Auto pre-release on push to dev/alpha/beta/rc branches + +name: "Universal: Pre-Release" + +on: + push: + branches: + - dev + - 'fix/**' + - 'patch/**' + - 'hotfix/**' + - 'bugfix/**' + - 'chore/**' + - alpha + - beta + - rc + 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 || github.ref_name }})" + runs-on: release + if: >- + github.event_name == 'workflow_dispatch' || + github.event_name == 'push' + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + token: ${{ secrets.MOKOGITEA_TOKEN }} + ref: ${{ github.ref_name }} + + - name: Setup moko-platform tools + env: + MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} + MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting + run: | + # Use pre-installed /opt/moko-platform if available (updated by cron every 6h) + if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/cli/manifest_element.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then + echo Using pre-installed /opt/moko-platform + echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV + else + echo Falling back to fresh clone + if ! command -v composer > /dev/null 2>&1; then + sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1 + fi + rm -rf /tmp/moko-platform-api + CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git + git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api + cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet + echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV + fi + + - name: Detect platform + id: platform + run: | + # Auto-detect and update platform if not set in manifest + php ${MOKO_CLI}/platform_detect.php --path . --github-output 2>/dev/null || true + php ${MOKO_CLI}/manifest_read.php --path . --github-output + + - name: Resolve metadata and bump version + id: meta + run: | + # Auto-detect stability from branch name on push, or use input on dispatch + if [ "${{ github.event_name }}" = "push" ]; then + case "${{ github.ref_name }}" in + rc) STABILITY="release-candidate" ;; + alpha) STABILITY="alpha" ;; + beta) STABILITY="beta" ;; + *) STABILITY="development" ;; + esac + else + STABILITY="${{ inputs.stability || 'development' }}" + fi + + case "$STABILITY" in + development) SUFFIX="-dev"; TAG="development" ;; + alpha) SUFFIX="-alpha"; TAG="alpha" ;; + beta) SUFFIX="-beta"; TAG="beta" ;; + release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;; + esac + + # Bump version via CLI: patch for dev/alpha/beta, minor for RC + case "$STABILITY" in + release-candidate) BUMP="minor" ;; + *) BUMP="patch" ;; + esac + + php ${MOKO_CLI}/version_bump.php --path . $([ "$BUMP" = "minor" ] && echo "--minor") 2>/dev/null || true + + # Set stability suffix and verify consistency + VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01") + VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//') + + php ${MOKO_CLI}/version_set_platform.php \ + --path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true + php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true + + # Ensure licensing tags (updateservers, dlid) if enabled in manifest.xml + php ${MOKO_CLI}/manifest_licensing.php --path . --fix 2>/dev/null || true + + # Append suffix for output + if [ -n "$SUFFIX" ]; then + VERSION="${VERSION}${SUFFIX}" + fi + + # Commit version bump + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" + git config --local user.name "gitea-actions[bot]" + git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" + git add -A + git diff --cached --quiet || { + git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]" + git push origin HEAD 2>&1 + } + + # Auto-detect element via manifest_element.php + php ${MOKO_CLI}/manifest_element.php \ + --path . --version "$VERSION" --stability "$STABILITY" \ + --repo "${GITEA_REPO}" --github-output + + # Read back element outputs + EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) + ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) + [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') + [ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip" + + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" + echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT" + echo "tag=${TAG}" >> "$GITHUB_OUTPUT" + echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT" + echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT" + + echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ===" + + - name: Create release + id: release + run: | + TAG="${{ steps.meta.outputs.tag }}" + VERSION="${{ steps.meta.outputs.version }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php ${MOKO_CLI}/release_create.php \ + --path . --version "$VERSION" --tag "$TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease + + - name: Update release notes from CHANGELOG.md + run: | + TAG="${{ steps.meta.outputs.tag }}" + VERSION="${{ steps.meta.outputs.version }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + + # Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading) + if [ -f "CHANGELOG.md" ]; then + NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md) + [ -z "$NOTES" ] && NOTES="Release ${VERSION}" + else + NOTES="Release ${VERSION}" + fi + + # Update release body via API + RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ + "${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) + + if [ -n "$RELEASE_ID" ]; then + python3 -c " + import json, urllib.request + body = open('/dev/stdin').read() + payload = json.dumps({'body': body}).encode() + req = urllib.request.Request( + '${API_BASE}/releases/${RELEASE_ID}', + data=payload, method='PATCH', + headers={ + 'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}', + 'Content-Type': 'application/json' + }) + urllib.request.urlopen(req) + " <<< "$NOTES" + echo "Release notes updated from CHANGELOG.md" + fi + + - name: Build package and upload + id: package + run: | + VERSION="${{ steps.meta.outputs.version }}" + TAG="${{ steps.meta.outputs.tag }}" + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + php ${MOKO_CLI}/release_package.php \ + --path . --version "$VERSION" --tag "$TAG" \ + --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ + --repo "${GITEA_REPO}" --output /tmp || true + + # updates.xml is generated dynamically by MokoGitea license server + # No need to build, commit, or sync updates.xml from workflows + + - name: "Delete lesser pre-release channels (cascade)" + continue-on-error: true + run: | + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" + + php ${MOKO_CLI}/release_cascade.php \ + --stability "${{ steps.meta.outputs.stability }}" \ + --token "${TOKEN}" \ + --api-base "${API_BASE}" + + - name: Summary + if: always() + run: | + VERSION="${{ steps.meta.outputs.version }}" + STABILITY="${{ steps.meta.outputs.stability }}" + ZIP_NAME="${{ steps.meta.outputs.zip_name }}" + SHA256="${{ steps.package.outputs.sha256_zip }}" + echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY + echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY + echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY + echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY + echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY diff --git a/README.md b/README.md index c6bf850..49fdc19 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ DEFGROUP: MokoPlatform.Root INGROUP: MokoPlatform REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform PATH: /README.md -VERSION: 09.25.02 +VERSION: 09.25.04 BRIEF: Project overview and documentation --> diff --git a/cli/branch_rename.php b/cli/branch_rename.php index 61b7967..89190e4 100644 --- a/cli/branch_rename.php +++ b/cli/branch_rename.php @@ -10,7 +10,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/branch_rename.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Rename a git branch via Gitea API (create new, update PR, delete old) */ diff --git a/cli/bulk_workflow_push.php b/cli/bulk_workflow_push.php index cc56f77..5d9c8d1 100644 --- a/cli/bulk_workflow_push.php +++ b/cli/bulk_workflow_push.php @@ -12,7 +12,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/bulk_workflow_push.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Push a workflow file to all governed repos via the Gitea Contents API */ diff --git a/cli/bulk_workflow_trigger.php b/cli/bulk_workflow_trigger.php index 3f4df33..bf5e08d 100644 --- a/cli/bulk_workflow_trigger.php +++ b/cli/bulk_workflow_trigger.php @@ -12,7 +12,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/bulk_workflow_trigger.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Trigger a workflow across multiple repos at once */ diff --git a/cli/client_dashboard.php b/cli/client_dashboard.php index 3fb51e5..5c6e1fd 100644 --- a/cli/client_dashboard.php +++ b/cli/client_dashboard.php @@ -12,7 +12,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/client_dashboard.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Generate unified client dashboard HTML */ diff --git a/cli/client_inventory.php b/cli/client_inventory.php index b40f30c..4df49ca 100644 --- a/cli/client_inventory.php +++ b/cli/client_inventory.php @@ -12,7 +12,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/client_inventory.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Discover and list all client-waas repos with their server configuration status */ diff --git a/cli/client_provision.php b/cli/client_provision.php index fdfa4d1..ff960c7 100644 --- a/cli/client_provision.php +++ b/cli/client_provision.php @@ -12,7 +12,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/client_provision.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Provision a new client environment end-to-end */ diff --git a/cli/grafana_dashboard.php b/cli/grafana_dashboard.php index 596f776..9c9f0ab 100644 --- a/cli/grafana_dashboard.php +++ b/cli/grafana_dashboard.php @@ -12,7 +12,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/grafana_dashboard.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Manage Grafana dashboards via API */ diff --git a/cli/joomla_build.php b/cli/joomla_build.php index 66f4a5b..61a0df8 100644 --- a/cli/joomla_build.php +++ b/cli/joomla_build.php @@ -10,7 +10,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/joomla_build.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Build a Joomla extension ZIP from manifest — all types supported * NOTE: Called by pre-release and auto-release workflows. */ diff --git a/cli/joomla_metadata_validate.php b/cli/joomla_metadata_validate.php new file mode 100644 index 0000000..b7b07d8 --- /dev/null +++ b/cli/joomla_metadata_validate.php @@ -0,0 +1,472 @@ +#!/usr/bin/env php + + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * FILE INFORMATION + * DEFGROUP: mokoplatform.CLI + * INGROUP: mokoplatform + * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform + * PATH: /cli/joomla_metadata_validate.php + * VERSION: 09.25.04 + * BRIEF: Validate MokoGitea repo metadata against Joomla extension manifest XML + */ + +declare(strict_types=1); + +require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; + +use MokoEnterprise\CliFramework; + +class JoomlaMetadataValidateCli extends CliFramework +{ + /** Joomla element prefix map — must match MokoGitea's cleanJoomlaElement() */ + private const JOOMLA_PREFIX = [ + 'package' => 'pkg_', + 'component' => 'com_', + 'module' => 'mod_', + 'template' => 'tpl_', + 'library' => 'lib_', + 'file' => 'file_', + ]; + + protected function configure(): void + { + $this->setDescription('Validate MokoGitea repo metadata against Joomla extension manifest XML'); + $this->addArgument('--path', 'Repo root path (default: current directory)', '.'); + $this->addArgument('--token', 'Gitea API token (or GITEA_TOKEN env)', ''); + $this->addArgument('--org', 'Gitea org', 'MokoConsulting'); + $this->addArgument('--repo', 'Repo name (auto-detected from git if empty)', ''); + $this->addArgument('--api-base', 'Gitea API base URL', 'https://git.mokoconsulting.tech/api/v1'); + $this->addArgument('--ci', 'CI mode: exit 1 on any error', false); + $this->addArgument('--json', 'Output as JSON', false); + } + + protected function run(): int + { + $path = realpath($this->getArgument('--path')) ?: $this->getArgument('--path'); + $token = $this->getArgument('--token') ?: getenv('GITEA_TOKEN') ?: ''; + $org = $this->getArgument('--org'); + $repoName = $this->getArgument('--repo'); + $apiBase = rtrim($this->getArgument('--api-base'), '/'); + $ciMode = (bool) $this->getArgument('--ci'); + $jsonMode = (bool) $this->getArgument('--json'); + + if (!is_dir($path)) { + $this->log('ERROR', "Path does not exist: {$path}"); + return 1; + } + + if ($repoName === '') { + $repoName = $this->detectRepoName($path); + } + + // ── Step 1: Find the Joomla extension manifest XML ────────── + $joomlaXml = $this->findJoomlaManifest($path); + + if ($joomlaXml === null) { + $this->log('ERROR', 'No Joomla extension manifest XML found'); + return 1; + } + + $this->log('INFO', "Joomla manifest: {$joomlaXml['path']}"); + + // ── Step 2: Load MokoGitea metadata ───────────────────────── + $metadata = $this->loadMetadata($path, $org, $repoName, $token, $apiBase); + + if ($metadata === null) { + $this->log('ERROR', 'Could not load MokoGitea metadata'); + return 1; + } + + // ── Step 3: Compare ───────────────────────────────────────── + $results = $this->compare($metadata, $joomlaXml, $path); + + // ── Step 4: Output ────────────────────────────────────────── + if ($jsonMode) { + echo json_encode([ + 'repo' => $repoName, + 'results' => $results, + ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n"; + } else { + $this->printResults($repoName, $results); + } + + $errors = count(array_filter($results, fn($r) => $r['status'] === 'error')); + + return ($ciMode && $errors > 0) ? 1 : 0; + } + + // ================================================================= + // Find Joomla manifest XML + // ================================================================= + + private function findJoomlaManifest(string $root): ?array + { + // Search common locations for a Joomla extension manifest + $candidates = []; + + // Package manifest: source/pkg_*.xml + foreach (glob("{$root}/source/pkg_*.xml") as $file) { + $candidates[] = $file; + } + + // Component manifest: source/packages/com_*/[name].xml + foreach (glob("{$root}/source/packages/com_*/*.xml") as $file) { + $basename = basename($file); + // Skip access.xml, config.xml, etc. + if (in_array($basename, ['access.xml', 'config.xml'], true)) { + continue; + } + $candidates[] = $file; + } + + // Direct source/*.xml + foreach (glob("{$root}/source/*.xml") as $file) { + if (basename($file) !== 'pkg_mokosuitebackup.xml') { + // Already caught above + } + $candidates[] = $file; + } + + // src/ fallback + foreach (glob("{$root}/src/pkg_*.xml") as $file) { + $candidates[] = $file; + } + + // Find the first one that has + foreach (array_unique($candidates) as $file) { + $content = file_get_contents($file); + if ($content === false) { + continue; + } + + if (preg_match('/]*type=["\']([^"\']+)["\']/', $content, $typeMatch)) { + $xml = @simplexml_load_string($content); + if ($xml === false) { + continue; + } + + $type = strtolower($typeMatch[1]); + $relPath = str_replace($root . '/', '', $file); + $relPath = str_replace($root . '\\', '', $relPath); + + return [ + 'path' => $relPath, + 'type' => $type, + 'xml' => $xml, + ]; + } + } + + return null; + } + + // ================================================================= + // Load metadata (from API) + // ================================================================= + + private function loadMetadata(string $root, string $org, string $repoName, string $token, string $apiBase): ?array + { + if ($token !== '') { + $url = "{$apiBase}/repos/{$org}/{$repoName}/metadata"; + $ctx = stream_context_create([ + 'http' => [ + 'header' => "Authorization: token {$token}\r\nAccept: application/json\r\n", + 'timeout' => 10, + ], + ]); + + $body = @file_get_contents($url, false, $ctx); + + if ($body !== false) { + $data = json_decode($body, true); + if (is_array($data)) { + $data['source'] = 'api'; + return $data; + } + } + } + + return null; + } + + // ================================================================= + // Compare metadata against Joomla manifest + // ================================================================= + + private function compare(array $metadata, array $joomlaXml, string $root): array + { + $results = []; + $xml = $joomlaXml['xml']; + $type = $joomlaXml['type']; + + // 1. Extension type + $metaType = $this->normalizeExtensionType($metadata['extension_type'] ?? ''); + $results[] = [ + 'field' => 'extension_type', + 'metadata' => $metaType, + 'joomla' => $type, + 'status' => ($metaType === $type) ? 'ok' : 'error', + 'message' => ($metaType === $type) + ? "matches " + : "metadata has \"{$metaType}\" but Joomla manifest has \"{$type}\"", + ]; + + // 2. Element name + $metaName = strtolower($metadata['name'] ?? ''); + $metaElement = $this->deriveElement($metaType, $metaName); + $joomlaElement = $this->extractJoomlaElement($xml, $type); + + $elementMatch = ($metaElement === $joomlaElement); + $results[] = [ + 'field' => 'element', + 'metadata' => $metaElement, + 'joomla' => $joomlaElement, + 'status' => $elementMatch ? 'ok' : 'error', + 'message' => $elementMatch + ? "derived correctly" + : "metadata derives \"{$metaElement}\" but Joomla uses \"{$joomlaElement}\"", + ]; + + // 3. Version + $metaVersion = $metadata['version'] ?? ''; + $joomlaVersion = (string) ($xml->version ?? ''); + + if ($metaVersion !== '' && $joomlaVersion !== '') { + // Strip dev/rc suffixes for comparison (CI bumps these) + $metaBase = preg_replace('/-(dev|rc|alpha|beta)\d*$/', '', $metaVersion); + $joomlaBase = preg_replace('/-(dev|rc|alpha|beta)\d*$/', '', $joomlaVersion); + $versionMatch = ($metaBase === $joomlaBase); + + $results[] = [ + 'field' => 'version', + 'metadata' => $metaVersion, + 'joomla' => $joomlaVersion, + 'status' => $versionMatch ? 'ok' : 'warn', + 'message' => $versionMatch + ? 'matches (base version)' + : "metadata has \"{$metaVersion}\" but Joomla has \"{$joomlaVersion}\"", + ]; + } + + // 4. PHP minimum (from composer.json) + $composerPhp = $this->readComposerPhpRequirement($root); + $metaPhp = $metadata['php_minimum'] ?? ''; + + if ($composerPhp !== '' && $metaPhp !== '') { + $phpMatch = ($metaPhp === $composerPhp); + $results[] = [ + 'field' => 'php_minimum', + 'metadata' => $metaPhp, + 'joomla' => $composerPhp . ' (composer.json)', + 'status' => $phpMatch ? 'ok' : 'warn', + 'message' => $phpMatch + ? 'matches composer.json' + : "metadata has \"{$metaPhp}\" but composer.json requires \"{$composerPhp}\"", + ]; + } + + // 5. Description + $metaDesc = $metadata['description'] ?? ''; + $joomlaDesc = (string) ($xml->description ?? ''); + + // Joomla descriptions are often language keys, skip those + if ($metaDesc !== '' && $joomlaDesc !== '' && !str_starts_with($joomlaDesc, 'COM_') && !str_starts_with($joomlaDesc, 'PKG_')) { + $descMatch = ($metaDesc === $joomlaDesc); + $results[] = [ + 'field' => 'description', + 'metadata' => substr($metaDesc, 0, 60) . (strlen($metaDesc) > 60 ? '...' : ''), + 'joomla' => substr($joomlaDesc, 0, 60) . (strlen($joomlaDesc) > 60 ? '...' : ''), + 'status' => $descMatch ? 'ok' : 'info', + 'message' => $descMatch ? 'matches' : 'descriptions differ (informational)', + ]; + } + + return $results; + } + + // ================================================================= + // Helpers + // ================================================================= + + /** + * Normalize extension_type — map MokoGitea types to Joomla types. + */ + private function normalizeExtensionType(string $type): string + { + return match (strtolower($type)) { + 'joomla-extension' => 'package', // legacy mapping + default => strtolower($type), + }; + } + + /** + * Derive the Joomla element name from type + name. + * Replicates MokoGitea's cleanJoomlaElement() + prefix logic. + */ + private function deriveElement(string $type, string $name): string + { + // Clean: lowercase, strip non-alphanumeric except . _ - + $clean = strtolower($name); + $clean = preg_replace('/[^a-z0-9._-]/', '', $clean); + + $prefix = self::JOOMLA_PREFIX[$type] ?? ''; + + return $prefix . $clean; + } + + /** + * Extract the element name from a Joomla manifest XML. + * Follows the same logic as Joomla's InstallerAdapter::getElement(). + */ + private function extractJoomlaElement(\SimpleXMLElement $xml, string $type): string + { + switch ($type) { + case 'package': + $packagename = (string) ($xml->packagename ?? ''); + if ($packagename !== '') { + return 'pkg_' . strtolower(preg_replace('/[^a-zA-Z0-9._-]/', '', $packagename)); + } + break; + + case 'component': + $element = (string) ($xml->element ?? ''); + if ($element !== '') { + $element = strtolower($element); + return str_starts_with($element, 'com_') ? $element : 'com_' . $element; + } + $name = (string) ($xml->name ?? ''); + $name = strtolower(preg_replace('/[^a-zA-Z0-9._-]/', '', $name)); + return str_starts_with($name, 'com_') ? $name : 'com_' . $name; + + case 'module': + $element = (string) ($xml->element ?? ''); + if ($element !== '') { + return strtolower($element); + } + break; + + case 'plugin': + // Plugins derive element from the file attribute + if (isset($xml->files)) { + foreach ($xml->files->children() as $file) { + $plugin = (string) ($file->attributes()->plugin ?? ''); + if ($plugin !== '') { + return strtolower($plugin); + } + } + } + break; + + case 'library': + $libname = (string) ($xml->libraryname ?? ''); + if ($libname !== '') { + return strtolower($libname); + } + break; + } + + // Fallback: use tag + $name = (string) ($xml->name ?? ''); + return strtolower(preg_replace('/[^a-zA-Z0-9._-]/', '', $name)); + } + + /** + * Read PHP version requirement from composer.json. + */ + private function readComposerPhpRequirement(string $root): string + { + $composerFile = "{$root}/composer.json"; + + if (!is_file($composerFile)) { + return ''; + } + + $data = json_decode(file_get_contents($composerFile), true); + + if (!is_array($data)) { + return ''; + } + + $phpReq = $data['require']['php'] ?? ''; + + // Extract version number from constraint like ">=8.1" + if (preg_match('/(\d+\.\d+)/', $phpReq, $m)) { + return $m[1]; + } + + return ''; + } + + private function detectRepoName(string $root): string + { + $gitConfig = "{$root}/.git/config"; + + if (!file_exists($gitConfig)) { + return basename($root); + } + + $content = file_get_contents($gitConfig); + + if (preg_match('/url\s*=\s*.*\/([^\/\s]+?)(?:\.git)?\s*$/m', $content, $m)) { + return $m[1]; + } + + return basename($root); + } + + // ================================================================= + // Output + // ================================================================= + + private function printResults(string $repoName, array $results): void + { + $errors = count(array_filter($results, fn($r) => $r['status'] === 'error')); + $warns = count(array_filter($results, fn($r) => $r['status'] === 'warn')); + $oks = count(array_filter($results, fn($r) => $r['status'] === 'ok')); + + $this->log('INFO', "Validating {$repoName} Joomla metadata...\n"); + + foreach ($results as $r) { + $icon = match ($r['status']) { + 'ok' => "\xE2\x9C\x93", // ✓ + 'error' => "\xE2\x9C\x97", // ✗ + 'warn' => "\xE2\x9A\xA0", // ⚠ + default => "\xE2\x84\xB9", // ℹ + }; + + $line = sprintf( + " %s %-16s %s", + $icon, + $r['field'], + $r['message'] + ); + + $this->log( + match ($r['status']) { + 'error' => 'ERROR', + 'warn' => 'WARN', + 'ok' => 'OK', + default => 'INFO', + }, + $line + ); + } + + echo "\n"; + + if ($errors > 0) { + $this->log('ERROR', "{$errors} error(s) — update delivery will fail"); + } elseif ($warns > 0) { + $this->log('WARN', "All critical checks passed, {$warns} warning(s)"); + } else { + $this->log('OK', "All {$oks} checks passed"); + } + } +} + +$app = new JoomlaMetadataValidateCli(); +exit($app->execute()); diff --git a/cli/manifest_licensing.php b/cli/manifest_licensing.php index 5c8096b..3058b66 100644 --- a/cli/manifest_licensing.php +++ b/cli/manifest_licensing.php @@ -10,7 +10,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/manifest_licensing.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Ensure licensing tags (updateservers, dlid) in Joomla extension manifests */ diff --git a/cli/manifest_read.php b/cli/manifest_read.php index 3572920..6bcfa13 100644 --- a/cli/manifest_read.php +++ b/cli/manifest_read.php @@ -10,7 +10,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/manifest_read.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Parse .manifest.xml and output requested field(s) for CI consumption */ diff --git a/cli/platform_detect.php b/cli/platform_detect.php index b07c6cb..0b19eff 100644 --- a/cli/platform_detect.php +++ b/cli/platform_detect.php @@ -10,7 +10,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/platform_detect.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Auto-detect repository platform type and optionally update manifest */ diff --git a/cli/release_cascade.php b/cli/release_cascade.php index 432947e..a760acd 100644 --- a/cli/release_cascade.php +++ b/cli/release_cascade.php @@ -10,7 +10,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/release_cascade.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: DEPRECATED — cascade behavior removed. Each release stream is independent. */ diff --git a/cli/release_publish.php b/cli/release_publish.php index 7b12819..cb22975 100644 --- a/cli/release_publish.php +++ b/cli/release_publish.php @@ -10,7 +10,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/release_publish.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Publish a release and create copies for all lesser stability streams. */ diff --git a/cli/scaffold_client.php b/cli/scaffold_client.php index 0bd2998..7df9e2c 100644 --- a/cli/scaffold_client.php +++ b/cli/scaffold_client.php @@ -12,7 +12,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/scaffold_client.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Scaffold a new client-waas repo from Template-Client-WaaS with pre-configured settings */ diff --git a/cli/updates_xml_sync.php b/cli/updates_xml_sync.php index 193d9d4..31ee8d0 100644 --- a/cli/updates_xml_sync.php +++ b/cli/updates_xml_sync.php @@ -10,7 +10,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/updates_xml_sync.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Sync updates.xml to target branches via Gitea API * NOTE: Called by pre-release and auto-release workflows after updates.xml * is modified on the current branch. Pushes the file to other branches diff --git a/cli/version_auto_bump.php b/cli/version_auto_bump.php index 9333d53..c028360 100644 --- a/cli/version_auto_bump.php +++ b/cli/version_auto_bump.php @@ -10,7 +10,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/version_auto_bump.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Auto patch-bump, set stability suffix, and commit — single CLI replacing inline workflow bash */ diff --git a/cli/version_check.php b/cli/version_check.php index 35db0da..fe84c9d 100644 --- a/cli/version_check.php +++ b/cli/version_check.php @@ -10,7 +10,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/version_check.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Validate version consistency across README, manifests, and sub-packages */ diff --git a/cli/wiki_sync.php b/cli/wiki_sync.php index 4d156e0..34c5f6b 100644 --- a/cli/wiki_sync.php +++ b/cli/wiki_sync.php @@ -10,7 +10,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/wiki_sync.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Sync select wiki pages from moko-platform to all template repos */ diff --git a/cli/workflow_sync.php b/cli/workflow_sync.php index c02ddb4..96bec10 100644 --- a/cli/workflow_sync.php +++ b/cli/workflow_sync.php @@ -10,7 +10,7 @@ * INGROUP: moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /cli/workflow_sync.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Sync workflows from Generic → platform templates → live repos based on manifest.platform */ diff --git a/deploy/backup-before-deploy.php b/deploy/backup-before-deploy.php index 4c8fb07..d2cea7a 100644 --- a/deploy/backup-before-deploy.php +++ b/deploy/backup-before-deploy.php @@ -12,7 +12,7 @@ * INGROUP: MokoPlatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /deploy/backup-before-deploy.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Snapshot Joomla directories before deployment for rollback capability */ diff --git a/deploy/deploy-dolibarr.php b/deploy/deploy-dolibarr.php index 9e0f256..9972774 100644 --- a/deploy/deploy-dolibarr.php +++ b/deploy/deploy-dolibarr.php @@ -12,7 +12,7 @@ * INGROUP: MokoPlatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /deploy/deploy-dolibarr.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Deploy Dolibarr module files to a remote server via SFTP/rsync */ diff --git a/deploy/health-check.php b/deploy/health-check.php index 0fd771d..af50850 100644 --- a/deploy/health-check.php +++ b/deploy/health-check.php @@ -12,7 +12,7 @@ * INGROUP: MokoPlatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /deploy/health-check.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Post-deploy health check — verify a Joomla site is responding correctly */ diff --git a/deploy/rollback-joomla.php b/deploy/rollback-joomla.php index 6250b70..a5ff9bc 100644 --- a/deploy/rollback-joomla.php +++ b/deploy/rollback-joomla.php @@ -12,7 +12,7 @@ * INGROUP: MokoPlatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /deploy/rollback-joomla.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Rollback a Joomla deployment by restoring from a pre-deploy snapshot */ diff --git a/deploy/sync-joomla.php b/deploy/sync-joomla.php index 804acd7..027ec79 100644 --- a/deploy/sync-joomla.php +++ b/deploy/sync-joomla.php @@ -12,7 +12,7 @@ * INGROUP: MokoPlatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /deploy/sync-joomla.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Sync Joomla site directories between two servers via rsync over SSH */ diff --git a/tests/Unit/VersionBumpTest.php b/tests/Unit/VersionBumpTest.php index 40e55e2..250fa35 100644 --- a/tests/Unit/VersionBumpTest.php +++ b/tests/Unit/VersionBumpTest.php @@ -63,7 +63,7 @@ class VersionBumpTest extends TestCase { file_put_contents( "{$this->tmpDir}/README.md", - "\nSome content\n" + "\nSome content\n" ); $this->execute(); diff --git a/tests/Unit/VersionReadTest.php b/tests/Unit/VersionReadTest.php index f0d7d22..a7060c1 100644 --- a/tests/Unit/VersionReadTest.php +++ b/tests/Unit/VersionReadTest.php @@ -34,7 +34,7 @@ class VersionReadTest extends TestCase { file_put_contents( "{$this->tmpDir}/README.md", - "# Test\n\n" + "# Test\n\n" ); $this->assertSame('02.03.04', trim($this->runScript())); @@ -68,7 +68,7 @@ class VersionReadTest extends TestCase { file_put_contents( "{$this->tmpDir}/README.md", - "\n" + "\n" ); mkdir("{$this->tmpDir}/src", 0755, true); file_put_contents( diff --git a/validate/check_file_integrity.php b/validate/check_file_integrity.php index bf37403..f5a60a0 100644 --- a/validate/check_file_integrity.php +++ b/validate/check_file_integrity.php @@ -12,7 +12,7 @@ * INGROUP: MokoPlatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * PATH: /validate/check_file_integrity.php - * VERSION: 09.25.02 + * VERSION: 09.25.04 * BRIEF: Compare deployed files on a remote server against the local repository to detect drift */