Compare commits
110 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 886e36f4f4 | |||
| 7f1e99c818 | |||
| 8e564b1fca | |||
| dea991a3b2 | |||
| 54e61ded92 | |||
| e746afd835 | |||
| 31a049141f | |||
| 67e9cc6b38 | |||
| 5c8192dd0d | |||
| eb99bae991 | |||
| 6d56ec550a | |||
| 44c653e09b | |||
| 4718ebf32d | |||
| af193fc2bb | |||
| 4850928e6d | |||
| ff1ee76d71 | |||
| 99caa708b8 | |||
| fd65148f38 | |||
| e7b5f78deb | |||
| 62bf4af481 | |||
| f1345b0010 | |||
| 8496b66db2 | |||
| 1155b0fa17 | |||
| f0a8ad6c39 | |||
| 5313723c68 | |||
| a867a8fdde | |||
| d2b4b70f0e | |||
| 1c0fa55955 | |||
| 9eb7ce03e6 | |||
| 7933909ac4 | |||
| 64482e59cd | |||
| 4d6a12c378 | |||
| 467406f2e2 | |||
| 9df6bea4b7 | |||
| ef77ae83da | |||
| 1fd90366d6 | |||
| 814b6879da | |||
| 9d67a045f3 | |||
| f4f47ad43a | |||
| c096172c2c | |||
| 35dac96d7a | |||
| fc61577422 | |||
| edda6b7a51 | |||
| c9df811602 | |||
| ff21765973 | |||
| 81d2f1ceea | |||
| d772f10b6a | |||
| 91edc73336 | |||
| 7f1bcb23bf | |||
| bdb135bcb4 | |||
| 1781ee8c67 | |||
| 803464a584 | |||
| 345483c6f9 | |||
| 19bbe92780 | |||
| 3aa1b43e96 | |||
| 27d26f15ca | |||
| a8d1e8f276 | |||
| f143a959be | |||
| 5e889bbcff | |||
| c46373265d | |||
| 8044106f19 | |||
| 291f04eb81 | |||
| dca4ef89a9 | |||
| ffa9edd33f | |||
| 70fe78e064 | |||
| ceb3cfacf7 | |||
| 6ecaf9923d | |||
| e8e8c689e8 | |||
| d26ada7d18 | |||
| f6e7082f44 | |||
| 5c048ef5db | |||
| 10b597b248 | |||
| 7f272aabf9 | |||
| 44030cdc9c | |||
| e21e345389 | |||
| 43646e826d | |||
| 91542cf759 | |||
| c4a77e2da7 | |||
| 156cb1713f | |||
| 59f37f09cf | |||
| 1308497e39 | |||
| 481893e182 | |||
| 8606acf2fd | |||
| 71a9da3f72 | |||
| c42b65ed38 | |||
| 9949bf7fda | |||
| 44ca197c36 | |||
| 706c088da1 | |||
| 1ebba18c16 | |||
| 70c2aaae05 | |||
| 9085ccf474 | |||
| 9d22ba0b10 | |||
| f03a522bb9 | |||
| 344673ab8a | |||
| 43b8549402 | |||
| a4f55f6ba7 | |||
| 222a52580c | |||
| 1c6c8a8473 | |||
| 1964c86ee0 | |||
| cd7bdc03c8 | |||
| 9d8fd4eed1 | |||
| 1404b699ad | |||
| b1d72bc23e | |||
| 67721d0247 | |||
| 7687da58c3 | |||
| a4d9d6d129 | |||
| 495083f89f | |||
| f47554e46c | |||
| eddc9c2fd4 | |||
| b88e68ee10 |
@@ -1,66 +1,66 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: mokocli.Release
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# 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
|
||||
- rc
|
||||
- 'feature/**'
|
||||
- 'patch/**'
|
||||
|
||||
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 mokocli 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/mokocli/cli" ]; then
|
||||
echo "MOKO_CLI=/opt/mokocli/cli" >> "$GITHUB_ENV"
|
||||
else
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/mokocli.git" \
|
||||
/tmp/mokocli
|
||||
cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet
|
||||
echo "MOKO_CLI=/tmp/mokocli/cli" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Bump version
|
||||
run: |
|
||||
php ${MOKO_CLI}/version_auto_bump.php \
|
||||
--path . --branch "${GITHUB_REF_NAME}" \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||
--repo-url "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: mokocli.Release
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# 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
|
||||
- rc
|
||||
- 'feature/**'
|
||||
- 'patch/**'
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
MOKOGITEA_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 mokocli 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/mokocli/cli" ]; then
|
||||
echo "MOKO_CLI=/opt/mokocli/cli" >> "$GITHUB_ENV"
|
||||
else
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/mokocli.git" \
|
||||
/tmp/mokocli
|
||||
cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet
|
||||
echo "MOKO_CLI=/tmp/mokocli/cli" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Bump version
|
||||
run: |
|
||||
php ${MOKO_CLI}/version_auto_bump.php \
|
||||
--path . --branch "${GITHUB_REF_NAME}" \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||
--repo-url "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
|
||||
@@ -7,12 +7,12 @@
|
||||
# INGROUP: mokocli.Release
|
||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli
|
||||
# PATH: /templates/workflows/universal/auto-release.yml.template
|
||||
# VERSION: 05.00.00
|
||||
# VERSION: 05.01.00
|
||||
# BRIEF: Universal build & release � detects platform from manifest.xml
|
||||
#
|
||||
# +========================================================================+
|
||||
# +=======================================================================+
|
||||
# | UNIVERSAL BUILD & RELEASE PIPELINE |
|
||||
# +========================================================================+
|
||||
# +=======================================================================+
|
||||
# | |
|
||||
# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. |
|
||||
# | |
|
||||
@@ -21,15 +21,24 @@
|
||||
# | 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]
|
||||
types: [opened, synchronize, closed]
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- '.mokogitea/workflows/**'
|
||||
- '*.md'
|
||||
- 'wiki/**'
|
||||
- '.editorconfig'
|
||||
- '.gitignore'
|
||||
- '.gitattributes'
|
||||
- '.gitmessage'
|
||||
- 'LICENSE'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
action:
|
||||
@@ -43,7 +52,7 @@ on:
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
MOKOGITEA_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 }}
|
||||
|
||||
@@ -51,12 +60,13 @@ permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
# ── PR Opened → Rename branch to RC and build RC release ─────────────────────
|
||||
# ── PR Opened → Rename branch to RC and build RC release ─────────────────────────
|
||||
promote-rc:
|
||||
name: Promote to RC
|
||||
runs-on: release
|
||||
if: >-
|
||||
(github.event.action == 'opened' && github.event.pull_request.merged != true) ||
|
||||
(github.event.action == 'synchronize' && github.event.pull_request.merged != true) ||
|
||||
(github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc')
|
||||
|
||||
steps:
|
||||
@@ -65,6 +75,7 @@ jobs:
|
||||
with:
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
fetch-depth: 1
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup mokocli tools
|
||||
env:
|
||||
@@ -92,7 +103,7 @@ jobs:
|
||||
php ${MOKO_CLI}/branch_rename.php \
|
||||
--from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||
--api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \
|
||||
--api-base "${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \
|
||||
--pr "${{ github.event.pull_request.number }}"
|
||||
|
||||
- name: Checkout rc and configure git
|
||||
@@ -111,7 +122,7 @@ jobs:
|
||||
|
||||
- name: Update RC release notes from CHANGELOG.md
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
|
||||
# Extract [Unreleased] section from changelog
|
||||
@@ -149,7 +160,7 @@ jobs:
|
||||
echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Branch renamed to rc, minor bump, RC release built" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# ── Merged PR → Build & Release (or promote RC to stable) ────────────────────
|
||||
# ── Merged PR → Build & Release (or promote RC to stable) ─────────────────────────
|
||||
release:
|
||||
name: Build & Release Pipeline
|
||||
runs-on: release
|
||||
@@ -163,6 +174,7 @@ jobs:
|
||||
with:
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
fetch-depth: 0
|
||||
submodules: recursive
|
||||
|
||||
- name: Configure git for bot pushes
|
||||
run: |
|
||||
@@ -205,6 +217,12 @@ jobs:
|
||||
echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: "Detect platform"
|
||||
id: platform
|
||||
run: |
|
||||
php ${MOKO_CLI}/platform_detect.php --path . --github-output 2>/dev/null || true
|
||||
php ${MOKO_CLI}/manifest_read.php --path . --github-output 2>/dev/null || true
|
||||
|
||||
- name: "Determine version bump level"
|
||||
id: bump
|
||||
run: |
|
||||
@@ -228,9 +246,57 @@ jobs:
|
||||
--path . --stability stable ${BUMP_FLAG} --branch main \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
|
||||
- name: "Read published version"
|
||||
id: version
|
||||
run: |
|
||||
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "")
|
||||
VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
|
||||
[ -z "$VERSION" ] && VERSION="00.00.00" && echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
PLATFORM="${{ steps.platform.outputs.platform }}"
|
||||
if [[ "$PLATFORM" == joomla* ]]; then
|
||||
echo "tag=stable" >> "$GITHUB_OUTPUT"
|
||||
echo "release_tag=stable" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "release_tag=v${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
echo "branch=main" >> "$GITHUB_OUTPUT"
|
||||
echo "Published version: ${VERSION}"
|
||||
|
||||
- name: "Create semver tag for non-Joomla repos"
|
||||
id: semver
|
||||
if: |
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
!startsWith(steps.platform.outputs.platform, 'joomla')
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
SEMVER_TAG="v${VERSION}"
|
||||
|
||||
echo "Creating semver tag: ${SEMVER_TAG}"
|
||||
|
||||
# Create the git tag via API
|
||||
HTTP_CODE=$(curl -sf -o /dev/null -w "%{http_code}" \
|
||||
-X POST -H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API_BASE}/tags" \
|
||||
-d "{\"tag_name\":\"${SEMVER_TAG}\",\"target\":\"main\",\"message\":\"Release ${VERSION}\"}" 2>/dev/null || echo "000")
|
||||
|
||||
if [ "$HTTP_CODE" = "201" ] || [ "$HTTP_CODE" = "200" ]; then
|
||||
echo "Created semver tag: ${SEMVER_TAG}"
|
||||
elif [ "$HTTP_CODE" = "409" ]; then
|
||||
echo "Semver tag ${SEMVER_TAG} already exists (skipped)"
|
||||
else
|
||||
echo "::warning::Failed to create semver tag ${SEMVER_TAG} (HTTP ${HTTP_CODE})"
|
||||
fi
|
||||
|
||||
echo "semver_tag=${SEMVER_TAG}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Update release notes and promote changelog
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
|
||||
# Get the stable release info (version and ID)
|
||||
@@ -299,7 +365,7 @@ jobs:
|
||||
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}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
php ${MOKO_CLI}/release_mirror.php \
|
||||
--version "$VERSION" --tag "$RELEASE_TAG" \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||
@@ -328,7 +394,7 @@ jobs:
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
|
||||
# Delete rc branch (ephemeral — created by promote-rc)
|
||||
@@ -352,7 +418,7 @@ jobs:
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
API_BASE="${MOKOGITEA_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}"
|
||||
@@ -373,7 +439,7 @@ jobs:
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
API_BASE="${MOKOGITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
php ${MOKO_CLI}/version_reset_dev.php \
|
||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \
|
||||
--branch dev --path . 2>&1 || true
|
||||
@@ -399,5 +465,5 @@ jobs:
|
||||
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
|
||||
echo "| Release | [View](${MOKOGITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: mokocli.Universal
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# PATH: /.mokogitea/workflows/ci-issue-reporter.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Reusable workflow — creates/updates a Gitea issue when a CI gate fails.
|
||||
# Clones MokoCLI and runs cli/ci_issue_reporter.sh.
|
||||
|
||||
name: "Universal: CI Issue Reporter"
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
gate:
|
||||
description: "CI gate name (e.g. PR Validation, Repository Health)"
|
||||
required: true
|
||||
type: string
|
||||
details:
|
||||
description: "Human-readable failure description"
|
||||
required: true
|
||||
type: string
|
||||
severity:
|
||||
description: "error or warning"
|
||||
required: false
|
||||
type: string
|
||||
default: "error"
|
||||
workflow:
|
||||
description: "Workflow name for the issue title"
|
||||
required: false
|
||||
type: string
|
||||
default: ""
|
||||
secrets:
|
||||
MOKOGITEA_TOKEN:
|
||||
required: true
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
report:
|
||||
name: "Report: ${{ inputs.gate }}"
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Clone MokoCLI
|
||||
env:
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
MOKOGITEA_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
|
||||
git clone --depth 1 --filter=blob:none --sparse "${MOKOGITEA_URL}/MokoConsulting/MokoCLI.git" /tmp/mokocli
|
||||
cd /tmp/mokocli && git sparse-checkout set cli/ci_issue_reporter.sh
|
||||
|
||||
- name: Report CI failure
|
||||
env:
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
run: |
|
||||
chmod +x /tmp/mokocli/cli/ci_issue_reporter.sh
|
||||
/tmp/mokocli/cli/ci_issue_reporter.sh \
|
||||
--gate "${{ inputs.gate }}" \
|
||||
--details "${{ inputs.details }}" \
|
||||
--severity "${{ inputs.severity }}" \
|
||||
--workflow "${{ inputs.workflow }}"
|
||||
@@ -45,17 +45,17 @@ jobs:
|
||||
fi
|
||||
php -v && composer --version
|
||||
|
||||
- name: Setup moko-platform tools
|
||||
- name: Setup mokocli tools
|
||||
env:
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN || github.token }}
|
||||
MOKO_CLONE_HOST: ${{ secrets.GA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }}
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN || secrets.GA_TOKEN || github.token }}
|
||||
MOKO_CLONE_HOST: ${{ secrets.MOKOGITEA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }}
|
||||
run: |
|
||||
if [ -d "/tmp/moko-platform" ] || [ -d "/opt/moko-platform" ]; then
|
||||
echo "moko-platform already available on runner — skipping clone"
|
||||
if [ -d "/opt/mokocli" ] || [ -d "/tmp/mokocli" ]; then
|
||||
echo "mokocli already available on runner — skipping clone"
|
||||
else
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
||||
/tmp/moko-platform 2>/dev/null || echo "moko-platform clone skipped — continuing without it"
|
||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git" \
|
||||
/tmp/mokocli 2>/dev/null || echo "mokocli clone skipped — continuing without it"
|
||||
fi
|
||||
|
||||
- name: Install dependencies
|
||||
@@ -245,10 +245,413 @@ jobs:
|
||||
echo "All ${CHECKED} directories contain index.html." >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
- name: Check config.xml and access.xml for components
|
||||
run: |
|
||||
echo "### Component Config & ACL Check" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=0
|
||||
|
||||
# Find all component manifests (XML with type="component")
|
||||
COMP_MANIFESTS=$(find . -maxdepth 4 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*" -exec grep -l '<extension[^>]*type="component"' {} ; 2>/dev/null || true)
|
||||
|
||||
if [ -z "$COMP_MANIFESTS" ]; then
|
||||
echo "No component extensions found — skipping." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
for MANIFEST in $COMP_MANIFESTS; do
|
||||
COMP_DIR=$(dirname "$MANIFEST")
|
||||
COMP_NAME=$(basename "$COMP_DIR")
|
||||
echo "Component: `${COMP_NAME}` (manifest: `${MANIFEST}`)" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Check access.xml exists
|
||||
ACCESS_FILE=$(find "$COMP_DIR" -name "access.xml" -not -path "./.git/*" 2>/dev/null | head -1)
|
||||
if [ -z "$ACCESS_FILE" ]; then
|
||||
echo "- Missing `access.xml` — ACL permissions will not work." >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
if command -v php &> /dev/null; then
|
||||
if ! php -r "@simplexml_load_file('$ACCESS_FILE') ?: exit(1);" 2>/dev/null; then
|
||||
echo "- `access.xml` is not well-formed XML." >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
for ACTION in core.admin core.manage; do
|
||||
if ! grep -q "name=\"${ACTION}\"" "$ACCESS_FILE" 2>/dev/null; then
|
||||
echo "- `access.xml` missing required action: `${ACTION}`" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
done
|
||||
echo "- `access.xml`: valid" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check config.xml exists
|
||||
CONFIG_FILE=$(find "$COMP_DIR" -name "config.xml" -not -path "./.git/*" 2>/dev/null | head -1)
|
||||
if [ -z "$CONFIG_FILE" ]; then
|
||||
echo "- Missing `config.xml` — component Options page will be empty." >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
if command -v php &> /dev/null; then
|
||||
if ! php -r "@simplexml_load_file('$CONFIG_FILE') ?: exit(1);" 2>/dev/null; then
|
||||
echo "- `config.xml` is not well-formed XML." >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo "- `config.xml`: valid" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
if [ "${ERRORS}" -gt 0 ]; then
|
||||
echo "**${ERRORS} config/ACL issue(s) found.**" >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
else
|
||||
echo "**Component config & ACL check passed.**" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
- name: SQL schema validation
|
||||
run: |
|
||||
echo "### SQL Schema Validation" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=0
|
||||
|
||||
# Find SQL files in source/htdocs
|
||||
SQL_FILES=$(find . -name "*.sql" -path "*/sql/*" -not -path "./.git/*" -not -path "./vendor/*" 2>/dev/null)
|
||||
if [ -z "$SQL_FILES" ]; then
|
||||
echo "No SQL files found — skipping." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "Found $(echo "$SQL_FILES" | wc -l) SQL file(s)" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
for FILE in $SQL_FILES; do
|
||||
# Basic syntax check: balanced parentheses, no empty files
|
||||
SIZE=$(wc -c < "$FILE" | tr -d ' ')
|
||||
if [ "$SIZE" -eq 0 ]; then
|
||||
echo "- Empty SQL file: \`${FILE}\`" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# Check for common SQL errors
|
||||
if grep -qP '^\s*$' "$FILE" && [ "$SIZE" -lt 5 ]; then
|
||||
echo "- Whitespace-only SQL file: \`${FILE}\`" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
echo "- \`${FILE}\`: ${SIZE} bytes" >> $GITHUB_STEP_SUMMARY
|
||||
done
|
||||
|
||||
# Check update SQL files follow version numbering pattern
|
||||
UPDATE_DIR=$(find . -path "*/sql/updates/mysql" -type d -not -path "./.git/*" 2>/dev/null | head -1)
|
||||
if [ -n "$UPDATE_DIR" ]; then
|
||||
BAD_NAMES=0
|
||||
for UFILE in "$UPDATE_DIR"/*.sql; do
|
||||
[ ! -f "$UFILE" ] && continue
|
||||
BASENAME=$(basename "$UFILE" .sql)
|
||||
if ! echo "$BASENAME" | grep -qP '^\d+\.\d+\.\d+'; then
|
||||
echo "- Update file \`${UFILE}\` does not follow version naming (expected X.Y.Z.sql)" >> $GITHUB_STEP_SUMMARY
|
||||
BAD_NAMES=$((BAD_NAMES + 1))
|
||||
fi
|
||||
done
|
||||
if [ "$BAD_NAMES" -gt 0 ]; then
|
||||
ERRORS=$((ERRORS + BAD_NAMES))
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
if [ "${ERRORS}" -gt 0 ]; then
|
||||
echo "**${ERRORS} SQL issue(s) found.**" >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
else
|
||||
echo "**SQL schema validation passed.**" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
- name: Manifest file references check
|
||||
run: |
|
||||
echo "### Manifest File References" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=0
|
||||
|
||||
MANIFEST=""
|
||||
for XML_FILE in $(find . -maxdepth 2 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do
|
||||
if grep -q "<extension" "$XML_FILE" 2>/dev/null; then
|
||||
MANIFEST="$XML_FILE"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$MANIFEST" ]; then
|
||||
echo "No manifest found — skipping." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
MANIFEST_DIR=$(dirname "$MANIFEST")
|
||||
|
||||
# Check <filename> references
|
||||
FILENAMES=$(grep -oP '<filename[^>]*>\K[^<]+' "$MANIFEST" 2>/dev/null || true)
|
||||
for F in $FILENAMES; do
|
||||
if [ ! -f "${MANIFEST_DIR}/${F}" ] && [ ! -d "${MANIFEST_DIR}/${F}" ]; then
|
||||
echo "- Missing: \`${F}\` (referenced in manifest)" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
# Check <folder> references
|
||||
FOLDERS=$(grep -oP '<folder[^>]*>\K[^<]+' "$MANIFEST" 2>/dev/null || true)
|
||||
for F in $FOLDERS; do
|
||||
if [ ! -d "${MANIFEST_DIR}/${F}" ]; then
|
||||
echo "- Missing folder: \`${F}\` (referenced in manifest)" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
done
|
||||
|
||||
# Check <file> references in package manifests (ZIP files won't exist in source)
|
||||
EXT_TYPE=$(grep -oP '<extension[^>]*\btype="\K[^"]+' "$MANIFEST" | head -1)
|
||||
if [ "$EXT_TYPE" != "package" ]; then
|
||||
FILES=$(grep -oP '<file[^>]*>\K[^<]+' "$MANIFEST" 2>/dev/null || true)
|
||||
for F in $FILES; do
|
||||
if [ ! -f "${MANIFEST_DIR}/${F}" ]; then
|
||||
echo "- Missing file: \`${F}\` (referenced in manifest)" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
if [ "${ERRORS}" -gt 0 ]; then
|
||||
echo "**${ERRORS} missing file reference(s).**" >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
else
|
||||
echo "**Manifest file references check passed.**" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
- name: Form XML validation
|
||||
run: |
|
||||
echo "### Form XML Validation" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=0
|
||||
|
||||
FORM_FILES=$(find . -name "*.xml" -path "*/forms/*" -not -path "./.git/*" -not -path "./vendor/*" 2>/dev/null)
|
||||
if [ -z "$FORM_FILES" ]; then
|
||||
echo "No form XML files found — skipping." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "Found $(echo "$FORM_FILES" | wc -l) form file(s)" >> $GITHUB_STEP_SUMMARY
|
||||
for FILE in $FORM_FILES; do
|
||||
if command -v php &> /dev/null; then
|
||||
if ! php -r "@simplexml_load_file('$FILE') ?: exit(1);" 2>/dev/null; then
|
||||
echo "- \`${FILE}\`: malformed XML" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
# Check for valid Joomla form structure
|
||||
if ! grep -qE '<form|<field|<fieldset' "$FILE" 2>/dev/null; then
|
||||
echo "- \`${FILE}\`: no \`<form>\`, \`<field>\`, or \`<fieldset>\` elements found" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo "- \`${FILE}\`: valid" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
if [ "${ERRORS}" -gt 0 ]; then
|
||||
echo "**${ERRORS} form XML issue(s).**" >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
else
|
||||
echo "**Form XML validation passed.**" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
- name: Deprecated Joomla API check
|
||||
continue-on-error: true
|
||||
run: |
|
||||
echo "### Deprecated Joomla API Check" >> $GITHUB_STEP_SUMMARY
|
||||
WARNINGS=0
|
||||
|
||||
SRC_DIR=""
|
||||
for DIR in source/ src/ htdocs/; do
|
||||
[ -d "$DIR" ] && SRC_DIR="$DIR" && break
|
||||
done
|
||||
|
||||
if [ -z "$SRC_DIR" ]; then
|
||||
echo "No source directory found — skipping." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
# Joomla 3/4 deprecated patterns that break in Joomla 6
|
||||
PATTERNS=(
|
||||
'JFactory::'
|
||||
'JText::'
|
||||
'JHtml::'
|
||||
'JRoute::'
|
||||
'JUri::'
|
||||
'JLog::'
|
||||
'JTable::'
|
||||
'JInput'
|
||||
'CMSFactory::\$application'
|
||||
'JApplicationCms'
|
||||
)
|
||||
|
||||
for PATTERN in "${PATTERNS[@]}"; do
|
||||
HITS=$(grep -rnl "$PATTERN" "$SRC_DIR" --include="*.php" 2>/dev/null || true)
|
||||
if [ -n "$HITS" ]; then
|
||||
COUNT=$(echo "$HITS" | wc -l)
|
||||
echo "- \`${PATTERN}\` found in ${COUNT} file(s)" >> $GITHUB_STEP_SUMMARY
|
||||
WARNINGS=$((WARNINGS + COUNT))
|
||||
fi
|
||||
done
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
if [ "$WARNINGS" -gt 0 ]; then
|
||||
echo "**${WARNINGS} deprecated API usage(s) found.** These will break in Joomla 6." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "**No deprecated APIs found.**" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Template output escaping check
|
||||
continue-on-error: true
|
||||
run: |
|
||||
echo "### Template Output Escaping" >> $GITHUB_STEP_SUMMARY
|
||||
WARNINGS=0
|
||||
|
||||
TMPL_FILES=$(find . -name "*.php" -path "*/tmpl/*" -not -path "./.git/*" -not -path "./vendor/*" 2>/dev/null)
|
||||
if [ -z "$TMPL_FILES" ]; then
|
||||
echo "No template files found — skipping." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "Found $(echo "$TMPL_FILES" | wc -l) template file(s)" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
for FILE in $TMPL_FILES; do
|
||||
# Check for unescaped output: <?= $var ?> or echo $var without escape()
|
||||
UNESCAPED=$(grep -nP '<\?=\s*\$(?!this->escape)' "$FILE" 2>/dev/null || true)
|
||||
if [ -n "$UNESCAPED" ]; then
|
||||
HITS=$(echo "$UNESCAPED" | wc -l)
|
||||
echo "- \`${FILE}\`: ${HITS} unescaped \`<?= \$var ?>\` output(s) — use \`<?= \$this->escape(\$var) ?>\`" >> $GITHUB_STEP_SUMMARY
|
||||
WARNINGS=$((WARNINGS + HITS))
|
||||
fi
|
||||
|
||||
# Check for echo without escaping in template context
|
||||
RAW_ECHO=$(grep -nP '^\s*echo\s+\$(?!this->escape)' "$FILE" 2>/dev/null || true)
|
||||
if [ -n "$RAW_ECHO" ]; then
|
||||
HITS=$(echo "$RAW_ECHO" | wc -l)
|
||||
echo "- \`${FILE}\`: ${HITS} raw \`echo \$var\` — consider \`echo \$this->escape(\$var)\`" >> $GITHUB_STEP_SUMMARY
|
||||
WARNINGS=$((WARNINGS + HITS))
|
||||
fi
|
||||
done
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
if [ "$WARNINGS" -gt 0 ]; then
|
||||
echo "**${WARNINGS} potential XSS risk(s) in templates.** Review unescaped output." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "**All template output appears properly escaped.**" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Namespace consistency check
|
||||
run: |
|
||||
echo "### Namespace Consistency" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=0
|
||||
|
||||
# Find component/plugin manifests with <namespace> tags
|
||||
MANIFESTS=$(find . -maxdepth 4 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*" -exec grep -l '<namespace' {} \; 2>/dev/null || true)
|
||||
|
||||
if [ -z "$MANIFESTS" ]; then
|
||||
echo "No manifests with \`<namespace>\` found — skipping." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
for MANIFEST in $MANIFESTS; do
|
||||
NS_PATH=$(grep -oP '<namespace[^>]*>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1)
|
||||
[ -z "$NS_PATH" ] && continue
|
||||
MANIFEST_DIR=$(dirname "$MANIFEST")
|
||||
|
||||
echo "Manifest: \`${MANIFEST}\` → namespace \`${NS_PATH}\`" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Check PHP files have matching namespace
|
||||
while IFS= read -r -d '' PHP_FILE; do
|
||||
FILE_NS=$(grep -oP '^\s*namespace\s+\K[^;]+' "$PHP_FILE" 2>/dev/null | head -1)
|
||||
[ -z "$FILE_NS" ] && continue
|
||||
|
||||
# Namespace should start with the manifest namespace path
|
||||
if ! echo "$FILE_NS" | grep -qF "${NS_PATH}"; then
|
||||
echo "- \`${PHP_FILE}\`: namespace \`${FILE_NS}\` doesn't match manifest \`${NS_PATH}\`" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
done < <(find "$MANIFEST_DIR" -name "*.php" -path "*/src/*" -not -path "./vendor/*" -print0 2>/dev/null)
|
||||
done
|
||||
fi
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
if [ "${ERRORS}" -gt 0 ]; then
|
||||
echo "**${ERRORS} namespace mismatch(es).**" >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
else
|
||||
echo "**Namespace consistency check passed.**" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
- name: SPDX license header check
|
||||
continue-on-error: true
|
||||
run: |
|
||||
echo "### SPDX License Headers" >> $GITHUB_STEP_SUMMARY
|
||||
MISSING=0
|
||||
|
||||
SRC_DIR=""
|
||||
for DIR in source/ src/ htdocs/; do
|
||||
[ -d "$DIR" ] && SRC_DIR="$DIR" && break
|
||||
done
|
||||
|
||||
if [ -z "$SRC_DIR" ]; then
|
||||
echo "No source directory found — skipping." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
TOTAL=0
|
||||
while IFS= read -r -d '' FILE; do
|
||||
TOTAL=$((TOTAL + 1))
|
||||
if ! head -10 "$FILE" | grep -qi "SPDX"; then
|
||||
echo "- Missing SPDX header: \`${FILE}\`" >> $GITHUB_STEP_SUMMARY
|
||||
MISSING=$((MISSING + 1))
|
||||
fi
|
||||
done < <(find "$SRC_DIR" -name "*.php" -not -path "./vendor/*" -print0)
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
if [ "$MISSING" -gt 0 ]; then
|
||||
echo "**${MISSING}/${TOTAL} PHP file(s) missing SPDX license header.**" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "**All ${TOTAL} PHP files have SPDX headers.**" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Service provider check
|
||||
run: |
|
||||
echo "### Service Provider Check" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=0
|
||||
|
||||
PROVIDERS=$(find . -name "provider.php" -path "*/services/*" -not -path "./.git/*" -not -path "./vendor/*" 2>/dev/null)
|
||||
if [ -z "$PROVIDERS" ]; then
|
||||
echo "No service providers found — skipping." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
for FILE in $PROVIDERS; do
|
||||
# Must return a ServiceProviderInterface
|
||||
if ! grep -qP 'ServiceProviderInterface|ComponentInterface|MVCFactoryInterface|DispatcherInterface' "$FILE" 2>/dev/null; then
|
||||
echo "- \`${FILE}\`: does not reference ServiceProviderInterface or component interfaces" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo "- \`${FILE}\`: valid service provider" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# Must have return statement
|
||||
if ! grep -qP '^\s*return\s+new\s+' "$FILE" 2>/dev/null; then
|
||||
echo "- \`${FILE}\`: missing \`return new ...\` statement" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
if [ "${ERRORS}" -gt 0 ]; then
|
||||
echo "**${ERRORS} service provider issue(s).**" >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
else
|
||||
echo "**Service provider check passed.**" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
release-readiness:
|
||||
name: Release Readiness Check
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request' && github.base_ref == 'main'
|
||||
continue-on-error: true
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
|
||||
@@ -21,7 +21,7 @@ permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
|
||||
jobs:
|
||||
cleanup:
|
||||
@@ -33,17 +33,17 @@ jobs:
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GA_TOKEN }}
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
|
||||
- name: Delete merged branches
|
||||
env:
|
||||
GA_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
echo "=== Merged Branch Cleanup ==="
|
||||
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
API="${MOKOGITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
|
||||
# List branches via API
|
||||
BRANCHES=$(curl -sS -H "Authorization: token ${GA_TOKEN}" \
|
||||
BRANCHES=$(curl -sS -H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
"${API}/branches?limit=50" | jq -r '.[].name')
|
||||
|
||||
DELETED=0
|
||||
@@ -56,7 +56,7 @@ jobs:
|
||||
# Check if branch is merged into main
|
||||
if git merge-base --is-ancestor "origin/${BRANCH}" origin/main 2>/dev/null; then
|
||||
echo " Deleting merged branch: ${BRANCH}"
|
||||
curl -sS -X DELETE -H "Authorization: token ${GA_TOKEN}" \
|
||||
curl -sS -X DELETE -H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
"${API}/branches/${BRANCH}" 2>/dev/null || true
|
||||
DELETED=$((DELETED + 1))
|
||||
fi
|
||||
@@ -66,20 +66,20 @@ jobs:
|
||||
|
||||
- name: Clean old workflow runs
|
||||
env:
|
||||
GA_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
echo "=== Workflow Run Cleanup ==="
|
||||
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
API="${MOKOGITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
CUTOFF=$(date -d "30 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -v-30d +%Y-%m-%dT%H:%M:%SZ)
|
||||
|
||||
# Get old completed runs
|
||||
RUNS=$(curl -sS -H "Authorization: token ${GA_TOKEN}" \
|
||||
RUNS=$(curl -sS -H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
"${API}/actions/runs?status=completed&limit=50" | \
|
||||
jq -r ".workflow_runs[] | select(.created_at < \"${CUTOFF}\") | .id" 2>/dev/null)
|
||||
|
||||
DELETED=0
|
||||
for RUN_ID in $RUNS; do
|
||||
curl -sS -X DELETE -H "Authorization: token ${GA_TOKEN}" \
|
||||
curl -sS -X DELETE -H "Authorization: token ${MOKOGITEA_TOKEN}" \
|
||||
"${API}/actions/runs/${RUN_ID}" 2>/dev/null || true
|
||||
DELETED=$((DELETED + 1))
|
||||
done
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Deploy
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API
|
||||
# PATH: /templates/workflows/joomla/deploy-manual.yml.template
|
||||
# VERSION: 04.07.00
|
||||
# BRIEF: Manual SFTP deploy to dev server for Joomla repos
|
||||
|
||||
name: "Universal: Deploy to Dev (Manual)"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
clear_remote:
|
||||
description: 'Delete all remote files before uploading'
|
||||
required: false
|
||||
default: 'false'
|
||||
type: boolean
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: SFTP Deploy to Dev
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Setup PHP
|
||||
run: |
|
||||
php -v && composer --version
|
||||
|
||||
- name: Setup MokoStandards tools
|
||||
env:
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN || github.token }}
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN || github.token }}
|
||||
MOKO_CLONE_HOST: ${{ secrets.MOKOGITEA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }}
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.MOKOGITEA_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \
|
||||
/tmp/mokostandards-api 2>/dev/null || true
|
||||
if [ -d "/tmp/mokostandards-api" ] && [ -f "/tmp/mokostandards-api/composer.json" ]; then
|
||||
cd /tmp/mokostandards-api && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
||||
fi
|
||||
|
||||
- name: Check FTP configuration
|
||||
id: check
|
||||
env:
|
||||
HOST: ${{ vars.DEV_FTP_HOST }}
|
||||
PATH_VAR: ${{ vars.DEV_FTP_PATH }}
|
||||
PORT: ${{ vars.DEV_FTP_PORT }}
|
||||
run: |
|
||||
if [ -z "$HOST" ] || [ -z "$PATH_VAR" ]; then
|
||||
echo "DEV_FTP_HOST or DEV_FTP_PATH not configured -- cannot deploy"
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
echo "host=$HOST" >> "$GITHUB_OUTPUT"
|
||||
|
||||
REMOTE="${PATH_VAR%/}"
|
||||
echo "remote=$REMOTE" >> "$GITHUB_OUTPUT"
|
||||
|
||||
[ -z "$PORT" ] && PORT="22"
|
||||
echo "port=$PORT" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Deploy via SFTP
|
||||
if: steps.check.outputs.skip != 'true'
|
||||
env:
|
||||
SFTP_KEY: ${{ secrets.DEV_FTP_KEY }}
|
||||
SFTP_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
|
||||
SFTP_USER: ${{ vars.DEV_FTP_USERNAME }}
|
||||
run: |
|
||||
SOURCE_DIR="src"
|
||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||
[ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ -- nothing to deploy"; exit 0; }
|
||||
|
||||
printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \
|
||||
"${{ steps.check.outputs.host }}" "${{ steps.check.outputs.port }}" "$SFTP_USER" "${{ steps.check.outputs.remote }}" \
|
||||
> /tmp/sftp-config.json
|
||||
|
||||
if [ -n "$SFTP_KEY" ]; then
|
||||
echo "$SFTP_KEY" > /tmp/deploy_key
|
||||
chmod 600 /tmp/deploy_key
|
||||
printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json
|
||||
else
|
||||
printf ',"password":"%s"}' "$SFTP_PASS" >> /tmp/sftp-config.json
|
||||
fi
|
||||
|
||||
DEPLOY_ARGS=(--path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json)
|
||||
[ "${{ inputs.clear_remote }}" = "true" ] && DEPLOY_ARGS+=(--clear-remote)
|
||||
|
||||
PLATFORM=$(php /tmp/mokostandards-api/cli/platform_detect.php --path . 2>/dev/null || true)
|
||||
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards-api/deploy/deploy-joomla.php" ]; then
|
||||
php /tmp/mokostandards-api/deploy/deploy-joomla.php "${DEPLOY_ARGS[@]}"
|
||||
else
|
||||
php /tmp/mokostandards-api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
|
||||
fi
|
||||
|
||||
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
||||
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
if [ "${{ steps.check.outputs.skip }}" = "true" ]; then
|
||||
echo "### Deploy Skipped -- FTP not configured" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "### Manual Dev Deploy Complete" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Host | \`${{ steps.check.outputs.host }}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Remote | \`${{ steps.check.outputs.remote }}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Clear | ${{ inputs.clear_remote }} |" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
@@ -25,10 +25,6 @@
|
||||
name: "Universal: Secret Scanning"
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- 'dev/**'
|
||||
schedule:
|
||||
- cron: '0 5 * * 1' # Weekly Monday 05:00 UTC
|
||||
workflow_dispatch:
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: moko-platform.Automation
|
||||
# VERSION: 02.48.52
|
||||
# INGROUP: mokocli.Automation
|
||||
# VERSION: 02.52.04
|
||||
# BRIEF: Auto-create feature branch when an issue is opened
|
||||
|
||||
name: "Universal: Issue Branch"
|
||||
@@ -19,7 +19,7 @@ permissions:
|
||||
issues: write
|
||||
|
||||
env:
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
MOKOGITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
|
||||
jobs:
|
||||
create-branch:
|
||||
@@ -28,8 +28,8 @@ jobs:
|
||||
steps:
|
||||
- name: Create branch and comment
|
||||
run: |
|
||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
API="${MOKOGITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
ISSUE_NUM="${{ github.event.issue.number }}"
|
||||
ISSUE_TITLE="${{ github.event.issue.title }}"
|
||||
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
echo "Created branch: ${BRANCH}"
|
||||
|
||||
# Comment on issue with branch link
|
||||
REPO_URL="${GITEA_URL}/${{ github.repository }}"
|
||||
REPO_URL="${MOKOGITEA_URL}/${{ github.repository }}"
|
||||
BODY="Branch created: [\`${BRANCH}\`](${REPO_URL}/src/branch/${BRANCH})\n\n\`\`\`bash\ngit fetch origin\ngit checkout ${BRANCH}\n\`\`\`"
|
||||
|
||||
curl -sf -X POST \
|
||||
|
||||
+521
-508
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@
|
||||
# INGROUP: mokocli.Release
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# PATH: /templates/workflows/universal/pre-release.yml.template
|
||||
# VERSION: 05.01.00
|
||||
# VERSION: 05.02.00
|
||||
# BRIEF: Auto pre-release on push to dev/alpha/beta/rc branches
|
||||
|
||||
name: "Universal: Pre-Release"
|
||||
@@ -59,6 +59,11 @@ jobs:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
ref: ${{ github.ref_name }}
|
||||
submodules: recursive
|
||||
|
||||
- name: Update submodules to main
|
||||
run: |
|
||||
git submodule foreach --quiet 'git checkout main && git pull --quiet origin main' 2>/dev/null || true
|
||||
|
||||
- name: Setup mokocli tools
|
||||
env:
|
||||
@@ -88,8 +93,20 @@ jobs:
|
||||
php ${MOKO_CLI}/platform_detect.php --path . --github-output 2>/dev/null || true
|
||||
php ${MOKO_CLI}/manifest_read.php --path . --github-output
|
||||
|
||||
- name: Check platform eligibility (Joomla only)
|
||||
id: eligibility
|
||||
run: |
|
||||
PLATFORM="${{ steps.platform.outputs.platform }}"
|
||||
if [[ "$PLATFORM" == joomla* ]] || [[ "$PLATFORM" == "joomla" ]]; then
|
||||
echo "proceed=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "proceed=false" >> "$GITHUB_OUTPUT"
|
||||
echo "::notice::Platform '$PLATFORM' — non-Joomla, skipping pre-release auto-bump"
|
||||
fi
|
||||
|
||||
- name: Resolve metadata and bump version
|
||||
id: meta
|
||||
if: steps.eligibility.outputs.proceed == 'true'
|
||||
run: |
|
||||
# Auto-detect stability from branch name on push, or use input on dispatch
|
||||
if [ "${{ github.event_name }}" = "push" ]; then
|
||||
@@ -166,6 +183,7 @@ jobs:
|
||||
|
||||
- name: Create release
|
||||
id: release
|
||||
if: steps.eligibility.outputs.proceed == 'true'
|
||||
run: |
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
@@ -176,6 +194,7 @@ jobs:
|
||||
--repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease
|
||||
|
||||
- name: Update release notes from CHANGELOG.md
|
||||
if: steps.eligibility.outputs.proceed == 'true'
|
||||
run: |
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
@@ -212,6 +231,7 @@ jobs:
|
||||
|
||||
- name: Build package and upload
|
||||
id: package
|
||||
if: steps.eligibility.outputs.proceed == 'true'
|
||||
run: |
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
@@ -225,6 +245,7 @@ jobs:
|
||||
# No need to build, commit, or sync updates.xml from workflows
|
||||
|
||||
- name: "Delete lesser pre-release channels (cascade)"
|
||||
if: steps.eligibility.outputs.proceed == 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoPlatform.Universal
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# INGROUP: mokocli.Universal
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# PATH: /.mokogitea/workflows/rc-revert.yml
|
||||
# VERSION: 09.23.00
|
||||
# BRIEF: Rename rc/ branch back to dev/ when PR is closed without merge
|
||||
@@ -29,12 +29,20 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Rename branch
|
||||
env:
|
||||
BRANCH: ${{ github.event.pull_request.head.ref }}
|
||||
REPO: ${{ github.repository }}
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
BRANCH="${{ github.event.pull_request.head.ref }}"
|
||||
set -euo pipefail
|
||||
# BRANCH is attacker-controlled (PR head ref). Strict allowlist before ANY use.
|
||||
if ! printf '%s' "$BRANCH" | grep -Eq '^rc/[A-Za-z0-9._/-]+$'; then
|
||||
echo "::error::Refusing unsafe branch name: $BRANCH"; exit 1
|
||||
fi
|
||||
SUFFIX="${BRANCH#rc/}"
|
||||
DEV_BRANCH="dev/${SUFFIX}"
|
||||
API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches"
|
||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||
API="${GITEA_URL}/api/v1/repos/${REPO}/branches"
|
||||
|
||||
# Create dev/ branch from rc/ branch
|
||||
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X POST \
|
||||
@@ -42,25 +50,22 @@ jobs:
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"new_branch_name\": \"${DEV_BRANCH}\", \"old_branch_name\": \"${BRANCH}\"}" \
|
||||
"${API}" 2>/dev/null || true)
|
||||
|
||||
if [ "$STATUS" = "201" ]; then
|
||||
echo "Created branch: ${DEV_BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Created branch: ${DEV_BRANCH}" >> "$GITHUB_STEP_SUMMARY"
|
||||
else
|
||||
echo "::error::Failed to create ${DEV_BRANCH} from ${BRANCH} (HTTP ${STATUS})"
|
||||
exit 1
|
||||
echo "::error::Failed to create ${DEV_BRANCH} from ${BRANCH} (HTTP ${STATUS})"; exit 1
|
||||
fi
|
||||
|
||||
# Delete rc/ branch
|
||||
ENCODED=$(php -r "echo rawurlencode('${BRANCH}');")
|
||||
# Read BRANCH from the environment inside PHP (getenv, no string interpolation -> no PHP injection)
|
||||
ENCODED=$(php -r 'echo rawurlencode(getenv("BRANCH"));')
|
||||
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
"${API}/${ENCODED}" 2>/dev/null || true)
|
||||
|
||||
if [ "$STATUS" = "204" ]; then
|
||||
echo "Deleted branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Deleted branch: ${BRANCH}" >> "$GITHUB_STEP_SUMMARY"
|
||||
else
|
||||
echo "::warning::Failed to delete ${BRANCH} (HTTP ${STATUS})"
|
||||
fi
|
||||
|
||||
echo "### RC Reverted" >> $GITHUB_STEP_SUMMARY
|
||||
echo "${BRANCH} → ${DEV_BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "### RC Reverted" >> "$GITHUB_STEP_SUMMARY"
|
||||
echo "${BRANCH} → ${DEV_BRANCH}" >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,130 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow.Template
|
||||
# INGROUP: MokoStandards.CI
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla
|
||||
# PATH: /.mokogitea/workflows/version-set.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Set or reset the extension version across all version-bearing files
|
||||
|
||||
name: "Joomla: Set Version"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Version number (e.g. 01.00.00)"
|
||||
required: true
|
||||
type: string
|
||||
branch:
|
||||
description: "Branch to update (default: current)"
|
||||
required: false
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
set-version:
|
||||
name: Set Version to ${{ inputs.version }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Validate version format
|
||||
run: |
|
||||
VERSION="${{ inputs.version }}"
|
||||
if ! echo "$VERSION" | grep -qP '^\d{2}\.\d{2}\.\d{2}$'; then
|
||||
echo "::error::Invalid version format '${VERSION}' — expected XX.YY.ZZ (e.g. 01.00.00)"
|
||||
exit 1
|
||||
fi
|
||||
echo "VERSION=${VERSION}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN || github.token }}
|
||||
ref: ${{ inputs.branch || github.ref }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Update manifest version
|
||||
run: |
|
||||
MANIFEST=""
|
||||
for XML_FILE in $(find . -maxdepth 3 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do
|
||||
if grep -q "<extension" "$XML_FILE" 2>/dev/null; then
|
||||
MANIFEST="$XML_FILE"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$MANIFEST" ]; then
|
||||
echo "::warning::No Joomla extension manifest found — skipping manifest update"
|
||||
else
|
||||
OLD_VER=$(grep -oP '<version>\K[^<]+' "$MANIFEST" | head -1)
|
||||
sed -i "s|<version>${OLD_VER}</version>|<version>${VERSION}</version>|" "$MANIFEST"
|
||||
echo "Manifest: ${OLD_VER} → ${VERSION} (${MANIFEST})"
|
||||
fi
|
||||
|
||||
- name: Update README.md version
|
||||
run: |
|
||||
if [ -f "README.md" ]; then
|
||||
if grep -qP '^\s*VERSION:\s*\d' README.md; then
|
||||
sed -i -E "s/(VERSION:\s*)[0-9]{2}\.[0-9]{2}\.[0-9]{2}/\1${VERSION}/" README.md
|
||||
echo "README.md version updated to ${VERSION}"
|
||||
else
|
||||
echo "::warning::No VERSION line found in README.md — skipping"
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Update CHANGELOG.md
|
||||
run: |
|
||||
if [ -f "CHANGELOG.md" ]; then
|
||||
DATE=$(date +%Y-%m-%d)
|
||||
# Check if this version already has an entry
|
||||
if grep -q "^\#\# \[${VERSION}\]" CHANGELOG.md; then
|
||||
echo "CHANGELOG.md already has entry for ${VERSION} — skipping"
|
||||
else
|
||||
# Insert new version entry after [Unreleased] or at the top after header
|
||||
if grep -q '^\#\# \[Unreleased\]' CHANGELOG.md; then
|
||||
sed -i "/^\#\# \[Unreleased\]/a\\\\n## [${VERSION}] --- ${DATE}" CHANGELOG.md
|
||||
else
|
||||
sed -i "/^\# Changelog/a\\\\n## [Unreleased]\n\n## [${VERSION}] --- ${DATE}" CHANGELOG.md
|
||||
fi
|
||||
echo "CHANGELOG.md: added entry for ${VERSION}"
|
||||
fi
|
||||
else
|
||||
echo "::warning::No CHANGELOG.md found — skipping"
|
||||
fi
|
||||
|
||||
- name: Update FILE INFORMATION blocks
|
||||
run: |
|
||||
# Update VERSION in file header blocks (# VERSION: XX.YY.ZZ)
|
||||
find . -maxdepth 1 -type f \( -name "*.yml" -o -name "*.yaml" -o -name "*.php" -o -name "*.md" \) \
|
||||
-not -path "./.git/*" -not -path "./vendor/*" -print0 2>/dev/null | \
|
||||
while IFS= read -r -d '' FILE; do
|
||||
if head -20 "$FILE" | grep -qP '^\s*#?\s*VERSION:\s*\d{2}\.\d{2}\.\d{2}'; then
|
||||
sed -i -E "s/(#?\s*VERSION:\s*)[0-9]{2}\.[0-9]{2}\.[0-9]{2}/\1${VERSION}/" "$FILE"
|
||||
echo "Updated FILE INFORMATION VERSION in ${FILE}"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Commit and push
|
||||
run: |
|
||||
git config user.name "Moko Consulting [bot]"
|
||||
git config user.email "hello@mokoconsulting.tech"
|
||||
git add -A
|
||||
if git diff --cached --quiet; then
|
||||
echo "No version changes detected — nothing to commit"
|
||||
else
|
||||
git commit -m "chore: set version to ${VERSION} [skip bump]
|
||||
|
||||
Authored-by: Moko Consulting"
|
||||
git push
|
||||
echo "### Version Set" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Version updated to \`${VERSION}\` on branch \`${GITHUB_REF_NAME}\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
@@ -4,8 +4,8 @@
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoPlatform.Universal
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
|
||||
# INGROUP: mokocli.Universal
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
# PATH: /.mokogitea/workflows/workflow-sync-trigger.yml
|
||||
# VERSION: 01.01.00
|
||||
# BRIEF: Trigger workflow sync to live repos when a PR is merged to main
|
||||
@@ -13,6 +13,7 @@
|
||||
name: "Universal: Workflow Sync Trigger"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
pull_request:
|
||||
types: [closed]
|
||||
branches:
|
||||
@@ -26,8 +27,9 @@ jobs:
|
||||
name: Sync workflows to live repos
|
||||
runs-on: ubuntu-latest
|
||||
if: >-
|
||||
github.event.pull_request.merged == true &&
|
||||
!contains(github.event.pull_request.title, '[skip sync]')
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(github.event.pull_request.merged == true &&
|
||||
!contains(github.event.pull_request.title, '[skip sync]'))
|
||||
|
||||
steps:
|
||||
- name: Determine platform from repo name
|
||||
@@ -45,16 +47,22 @@ jobs:
|
||||
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
|
||||
echo "Platform: ${PLATFORM:-all}"
|
||||
|
||||
- name: Clone mokoplatform
|
||||
- name: Clone mokocli
|
||||
env:
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
run: |
|
||||
GITEA_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
|
||||
git clone --depth 1 "${GITEA_URL}/MokoConsulting/mokoplatform.git" /tmp/mokoplatform
|
||||
MOKOGITEA_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
|
||||
git clone --depth 1 "${MOKOGITEA_URL}/MokoConsulting/mokocli.git" /tmp/mokocli
|
||||
|
||||
- name: Install PHP
|
||||
run: |
|
||||
if ! command -v php &> /dev/null; then
|
||||
apt-get update -qq && apt-get install -y -qq php-cli php-json php-curl > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cd /tmp/mokoplatform
|
||||
cd /tmp/mokocli
|
||||
composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
||||
|
||||
- name: Run workflow sync
|
||||
@@ -70,4 +78,4 @@ jobs:
|
||||
ARGS="${ARGS} --platform-filter ${PLATFORM}"
|
||||
fi
|
||||
|
||||
php /tmp/mokoplatform/cli/workflow_sync.php ${ARGS}
|
||||
php /tmp/mokocli/cli/workflow_sync.php ${ARGS}
|
||||
|
||||
+7
-58
@@ -14,72 +14,21 @@
|
||||
INGROUP: MokoSuiteClient.Documentation
|
||||
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
|
||||
PATH: ./CHANGELOG.md
|
||||
VERSION: 02.48.52
|
||||
VERSION: 02.52.04
|
||||
BRIEF: Version history using `Keep a Changelog`
|
||||
-->
|
||||
|
||||
# Changelog
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- **Mirror Domains & Staging** — repeatable subform table in DevTools plugin for configuring domain aliases with per-alias offline bypass, robots directive, and labels
|
||||
- **Daily Support PIN** — HMAC-SHA256 rotating PIN shown on cpanel module, component dashboard, and HQ site cards
|
||||
- **Support PIN in status bar** — cache/temp module now shows PIN request button instead of domain; click to request, click again to copy
|
||||
- **Frontend link in status bar** — cache/temp module now has 4 buttons: Site (frontend link), PIN, Cache, Temp
|
||||
- **Help buttons** — all admin views link to Gitea wiki pages via toolbar help button
|
||||
- **Support PIN in heartbeat** — core system plugin includes current PIN in heartbeat payload to HQ
|
||||
- **HQ config sync** — client stores HQ-configured `support_pin_hours` from heartbeat response, PIN TTL now configurable from HQ
|
||||
## [02.52.04] --- 2026-06-29
|
||||
|
||||
### Changed
|
||||
- **Support PIN UI unified** — `SupportPinHelper::renderBadge()` and `renderScript()` replace 3 separate inline implementations (dashboard, cpanel module, cache module) with click-to-copy on all PIN badges
|
||||
- Admin sidebar menu module now loads component-local language files (fixes untranslated keys for MokoSuiteCross and other components)
|
||||
- Support PIN TTL is now configurable via HQ global options instead of hardcoded 72 hours
|
||||
- Removed MokoSuiteHQ from extension catalog (internal app, not for client sites)
|
||||
- **SupportPinHelper** — shared helper centralises PIN generation across dashboard, cpanel module, cache module, and AJAX controller
|
||||
- **Current IP display** — firewall plugin settings show admin's IP with copy button
|
||||
- **Heartbeat monitor** — consolidated into core plugin from retired monitor plugin, with diagnostic logging on all bail-out points
|
||||
- **Backup bridge plugin** — discovers MokoSuiteBackup's BackupStatusHelper and sends status in heartbeat payloads
|
||||
- **Activity log** — blockchain-style hash chain for tamper detection in MokoSuiteHQ
|
||||
- **Dev domain in heartbeat** — client sends dev alias to HQ for display on dashboard
|
||||
- **Login app badges** — recent logins table shows Admin/Site badge parsed from Joomla action log message JSON
|
||||
## [02.52.04] --- 2026-06-29
|
||||
|
||||
### Changed
|
||||
- **Plugin install** — self-healing: extracts plugin zips from package on every update, creates missing extension records with namespace
|
||||
- **Menu naming** — MokoSuiteClient displays as "MokoSuite", MokoSuiteHQ as "MokoHQ", others stripped of prefix
|
||||
- **Menu ordering** — HQ first, MokoSuite second, others alphabetical
|
||||
- **Cpanel module** — always starts collapsed, access level 3 (Special), pretty plugin badge labels
|
||||
- **Module namespaces** — fixed cpanel (MokoSuiteCpanel → MokoSuiteClientCpanel) and cache (MokoSuiteCache → MokoSuiteClientCache)
|
||||
- **Health checks** — return status:error on exceptions instead of false status:ok; MokoSuiteBackup detection queries correct table
|
||||
- **Heartbeat** — correct URL (suite.dev), correct API route (mokosuitehq), correct headers (X-MokoSuite-*), fresh RSA key pair
|
||||
- **Date formats** — all templates use Joomla locale-aware DATE_FORMAT_LC2/LC4
|
||||
- **Domains** — updated from waas.dev to suite.dev.mokoconsulting.tech throughout
|
||||
- **Dashboard info bar** — reverted stacked layout; info items back to horizontal row
|
||||
- **Extension version bar** — full-width auto-sized strip with equal-width cells and border separators
|
||||
- **Recent logins** — exact match on LOGGED_IN key (excludes logout noise), limit increased to 10
|
||||
## [02.52.00] --- 2026-06-29
|
||||
|
||||
### Removed
|
||||
- **Helpdesk/tickets** — migrated to MokoSuiteCRM (issue #67)
|
||||
- **Monitor plugin** — retired, config consolidated into core plugin
|
||||
- **Backup bridge** — temporarily removed from package manifest (build pipeline issue)
|
||||
- **Update server migration** — removed migrateUpdateServerUrls, cleanupStaleUpdateSites, fixUpdateRecords, enableUpdateServer calls
|
||||
## [02.52.00] --- 2026-06-29
|
||||
|
||||
### Fixed
|
||||
- **Regular Labs import** — destination tables missing from SQL update files; sites that upgraded never got the tables, causing "No data found" on import
|
||||
- **Regular Labs import banner** — detection now requires both source AND destination tables before showing the import button
|
||||
- **DB-IP auto-enrichment** — all IPs in `<code>` tags in admin backend now show country flag emoji and geo tooltip on hover
|
||||
- **MokoSuiteBackup quick action** — dashboard now includes MokoSuiteBackup button when component is installed
|
||||
- **PIN copy** — fixed duplicate click handlers (4 toast messages), "Copied!" not reverting, added "Click to copy" hover tooltip
|
||||
- Health endpoint cron check SQL error — orphan `setQuery(getQuery(true), 0, 5)` produced bare `LIMIT 5`, returning 503 for all health polls
|
||||
- License plugin missing `src/` and `language/` directories causing install failure
|
||||
- PIN generation inconsistency — controller used `floor(now/TTL)` while display used `floor(requestedAt/TTL)`
|
||||
- Plugin files installing to group root instead of element subdirectory (ALTER TABLE DEFAULT '' + empty element cleanup)
|
||||
- Orphan extension rows with empty element or display-name-as-element
|
||||
- Module not publishing (ensureAdminModule direct DB update bypasses checked_out)
|
||||
- RSA key pair had Windows line endings causing signature verification failure
|
||||
- Heartbeat connection failing due to wrong domain, route, and header names
|
||||
## [02.51.10] --- 2026-06-29
|
||||
|
||||
## [02.44.00] --- 2026-06-20
|
||||
|
||||
## [02.42.00] --- 2026-06-20
|
||||
|
||||
## [02.42.00] --- 2026-06-20
|
||||
## [02.51.10] --- 2026-06-29
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
DEFGROUP: Joomla.Plugin
|
||||
INGROUP: MokoSuiteClient.Documentation
|
||||
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
|
||||
VERSION: 02.48.52
|
||||
VERSION: 02.52.04
|
||||
PATH: ./CODE_OF_CONDUCT.md
|
||||
BRIEF: Reference + packaging repo for Moko Consulting Developer GPT Other Default
|
||||
-->
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@
|
||||
DEFGROUP: mokoconsulting-tech.MokoSuiteClientBrand
|
||||
INGROUP: MokoStandards.Governance
|
||||
REPO: https://github.com/mokoconsulting-tech/MokoSuiteClientBrand
|
||||
VERSION: 02.48.52
|
||||
VERSION: 02.52.04
|
||||
PATH: /GOVERNANCE.md
|
||||
BRIEF: Project governance rules, roles, and decision process for MokoSuiteClientBrand
|
||||
-->
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
INGROUP: MokoSuiteClient.Documentation
|
||||
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
|
||||
PATH: ./LICENSE.md
|
||||
VERSION: 02.48.52
|
||||
VERSION: 02.52.04
|
||||
BRIEF: Project license (GPL-3.0-or-later)
|
||||
-->
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
@@ -1,333 +0,0 @@
|
||||
# Makefile for Joomla Extensions
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# This is a reference Makefile for building Joomla extensions.
|
||||
# Copy this to your repository root as "Makefile" and customize as needed.
|
||||
#
|
||||
# Supports: Modules, Plugins, Components, Packages, Templates
|
||||
|
||||
# ==============================================================================
|
||||
# CONFIGURATION - Customize these for your extension
|
||||
# ==============================================================================
|
||||
|
||||
# Extension Configuration
|
||||
EXTENSION_NAME := mokosuiteclient
|
||||
EXTENSION_TYPE := package
|
||||
# Options: module, plugin, component, package, template
|
||||
EXTENSION_VERSION := 02.35.00
|
||||
|
||||
# Module Configuration (for modules only)
|
||||
MODULE_TYPE := site
|
||||
# Options: site, admin
|
||||
|
||||
# Plugin Configuration (for plugins only)
|
||||
PLUGIN_GROUP := system
|
||||
# Options: system, content, user, authentication, etc.
|
||||
|
||||
# Directories
|
||||
SRC_DIR := .
|
||||
BUILD_DIR := build
|
||||
DIST_DIR := dist
|
||||
DOCS_DIR := docs
|
||||
|
||||
# Joomla Installation (for local testing - customize paths)
|
||||
JOOMLA_ROOT := /var/www/html/joomla
|
||||
JOOMLA_VERSION := 4
|
||||
|
||||
# Tools
|
||||
PHP := php
|
||||
COMPOSER := composer
|
||||
NPM := npm
|
||||
PHPCS := vendor/bin/phpcs
|
||||
PHPCBF := vendor/bin/phpcbf
|
||||
PHPUNIT := vendor/bin/phpunit
|
||||
ZIP := zip
|
||||
|
||||
# Coding Standards
|
||||
PHPCS_STANDARD := Joomla
|
||||
|
||||
# Colors for output
|
||||
COLOR_RESET := \033[0m
|
||||
COLOR_GREEN := \033[32m
|
||||
COLOR_YELLOW := \033[33m
|
||||
COLOR_BLUE := \033[34m
|
||||
COLOR_RED := \033[31m
|
||||
|
||||
# ==============================================================================
|
||||
# TARGETS
|
||||
# ==============================================================================
|
||||
|
||||
.PHONY: help
|
||||
help: ## Show this help message
|
||||
@echo "$(COLOR_BLUE)╔════════════════════════════════════════════════════════════╗$(COLOR_RESET)"
|
||||
@echo "$(COLOR_BLUE)║ Joomla Extension Makefile ║$(COLOR_RESET)"
|
||||
@echo "$(COLOR_BLUE)╚════════════════════════════════════════════════════════════╝$(COLOR_RESET)"
|
||||
@echo ""
|
||||
@echo "Extension: $(EXTENSION_NAME) ($(EXTENSION_TYPE)) v$(EXTENSION_VERSION)"
|
||||
@echo ""
|
||||
@echo "$(COLOR_GREEN)Available targets:$(COLOR_RESET)"
|
||||
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " $(COLOR_BLUE)%-20s$(COLOR_RESET) %s\n", $$1, $$2}'
|
||||
@echo ""
|
||||
@echo "$(COLOR_YELLOW)Quick Start:$(COLOR_RESET)"
|
||||
@echo " 1. make install-deps # Install dependencies"
|
||||
@echo " 2. make build # Build extension package"
|
||||
@echo " 3. make test # Run tests"
|
||||
@echo ""
|
||||
|
||||
.PHONY: install-deps
|
||||
install-deps: ## Install all dependencies (Composer + npm)
|
||||
@echo "$(COLOR_BLUE)Installing dependencies...$(COLOR_RESET)"
|
||||
@if [ -f "composer.json" ]; then \
|
||||
$(COMPOSER) install; \
|
||||
echo "$(COLOR_GREEN)✓ Composer dependencies installed$(COLOR_RESET)"; \
|
||||
fi
|
||||
@if [ -f "package.json" ]; then \
|
||||
$(NPM) install; \
|
||||
echo "$(COLOR_GREEN)✓ npm dependencies installed$(COLOR_RESET)"; \
|
||||
fi
|
||||
|
||||
.PHONY: update-deps
|
||||
update-deps: ## Update all dependencies
|
||||
@echo "$(COLOR_BLUE)Updating dependencies...$(COLOR_RESET)"
|
||||
@if [ -f "composer.json" ]; then \
|
||||
$(COMPOSER) update; \
|
||||
echo "$(COLOR_GREEN)✓ Composer dependencies updated$(COLOR_RESET)"; \
|
||||
fi
|
||||
@if [ -f "package.json" ]; then \
|
||||
$(NPM) update; \
|
||||
echo "$(COLOR_GREEN)✓ npm dependencies updated$(COLOR_RESET)"; \
|
||||
fi
|
||||
|
||||
.PHONY: lint
|
||||
lint: ## Run PHP linter (syntax check)
|
||||
@echo "$(COLOR_BLUE)Running PHP linter...$(COLOR_RESET)"
|
||||
@find . -name "*.php" ! -path "./vendor/*" ! -path "./node_modules/*" ! -path "./$(BUILD_DIR)/*" \
|
||||
-exec $(PHP) -l {} \; | grep -v "No syntax errors" || true
|
||||
@echo "$(COLOR_GREEN)✓ PHP linting complete$(COLOR_RESET)"
|
||||
|
||||
.PHONY: phpcs
|
||||
phpcs: ## Run PHP CodeSniffer (Joomla standards)
|
||||
@echo "$(COLOR_BLUE)Running PHP CodeSniffer...$(COLOR_RESET)"
|
||||
@if [ -f "$(PHPCS)" ]; then \
|
||||
$(PHPCS) --standard=$(PHPCS_STANDARD) --extensions=php --ignore=vendor,node_modules,$(BUILD_DIR) .; \
|
||||
else \
|
||||
echo "$(COLOR_YELLOW)⚠ PHP CodeSniffer not installed. Run: make install-deps$(COLOR_RESET)"; \
|
||||
fi
|
||||
|
||||
.PHONY: phpcbf
|
||||
phpcbf: ## Fix coding standards automatically
|
||||
@echo "$(COLOR_BLUE)Running PHP Code Beautifier...$(COLOR_RESET)"
|
||||
@if [ -f "$(PHPCBF)" ]; then \
|
||||
$(PHPCBF) --standard=$(PHPCS_STANDARD) --extensions=php --ignore=vendor,node_modules,$(BUILD_DIR) .; \
|
||||
echo "$(COLOR_GREEN)✓ Code formatting applied$(COLOR_RESET)"; \
|
||||
else \
|
||||
echo "$(COLOR_YELLOW)⚠ PHP Code Beautifier not installed. Run: make install-deps$(COLOR_RESET)"; \
|
||||
fi
|
||||
|
||||
.PHONY: validate
|
||||
validate: lint phpcs ## Run all validation checks
|
||||
@echo "$(COLOR_GREEN)✓ All validation checks passed$(COLOR_RESET)"
|
||||
|
||||
.PHONY: test
|
||||
test: ## Run PHPUnit tests
|
||||
@echo "$(COLOR_BLUE)Running tests...$(COLOR_RESET)"
|
||||
@if [ -f "$(PHPUNIT)" ] && [ -f "phpunit.xml" ]; then \
|
||||
$(PHPUNIT); \
|
||||
else \
|
||||
echo "$(COLOR_YELLOW)⚠ PHPUnit not configured$(COLOR_RESET)"; \
|
||||
fi
|
||||
|
||||
.PHONY: test-coverage
|
||||
test-coverage: ## Run tests with coverage report
|
||||
@echo "$(COLOR_BLUE)Running tests with coverage...$(COLOR_RESET)"
|
||||
@if [ -f "$(PHPUNIT)" ] && [ -f "phpunit.xml" ]; then \
|
||||
$(PHPUNIT) --coverage-html $(BUILD_DIR)/coverage; \
|
||||
echo "$(COLOR_GREEN)✓ Coverage report: $(BUILD_DIR)/coverage/index.html$(COLOR_RESET)"; \
|
||||
else \
|
||||
echo "$(COLOR_YELLOW)⚠ PHPUnit not configured$(COLOR_RESET)"; \
|
||||
fi
|
||||
|
||||
.PHONY: clean
|
||||
clean: ## Clean build artifacts
|
||||
@echo "$(COLOR_BLUE)Cleaning build artifacts...$(COLOR_RESET)"
|
||||
@rm -rf $(BUILD_DIR) $(DIST_DIR)
|
||||
@echo "$(COLOR_GREEN)✓ Build artifacts cleaned$(COLOR_RESET)"
|
||||
|
||||
.PHONY: build
|
||||
build: clean validate ## Build extension package
|
||||
@echo "$(COLOR_BLUE)Building Joomla extension package...$(COLOR_RESET)"
|
||||
@mkdir -p $(DIST_DIR) $(BUILD_DIR)
|
||||
|
||||
# Determine package prefix based on extension type
|
||||
@case "$(EXTENSION_TYPE)" in \
|
||||
module) \
|
||||
PACKAGE_PREFIX="mod_$(EXTENSION_NAME)"; \
|
||||
BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \
|
||||
;; \
|
||||
plugin) \
|
||||
PACKAGE_PREFIX="plg_$(PLUGIN_GROUP)_$(EXTENSION_NAME)"; \
|
||||
BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \
|
||||
;; \
|
||||
component) \
|
||||
PACKAGE_PREFIX="com_$(EXTENSION_NAME)"; \
|
||||
BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \
|
||||
;; \
|
||||
package) \
|
||||
PACKAGE_PREFIX="pkg_$(EXTENSION_NAME)"; \
|
||||
BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \
|
||||
;; \
|
||||
template) \
|
||||
PACKAGE_PREFIX="tpl_$(EXTENSION_NAME)"; \
|
||||
BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \
|
||||
;; \
|
||||
*) \
|
||||
echo "$(COLOR_RED)✗ Unknown extension type: $(EXTENSION_TYPE)$(COLOR_RESET)"; \
|
||||
exit 1; \
|
||||
;; \
|
||||
esac; \
|
||||
\
|
||||
mkdir -p "$$BUILD_TARGET"; \
|
||||
\
|
||||
echo "Building $$PACKAGE_PREFIX..."; \
|
||||
\
|
||||
rsync -av --progress \
|
||||
--exclude='$(BUILD_DIR)' \
|
||||
--exclude='$(DIST_DIR)' \
|
||||
--exclude='.git*' \
|
||||
--exclude='vendor/' \
|
||||
--exclude='node_modules/' \
|
||||
--exclude='tests/' \
|
||||
--exclude='Makefile' \
|
||||
--exclude='composer.json' \
|
||||
--exclude='composer.lock' \
|
||||
--exclude='package.json' \
|
||||
--exclude='package-lock.json' \
|
||||
--exclude='phpunit.xml' \
|
||||
--exclude='*.md' \
|
||||
--exclude='.editorconfig' \
|
||||
. "$$BUILD_TARGET/"; \
|
||||
\
|
||||
cd $(BUILD_DIR) && $(ZIP) -r "../$(DIST_DIR)/$${PACKAGE_PREFIX}-$(EXTENSION_VERSION).zip" "$${PACKAGE_PREFIX}"; \
|
||||
\
|
||||
echo "$(COLOR_GREEN)✓ Package created: $(DIST_DIR)/$${PACKAGE_PREFIX}-$(EXTENSION_VERSION).zip$(COLOR_RESET)"
|
||||
|
||||
.PHONY: package
|
||||
package: build ## Alias for build
|
||||
@echo "$(COLOR_GREEN)✓ Package ready for distribution$(COLOR_RESET)"
|
||||
|
||||
.PHONY: install-local
|
||||
install-local: build ## Install to local Joomla (upload via admin)
|
||||
@echo "$(COLOR_BLUE)Package ready for installation$(COLOR_RESET)"
|
||||
@case "$(EXTENSION_TYPE)" in \
|
||||
module) PACKAGE="mod_$(EXTENSION_NAME)";; \
|
||||
plugin) PACKAGE="plg_$(PLUGIN_GROUP)_$(EXTENSION_NAME)";; \
|
||||
component) PACKAGE="com_$(EXTENSION_NAME)";; \
|
||||
package) PACKAGE="pkg_$(EXTENSION_NAME)";; \
|
||||
template) PACKAGE="tpl_$(EXTENSION_NAME)";; \
|
||||
esac; \
|
||||
echo "$(COLOR_YELLOW)Upload $(DIST_DIR)/$${PACKAGE}-$(EXTENSION_VERSION).zip via Joomla Administrator$(COLOR_RESET)"; \
|
||||
echo "Admin URL: $(JOOMLA_ROOT) → Extensions → Install"
|
||||
|
||||
.PHONY: dev-install
|
||||
dev-install: ## Create symlink for development (Joomla 4+)
|
||||
@echo "$(COLOR_BLUE)Creating development symlink...$(COLOR_RESET)"
|
||||
@if [ ! -d "$(JOOMLA_ROOT)" ]; then \
|
||||
echo "$(COLOR_RED)✗ Joomla root not found at $(JOOMLA_ROOT)$(COLOR_RESET)"; \
|
||||
echo "Update JOOMLA_ROOT in Makefile"; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
@case "$(EXTENSION_TYPE)" in \
|
||||
module) \
|
||||
if [ "$(MODULE_TYPE)" = "admin" ]; then \
|
||||
TARGET="$(JOOMLA_ROOT)/administrator/modules/mod_$(EXTENSION_NAME)"; \
|
||||
else \
|
||||
TARGET="$(JOOMLA_ROOT)/modules/mod_$(EXTENSION_NAME)"; \
|
||||
fi; \
|
||||
;; \
|
||||
plugin) \
|
||||
TARGET="$(JOOMLA_ROOT)/plugins/$(PLUGIN_GROUP)/$(EXTENSION_NAME)"; \
|
||||
;; \
|
||||
component) \
|
||||
echo "$(COLOR_YELLOW)⚠ Components require complex symlink setup$(COLOR_RESET)"; \
|
||||
echo "Manual setup recommended for component development"; \
|
||||
exit 1; \
|
||||
;; \
|
||||
*) \
|
||||
echo "$(COLOR_RED)✗ dev-install not supported for $(EXTENSION_TYPE)$(COLOR_RESET)"; \
|
||||
exit 1; \
|
||||
;; \
|
||||
esac; \
|
||||
\
|
||||
rm -rf "$$TARGET"; \
|
||||
ln -s "$(PWD)" "$$TARGET"; \
|
||||
echo "$(COLOR_GREEN)✓ Development symlink created at $$TARGET$(COLOR_RESET)"
|
||||
|
||||
.PHONY: watch
|
||||
watch: ## Watch for changes and rebuild
|
||||
@echo "$(COLOR_BLUE)Watching for changes...$(COLOR_RESET)"
|
||||
@echo "$(COLOR_YELLOW)Press Ctrl+C to stop$(COLOR_RESET)"
|
||||
@while true; do \
|
||||
inotifywait -r -e modify,create,delete --exclude '($(BUILD_DIR)|$(DIST_DIR)|vendor|node_modules)' . 2>/dev/null || \
|
||||
(echo "$(COLOR_YELLOW)⚠ inotifywait not installed. Install: apt-get install inotify-tools$(COLOR_RESET)" && sleep 5); \
|
||||
make build; \
|
||||
done
|
||||
|
||||
.PHONY: version
|
||||
version: ## Display version information
|
||||
@echo "$(COLOR_BLUE)Extension Information:$(COLOR_RESET)"
|
||||
@echo " Name: $(EXTENSION_NAME)"
|
||||
@echo " Type: $(EXTENSION_TYPE)"
|
||||
@echo " Version: $(EXTENSION_VERSION)"
|
||||
@if [ "$(EXTENSION_TYPE)" = "module" ]; then \
|
||||
echo " Module: $(MODULE_TYPE)"; \
|
||||
fi
|
||||
@if [ "$(EXTENSION_TYPE)" = "plugin" ]; then \
|
||||
echo " Group: $(PLUGIN_GROUP)"; \
|
||||
fi
|
||||
|
||||
.PHONY: docs
|
||||
docs: ## Generate documentation
|
||||
@echo "$(COLOR_BLUE)Generating documentation...$(COLOR_RESET)"
|
||||
@mkdir -p $(DOCS_DIR)
|
||||
@echo "$(COLOR_YELLOW)⚠ Documentation generation not configured$(COLOR_RESET)"
|
||||
@echo "Consider adding phpDocumentor or similar"
|
||||
|
||||
.PHONY: release
|
||||
release: validate test build ## Create a release (validate + test + build)
|
||||
@echo "$(COLOR_GREEN)✓ Release package ready$(COLOR_RESET)"
|
||||
@echo ""
|
||||
@echo "$(COLOR_BLUE)Release Checklist:$(COLOR_RESET)"
|
||||
@echo " [ ] Update CHANGELOG.md"
|
||||
@echo " [ ] Update version in XML manifest"
|
||||
@echo " [ ] Test installation in clean Joomla"
|
||||
@echo " [ ] Tag release in git: git tag v$(EXTENSION_VERSION)"
|
||||
@echo " [ ] Push tags: git push --tags"
|
||||
@echo " [ ] Create GitHub release"
|
||||
@echo ""
|
||||
@case "$(EXTENSION_TYPE)" in \
|
||||
module) PACKAGE="mod_$(EXTENSION_NAME)";; \
|
||||
plugin) PACKAGE="plg_$(PLUGIN_GROUP)_$(EXTENSION_NAME)";; \
|
||||
component) PACKAGE="com_$(EXTENSION_NAME)";; \
|
||||
package) PACKAGE="pkg_$(EXTENSION_NAME)";; \
|
||||
template) PACKAGE="tpl_$(EXTENSION_NAME)";; \
|
||||
esac; \
|
||||
echo "$(COLOR_GREEN)Package: $(DIST_DIR)/$${PACKAGE}-$(EXTENSION_VERSION).zip$(COLOR_RESET)"
|
||||
|
||||
.PHONY: security-check
|
||||
security-check: ## Run security checks on dependencies
|
||||
@echo "$(COLOR_BLUE)Running security checks...$(COLOR_RESET)"
|
||||
@if [ -f "composer.json" ]; then \
|
||||
$(COMPOSER) audit || echo "$(COLOR_YELLOW)⚠ Vulnerabilities found$(COLOR_RESET)"; \
|
||||
fi
|
||||
@if [ -f "package.json" ]; then \
|
||||
$(NPM) audit || echo "$(COLOR_YELLOW)⚠ Vulnerabilities found$(COLOR_RESET)"; \
|
||||
fi
|
||||
|
||||
.PHONY: all
|
||||
all: install-deps validate test build ## Run complete build pipeline
|
||||
@echo "$(COLOR_GREEN)✓ Complete build pipeline finished$(COLOR_RESET)"
|
||||
|
||||
# Default target
|
||||
.DEFAULT_GOAL := help
|
||||
@@ -9,7 +9,7 @@
|
||||
DEFGROUP: Joomla.Plugin
|
||||
INGROUP: MokoSuiteClient
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteClient
|
||||
VERSION: 02.48.52
|
||||
VERSION: 02.52.04
|
||||
PATH: /README.md
|
||||
BRIEF: MokoSuiteClient platform plugin for Joomla
|
||||
-->
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ DEFGROUP: [PROJECT_NAME]
|
||||
INGROUP: [PROJECT_NAME].Documentation
|
||||
REPO: [REPOSITORY_URL]
|
||||
PATH: /SECURITY.md
|
||||
VERSION: 02.48.52
|
||||
VERSION: 02.52.04
|
||||
BRIEF: Security vulnerability reporting and handling policy
|
||||
-->
|
||||
|
||||
|
||||
@@ -1,237 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# ============================================================================
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Automation.CI
|
||||
# INGROUP: moko-platform.Automation
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# PATH: /automation/ci-issue-reporter.sh
|
||||
# VERSION: 09.23.00
|
||||
# BRIEF: Creates or updates a Gitea issue when a CI gate fails.
|
||||
# Deduplicates by searching open issues with the "ci-auto" label
|
||||
# whose title matches the gate. If a matching issue exists, a comment
|
||||
# is appended instead of opening a duplicate.
|
||||
# ============================================================================
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# ── Defaults ────────────────────────────────────────────────────────────────
|
||||
GITEA_URL="${GITEA_URL:-https://git.mokoconsulting.tech}"
|
||||
GITEA_TOKEN="${GITEA_TOKEN:-}"
|
||||
REPO="${GITHUB_REPOSITORY:-}"
|
||||
RUN_URL="${GITHUB_SERVER_URL:-${GITEA_URL}}/${REPO}/actions/runs/${GITHUB_RUN_ID:-0}"
|
||||
LABEL_NAME="ci-auto"
|
||||
LABEL_COLOR="#e11d48"
|
||||
|
||||
GATE=""
|
||||
DETAILS=""
|
||||
SEVERITY="error"
|
||||
WORKFLOW=""
|
||||
|
||||
# ── Parse arguments ─────────────────────────────────────────────────────────
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: ci-issue-reporter.sh --gate NAME --details TEXT [OPTIONS]
|
||||
|
||||
Required:
|
||||
--gate CI gate name (e.g. "Code Quality", "Self-Health")
|
||||
--details Human-readable failure description
|
||||
|
||||
Optional:
|
||||
--severity "error" (default) or "warning"
|
||||
--workflow Workflow name for the issue title
|
||||
--repo owner/repo (default: \$GITHUB_REPOSITORY)
|
||||
--run-url URL to the CI run (auto-detected from env)
|
||||
--token Gitea API token (default: \$GITEA_TOKEN)
|
||||
--url Gitea base URL (default: \$GITEA_URL)
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--gate) GATE="$2"; shift 2 ;;
|
||||
--details) DETAILS="$2"; shift 2 ;;
|
||||
--severity) SEVERITY="$2"; shift 2 ;;
|
||||
--workflow) WORKFLOW="$2"; shift 2 ;;
|
||||
--repo) REPO="$2"; shift 2 ;;
|
||||
--run-url) RUN_URL="$2"; shift 2 ;;
|
||||
--token) GITEA_TOKEN="$2"; shift 2 ;;
|
||||
--url) GITEA_URL="$2"; shift 2 ;;
|
||||
-h|--help) usage ;;
|
||||
*) echo "Unknown option: $1"; usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -z "$GATE" ]] && { echo "ERROR: --gate is required"; usage; }
|
||||
[[ -z "$DETAILS" ]] && { echo "ERROR: --details is required"; usage; }
|
||||
[[ -z "$GITEA_TOKEN" ]] && { echo "ERROR: GITEA_TOKEN not set"; exit 1; }
|
||||
[[ -z "$REPO" ]] && { echo "ERROR: GITHUB_REPOSITORY not set"; exit 1; }
|
||||
|
||||
API="${GITEA_URL}/api/v1/repos/${REPO}"
|
||||
|
||||
# ── Build title ─────────────────────────────────────────────────────────────
|
||||
if [[ -n "$WORKFLOW" ]]; then
|
||||
TITLE="[CI] ${WORKFLOW}: ${GATE} failed"
|
||||
else
|
||||
TITLE="[CI] ${GATE} failed"
|
||||
fi
|
||||
|
||||
# ── Ensure label exists ─────────────────────────────────────────────────────
|
||||
ensure_label() {
|
||||
local exists
|
||||
exists=$(curl -sf -o /dev/null -w '%{http_code}' \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${API}/labels" 2>/dev/null || echo "000")
|
||||
|
||||
if [[ "$exists" == "200" ]]; then
|
||||
# Check if label already exists
|
||||
local found
|
||||
found=$(curl -sf \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${API}/labels" 2>/dev/null \
|
||||
| grep -o "\"name\":\"${LABEL_NAME}\"" || true)
|
||||
|
||||
if [[ -z "$found" ]]; then
|
||||
curl -sf -X POST \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/labels" \
|
||||
-d "{\"name\":\"${LABEL_NAME}\",\"color\":\"${LABEL_COLOR}\",\"description\":\"Auto-created by CI issue reporter\"}" \
|
||||
> /dev/null 2>&1 || true
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Search for existing open issue ──────────────────────────────────────────
|
||||
find_existing_issue() {
|
||||
# URL-encode the gate name for the query
|
||||
local query
|
||||
query=$(printf '%s' "[CI] ${GATE}" | sed 's/ /%20/g; s/\[/%5B/g; s/\]/%5D/g')
|
||||
|
||||
local response
|
||||
response=$(curl -sf \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${API}/issues?type=issues&state=open&labels=${LABEL_NAME}&q=${query}&limit=5" \
|
||||
2>/dev/null || echo "[]")
|
||||
|
||||
# Extract the first matching issue number
|
||||
echo "$response" \
|
||||
| grep -oP '"number":\s*\K[0-9]+' \
|
||||
| head -1
|
||||
}
|
||||
|
||||
# ── Build issue body ────────────────────────────────────────────────────────
|
||||
build_body() {
|
||||
local severity_badge
|
||||
if [[ "$SEVERITY" == "error" ]]; then
|
||||
severity_badge="**Severity:** Error"
|
||||
else
|
||||
severity_badge="**Severity:** Warning"
|
||||
fi
|
||||
|
||||
cat <<BODY
|
||||
## CI Gate Failure: ${GATE}
|
||||
|
||||
${severity_badge}
|
||||
**Workflow:** ${WORKFLOW:-unknown}
|
||||
**Branch:** ${GITHUB_REF_NAME:-unknown}
|
||||
**Commit:** \`${GITHUB_SHA:0:8}\`
|
||||
**Run:** [View CI run](${RUN_URL})
|
||||
|
||||
### Details
|
||||
|
||||
${DETAILS}
|
||||
|
||||
### Resolution
|
||||
|
||||
Fix the issue described above and push a new commit. This issue will be closed automatically when the gate passes, or can be closed manually.
|
||||
|
||||
---
|
||||
*Auto-created by [ci-issue-reporter](${GITEA_URL}/${REPO}/src/branch/main/automation/ci-issue-reporter.sh)*
|
||||
BODY
|
||||
}
|
||||
|
||||
# ── Build comment body (for existing issues) ────────────────────────────────
|
||||
build_comment() {
|
||||
cat <<COMMENT
|
||||
### CI failure recurrence
|
||||
|
||||
**Branch:** ${GITHUB_REF_NAME:-unknown}
|
||||
**Commit:** \`${GITHUB_SHA:0:8}\`
|
||||
**Run:** [View CI run](${RUN_URL})
|
||||
|
||||
${DETAILS}
|
||||
COMMENT
|
||||
}
|
||||
|
||||
# ── Main ────────────────────────────────────────────────────────────────────
|
||||
ensure_label
|
||||
|
||||
EXISTING=$(find_existing_issue)
|
||||
|
||||
if [[ -n "$EXISTING" ]]; then
|
||||
# Append comment to existing issue
|
||||
COMMENT_BODY=$(build_comment)
|
||||
COMMENT_JSON=$(printf '%s' "$COMMENT_BODY" | python3 -c "
|
||||
import sys, json
|
||||
print(json.dumps({'body': sys.stdin.read()}))" 2>/dev/null)
|
||||
|
||||
HTTP=$(curl -sf -o /dev/null -w '%{http_code}' -X POST \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/issues/${EXISTING}/comments" \
|
||||
-d "${COMMENT_JSON}" 2>/dev/null || echo "000")
|
||||
|
||||
if [[ "$HTTP" == "201" ]]; then
|
||||
echo "Commented on existing issue #${EXISTING}"
|
||||
else
|
||||
echo "WARNING: Failed to comment on issue #${EXISTING} (HTTP ${HTTP})"
|
||||
fi
|
||||
else
|
||||
# Create new issue
|
||||
ISSUE_BODY=$(build_body)
|
||||
ISSUE_JSON=$(python3 -c "
|
||||
import sys, json
|
||||
body = sys.stdin.read()
|
||||
print(json.dumps({
|
||||
'title': sys.argv[1],
|
||||
'body': body,
|
||||
'labels': []
|
||||
}))" "$TITLE" <<< "$ISSUE_BODY" 2>/dev/null)
|
||||
|
||||
# Create the issue
|
||||
RESPONSE=$(curl -sf -X POST \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/issues" \
|
||||
-d "${ISSUE_JSON}" 2>/dev/null || echo "{}")
|
||||
|
||||
ISSUE_NUM=$(echo "$RESPONSE" | grep -oP '"number":\s*\K[0-9]+' | head -1)
|
||||
|
||||
if [[ -n "$ISSUE_NUM" ]]; then
|
||||
# Apply label (separate call — more reliable across Gitea versions)
|
||||
LABEL_ID=$(curl -sf \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
"${API}/labels" 2>/dev/null \
|
||||
| grep -oP "\"id\":\s*\K[0-9]+(?=[^}]*\"name\":\s*\"${LABEL_NAME}\")" \
|
||||
| head -1 || true)
|
||||
|
||||
if [[ -n "$LABEL_ID" ]]; then
|
||||
curl -sf -X POST \
|
||||
-H "Authorization: token ${GITEA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/issues/${ISSUE_NUM}/labels" \
|
||||
-d "{\"labels\":[${LABEL_ID}]}" \
|
||||
> /dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
echo "Created issue #${ISSUE_NUM}: ${TITLE}"
|
||||
else
|
||||
echo "WARNING: Failed to create issue"
|
||||
echo "Response: ${RESPONSE}"
|
||||
fi
|
||||
fi
|
||||
@@ -11,13 +11,13 @@
|
||||
INGROUP: MokoSuiteClient.Build
|
||||
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
|
||||
FILE: build-guide.md
|
||||
VERSION: 02.48.52
|
||||
VERSION: 02.52.04
|
||||
PATH: /docs/guides/
|
||||
BRIEF: Build and packaging guide for the MokoSuiteClient system plugin
|
||||
NOTE: Defines environment setup, repository layout, packaging rules, and release preparation
|
||||
-->
|
||||
|
||||
# MokoSuiteClient Build Guide (VERSION: 02.48.52)
|
||||
# MokoSuiteClient Build Guide (VERSION: 02.52.04)
|
||||
|
||||
## 1. Purpose
|
||||
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
DEFGROUP: Joomla.Plugin
|
||||
INGROUP: MokoSuiteClient.Guides
|
||||
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
|
||||
VERSION: 02.48.52
|
||||
VERSION: 02.52.04
|
||||
PATH: /docs/guides/configuration-guide.md
|
||||
BRIEF: Configuration guide for the MokoSuiteClient system plugin
|
||||
NOTE: Defines plugin parameters, expected behaviors, and recommended defaults
|
||||
-->
|
||||
|
||||
# MokoSuiteClient Configuration Guide (VERSION: 02.48.52)
|
||||
# MokoSuiteClient Configuration Guide (VERSION: 02.52.04)
|
||||
|
||||
## 1. Objective
|
||||
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
DEFGROUP: Joomla.Plugin
|
||||
INGROUP: MokoSuiteClient.Guides
|
||||
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
|
||||
VERSION: 02.48.52
|
||||
VERSION: 02.52.04
|
||||
PATH: /docs/guides/installation-guide.md
|
||||
BRIEF: Installation guide for the MokoSuiteClient system plugin
|
||||
NOTE: First document in the guide set
|
||||
-->
|
||||
|
||||
# MokoSuiteClient Installation Guide (VERSION: 02.48.52)
|
||||
# MokoSuiteClient Installation Guide (VERSION: 02.52.04)
|
||||
|
||||
## Introduction
|
||||
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
DEFGROUP: Joomla.Plugin
|
||||
INGROUP: MokoSuiteClient.Guides
|
||||
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
|
||||
VERSION: 02.48.52
|
||||
VERSION: 02.52.04
|
||||
PATH: /docs/guides/operations-guide.md
|
||||
BRIEF: Operational guide for administering and managing the MokoSuiteClient system plugin
|
||||
NOTE: Defines lifecycle, responsibilities, and operational behaviors
|
||||
-->
|
||||
|
||||
# MokoSuiteClient Operations Guide (VERSION: 02.48.52)
|
||||
# MokoSuiteClient Operations Guide (VERSION: 02.52.04)
|
||||
|
||||
## Introduction
|
||||
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
DEFGROUP: Joomla.Plugin
|
||||
INGROUP: MokoSuiteClient.Guides
|
||||
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
|
||||
VERSION: 02.48.52
|
||||
VERSION: 02.52.04
|
||||
PATH: /docs/guides/rollback-and-recovery-guide.md
|
||||
BRIEF: Rollback and recovery guide for restoring stable operation after plugin related incidents
|
||||
NOTE: Completes the core guide set for Suite plugin governance
|
||||
-->
|
||||
|
||||
# MokoSuiteClient Rollback and Recovery Guide (VERSION: 02.48.52)
|
||||
# MokoSuiteClient Rollback and Recovery Guide (VERSION: 02.52.04)
|
||||
|
||||
## Introduction
|
||||
|
||||
|
||||
@@ -7,13 +7,13 @@
|
||||
DEFGROUP: Joomla.Plugin
|
||||
INGROUP: MokoSuiteClient.Guides
|
||||
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
|
||||
VERSION: 02.48.52
|
||||
VERSION: 02.52.04
|
||||
PATH: /docs/guides/testing-guide.md
|
||||
BRIEF: Testing guide for MokoSuiteClient v02.01.08
|
||||
NOTE: Covers manual test procedures for language overrides, install/uninstall, and configuration
|
||||
-->
|
||||
|
||||
# MokoSuiteClient Testing Guide (VERSION: 02.48.52)
|
||||
# MokoSuiteClient Testing Guide (VERSION: 02.52.04)
|
||||
|
||||
## 1. Prerequisites
|
||||
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
DEFGROUP: Joomla.Plugin
|
||||
INGROUP: MokoSuiteClient.Guides
|
||||
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
|
||||
VERSION: 02.48.52
|
||||
VERSION: 02.52.04
|
||||
PATH: /docs/guides/troubleshooting-guide.md
|
||||
BRIEF: Troubleshooting guide for diagnosing and resolving issues related to the MokoSuiteClient plugin
|
||||
NOTE: Designed for administrators and Suite operations teams
|
||||
-->
|
||||
|
||||
# MokoSuiteClient Troubleshooting Guide (VERSION: 02.48.52)
|
||||
# MokoSuiteClient Troubleshooting Guide (VERSION: 02.52.04)
|
||||
|
||||
## Introduction
|
||||
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
DEFGROUP: Joomla.Plugin
|
||||
INGROUP: MokoSuiteClient.Guides
|
||||
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
|
||||
VERSION: 02.48.52
|
||||
VERSION: 02.52.04
|
||||
PATH: /docs/guides/upgrade-and-versioning-guide.md
|
||||
BRIEF: Guide for updating, versioning, and maintaining the MokoSuiteClient plugin
|
||||
NOTE: Defines release flow, version rules, and upgrade validation
|
||||
-->
|
||||
|
||||
# MokoSuiteClient Upgrade and Versioning Guide (VERSION: 02.48.52)
|
||||
# MokoSuiteClient Upgrade and Versioning Guide (VERSION: 02.52.04)
|
||||
|
||||
## Introduction
|
||||
|
||||
|
||||
+2
-2
@@ -10,13 +10,13 @@
|
||||
DEFGROUP: Joomla.Plugin
|
||||
INGROUP: MokoSuiteClient.Documentation
|
||||
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
|
||||
VERSION: 02.48.52
|
||||
VERSION: 02.52.04
|
||||
PATH: /docs/index.md
|
||||
BRIEF: Master index of all documentation for the MokoSuiteClient plugin
|
||||
NOTE: Automatically maintained index for all guide canvases
|
||||
-->
|
||||
|
||||
# MokoSuiteClient Documentation Index (VERSION: 02.48.52)
|
||||
# MokoSuiteClient Documentation Index (VERSION: 02.52.04)
|
||||
|
||||
## Introduction
|
||||
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
INGROUP: MokoSuiteClient
|
||||
REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
|
||||
PATH: /docs/plugin-basic.md
|
||||
VERSION: 02.48.52
|
||||
VERSION: 02.52.04
|
||||
BRIEF: Baseline documentation for the MokoSuiteClient system plugin
|
||||
NOTE: Foundational reference for internal and external stakeholders
|
||||
-->
|
||||
|
||||
# MokoSuiteClient Plugin Overview (VERSION: 02.48.52)
|
||||
# MokoSuiteClient Plugin Overview (VERSION: 02.52.04)
|
||||
|
||||
## Introduction
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ DEFGROUP: MokoSuiteClient.Documentation
|
||||
INGROUP: MokoStandards.Templates
|
||||
REPO: https://github.com/mokoconsulting-tech/MokoSuiteClient
|
||||
PATH: /docs/update-server.md
|
||||
VERSION: 02.48.52
|
||||
VERSION: 02.52.04
|
||||
BRIEF: How this extension's Joomla update server file (update.xml) is managed
|
||||
-->
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
-- 02.52.00 — Create license cache table (missing from prior migrations)
|
||||
-- This table was in install.mysql.sql but never had an update file,
|
||||
-- so existing installs never received it.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `#__mokosuite_license_cache` (
|
||||
`dlid_hash` CHAR(64) NOT NULL COMMENT 'SHA-256 of DLID (never store raw DLID)',
|
||||
`response_data` TEXT NOT NULL COMMENT 'JSON validation response from MokoGitea',
|
||||
`checked_at` DATETIME NOT NULL,
|
||||
PRIMARY KEY (`dlid_hash`),
|
||||
KEY `idx_checked` (`checked_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
@@ -85,7 +85,12 @@ class DisplayController extends BaseController
|
||||
|
||||
public function sendHeartbeat()
|
||||
{
|
||||
Session::checkToken() or die(Text::_('JINVALID_TOKEN'));
|
||||
if (!Session::checkToken())
|
||||
{
|
||||
$this->jsonResponse(['success' => false, 'message' => 'Session expired — please reload the page.']);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -112,6 +112,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
// Heartbeat + PIN send button
|
||||
var hbBtn = document.getElementById('mokosuiteclient-btn-heartbeat-pin');
|
||||
var hbIconTimeout = null;
|
||||
if (hbBtn) {
|
||||
hbBtn.addEventListener('click', function () {
|
||||
var btn = this;
|
||||
@@ -119,27 +120,40 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
var token = btn.dataset.token;
|
||||
var icon = btn.querySelector('span');
|
||||
|
||||
if (hbIconTimeout) { clearTimeout(hbIconTimeout); hbIconTimeout = null; }
|
||||
btn.disabled = true;
|
||||
if (icon) icon.className = 'icon-spinner icon-spin';
|
||||
if (icon) { icon.className = 'icon-spinner icon-spin'; icon.style.color = ''; }
|
||||
|
||||
var fd = new FormData();
|
||||
fd.append(token, '1');
|
||||
|
||||
fetch(url, {method: 'POST', body: fd, headers: {'X-Requested-With': 'XMLHttpRequest'}})
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (r) {
|
||||
return r.text().then(function (text) {
|
||||
try { return JSON.parse(text); }
|
||||
catch (e) { return {success: false, message: 'Server error: ' + text.substring(0, 200)}; }
|
||||
});
|
||||
})
|
||||
.then(function (d) {
|
||||
var msg = d.message || (d.success ? 'Heartbeat sent to HQ.' : 'Heartbeat failed.');
|
||||
if (d.success) {
|
||||
Joomla.renderMessages({message: [d.message || 'Heartbeat sent to HQ.']});
|
||||
if (icon) { icon.className = 'icon-check'; icon.style.color = '#198754'; }
|
||||
Joomla.renderMessages({message: [msg]});
|
||||
} else {
|
||||
Joomla.renderMessages({error: [d.message || 'Heartbeat failed.']});
|
||||
if (icon) { icon.className = 'icon-times'; icon.style.color = '#dc3545'; }
|
||||
Joomla.renderMessages({error: [msg]});
|
||||
}
|
||||
})
|
||||
.catch(function () {
|
||||
Joomla.renderMessages({error: ['Network error sending heartbeat.']});
|
||||
.catch(function (err) {
|
||||
if (icon) { icon.className = 'icon-times'; icon.style.color = '#dc3545'; }
|
||||
Joomla.renderMessages({error: ['Heartbeat failed: ' + (err.message || 'network error')]});
|
||||
})
|
||||
.finally(function () {
|
||||
btn.disabled = false;
|
||||
if (icon) icon.className = 'icon-upload';
|
||||
hbIconTimeout = setTimeout(function () {
|
||||
if (icon) { icon.className = 'icon-upload'; icon.style.color = ''; }
|
||||
hbIconTimeout = null;
|
||||
}, 3000);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<license>GPL-3.0-or-later</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.48.52</version>
|
||||
<version>02.52.04</version>
|
||||
<description>MokoSuiteClient admin dashboard and REST API. Provides a control panel for managing MokoSuiteClient feature plugins, site health monitoring, and remote management endpoints.</description>
|
||||
|
||||
<namespace path="src">Moko\Component\MokoSuiteClient</namespace>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<license>GPL-3.0-or-later</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.48.52</version>
|
||||
<version>02.52.04</version>
|
||||
<description>MOD_MOKOSUITECLIENT_CACHE_DESC</description>
|
||||
<namespace path="src">Moko\Module\MokoSuiteClientCache</namespace>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<license>GPL-3.0-or-later</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.48.52</version>
|
||||
<version>02.52.04</version>
|
||||
<description>MOD_MOKOSUITECLIENT_CATEGORIES_DESC</description>
|
||||
<namespace path="src">Moko\Module\MokoSuiteClientCategories</namespace>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<license>GPL-3.0-or-later</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.48.52</version>
|
||||
<version>02.52.04</version>
|
||||
<description>MOD_MOKOSUITECLIENT_CPANEL_DESC</description>
|
||||
<namespace path="src">Moko\Module\MokoSuiteClientCpanel</namespace>
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<license>GPL-3.0-or-later</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.48.52</version>
|
||||
<version>02.52.04</version>
|
||||
<description>MokoSuiteClient admin sidebar menu — renders a dedicated MokoSuiteClient section in the admin menu before Joomla's default menu.</description>
|
||||
<namespace path="src">Moko\Module\MokoSuiteClientMenu</namespace>
|
||||
|
||||
|
||||
@@ -262,7 +262,7 @@ $iconStyle = 'display:inline-block!important;width:1.25em;text-align:center;marg
|
||||
$hasChildren = !empty($comp['children']);
|
||||
?>
|
||||
<?php if ($hasChildren): ?>
|
||||
<li class="item parent item-level-2 mokosuiteclient-ext-item<?php echo $compActive ? ' mm-active' : ''; ?>">
|
||||
<li class="item parent item-level-1 mokosuiteclient-ext-item<?php echo $compActive ? ' mm-active' : ''; ?>">
|
||||
<a class="has-arrow<?php echo $compActive ? ' mm-active' : ''; ?>" href="#">
|
||||
<span class="<?php echo htmlspecialchars($comp['icon'], ENT_QUOTES, 'UTF-8'); ?>" aria-hidden="true" style="<?php echo $iconStyle; ?>"></span>
|
||||
<span class="sidebar-item-title"><?php echo htmlspecialchars($comp['title'], ENT_QUOTES, 'UTF-8'); ?></span>
|
||||
@@ -282,7 +282,7 @@ $iconStyle = 'display:inline-block!important;width:1.25em;text-align:center;marg
|
||||
: ($currentView === $childView);
|
||||
}
|
||||
?>
|
||||
<li class="item mokosuiteclient-ext-child<?php echo $childActive ? ' mm-active' : ''; ?>">
|
||||
<li class="item item-level-2 mokosuiteclient-ext-child<?php echo $childActive ? ' mm-active' : ''; ?>">
|
||||
<a class="no-dropdown<?php echo $childActive ? ' mm-active' : ''; ?>" href="<?php echo Route::_($child['link']); ?>"<?php echo $childActive ? ' aria-current="page"' : ''; ?>>
|
||||
<span class="<?php echo htmlspecialchars($child['icon'], ENT_QUOTES, 'UTF-8'); ?>" aria-hidden="true" style="<?php echo $iconStyle; ?>"></span>
|
||||
<span class="sidebar-item-title"><?php echo htmlspecialchars($child['title'], ENT_QUOTES, 'UTF-8'); ?></span>
|
||||
@@ -292,7 +292,7 @@ $iconStyle = 'display:inline-block!important;width:1.25em;text-align:center;marg
|
||||
</ul>
|
||||
</li>
|
||||
<?php else: ?>
|
||||
<li class="item mokosuiteclient-ext-item<?php echo $compActive ? ' mm-active' : ''; ?>">
|
||||
<li class="item item-level-1 mokosuiteclient-ext-item<?php echo $compActive ? ' mm-active' : ''; ?>">
|
||||
<a class="no-dropdown<?php echo $compActive ? ' mm-active' : ''; ?>" href="<?php echo Route::_($comp['link']); ?>"<?php echo $compActive ? ' aria-current="page"' : ''; ?>>
|
||||
<span class="<?php echo htmlspecialchars($comp['icon'], ENT_QUOTES, 'UTF-8'); ?>" aria-hidden="true" style="<?php echo $iconStyle; ?>"></span>
|
||||
<span class="sidebar-item-title"><?php echo htmlspecialchars($comp['title'], ENT_QUOTES, 'UTF-8'); ?></span>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* DEFGROUP: Joomla.Plugin
|
||||
* INGROUP: MokoSuiteClient
|
||||
* REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
|
||||
* VERSION: 02.48.52
|
||||
* VERSION: 02.52.04
|
||||
* PATH: /src/Extension/MokoSuiteClient.php
|
||||
* NOTE: Core system plugin for MokoSuiteClient admin tools suite
|
||||
*/
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: Joomla.Plugin
|
||||
* INGROUP: MokoSuiteClient
|
||||
* VERSION: 02.48.52
|
||||
* VERSION: 02.52.04
|
||||
* PATH: /src/Field/ArticlesField.php
|
||||
* BRIEF: List field that populates with published Joomla articles
|
||||
*/
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: Joomla.Plugin
|
||||
* INGROUP: MokoSuiteClient
|
||||
* VERSION: 02.48.52
|
||||
* VERSION: 02.52.04
|
||||
* PATH: /src/Field/CopyableTokenField.php
|
||||
* BRIEF: Read-only token field with a copy-to-clipboard button
|
||||
*/
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<license>GNU General Public License version 3 or later; see LICENSE.md</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.48.52</version>
|
||||
<version>02.52.04</version>
|
||||
<description>MokoSuiteClient core system plugin — coordinates feature plugins, heartbeat, health checks, and admin customizations.</description>
|
||||
<namespace path=".">Moko\Plugin\System\MokoSuiteClient</namespace>
|
||||
<scriptfile>script.php</scriptfile>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* DEFGROUP: Joomla.Plugin
|
||||
* INGROUP: MokoSuiteClient
|
||||
* REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
|
||||
* VERSION: 02.48.52
|
||||
* VERSION: 02.52.04
|
||||
* PATH: /src/script.php
|
||||
* BRIEF: Installation script for MokoSuiteClient plugin
|
||||
* NOTE: Handles installation, update, and uninstallation tasks including language override deployment
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
* DEFGROUP: Joomla.Plugin
|
||||
* INGROUP: MokoSuiteClient
|
||||
* REPO: https://github.com/mokoconsulting-tech/mokosuiteclient
|
||||
* VERSION: 02.48.52
|
||||
* VERSION: 02.52.04
|
||||
* PATH: /src/services/provider.php
|
||||
* BRIEF: Service provider for dependency injection in Joomla 5.x
|
||||
* NOTE: Registers the plugin with Joomla's DI container
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<license>GPL-3.0-or-later</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.48.52</version>
|
||||
<version>02.52.04</version>
|
||||
<description>PLG_SYSTEM_MOKOSUITECLIENT_BACKUP_DESC</description>
|
||||
<namespace path="src">Moko\Plugin\System\MokoSuiteClientBackup</namespace>
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<license>GPL-3.0-or-later</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.48.52</version>
|
||||
<version>02.52.04</version>
|
||||
<description>PLG_SYSTEM_MOKOSUITECLIENT_DBIP_DESC</description>
|
||||
<namespace path="src">Moko\Plugin\System\MokoSuiteClientDBIP</namespace>
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<license>GPL-3.0-or-later</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.48.52</version>
|
||||
<version>02.52.04</version>
|
||||
<description>PLG_SYSTEM_MOKOSUITECLIENT_DEVTOOLS_DESC</description>
|
||||
<namespace path="src">Moko\Plugin\System\MokoSuiteClientDevTools</namespace>
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<license>GPL-3.0-or-later</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.48.52</version>
|
||||
<version>02.52.04</version>
|
||||
<description>PLG_SYSTEM_MOKOSUITECLIENT_FIREWALL_DESC</description>
|
||||
<namespace path="src">Moko\Plugin\System\MokoSuiteClientFirewall</namespace>
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<license>GPL-3.0-or-later</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.48.52</version>
|
||||
<version>02.52.04</version>
|
||||
<description>PLG_SYSTEM_MOKOSUITECLIENT_LICENSE_DESC</description>
|
||||
<namespace path="src">Moko\Plugin\System\MokoSuiteClientLicense</namespace>
|
||||
<files><folder>src</folder><folder>services</folder><folder>language</folder></files>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<license>GPL-3.0-or-later</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.48.52</version>
|
||||
<version>02.52.04</version>
|
||||
<description>PLG_SYSTEM_MOKOSUITECLIENT_OFFLINE_DESC</description>
|
||||
<namespace path="src">Moko\Plugin\System\MokoSuiteClientOffline</namespace>
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<license>GPL-3.0-or-later</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.48.52</version>
|
||||
<version>02.52.04</version>
|
||||
<description>PLG_SYSTEM_MOKOSUITECLIENT_TENANT_DESC</description>
|
||||
<namespace path="src">Moko\Plugin\System\MokoSuiteClientTenant</namespace>
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<license>GNU General Public License version 3 or later; see LICENSE</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.48.52</version>
|
||||
<version>02.52.04</version>
|
||||
<description>PLG_TASK_MOKOSUITECLIENTDEMO_DESC</description>
|
||||
<namespace path="src">Moko\Plugin\Task\MokoSuiteClientDemo</namespace>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* INGROUP: MokoSuiteClient
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteClient
|
||||
* PATH: /src/packages/plg_system_mokosuiteclient/Service/DemoResetService.php
|
||||
* VERSION: 02.48.52
|
||||
* VERSION: 02.52.04
|
||||
* BRIEF: Content-only snapshot/restore for demo site reset
|
||||
*/
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
<license>GNU General Public License version 3 or later; see LICENSE</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.48.52</version>
|
||||
<version>02.52.04</version>
|
||||
<description>PLG_TASK_MOKOSUITECLIENTSYNC_DESC</description>
|
||||
<namespace path="src">Moko\Plugin\Task\MokoSuiteClientSync</namespace>
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* INGROUP: MokoSuiteClient
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteClient
|
||||
* PATH: /src/packages/plg_system_mokosuiteclient/Service/ContentSyncReceiver.php
|
||||
* VERSION: 02.48.52
|
||||
* VERSION: 02.52.04
|
||||
* BRIEF: Receiver-side content sync — applies incoming payload to local DB
|
||||
*/
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* INGROUP: MokoSuiteClient
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoSuiteClient
|
||||
* PATH: /src/packages/plg_system_mokosuiteclient/Service/ContentSyncService.php
|
||||
* VERSION: 02.48.52
|
||||
* VERSION: 02.52.04
|
||||
* BRIEF: Sender-side content sync — builds payload and pushes to remote sites
|
||||
*/
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<license>GPL-3.0-or-later</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.48.52</version>
|
||||
<version>02.52.04</version>
|
||||
<description>Joomla Web Services API routes for MokoSuiteClient site management — health checks, cache, updates, backups, and site info.</description>
|
||||
<namespace path="src">Moko\Plugin\WebServices\MokoSuiteClient</namespace>
|
||||
<files>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<extension type="package" method="upgrade">
|
||||
<name>Package - MokoSuiteClient</name>
|
||||
<packagename>mokosuiteclient</packagename>
|
||||
<version>02.48.52</version>
|
||||
<version>02.52.04</version>
|
||||
<creationDate>2026-06-02</creationDate>
|
||||
<author>Moko Consulting</author>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
|
||||
Reference in New Issue
Block a user