Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 59875466f4 | |||
| 32cb7cecd6 | |||
| 24e42d9132 | |||
| d957022fc1 | |||
| 82d5beb0f0 | |||
| bf202a4a50 | |||
| 9968c81660 | |||
| 6802b256d0 | |||
| 7ebd2e6385 | |||
| d0ea5e43c0 | |||
| e0ef643c04 | |||
| 7b9ea92faa | |||
| 9f628201ad | |||
| 86f341bd72 | |||
| 4cdf7234d9 | |||
| e19766cf00 | |||
| 368d5eb2ed | |||
| 6c9538e36c | |||
| 08ec94953a | |||
| 9b30244361 | |||
| 121219a810 | |||
| 6cb8751f89 | |||
| 3b118d7b49 | |||
| fccd80da3c | |||
| 58fb87bb05 | |||
| 9680004f78 | |||
| 86faa5086f | |||
| 696c5a45c4 | |||
| cbdbdcd553 | |||
| a5bab02fd1 | |||
| c9a2cfa15c | |||
| af76a8f2cb | |||
| 1001df963e | |||
| ed7d356e99 | |||
| 396b60902a | |||
| c7d55e7fdd | |||
| ad7fc53fb9 | |||
| ad6a7e90e9 | |||
| 041111b8ab | |||
| b65dd95dc4 | |||
| 8428f29058 | |||
| 9e6cebb9aa | |||
| 595ee55499 | |||
| 6e05b6aea0 | |||
| 35bc550e0c | |||
| c6b52100de | |||
| c9470a45fe | |||
| 7962b370fa | |||
| 0e4b0d55cb | |||
| 2f7cbb57f9 | |||
| 006b47bbdf | |||
| 0cc2fea1bc | |||
| 5a9e8d86a1 | |||
| 0cdd186eae | |||
| 5e77328b66 | |||
| ac05b97262 | |||
| b1391c6d03 | |||
| ec75322bed | |||
| 6a6a8d7c0c |
@@ -9,7 +9,7 @@
|
|||||||
<display-name>Template - MokoOnyx</display-name>
|
<display-name>Template - MokoOnyx</display-name>
|
||||||
<org>MokoConsulting</org>
|
<org>MokoConsulting</org>
|
||||||
<description>MokoOnyx - Joomla site template (successor to MokoCassiopeia)</description>
|
<description>MokoOnyx - Joomla site template (successor to MokoCassiopeia)</description>
|
||||||
<version>02.20.00</version>
|
<version>02.19.06</version>
|
||||||
<license spdx="GPL-3.0-or-later">GNU General Public License v3</license>
|
<license spdx="GPL-3.0-or-later">GNU General Public License v3</license>
|
||||||
</identity>
|
</identity>
|
||||||
<governance>
|
<governance>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. |
|
# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. |
|
||||||
# | |
|
# | |
|
||||||
# | Platform-specific: |
|
# | Platform-specific: |
|
||||||
# | joomla: XML manifest, updates.xml, type-prefixed packages |
|
# | joomla: XML manifest, type-prefixed packages |
|
||||||
# | dolibarr: mod*.class.php, update.txt, dev version reset |
|
# | dolibarr: mod*.class.php, update.txt, dev version reset |
|
||||||
# | generic: README-only, no update stream |
|
# | generic: README-only, no update stream |
|
||||||
# | |
|
# | |
|
||||||
@@ -71,20 +71,25 @@ jobs:
|
|||||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||||
run: |
|
run: |
|
||||||
if ! command -v composer &> /dev/null; then
|
if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; 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
|
echo Using pre-installed /opt/moko-platform
|
||||||
|
echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo Falling back to fresh clone
|
||||||
|
if ! command -v composer > /dev/null 2>&1; then
|
||||||
|
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1
|
||||||
|
fi
|
||||||
|
rm -rf /tmp/moko-platform-api
|
||||||
|
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git
|
||||||
|
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api
|
||||||
|
cd /tmp/moko-platform-api
|
||||||
|
composer install --no-dev --no-interaction --quiet
|
||||||
|
echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
|
||||||
fi
|
fi
|
||||||
# Always fetch latest CLI tools — never use stale cache from previous runs
|
|
||||||
rm -rf /tmp/moko-platform-api
|
|
||||||
git clone --depth 1 --branch main --quiet \
|
|
||||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
|
||||||
/tmp/moko-platform-api
|
|
||||||
cd /tmp/moko-platform-api
|
|
||||||
composer install --no-dev --no-interaction --quiet
|
|
||||||
|
|
||||||
- name: Rename branch to rc
|
- name: Rename branch to rc
|
||||||
run: |
|
run: |
|
||||||
php /tmp/moko-platform-api/cli/branch_rename.php \
|
php ${MOKO_CLI}/branch_rename.php \
|
||||||
--from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \
|
--from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \
|
||||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||||
--api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \
|
--api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \
|
||||||
@@ -100,7 +105,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Publish RC release
|
- name: Publish RC release
|
||||||
run: |
|
run: |
|
||||||
php /tmp/moko-platform-api/cli/release_publish.php \
|
php ${MOKO_CLI}/release_publish.php \
|
||||||
--path . --stability rc --bump minor --branch rc \
|
--path . --stability rc --bump minor --branch rc \
|
||||||
--token "${{ secrets.MOKOGITEA_TOKEN }}"
|
--token "${{ secrets.MOKOGITEA_TOKEN }}"
|
||||||
|
|
||||||
@@ -108,7 +113,7 @@ jobs:
|
|||||||
if: always()
|
if: always()
|
||||||
run: |
|
run: |
|
||||||
echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY
|
echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "Branch renamed to rc, minor bump, RC + lesser stream releases built, updates.xml synced" >> $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:
|
release:
|
||||||
@@ -150,25 +155,61 @@ jobs:
|
|||||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}'
|
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}'
|
||||||
run: |
|
run: |
|
||||||
# Ensure PHP + Composer are available
|
if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then
|
||||||
if ! command -v composer &> /dev/null; then
|
echo Using pre-installed /opt/moko-platform
|
||||||
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
|
echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo Falling back to fresh clone
|
||||||
|
if ! command -v composer > /dev/null 2>&1; then
|
||||||
|
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1
|
||||||
|
fi
|
||||||
|
rm -rf /tmp/moko-platform-api
|
||||||
|
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git
|
||||||
|
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api
|
||||||
|
cd /tmp/moko-platform-api
|
||||||
|
composer install --no-dev --no-interaction --quiet
|
||||||
|
echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
|
||||||
fi
|
fi
|
||||||
# Always fetch latest CLI tools — never use stale cache from previous runs
|
|
||||||
rm -rf /tmp/moko-platform-api
|
|
||||||
git clone --depth 1 --branch main --quiet \
|
|
||||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
|
||||||
/tmp/moko-platform-api
|
|
||||||
cd /tmp/moko-platform-api
|
|
||||||
composer install --no-dev --no-interaction --quiet
|
|
||||||
|
|
||||||
|
|
||||||
- name: "Publish stable release"
|
- name: "Publish stable release"
|
||||||
run: |
|
run: |
|
||||||
php /tmp/moko-platform-api/cli/release_publish.php \
|
php ${MOKO_CLI}/release_publish.php \
|
||||||
--path . --stability stable --bump minor --branch main \
|
--path . --stability stable --bump minor --branch main \
|
||||||
--token "${{ secrets.MOKOGITEA_TOKEN }}"
|
--token "${{ secrets.MOKOGITEA_TOKEN }}"
|
||||||
|
|
||||||
|
- name: Update release notes from CHANGELOG.md
|
||||||
|
run: |
|
||||||
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
|
|
||||||
|
# Extract [Unreleased] section from changelog
|
||||||
|
if [ -f "CHANGELOG.md" ]; then
|
||||||
|
NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
|
||||||
|
[ -z "$NOTES" ] && NOTES="Stable release"
|
||||||
|
else
|
||||||
|
NOTES="Stable release"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update release body via API
|
||||||
|
RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||||
|
"${API_BASE}/releases/tags/stable" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
||||||
|
|
||||||
|
if [ -n "$RELEASE_ID" ]; then
|
||||||
|
python3 -c "
|
||||||
|
import json, urllib.request
|
||||||
|
body = open('/dev/stdin').read()
|
||||||
|
payload = json.dumps({'body': body}).encode()
|
||||||
|
req = urllib.request.Request(
|
||||||
|
'${API_BASE}/releases/${RELEASE_ID}',
|
||||||
|
data=payload, method='PATCH',
|
||||||
|
headers={
|
||||||
|
'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
})
|
||||||
|
urllib.request.urlopen(req)
|
||||||
|
" <<< "$NOTES"
|
||||||
|
echo "Release notes updated from CHANGELOG.md"
|
||||||
|
fi
|
||||||
|
|
||||||
# -- STEP 9: Mirror to GitHub (stable only) --------------------------------
|
# -- STEP 9: Mirror to GitHub (stable only) --------------------------------
|
||||||
- name: "Step 9: Mirror release to GitHub"
|
- name: "Step 9: Mirror release to GitHub"
|
||||||
if: >-
|
if: >-
|
||||||
@@ -180,7 +221,7 @@ jobs:
|
|||||||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||||
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
|
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
php /tmp/moko-platform-api/cli/release_mirror.php \
|
php ${MOKO_CLI}/release_mirror.php \
|
||||||
--version "$VERSION" --tag "$RELEASE_TAG" \
|
--version "$VERSION" --tag "$RELEASE_TAG" \
|
||||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||||
--gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \
|
--gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \
|
||||||
@@ -254,7 +295,7 @@ jobs:
|
|||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: |
|
run: |
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
php /tmp/moko-platform-api/cli/version_reset_dev.php \
|
php ${MOKO_CLI}/version_reset_dev.php \
|
||||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \
|
||||||
--branch dev --path . 2>&1 || true
|
--branch dev --path . 2>&1 || true
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: Gitea.Workflow
|
# DEFGROUP: Gitea.Workflow
|
||||||
# INGROUP: moko-platform.Automation
|
# INGROUP: moko-platform.Automation
|
||||||
# VERSION: 02.20.00
|
# VERSION: 02.19.06
|
||||||
# BRIEF: Auto-create feature branch when an issue is opened
|
# BRIEF: Auto-create feature branch when an issue is opened
|
||||||
|
|
||||||
name: "Universal: Issue Branch"
|
name: "Universal: Issue Branch"
|
||||||
|
|||||||
@@ -105,6 +105,19 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Check for merge conflict markers
|
||||||
|
run: |
|
||||||
|
CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true)
|
||||||
|
if [ -n "$CONFLICTS" ]; then
|
||||||
|
echo "::error::Merge conflict markers found in source files"
|
||||||
|
echo "## Conflict Markers Found" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "No conflict markers found"
|
||||||
|
|
||||||
- name: Detect platform
|
- name: Detect platform
|
||||||
id: platform
|
id: platform
|
||||||
run: |
|
run: |
|
||||||
@@ -134,6 +147,98 @@ jobs:
|
|||||||
echo "PHP lint: ${ERRORS} error(s)"
|
echo "PHP lint: ${ERRORS} error(s)"
|
||||||
[ "$ERRORS" -eq 0 ] || { echo "::error::PHP syntax errors found"; exit 1; }
|
[ "$ERRORS" -eq 0 ] || { echo "::error::PHP syntax errors found"; exit 1; }
|
||||||
|
|
||||||
|
- name: Joomla JEXEC guard check
|
||||||
|
if: steps.platform.outputs.platform == 'joomla'
|
||||||
|
run: |
|
||||||
|
ERRORS=0
|
||||||
|
while IFS= read -r -d '' file; do
|
||||||
|
# Skip vendor, node_modules, and index.html stub files
|
||||||
|
case "$file" in ./vendor/*|./node_modules/*) continue ;; esac
|
||||||
|
# Check first 10 lines for JEXEC or JPATH guard
|
||||||
|
if ! head -20 "$file" | grep -qE "defined\s*\(\s*['\"](_JEXEC|JPATH_BASE|\\\\JPATH_PLATFORM)['\"]"; then
|
||||||
|
echo "::error file=${file}::Missing JEXEC guard: ${file}"
|
||||||
|
ERRORS=$((ERRORS + 1))
|
||||||
|
fi
|
||||||
|
done < <(find . -name "*.php" -path "*/src/*" -not -path "./.git/*" -not -path "./vendor/*" -print0)
|
||||||
|
if [ "$ERRORS" -gt 0 ]; then
|
||||||
|
echo "::error::${ERRORS} PHP file(s) missing defined('_JEXEC') or die guard"
|
||||||
|
echo "## JEXEC Guard Check: Failed" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "${ERRORS} file(s) in src/ are missing the Joomla execution guard." >> $GITHUB_STEP_SUMMARY
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "JEXEC guard: OK"
|
||||||
|
|
||||||
|
- name: Joomla directory listing protection
|
||||||
|
if: steps.platform.outputs.platform == 'joomla'
|
||||||
|
run: |
|
||||||
|
MISSING=0
|
||||||
|
SOURCE_DIR="src"
|
||||||
|
[ ! -d "$SOURCE_DIR" ] && exit 0
|
||||||
|
while IFS= read -r dir; do
|
||||||
|
if [ ! -f "${dir}/index.html" ]; then
|
||||||
|
echo "::warning::Missing index.html in ${dir} (directory listing protection)"
|
||||||
|
MISSING=$((MISSING + 1))
|
||||||
|
fi
|
||||||
|
done < <(find "$SOURCE_DIR" -type d -not -path "./.git/*" -not -path "*/vendor/*" -not -path "*/node_modules/*")
|
||||||
|
if [ "$MISSING" -gt 0 ]; then
|
||||||
|
echo "## Directory Protection" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "${MISSING} director(ies) missing index.html" >> $GITHUB_STEP_SUMMARY
|
||||||
|
fi
|
||||||
|
echo "Directory protection: ${MISSING} missing (advisory)"
|
||||||
|
|
||||||
|
- name: Joomla script file and asset checks
|
||||||
|
if: steps.platform.outputs.platform == 'joomla'
|
||||||
|
run: |
|
||||||
|
ERRORS=0
|
||||||
|
MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||||
|
[ -z "$MANIFEST" ] && exit 0
|
||||||
|
MANIFEST_DIR=$(dirname "$MANIFEST")
|
||||||
|
|
||||||
|
# Check scriptfile exists if declared
|
||||||
|
SCRIPTFILE=$(sed -n 's/.*<scriptfile>\([^<]*\)<\/scriptfile>.*/\1/p' "$MANIFEST" 2>/dev/null)
|
||||||
|
if [ -n "$SCRIPTFILE" ]; then
|
||||||
|
if [ ! -f "${MANIFEST_DIR}/${SCRIPTFILE}" ]; then
|
||||||
|
echo "::error::Manifest declares <scriptfile>${SCRIPTFILE}</scriptfile> but file not found at ${MANIFEST_DIR}/${SCRIPTFILE}"
|
||||||
|
ERRORS=$((ERRORS + 1))
|
||||||
|
else
|
||||||
|
echo "Script file: ${MANIFEST_DIR}/${SCRIPTFILE} (OK)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Require joomla.asset.json and validate it
|
||||||
|
ASSET_JSON=$(find "$MANIFEST_DIR" -name "joomla.asset.json" -not -path "./.git/*" 2>/dev/null | head -1)
|
||||||
|
if [ -z "$ASSET_JSON" ]; then
|
||||||
|
echo "::error::joomla.asset.json not found — Joomla asset system is required"
|
||||||
|
ERRORS=$((ERRORS + 1))
|
||||||
|
else
|
||||||
|
if command -v php &> /dev/null; then
|
||||||
|
php -r "json_decode(file_get_contents('$ASSET_JSON')); if(json_last_error()!==JSON_ERROR_NONE){echo json_last_error_msg();exit(1);}" 2>&1 || {
|
||||||
|
echo "::error::joomla.asset.json is not valid JSON"
|
||||||
|
ERRORS=$((ERRORS + 1))
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
echo "joomla.asset.json: valid"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Validate all XML files in src/ are well-formed
|
||||||
|
XML_ERRORS=0
|
||||||
|
if command -v php &> /dev/null; then
|
||||||
|
while IFS= read -r -d '' xmlfile; do
|
||||||
|
if ! php -r "libxml_use_internal_errors(true); \$x = simplexml_load_file('$xmlfile'); if(!\$x){foreach(libxml_get_errors() as \$e) echo trim(\$e->message) . ' in $xmlfile'; exit(1);}" 2>&1; then
|
||||||
|
XML_ERRORS=$((XML_ERRORS + 1))
|
||||||
|
fi
|
||||||
|
done < <(find "$MANIFEST_DIR" -name "*.xml" -not -path "./.git/*" -print0)
|
||||||
|
fi
|
||||||
|
if [ "$XML_ERRORS" -gt 0 ]; then
|
||||||
|
echo "::error::${XML_ERRORS} XML file(s) are malformed"
|
||||||
|
ERRORS=$((ERRORS + 1))
|
||||||
|
else
|
||||||
|
echo "XML well-formedness: OK"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ "$ERRORS" -gt 0 ] && exit 1
|
||||||
|
echo "Joomla asset checks: OK"
|
||||||
|
|
||||||
- name: Validate platform manifest
|
- name: Validate platform manifest
|
||||||
run: |
|
run: |
|
||||||
PLATFORM="${{ steps.platform.outputs.platform }}"
|
PLATFORM="${{ steps.platform.outputs.platform }}"
|
||||||
@@ -151,6 +256,13 @@ jobs:
|
|||||||
for ELEMENT in name version description; do
|
for ELEMENT in name version description; do
|
||||||
grep -q "<${ELEMENT}>" "$MANIFEST" || { echo "::error::Missing <${ELEMENT}> in manifest"; exit 1; }
|
grep -q "<${ELEMENT}>" "$MANIFEST" || { echo "::error::Missing <${ELEMENT}> in manifest"; exit 1; }
|
||||||
done
|
done
|
||||||
|
# Block legacy raw/branch update server URLs on MokoGitea
|
||||||
|
RAW_URLS=$(grep -n 'raw/branch' "$MANIFEST" | grep -i 'mokoconsulting\|mokogitea\|git\.mokoconsulting\.tech' || true)
|
||||||
|
if [ -n "$RAW_URLS" ]; then
|
||||||
|
echo "::error::Manifest contains legacy raw/branch update server URL on MokoGitea. Use the Gitea Pages URL instead (e.g. /{REPO}/updates.xml not /{REPO}/raw/branch/main/updates.xml)"
|
||||||
|
echo "$RAW_URLS"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
echo "Joomla manifest valid"
|
echo "Joomla manifest valid"
|
||||||
;;
|
;;
|
||||||
dolibarr)
|
dolibarr)
|
||||||
@@ -183,6 +295,138 @@ jobs:
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
- name: Validate Joomla language files
|
||||||
|
if: steps.platform.outputs.platform == 'joomla'
|
||||||
|
run: |
|
||||||
|
ERRORS=0
|
||||||
|
WARNINGS=0
|
||||||
|
|
||||||
|
# Require both en-GB and en-US language directories
|
||||||
|
LANG_ROOT=$(find . -path "*/language" -type d -not -path "./.git/*" 2>/dev/null | head -1)
|
||||||
|
if [ -z "$LANG_ROOT" ]; then
|
||||||
|
echo "No language/ directory found — skipping"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "$LANG_ROOT/en-GB" ]; then
|
||||||
|
echo "::error::Missing en-GB language directory (${LANG_ROOT}/en-GB)"
|
||||||
|
ERRORS=$((ERRORS + 1))
|
||||||
|
fi
|
||||||
|
if [ ! -d "$LANG_ROOT/en-US" ]; then
|
||||||
|
echo "::error::Missing en-US language directory (${LANG_ROOT}/en-US)"
|
||||||
|
ERRORS=$((ERRORS + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check that en-GB and en-US have matching .ini files
|
||||||
|
if [ -d "$LANG_ROOT/en-GB" ] && [ -d "$LANG_ROOT/en-US" ]; then
|
||||||
|
for GB_INI in "$LANG_ROOT/en-GB"/*.ini; do
|
||||||
|
[ ! -f "$GB_INI" ] && continue
|
||||||
|
US_INI="$LANG_ROOT/en-US/$(basename "$GB_INI")"
|
||||||
|
if [ ! -f "$US_INI" ]; then
|
||||||
|
echo "::error::$(basename "$GB_INI") exists in en-GB but missing from en-US"
|
||||||
|
ERRORS=$((ERRORS + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
for US_INI in "$LANG_ROOT/en-US"/*.ini; do
|
||||||
|
[ ! -f "$US_INI" ] && continue
|
||||||
|
GB_INI="$LANG_ROOT/en-GB/$(basename "$US_INI")"
|
||||||
|
if [ ! -f "$GB_INI" ]; then
|
||||||
|
echo "::error::$(basename "$US_INI") exists in en-US but missing from en-GB"
|
||||||
|
ERRORS=$((ERRORS + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Find all .ini language files
|
||||||
|
INI_FILES=$(find . -path "*/language/*/*.ini" -not -path "./.git/*" 2>/dev/null)
|
||||||
|
if [ -z "$INI_FILES" ]; then
|
||||||
|
echo "No .ini language files found"
|
||||||
|
[ "$ERRORS" -gt 0 ] && exit 1
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Found $(echo "$INI_FILES" | wc -l) language file(s)"
|
||||||
|
|
||||||
|
for FILE in $INI_FILES; do
|
||||||
|
FNAME=$(basename "$FILE")
|
||||||
|
LINENUM=0
|
||||||
|
SEEN_KEYS=""
|
||||||
|
|
||||||
|
while IFS= read -r line || [ -n "$line" ]; do
|
||||||
|
LINENUM=$((LINENUM + 1))
|
||||||
|
|
||||||
|
# Skip empty lines and comments
|
||||||
|
[ -z "$line" ] && continue
|
||||||
|
echo "$line" | grep -qE '^\s*;' && continue
|
||||||
|
echo "$line" | grep -qE '^\s*$' && continue
|
||||||
|
|
||||||
|
# Must match KEY="VALUE" format
|
||||||
|
if ! echo "$line" | grep -qE '^[A-Z_][A-Z0-9_]*=".*"$'; then
|
||||||
|
echo "::error file=${FILE},line=${LINENUM}::Malformed line: ${line}"
|
||||||
|
ERRORS=$((ERRORS + 1))
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract key and check for duplicates
|
||||||
|
KEY=$(echo "$line" | sed 's/=.*//')
|
||||||
|
if echo "$SEEN_KEYS" | grep -qx "$KEY"; then
|
||||||
|
echo "::error file=${FILE},line=${LINENUM}::Duplicate key: ${KEY}"
|
||||||
|
ERRORS=$((ERRORS + 1))
|
||||||
|
fi
|
||||||
|
SEEN_KEYS="${SEEN_KEYS}
|
||||||
|
${KEY}"
|
||||||
|
done < "$FILE"
|
||||||
|
|
||||||
|
echo " ${FILE}: checked ${LINENUM} lines"
|
||||||
|
done
|
||||||
|
|
||||||
|
# Cross-check en-GB vs en-US key consistency
|
||||||
|
GB_DIR=$(find . -path "*/language/en-GB" -type d -not -path "./.git/*" 2>/dev/null | head -1)
|
||||||
|
US_DIR=$(find . -path "*/language/en-US" -type d -not -path "./.git/*" 2>/dev/null | head -1)
|
||||||
|
|
||||||
|
if [ -n "$GB_DIR" ] && [ -n "$US_DIR" ]; then
|
||||||
|
for GB_FILE in "$GB_DIR"/*.ini; do
|
||||||
|
[ ! -f "$GB_FILE" ] && continue
|
||||||
|
FNAME=$(basename "$GB_FILE")
|
||||||
|
US_FILE="$US_DIR/$FNAME"
|
||||||
|
[ ! -f "$US_FILE" ] && continue
|
||||||
|
|
||||||
|
GB_KEYS=$(grep -oP '^[A-Z_][A-Z0-9_]*(?==)' "$GB_FILE" 2>/dev/null | sort)
|
||||||
|
US_KEYS=$(grep -oP '^[A-Z_][A-Z0-9_]*(?==)' "$US_FILE" 2>/dev/null | sort)
|
||||||
|
|
||||||
|
# Keys in en-GB but not en-US
|
||||||
|
MISSING_US=$(comm -23 <(echo "$GB_KEYS") <(echo "$US_KEYS"))
|
||||||
|
if [ -n "$MISSING_US" ]; then
|
||||||
|
echo "::warning::Keys in en-GB/$FNAME but missing from en-US/$FNAME:"
|
||||||
|
echo "$MISSING_US" | while read -r k; do echo " - $k"; done
|
||||||
|
WARNINGS=$((WARNINGS + 1))
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Keys in en-US but not en-GB
|
||||||
|
MISSING_GB=$(comm -13 <(echo "$GB_KEYS") <(echo "$US_KEYS"))
|
||||||
|
if [ -n "$MISSING_GB" ]; then
|
||||||
|
echo "::warning::Keys in en-US/$FNAME but missing from en-GB/$FNAME:"
|
||||||
|
echo "$MISSING_GB" | while read -r k; do echo " - $k"; done
|
||||||
|
WARNINGS=$((WARNINGS + 1))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "### Language File Validation"
|
||||||
|
echo "| Metric | Count |"
|
||||||
|
echo "|---|---|"
|
||||||
|
echo "| Files checked | $(echo "$INI_FILES" | wc -l) |"
|
||||||
|
echo "| Errors | ${ERRORS} |"
|
||||||
|
echo "| Warnings | ${WARNINGS} |"
|
||||||
|
} >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
if [ "$ERRORS" -gt 0 ]; then
|
||||||
|
echo "::error::Language validation failed with ${ERRORS} error(s)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Language files: OK (${WARNINGS} warning(s))"
|
||||||
|
|
||||||
- name: Check changelog has unreleased entry
|
- name: Check changelog has unreleased entry
|
||||||
run: |
|
run: |
|
||||||
if [ ! -f "CHANGELOG.md" ]; then
|
if [ ! -f "CHANGELOG.md" ]; then
|
||||||
|
|||||||
@@ -1,233 +1,243 @@
|
|||||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: Gitea.Workflow
|
# DEFGROUP: Gitea.Workflow
|
||||||
# INGROUP: moko-platform.Release
|
# INGROUP: moko-platform.Release
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||||
# PATH: /templates/workflows/universal/pre-release.yml.template
|
# PATH: /templates/workflows/universal/pre-release.yml.template
|
||||||
# VERSION: 05.01.00
|
# VERSION: 05.01.00
|
||||||
# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch
|
# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch
|
||||||
|
|
||||||
name: "Universal: Pre-Release"
|
name: "Universal: Pre-Release"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [closed]
|
types: [closed]
|
||||||
branches:
|
branches:
|
||||||
- dev
|
- dev
|
||||||
workflow_dispatch:
|
pull_request_target:
|
||||||
inputs:
|
types: [synchronize, opened, reopened]
|
||||||
stability:
|
branches:
|
||||||
description: 'Pre-release channel'
|
- main
|
||||||
required: true
|
workflow_dispatch:
|
||||||
type: choice
|
inputs:
|
||||||
options:
|
stability:
|
||||||
- development
|
description: 'Pre-release channel'
|
||||||
- alpha
|
required: true
|
||||||
- beta
|
type: choice
|
||||||
- release-candidate
|
options:
|
||||||
|
- development
|
||||||
permissions:
|
- alpha
|
||||||
contents: write
|
- beta
|
||||||
|
- release-candidate
|
||||||
env:
|
|
||||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
permissions:
|
||||||
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
|
contents: write
|
||||||
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
|
|
||||||
|
env:
|
||||||
jobs:
|
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||||
build:
|
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
|
||||||
name: "Build Pre-Release (${{ inputs.stability || 'development' }})"
|
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
|
||||||
runs-on: release
|
|
||||||
if: >-
|
jobs:
|
||||||
github.event_name == 'workflow_dispatch' ||
|
build:
|
||||||
(github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev')
|
name: "Build Pre-Release (${{ inputs.stability || 'development' }})"
|
||||||
|
runs-on: release
|
||||||
steps:
|
if: >-
|
||||||
- name: Checkout
|
github.event_name == 'workflow_dispatch' ||
|
||||||
uses: actions/checkout@v4
|
(github.event_name == 'pull_request' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev') ||
|
||||||
with:
|
(github.event_name == 'pull_request_target' && github.event.pull_request.base.ref == 'main')
|
||||||
fetch-depth: 0
|
|
||||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
steps:
|
||||||
|
- name: Checkout
|
||||||
- name: Setup moko-platform tools
|
uses: actions/checkout@v4
|
||||||
env:
|
with:
|
||||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
fetch-depth: 0
|
||||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
run: |
|
ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || '' }}
|
||||||
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
|
- name: Setup moko-platform tools
|
||||||
fi
|
env:
|
||||||
# Always fetch latest CLI tools — never use stale cache from previous runs
|
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
rm -rf /tmp/moko-platform-api
|
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||||
git clone --depth 1 --branch main --quiet \
|
run: |
|
||||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
# Use pre-installed /opt/moko-platform if available (updated by cron every 6h)
|
||||||
/tmp/moko-platform-api
|
if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/cli/manifest_element.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then
|
||||||
cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
|
echo Using pre-installed /opt/moko-platform
|
||||||
echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
|
echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV
|
||||||
|
else
|
||||||
- name: Detect platform
|
echo Falling back to fresh clone
|
||||||
id: platform
|
if ! command -v composer > /dev/null 2>&1; then
|
||||||
run: |
|
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
|
||||||
php ${MOKO_CLI}/manifest_read.php --path . --github-output
|
fi
|
||||||
|
rm -rf /tmp/moko-platform-api
|
||||||
- name: Resolve metadata and bump version
|
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git
|
||||||
id: meta
|
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api
|
||||||
run: |
|
cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
|
||||||
STABILITY="${{ inputs.stability || 'development' }}"
|
echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
case "$STABILITY" in
|
|
||||||
development) SUFFIX="-dev"; TAG="development" ;;
|
- name: Detect platform
|
||||||
alpha) SUFFIX="-alpha"; TAG="alpha" ;;
|
id: platform
|
||||||
beta) SUFFIX="-beta"; TAG="beta" ;;
|
run: |
|
||||||
release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
|
php ${MOKO_CLI}/manifest_read.php --path . --github-output
|
||||||
esac
|
|
||||||
|
- name: Resolve metadata and bump version
|
||||||
# Read current version (bump already handled by push workflow)
|
id: meta
|
||||||
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null)
|
run: |
|
||||||
[ -z "$VERSION" ] && VERSION="00.00.01"
|
# Auto-detect stability: RC for PRs targeting main, else use input or default to development
|
||||||
|
if [ "${{ github.event_name }}" = "pull_request_target" ] && [ "${{ github.event.pull_request.base.ref }}" = "main" ]; then
|
||||||
# Strip any existing suffix from version before applying stability
|
STABILITY="release-candidate"
|
||||||
VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
|
else
|
||||||
|
STABILITY="${{ inputs.stability || 'development' }}"
|
||||||
php ${MOKO_CLI}/version_set_platform.php \
|
fi
|
||||||
--path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true
|
|
||||||
|
case "$STABILITY" in
|
||||||
# Verify version consistency across all files
|
development) SUFFIX="-dev"; TAG="development" ;;
|
||||||
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
|
alpha) SUFFIX="-alpha"; TAG="alpha" ;;
|
||||||
|
beta) SUFFIX="-beta"; TAG="beta" ;;
|
||||||
# Update VERSION variable with suffix
|
release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
|
||||||
if [ -n "$SUFFIX" ]; then
|
esac
|
||||||
VERSION="${VERSION}${SUFFIX}"
|
|
||||||
fi
|
# Bump version via CLI: patch for dev/alpha/beta, minor for RC
|
||||||
|
case "$STABILITY" in
|
||||||
# Commit version bump
|
release-candidate) BUMP="minor" ;;
|
||||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
*) BUMP="patch" ;;
|
||||||
git config --local user.name "gitea-actions[bot]"
|
esac
|
||||||
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
|
||||||
git add -A
|
php ${MOKO_CLI}/version_bump.php --path . $([ "$BUMP" = "minor" ] && echo "--minor") 2>/dev/null || true
|
||||||
git diff --cached --quiet || {
|
|
||||||
git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
|
# Set stability suffix and verify consistency
|
||||||
git push origin HEAD 2>&1
|
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01")
|
||||||
}
|
VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
|
||||||
|
|
||||||
# Auto-detect element via manifest_element.php
|
php ${MOKO_CLI}/version_set_platform.php \
|
||||||
php ${MOKO_CLI}/manifest_element.php \
|
--path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true
|
||||||
--path . --version "$VERSION" --stability "$STABILITY" \
|
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
|
||||||
--repo "${GITEA_REPO}" --github-output
|
|
||||||
|
# Ensure licensing tags (updateservers, dlid) if enabled in manifest.xml
|
||||||
# Read back element outputs
|
php ${MOKO_CLI}/manifest_licensing.php --path . --fix 2>/dev/null || true
|
||||||
EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
|
|
||||||
ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
|
# Append suffix for output
|
||||||
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
if [ -n "$SUFFIX" ]; then
|
||||||
[ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip"
|
VERSION="${VERSION}${SUFFIX}"
|
||||||
|
fi
|
||||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
|
# Commit version bump
|
||||||
echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
|
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||||
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
git config --local user.name "gitea-actions[bot]"
|
||||||
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
|
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||||
echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
|
git add -A
|
||||||
|
git diff --cached --quiet || {
|
||||||
echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
|
git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
|
||||||
|
git push origin HEAD 2>&1
|
||||||
- name: Create release
|
}
|
||||||
id: release
|
|
||||||
run: |
|
# Auto-detect element via manifest_element.php
|
||||||
TAG="${{ steps.meta.outputs.tag }}"
|
php ${MOKO_CLI}/manifest_element.php \
|
||||||
VERSION="${{ steps.meta.outputs.version }}"
|
--path . --version "$VERSION" --stability "$STABILITY" \
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
--repo "${GITEA_REPO}" --github-output
|
||||||
php ${MOKO_CLI}/release_create.php \
|
|
||||||
--path . --version "$VERSION" --tag "$TAG" \
|
# Read back element outputs
|
||||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
|
||||||
--repo "${GITEA_REPO}" --branch dev --prerelease
|
ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
|
||||||
|
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||||
- name: Build package and upload
|
[ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip"
|
||||||
id: package
|
|
||||||
run: |
|
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
VERSION="${{ steps.meta.outputs.version }}"
|
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
|
||||||
TAG="${{ steps.meta.outputs.tag }}"
|
echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
||||||
php ${MOKO_CLI}/release_package.php \
|
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
|
||||||
--path . --version "$VERSION" --tag "$TAG" \
|
echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
|
||||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
|
||||||
--repo "${GITEA_REPO}" --output /tmp || true
|
echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
|
||||||
|
|
||||||
- name: Update updates.xml
|
- name: Create release
|
||||||
if: steps.platform.outputs.platform == 'joomla'
|
id: release
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.meta.outputs.version }}"
|
TAG="${{ steps.meta.outputs.tag }}"
|
||||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
VERSION="${{ steps.meta.outputs.version }}"
|
||||||
SHA256="${{ steps.package.outputs.sha256_zip }}"
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
|
php ${MOKO_CLI}/release_create.php \
|
||||||
if [ ! -f "updates.xml" ]; then
|
--path . --version "$VERSION" --tag "$TAG" \
|
||||||
echo "No updates.xml -- skipping"
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||||
exit 0
|
--repo "${GITEA_REPO}" --branch dev --prerelease
|
||||||
fi
|
|
||||||
|
- name: Update release notes from CHANGELOG.md
|
||||||
SHA_FLAG=""
|
run: |
|
||||||
[ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}"
|
TAG="${{ steps.meta.outputs.tag }}"
|
||||||
|
VERSION="${{ steps.meta.outputs.version }}"
|
||||||
php ${MOKO_CLI}/updates_xml_build.php \
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
--path . --version "${VERSION}" --stability "${STABILITY}" \
|
|
||||||
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
|
# Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading)
|
||||||
${SHA_FLAG}
|
if [ -f "CHANGELOG.md" ]; then
|
||||||
|
NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
|
||||||
# Commit and push
|
[ -z "$NOTES" ] && NOTES="Release ${VERSION}"
|
||||||
if ! git diff --quiet updates.xml 2>/dev/null; then
|
else
|
||||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
NOTES="Release ${VERSION}"
|
||||||
git config --local user.name "gitea-actions[bot]"
|
fi
|
||||||
git add updates.xml
|
|
||||||
git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]"
|
# Update release body via API
|
||||||
git push origin HEAD 2>&1 || echo "WARNING: push failed"
|
RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||||
fi
|
"${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
||||||
|
|
||||||
- name: "Sync updates.xml to all branches"
|
if [ -n "$RELEASE_ID" ]; then
|
||||||
if: steps.platform.outputs.platform == 'joomla'
|
python3 -c "
|
||||||
run: |
|
import json, urllib.request
|
||||||
CURRENT_BRANCH="${{ github.ref_name }}"
|
body = open('/dev/stdin').read()
|
||||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
payload = json.dumps({'body': body}).encode()
|
||||||
git config --local user.name "gitea-actions[bot]"
|
req = urllib.request.Request(
|
||||||
|
'${API_BASE}/releases/${RELEASE_ID}',
|
||||||
for BRANCH in main dev; do
|
data=payload, method='PATCH',
|
||||||
[ "$BRANCH" = "$CURRENT_BRANCH" ] && continue
|
headers={
|
||||||
echo "Syncing updates.xml -> ${BRANCH}"
|
'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}',
|
||||||
git fetch origin "${BRANCH}" 2>/dev/null || continue
|
'Content-Type': 'application/json'
|
||||||
git checkout "origin/${BRANCH}" -- updates.xml 2>/dev/null || continue
|
})
|
||||||
git checkout "${CURRENT_BRANCH}" -- updates.xml
|
urllib.request.urlopen(req)
|
||||||
if ! git diff --quiet updates.xml 2>/dev/null; then
|
" <<< "$NOTES"
|
||||||
git add updates.xml
|
echo "Release notes updated from CHANGELOG.md"
|
||||||
git commit -m "chore: sync updates.xml from ${CURRENT_BRANCH} [skip ci]"
|
fi
|
||||||
git push origin HEAD:refs/heads/${BRANCH} 2>&1 || echo "WARNING: push to ${BRANCH} failed"
|
|
||||||
fi
|
- name: Build package and upload
|
||||||
git checkout "${CURRENT_BRANCH}" 2>/dev/null
|
id: package
|
||||||
done
|
run: |
|
||||||
|
VERSION="${{ steps.meta.outputs.version }}"
|
||||||
- name: "Delete lesser pre-release channels (cascade)"
|
TAG="${{ steps.meta.outputs.tag }}"
|
||||||
continue-on-error: true
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
run: |
|
php ${MOKO_CLI}/release_package.php \
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
--path . --version "$VERSION" --tag "$TAG" \
|
||||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||||
|
--repo "${GITEA_REPO}" --output /tmp || true
|
||||||
php ${MOKO_CLI}/release_cascade.php \
|
|
||||||
--stability "${{ steps.meta.outputs.stability }}" \
|
# updates.xml is generated dynamically by MokoGitea license server
|
||||||
--token "${TOKEN}" \
|
# No need to build, commit, or sync updates.xml from workflows
|
||||||
--api-base "${API_BASE}"
|
|
||||||
|
- name: "Delete lesser pre-release channels (cascade)"
|
||||||
- name: Summary
|
continue-on-error: true
|
||||||
if: always()
|
run: |
|
||||||
run: |
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
VERSION="${{ steps.meta.outputs.version }}"
|
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
|
||||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
php ${MOKO_CLI}/release_cascade.php \
|
||||||
SHA256="${{ steps.package.outputs.sha256_zip }}"
|
--stability "${{ steps.meta.outputs.stability }}" \
|
||||||
echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY
|
--token "${TOKEN}" \
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
--api-base "${API_BASE}"
|
||||||
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
- name: Summary
|
||||||
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
|
if: always()
|
||||||
echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY
|
run: |
|
||||||
echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY
|
VERSION="${{ steps.meta.outputs.version }}"
|
||||||
echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY
|
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||||
|
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||||
|
SHA256="${{ steps.package.outputs.sha256_zip }}"
|
||||||
|
echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|||||||
@@ -1,312 +0,0 @@
|
|||||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
#
|
|
||||||
# FILE INFORMATION
|
|
||||||
# DEFGROUP: Gitea.Workflow
|
|
||||||
# INGROUP: moko-platform.Universal
|
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
|
||||||
# PATH: /templates/workflows/update-server.yml
|
|
||||||
# VERSION: 05.00.00
|
|
||||||
# BRIEF: Pre-release build + update server XML for dev/alpha/beta/rc branches
|
|
||||||
#
|
|
||||||
# Thin wrapper around moko-platform CLI tools.
|
|
||||||
# Builds packages, updates updates.xml, and optionally deploys via SFTP.
|
|
||||||
#
|
|
||||||
# Joomla filters update entries by the user's "Minimum Stability" setting.
|
|
||||||
|
|
||||||
name: "Update Server"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'dev'
|
|
||||||
- 'dev/**'
|
|
||||||
- 'alpha/**'
|
|
||||||
- 'beta/**'
|
|
||||||
- 'rc/**'
|
|
||||||
paths:
|
|
||||||
- 'src/**'
|
|
||||||
- 'htdocs/**'
|
|
||||||
pull_request:
|
|
||||||
types: [closed]
|
|
||||||
branches:
|
|
||||||
- 'dev'
|
|
||||||
- 'dev/**'
|
|
||||||
- 'alpha/**'
|
|
||||||
- 'beta/**'
|
|
||||||
- 'rc/**'
|
|
||||||
paths:
|
|
||||||
- 'src/**'
|
|
||||||
- 'htdocs/**'
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
stability:
|
|
||||||
description: 'Stability tag'
|
|
||||||
required: true
|
|
||||||
default: 'development'
|
|
||||||
type: choice
|
|
||||||
options:
|
|
||||||
- development
|
|
||||||
- alpha
|
|
||||||
- beta
|
|
||||||
- rc
|
|
||||||
- stable
|
|
||||||
|
|
||||||
env:
|
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
||||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
|
||||||
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
|
|
||||||
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
update-xml:
|
|
||||||
name: Update Server
|
|
||||||
runs-on: release
|
|
||||||
if: >-
|
|
||||||
github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' || github.event_name == 'push'
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Setup moko-platform tools
|
|
||||||
env:
|
|
||||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
|
||||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
|
||||||
COMPOSER_AUTH: '{"http-basic":{"git.mokoconsulting.tech":{"username":"token","password":"${{ secrets.MOKOGITEA_TOKEN }}"}}}'
|
|
||||||
run: |
|
|
||||||
if ! command -v composer &> /dev/null; then
|
|
||||||
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1
|
|
||||||
fi
|
|
||||||
# Always fetch latest CLI tools — never use stale cache from previous runs
|
|
||||||
rm -rf /tmp/moko-platform
|
|
||||||
git clone --depth 1 --branch main --quiet \
|
|
||||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
|
||||||
/tmp/moko-platform 2>/dev/null || true
|
|
||||||
if [ -d "/tmp/moko-platform" ] && [ -f "/tmp/moko-platform/composer.json" ]; then
|
|
||||||
cd /tmp/moko-platform && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
echo "MOKO_CLI=/tmp/moko-platform/cli" >> "$GITHUB_ENV"
|
|
||||||
|
|
||||||
- name: Detect platform
|
|
||||||
id: platform
|
|
||||||
run: php ${MOKO_CLI}/manifest_read.php --path . --github-output
|
|
||||||
|
|
||||||
- name: Resolve stability and bump version
|
|
||||||
id: meta
|
|
||||||
run: |
|
|
||||||
BRANCH="${{ github.ref_name }}"
|
|
||||||
|
|
||||||
# Configure git for bot pushes
|
|
||||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
|
||||||
git config --local user.name "gitea-actions[bot]"
|
|
||||||
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
|
||||||
|
|
||||||
# Auto-bump patch version
|
|
||||||
php ${MOKO_CLI}/version_bump.php --path . 2>/dev/null || true
|
|
||||||
|
|
||||||
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "0.0.0")
|
|
||||||
|
|
||||||
# Strip any existing suffix before applying stability
|
|
||||||
VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
|
|
||||||
|
|
||||||
# Determine stability from branch or manual input
|
|
||||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
|
||||||
STABILITY="${{ inputs.stability }}"
|
|
||||||
elif [[ "$BRANCH" == rc/* ]]; then
|
|
||||||
STABILITY="rc"
|
|
||||||
elif [[ "$BRANCH" == beta/* ]]; then
|
|
||||||
STABILITY="beta"
|
|
||||||
elif [[ "$BRANCH" == alpha/* ]]; then
|
|
||||||
STABILITY="alpha"
|
|
||||||
else
|
|
||||||
STABILITY="development"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Version suffix per stability stream
|
|
||||||
case "$STABILITY" in
|
|
||||||
development) SUFFIX="-dev"; TAG="development" ;;
|
|
||||||
alpha) SUFFIX="-alpha"; TAG="alpha" ;;
|
|
||||||
beta) SUFFIX="-beta"; TAG="beta" ;;
|
|
||||||
rc) SUFFIX="-rc"; TAG="release-candidate" ;;
|
|
||||||
*) SUFFIX=""; TAG="stable" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Propagate version with stability suffix to all manifest files
|
|
||||||
php ${MOKO_CLI}/version_set_platform.php \
|
|
||||||
--path . --version "$VERSION" --branch "$BRANCH" --stability "$STABILITY" 2>/dev/null || true
|
|
||||||
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
|
|
||||||
|
|
||||||
# Re-read version (now includes suffix from version_set_platform)
|
|
||||||
if [ -n "$SUFFIX" ]; then
|
|
||||||
VERSION="${VERSION}${SUFFIX}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "display_version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
# Commit version bump if changed
|
|
||||||
git add -A
|
|
||||||
git diff --cached --quiet || {
|
|
||||||
git commit -m "chore(version): auto-bump ${VERSION} [skip ci]" \
|
|
||||||
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
|
|
||||||
git push
|
|
||||||
}
|
|
||||||
|
|
||||||
- name: Create release and upload package
|
|
||||||
id: package
|
|
||||||
run: |
|
|
||||||
VERSION="${{ steps.meta.outputs.version }}"
|
|
||||||
TAG="${{ steps.meta.outputs.tag }}"
|
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
|
||||||
|
|
||||||
# Create or update Gitea release
|
|
||||||
php ${MOKO_CLI}/release_create.php \
|
|
||||||
--path . --version "$VERSION" --tag "$TAG" \
|
|
||||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
|
||||||
--repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease
|
|
||||||
|
|
||||||
# Build package and upload
|
|
||||||
php ${MOKO_CLI}/release_package.php \
|
|
||||||
--path . --version "$VERSION" --tag "$TAG" \
|
|
||||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
|
||||||
--repo "${GITEA_REPO}" --output /tmp || true
|
|
||||||
|
|
||||||
- name: Update updates.xml
|
|
||||||
if: steps.platform.outputs.platform == 'joomla'
|
|
||||||
run: |
|
|
||||||
VERSION="${{ steps.meta.outputs.version }}"
|
|
||||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
|
||||||
SHA256="${{ steps.package.outputs.sha256_zip }}"
|
|
||||||
|
|
||||||
if [ ! -f "updates.xml" ]; then
|
|
||||||
echo "No updates.xml — skipping"
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
SHA_FLAG=""
|
|
||||||
[ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}"
|
|
||||||
|
|
||||||
php ${MOKO_CLI}/updates_xml_build.php \
|
|
||||||
--path . --version "${VERSION}" --stability "${STABILITY}" \
|
|
||||||
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
|
|
||||||
${SHA_FLAG}
|
|
||||||
|
|
||||||
# Commit and push updates.xml
|
|
||||||
git add updates.xml
|
|
||||||
git diff --cached --quiet || {
|
|
||||||
git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]"
|
|
||||||
git push
|
|
||||||
}
|
|
||||||
|
|
||||||
- name: Sync updates.xml to main
|
|
||||||
if: github.ref_name != 'main' && steps.platform.outputs.platform == 'joomla'
|
|
||||||
run: |
|
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
|
||||||
GITEA_TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
|
||||||
|
|
||||||
FILE_SHA=$(curl -sf -H "Authorization: token ${GITEA_TOKEN}" \
|
|
||||||
"${API_BASE}/contents/updates.xml?ref=main" | python3 -c "import sys,json; print(json.load(sys.stdin).get('sha',''))" 2>/dev/null || true)
|
|
||||||
|
|
||||||
if [ -n "$FILE_SHA" ] && [ -f "updates.xml" ]; then
|
|
||||||
python3 -c "
|
|
||||||
import base64, json, urllib.request, sys
|
|
||||||
with open('updates.xml', 'rb') as f:
|
|
||||||
content = base64.b64encode(f.read()).decode()
|
|
||||||
payload = json.dumps({
|
|
||||||
'content': content,
|
|
||||||
'sha': '${FILE_SHA}',
|
|
||||||
'message': 'chore: sync updates.xml from ${{ steps.meta.outputs.stability }} [skip ci]',
|
|
||||||
'branch': 'main'
|
|
||||||
}).encode()
|
|
||||||
req = urllib.request.Request(
|
|
||||||
'${API_BASE}/contents/updates.xml',
|
|
||||||
data=payload, method='PUT',
|
|
||||||
headers={
|
|
||||||
'Authorization': 'token ${GITEA_TOKEN}',
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
})
|
|
||||||
try:
|
|
||||||
urllib.request.urlopen(req)
|
|
||||||
print('updates.xml synced to main')
|
|
||||||
except Exception as e:
|
|
||||||
print(f'WARNING: sync to main failed: {e}', file=sys.stderr)
|
|
||||||
"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: SFTP deploy to dev server
|
|
||||||
if: contains(github.ref, 'dev/') || github.ref == 'refs/heads/dev'
|
|
||||||
env:
|
|
||||||
DEV_HOST: ${{ vars.DEV_FTP_HOST }}
|
|
||||||
DEV_PATH: ${{ vars.DEV_FTP_PATH }}
|
|
||||||
DEV_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }}
|
|
||||||
DEV_USER: ${{ vars.DEV_FTP_USERNAME }}
|
|
||||||
DEV_PORT: ${{ vars.DEV_FTP_PORT }}
|
|
||||||
DEV_KEY: ${{ secrets.DEV_FTP_KEY }}
|
|
||||||
DEV_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
|
|
||||||
run: |
|
|
||||||
# Permission check: admin or maintain role required
|
|
||||||
ACTOR="${{ github.actor }}"
|
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
|
||||||
|
|
||||||
PERMISSION=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
|
|
||||||
"${API_BASE}/collaborators/${ACTOR}/permission" 2>/dev/null | \
|
|
||||||
python3 -c "import sys,json; print(json.load(sys.stdin).get('permission','read'))" 2>/dev/null || echo "read")
|
|
||||||
case "$PERMISSION" in
|
|
||||||
admin|maintain|write) ;;
|
|
||||||
*)
|
|
||||||
echo "Deploy denied: ${ACTOR} has '${PERMISSION}' — requires admin, maintain, or write"
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
[ -z "$DEV_HOST" ] || [ -z "$DEV_PATH" ] && { echo "DEV FTP not configured — skipping SFTP"; exit 0; }
|
|
||||||
|
|
||||||
SOURCE_DIR="src"
|
|
||||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
|
||||||
[ ! -d "$SOURCE_DIR" ] && exit 0
|
|
||||||
|
|
||||||
PORT="${DEV_PORT:-22}"
|
|
||||||
REMOTE="${DEV_PATH%/}"
|
|
||||||
[ -n "$DEV_SUFFIX" ] && REMOTE="${REMOTE}/${DEV_SUFFIX#/}"
|
|
||||||
|
|
||||||
printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \
|
|
||||||
"$DEV_HOST" "$PORT" "$DEV_USER" "$REMOTE" > /tmp/sftp-config.json
|
|
||||||
if [ -n "$DEV_KEY" ]; then
|
|
||||||
echo "$DEV_KEY" > /tmp/deploy_key && chmod 600 /tmp/deploy_key
|
|
||||||
printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json
|
|
||||||
else
|
|
||||||
printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json
|
|
||||||
fi
|
|
||||||
|
|
||||||
PLATFORM=$(php ${MOKO_CLI}/platform_detect.php --path . 2>/dev/null || true)
|
|
||||||
if [ "$PLATFORM" = "waas-component" ] && [ -f "${MOKO_CLI}/../deploy/deploy-joomla.php" ]; then
|
|
||||||
php ${MOKO_CLI}/../deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
|
|
||||||
elif [ -f "${MOKO_CLI}/../deploy/deploy-sftp.php" ]; then
|
|
||||||
php ${MOKO_CLI}/../deploy/deploy-sftp.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
|
|
||||||
fi
|
|
||||||
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
|
||||||
echo "SFTP deploy to dev complete" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
- name: Summary
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
VERSION="${{ steps.meta.outputs.version }}"
|
|
||||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
|
||||||
DISPLAY="${{ steps.meta.outputs.display_version }}"
|
|
||||||
echo "## Update Server" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "| Stability | \`${STABILITY}\` |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "| Version | \`${DISPLAY}\` |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
#
|
|
||||||
# BRIEF: Update MokoOnyx submodule in MokoWaas on main and dev branches
|
|
||||||
|
|
||||||
name: "Update MokoWaas Submodule"
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
|
|
||||||
env:
|
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
||||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
|
||||||
WAAS_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
|
|
||||||
WAAS_REPO: MokoWaaS
|
|
||||||
# Path where MokoOnyx lives as a submodule inside MokoWaaS
|
|
||||||
SUBMODULE_PATH: src/packages/tpl_mokoonyx
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
update-submodule:
|
|
||||||
name: "Update submodule on ${{ matrix.branch }}"
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
branch: [main, dev]
|
|
||||||
fail-fast: false
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Get release info
|
|
||||||
id: release
|
|
||||||
run: |
|
|
||||||
TAG="${{ github.event.release.tag_name }}"
|
|
||||||
IS_PRE="${{ github.event.release.prerelease }}"
|
|
||||||
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
|
||||||
echo "prerelease=${IS_PRE}" >> $GITHUB_OUTPUT
|
|
||||||
echo "Release tag: ${TAG} (prerelease: ${IS_PRE})"
|
|
||||||
|
|
||||||
# Skip pre-releases (RC) — only update on stable releases
|
|
||||||
- name: Skip pre-releases
|
|
||||||
if: steps.release.outputs.prerelease == 'true'
|
|
||||||
run: |
|
|
||||||
echo "Skipping pre-release ${TAG} — only stable releases update MokoWaas"
|
|
||||||
exit 0
|
|
||||||
|
|
||||||
- name: Clone MokoWaas
|
|
||||||
if: steps.release.outputs.prerelease != 'true'
|
|
||||||
run: |
|
|
||||||
git clone --branch "${{ matrix.branch }}" --depth 1 --recurse-submodules \
|
|
||||||
"https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${WAAS_ORG}/${WAAS_REPO}.git" \
|
|
||||||
waas
|
|
||||||
env:
|
|
||||||
GIT_TERMINAL_PROMPT: 0
|
|
||||||
|
|
||||||
- name: Configure git
|
|
||||||
if: steps.release.outputs.prerelease != 'true'
|
|
||||||
working-directory: waas
|
|
||||||
run: |
|
|
||||||
git config user.email "gitea-actions[bot]@mokoconsulting.tech"
|
|
||||||
git config user.name "gitea-actions[bot]"
|
|
||||||
git remote set-url origin \
|
|
||||||
"https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${WAAS_ORG}/${WAAS_REPO}.git"
|
|
||||||
|
|
||||||
- name: Update submodule to release tag
|
|
||||||
if: steps.release.outputs.prerelease != 'true'
|
|
||||||
working-directory: waas
|
|
||||||
run: |
|
|
||||||
TAG="${{ steps.release.outputs.tag }}"
|
|
||||||
SUBPATH="${SUBMODULE_PATH}"
|
|
||||||
|
|
||||||
# Verify submodule exists
|
|
||||||
if [ ! -f ".gitmodules" ] || ! grep -q "${SUBPATH}" .gitmodules; then
|
|
||||||
echo "::error::Submodule '${SUBPATH}' not found in .gitmodules on ${{ matrix.branch }}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Fetch the tag in the submodule and update to it
|
|
||||||
cd "${SUBPATH}"
|
|
||||||
git fetch origin "refs/tags/${TAG}:refs/tags/${TAG}" --depth 1
|
|
||||||
git checkout "${TAG}"
|
|
||||||
cd -
|
|
||||||
|
|
||||||
# Stage the submodule pointer change
|
|
||||||
git add "${SUBPATH}"
|
|
||||||
|
|
||||||
# Only commit if there's actually a change
|
|
||||||
if git diff --cached --quiet; then
|
|
||||||
echo "Submodule already at ${TAG} on ${{ matrix.branch }} — nothing to do"
|
|
||||||
else
|
|
||||||
git commit -m "chore: update MokoOnyx submodule to ${TAG}
|
|
||||||
|
|
||||||
Authored-by: Moko Consulting"
|
|
||||||
git push origin "${{ matrix.branch }}"
|
|
||||||
echo "Updated MokoOnyx to ${TAG} on ${{ matrix.branch }}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Summary
|
|
||||||
if: always() && steps.release.outputs.prerelease != 'true'
|
|
||||||
run: |
|
|
||||||
TAG="${{ steps.release.outputs.tag }}"
|
|
||||||
echo "## MokoWaas Submodule Update (${{ matrix.branch }})" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "Updated \`${SUBMODULE_PATH}\` to \`${TAG}\`" >> $GITHUB_STEP_SUMMARY
|
|
||||||
+13
-6
@@ -8,19 +8,26 @@
|
|||||||
DEFGROUP: Joomla.Template.Site
|
DEFGROUP: Joomla.Template.Site
|
||||||
INGROUP: MokoOnyx.Documentation
|
INGROUP: MokoOnyx.Documentation
|
||||||
PATH: ./CHANGELOG.md
|
PATH: ./CHANGELOG.md
|
||||||
VERSION: 02.20.00
|
VERSION: 02.19.06
|
||||||
BRIEF: Changelog file documenting version history of MokoOnyx
|
BRIEF: Changelog file documenting version history of MokoOnyx
|
||||||
-->
|
-->
|
||||||
|
|
||||||
# Changelog — MokoOnyx (VERSION: 02.20.00)
|
# Changelog — MokoOnyx (VERSION: 02.19.06)
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Strip Joomla-injected `p-2` padding class from Font Awesome icons in all menu overrides (default, mainmenu, horizontal)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Migrated update server URL from raw file endpoint to Gitea Pages
|
||||||
|
- Release workflow no longer manages updates.xml (decoupled to Gitea Pages)
|
||||||
|
- Added conflict-marker guard to PR check and release workflows
|
||||||
|
- Added Joomla language file validation (syntax, duplicates, en-GB/en-US consistency)
|
||||||
|
- Added JEXEC guard, joomla.asset.json, XML well-formedness, and script file CI checks
|
||||||
|
- Removed RS_FTP_PATH_SUFFIX from repo health requirements
|
||||||
|
|
||||||
## [02.20.00] --- 2026-06-04
|
## [02.20.00] --- 2026-06-04
|
||||||
|
|
||||||
## [02.19.00] --- 2026-06-04
|
|
||||||
|
|
||||||
## [02.18.00] --- 2026-06-02
|
## [02.18.00] --- 2026-06-02
|
||||||
|
|
||||||
## [02.17.00] --- 2026-06-02
|
|
||||||
|
|
||||||
## [02.15.00] --- 2026-05-30
|
## [02.15.00] --- 2026-05-30
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@
|
|||||||
INGROUP: MokoOnyx.Governance
|
INGROUP: MokoOnyx.Governance
|
||||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
|
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
|
||||||
FILE: SECURITY.md
|
FILE: SECURITY.md
|
||||||
VERSION: 02.20.00
|
VERSION: 02.19.06
|
||||||
BRIEF: Security policy and vulnerability reporting process for MokoOnyx.
|
BRIEF: Security policy and vulnerability reporting process for MokoOnyx.
|
||||||
PATH: /SECURITY.md
|
PATH: /SECURITY.md
|
||||||
NOTE: This policy is process oriented and does not replace secure engineering practices.
|
NOTE: This policy is process oriented and does not replace secure engineering practices.
|
||||||
|
|||||||
@@ -236,11 +236,11 @@ use Joomla\CMS\Log\Log;
|
|||||||
|
|
||||||
// Update the update server
|
// Update the update server
|
||||||
try {
|
try {
|
||||||
$onyxUpdatesUrl = 'https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/updates.xml';
|
$onyxUpdatesUrl = 'https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/updates.xml';
|
||||||
$query = $db->getQuery(true)
|
$query = $db->getQuery(true)
|
||||||
->update('#__update_sites')
|
->update('#__update_sites')
|
||||||
->set($db->quoteName('location') . ' = ' . $db->quote($onyxUpdatesUrl))
|
->set($db->quoteName('location') . ' = ' . $db->quote($onyxUpdatesUrl))
|
||||||
->set($db->quoteName('name') . ' = ' . $db->quote($newDisplay))
|
->set($db->quoteName('name') . ' = ' . $db->quote('Template - MokoOnyx'))
|
||||||
->where($db->quoteName('location') . ' LIKE ' . $db->quote('%MokoCassiopeia%'));
|
->where($db->quoteName('location') . ' LIKE ' . $db->quote('%MokoCassiopeia%'));
|
||||||
$db->setQuery($query)->execute();
|
$db->setQuery($query)->execute();
|
||||||
$n = $db->getAffectedRows();
|
$n = $db->getAffectedRows();
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
* INGROUP: MokoOnyx
|
* INGROUP: MokoOnyx
|
||||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
|
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
|
||||||
* PATH: /html/layouts/joomla/module/card.php
|
* PATH: /html/layouts/joomla/module/card.php
|
||||||
* VERSION: 02.20.00
|
* VERSION: 02.19.06
|
||||||
* BRIEF: Custom card module chrome — renders module titles for all modules
|
* BRIEF: Custom card module chrome — renders module titles for all modules
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
* INGROUP: MokoOnyx.Layouts
|
* INGROUP: MokoOnyx.Layouts
|
||||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
|
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
|
||||||
* PATH: /src/html/layouts/mokoonyx/article-metadata.php
|
* PATH: /src/html/layouts/mokoonyx/article-metadata.php
|
||||||
* VERSION: 02.20.00
|
* VERSION: 02.19.06
|
||||||
* BRIEF: Article metadata footer layout -- renders jcfields grouped by field group
|
* BRIEF: Article metadata footer layout -- renders jcfields grouped by field group
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ if ($item->anchor_rel) {
|
|||||||
$linktype = $item->title;
|
$linktype = $item->title;
|
||||||
|
|
||||||
if ($item->menu_icon) {
|
if ($item->menu_icon) {
|
||||||
|
// Strip Joomla-injected padding classes that conflict with FA icon sizing
|
||||||
|
$item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon));
|
||||||
if ($itemParams->get('menu_text', 1)) {
|
if ($itemParams->get('menu_text', 1)) {
|
||||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ $anchor_css = $item->anchor_css ?: '';
|
|||||||
$linktype = $item->title;
|
$linktype = $item->title;
|
||||||
|
|
||||||
if ($item->menu_icon) {
|
if ($item->menu_icon) {
|
||||||
|
// Strip Joomla-injected padding classes that conflict with FA icon sizing
|
||||||
|
$item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon));
|
||||||
if ($itemParams->get('menu_text', 1)) {
|
if ($itemParams->get('menu_text', 1)) {
|
||||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ $anchor_css = $item->anchor_css ?: '';
|
|||||||
$linktype = $item->title;
|
$linktype = $item->title;
|
||||||
|
|
||||||
if ($item->menu_icon) {
|
if ($item->menu_icon) {
|
||||||
|
// Strip Joomla-injected padding classes that conflict with FA icon sizing
|
||||||
|
$item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon));
|
||||||
if ($itemParams->get('menu_text', 1)) {
|
if ($itemParams->get('menu_text', 1)) {
|
||||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ if ($item->anchor_rel) {
|
|||||||
$linktype = $item->title;
|
$linktype = $item->title;
|
||||||
|
|
||||||
if ($item->menu_icon) {
|
if ($item->menu_icon) {
|
||||||
|
// Strip Joomla-injected padding classes that conflict with FA icon sizing
|
||||||
|
$item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon));
|
||||||
if ($itemParams->get('menu_text', 1)) {
|
if ($itemParams->get('menu_text', 1)) {
|
||||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ if ($item->anchor_rel) {
|
|||||||
$linktype = $item->title;
|
$linktype = $item->title;
|
||||||
|
|
||||||
if ($item->menu_icon) {
|
if ($item->menu_icon) {
|
||||||
|
// Strip Joomla-injected padding classes that conflict with FA icon sizing
|
||||||
|
$item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon));
|
||||||
if ($itemParams->get('menu_text', 1)) {
|
if ($itemParams->get('menu_text', 1)) {
|
||||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ $anchor_css = $item->anchor_css ?: '';
|
|||||||
$linktype = $item->title;
|
$linktype = $item->title;
|
||||||
|
|
||||||
if ($item->menu_icon) {
|
if ($item->menu_icon) {
|
||||||
|
// Strip Joomla-injected padding classes that conflict with FA icon sizing
|
||||||
|
$item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon));
|
||||||
if ($itemParams->get('menu_text', 1)) {
|
if ($itemParams->get('menu_text', 1)) {
|
||||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ $anchor_css = $item->anchor_css ?: '';
|
|||||||
$linktype = $item->title;
|
$linktype = $item->title;
|
||||||
|
|
||||||
if ($item->menu_icon) {
|
if ($item->menu_icon) {
|
||||||
|
// Strip Joomla-injected padding classes that conflict with FA icon sizing
|
||||||
|
$item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon));
|
||||||
if ($itemParams->get('menu_text', 1)) {
|
if ($itemParams->get('menu_text', 1)) {
|
||||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ if ($item->anchor_rel) {
|
|||||||
$linktype = $item->title;
|
$linktype = $item->title;
|
||||||
|
|
||||||
if ($item->menu_icon) {
|
if ($item->menu_icon) {
|
||||||
|
// Strip Joomla-injected padding classes that conflict with FA icon sizing
|
||||||
|
$item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon));
|
||||||
if ($itemParams->get('menu_text', 1)) {
|
if ($itemParams->get('menu_text', 1)) {
|
||||||
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
$linktype = '<span class="' . $item->menu_icon . '" aria-hidden="true"></span>' . $item->title;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ if ($item->anchor_rel) {
|
|||||||
$linktype = $item->title;
|
$linktype = $item->title;
|
||||||
|
|
||||||
if ($item->menu_icon) {
|
if ($item->menu_icon) {
|
||||||
|
// Strip Joomla-injected padding classes that conflict with FA icon sizing
|
||||||
|
$item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon));
|
||||||
// The link is an icon
|
// The link is an icon
|
||||||
if ($itemParams->get('menu_text', 1)) {
|
if ($itemParams->get('menu_text', 1)) {
|
||||||
// If the link text is to be displayed, the icon is added with aria-hidden
|
// If the link text is to be displayed, the icon is added with aria-hidden
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ $anchor_css = $item->anchor_css ?: '';
|
|||||||
$linktype = $item->title;
|
$linktype = $item->title;
|
||||||
|
|
||||||
if ($item->menu_icon) {
|
if ($item->menu_icon) {
|
||||||
|
// Strip Joomla-injected padding classes that conflict with FA icon sizing
|
||||||
|
$item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon));
|
||||||
// The link is an icon
|
// The link is an icon
|
||||||
if ($itemParams->get('menu_text', 1)) {
|
if ($itemParams->get('menu_text', 1)) {
|
||||||
// If the link text is to be displayed, the icon is added with aria-hidden
|
// If the link text is to be displayed, the icon is added with aria-hidden
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ $anchor_css = $item->anchor_css ?: '';
|
|||||||
$linktype = $item->title;
|
$linktype = $item->title;
|
||||||
|
|
||||||
if ($item->menu_icon) {
|
if ($item->menu_icon) {
|
||||||
|
// Strip Joomla-injected padding classes that conflict with FA icon sizing
|
||||||
|
$item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon));
|
||||||
// The link is an icon
|
// The link is an icon
|
||||||
if ($itemParams->get('menu_text', 1)) {
|
if ($itemParams->get('menu_text', 1)) {
|
||||||
// If the link text is to be displayed, the icon is added with aria-hidden
|
// If the link text is to be displayed, the icon is added with aria-hidden
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ if ($item->anchor_rel) {
|
|||||||
$linktype = $item->title;
|
$linktype = $item->title;
|
||||||
|
|
||||||
if ($item->menu_icon) {
|
if ($item->menu_icon) {
|
||||||
|
// Strip Joomla-injected padding classes that conflict with FA icon sizing
|
||||||
|
$item->menu_icon = trim(preg_replace('/\bp-[0-5]\b/', '', $item->menu_icon));
|
||||||
// The link is an icon
|
// The link is an icon
|
||||||
if ($itemParams->get('menu_text', 1)) {
|
if ($itemParams->get('menu_text', 1)) {
|
||||||
// If the link text is to be displayed, the icon is added with aria-hidden
|
// If the link text is to be displayed, the icon is added with aria-hidden
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
* INGROUP: MokoOnyx.Accessibility
|
* INGROUP: MokoOnyx.Accessibility
|
||||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
|
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
|
||||||
* PATH: ./media/css/a11y-high-contrast.css
|
* PATH: ./media/css/a11y-high-contrast.css
|
||||||
* VERSION: 02.20.00
|
* VERSION: 02.19.06
|
||||||
* BRIEF: High-contrast stylesheet for accessibility toolbar
|
* BRIEF: High-contrast stylesheet for accessibility toolbar
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -533,6 +533,37 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove faulty "template-mokoonyx" duplicate (wrong element name from bad install)
|
||||||
|
$faultyId = (int) $db->setQuery(
|
||||||
|
$db->getQuery(true)
|
||||||
|
->select('extension_id')
|
||||||
|
->from('#__extensions')
|
||||||
|
->where($db->quoteName('element') . ' = ' . $db->quote('template-mokoonyx'))
|
||||||
|
->where($db->quoteName('type') . ' = ' . $db->quote('template'))
|
||||||
|
)->loadResult();
|
||||||
|
|
||||||
|
if ($faultyId) {
|
||||||
|
$db->setQuery(
|
||||||
|
$db->getQuery(true)
|
||||||
|
->delete('#__update_sites_extensions')
|
||||||
|
->where('extension_id = ' . $faultyId)
|
||||||
|
)->execute();
|
||||||
|
|
||||||
|
$db->setQuery(
|
||||||
|
$db->getQuery(true)
|
||||||
|
->delete('#__extensions')
|
||||||
|
->where('extension_id = ' . $faultyId)
|
||||||
|
)->execute();
|
||||||
|
|
||||||
|
$db->setQuery(
|
||||||
|
$db->getQuery(true)
|
||||||
|
->delete('#__template_styles')
|
||||||
|
->where($db->quoteName('template') . ' = ' . $db->quote('template-mokoonyx'))
|
||||||
|
)->execute();
|
||||||
|
|
||||||
|
$this->logMessage('Removed faulty template-mokoonyx duplicate extension.');
|
||||||
|
}
|
||||||
|
|
||||||
// Remove stale MokoCassiopeia if not set as default
|
// Remove stale MokoCassiopeia if not set as default
|
||||||
$oldExt = (int) $db->setQuery(
|
$oldExt = (int) $db->setQuery(
|
||||||
$db->getQuery(true)
|
$db->getQuery(true)
|
||||||
|
|||||||
@@ -31,12 +31,11 @@
|
|||||||
-->
|
-->
|
||||||
<extension type="template" client="site" method="upgrade">
|
<extension type="template" client="site" method="upgrade">
|
||||||
<updateservers>
|
<updateservers>
|
||||||
<server type="extension" priority="1" name="MokoOnyx Update Server (MokoGitea)">
|
<server type="extension" name="Template - MokoOnyx">https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/updates.xml</server>
|
||||||
https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/updates.xml
|
|
||||||
</server>
|
|
||||||
</updateservers>
|
</updateservers>
|
||||||
|
<dlid prefix="dlid=" suffix=""/>
|
||||||
<name>mokoonyx</name>
|
<name>mokoonyx</name>
|
||||||
<version>02.20.00</version>
|
<version>02.19.06-dev</version>
|
||||||
<scriptfile>script.php</scriptfile>
|
<scriptfile>script.php</scriptfile>
|
||||||
<creationDate>2026-05-16</creationDate>
|
<creationDate>2026-05-16</creationDate>
|
||||||
<author>Jonathan Miller || Moko Consulting</author>
|
<author>Jonathan Miller || Moko Consulting</author>
|
||||||
|
|||||||
-48
@@ -1,48 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
|
||||||
<!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
||||||
SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
VERSION: 02.20.00-dev
|
|
||||||
-->
|
|
||||||
|
|
||||||
<updates>
|
|
||||||
<update>
|
|
||||||
<name>Template - MokoOnyx</name>
|
|
||||||
<description>Template - MokoOnyx development build.</description>
|
|
||||||
<element>mokoonyx</element>
|
|
||||||
<type>template</type>
|
|
||||||
<client>site</client>
|
|
||||||
<version>02.19.02-dev</version>
|
|
||||||
<creationDate>2026-06-04</creationDate>
|
|
||||||
<infourl title='Template - MokoOnyx'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/development</infourl>
|
|
||||||
<downloads>
|
|
||||||
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/development/tpl_mokoonyx-02.19.02-dev.zip</downloadurl>
|
|
||||||
</downloads>
|
|
||||||
<sha256>5811cc175564744865053f5a2cf259e66016c4e41f2d87571d19b1dd5f7fd8ba</sha256>
|
|
||||||
<tags><tag>dev</tag></tags>
|
|
||||||
<changelogurl>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/CHANGELOG.md</changelogurl>
|
|
||||||
<maintainer>Moko Consulting</maintainer>
|
|
||||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
|
||||||
<targetplatform name="joomla" version="(5|6)\..*" />
|
|
||||||
<php_minimum>8.1.0</php_minimum>
|
|
||||||
</update>
|
|
||||||
<update>
|
|
||||||
<name>Template - MokoOnyx</name>
|
|
||||||
<description>Template - MokoOnyx stable build.</description>
|
|
||||||
<element>mokoonyx</element>
|
|
||||||
<type>template</type>
|
|
||||||
<client>site</client>
|
|
||||||
<version>02.19.00</version>
|
|
||||||
<creationDate>2026-06-04</creationDate>
|
|
||||||
<infourl title="Template - MokoOnyx">https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable</infourl>
|
|
||||||
<downloads>
|
|
||||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.19.00.zip</downloadurl>
|
|
||||||
</downloads>
|
|
||||||
<sha256>1b4907b450be64d1d23ea0da4314e542e6abf2c9e6e2085ad4b2e49079bd1d83</sha256>
|
|
||||||
<tags><tag>stable</tag></tags>
|
|
||||||
<changelogurl>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/CHANGELOG.md</changelogurl>
|
|
||||||
<maintainer>Moko Consulting</maintainer>
|
|
||||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
|
||||||
<targetplatform name="joomla" version="(5|6)\..*"/>
|
|
||||||
<php_minimum>8.1.0</php_minimum>
|
|
||||||
</update>
|
|
||||||
</updates>
|
|
||||||
Reference in New Issue
Block a user