Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e948074c6a | |||
| 188db2d4b8 | |||
| 1ed11dca03 | |||
| ecc5d624d5 | |||
| 43abc6514e | |||
| 8d42ef40c5 | |||
| 0546dde89f | |||
| 598ec0712c | |||
| 6f9df77f79 | |||
| 89aaef14e7 | |||
| f72cafe4d7 | |||
| a965bcf0ef | |||
| bd6eec88af | |||
| ce7e36f779 | |||
| 46b1469121 | |||
| 1e936a67c4 | |||
| 0903a4b335 | |||
| a7823c6440 | |||
| ed720b2ea9 | |||
| 263ac78515 | |||
| b9f83c43bc | |||
| f4609088e3 | |||
| d9326ea34b | |||
| 6589adcf75 | |||
| 2e2c1b82b3 | |||
| 0451fa2138 | |||
| 66b90754f8 | |||
| e66b7e9a79 | |||
| 4f056763e9 | |||
| de70224728 | |||
| 6f69af666f | |||
| 1f7278022c | |||
| b5e8d3dfe2 | |||
| 3edec0687c | |||
| a503e12ef9 | |||
| ea60ac60ba | |||
| 825820f7b9 | |||
| ba4a806cd7 | |||
| effd1fd588 | |||
| bf2b01df2d | |||
| 4581088a0a | |||
| 863dbb02f4 | |||
| 8fd8015b19 | |||
| 83ddbf0d73 | |||
| fad0170cef | |||
| a734d381ac | |||
| 0b8f492613 | |||
| 11c3488438 | |||
| cc709a0231 | |||
| 0a0d998208 | |||
| 03839601bb | |||
| 3e28dd4fae | |||
| 2674111e0b | |||
| 7488225aa6 | |||
| c1a9816c57 | |||
| 2d1932719a | |||
| 315be81e20 | |||
| 65d9aa3e9f | |||
| 8243e8c49d | |||
| c9d31b3ba4 | |||
| 29cfee7154 | |||
| bbae842fdb | |||
| 85e966a3f4 | |||
| 3d8bfb6112 | |||
| 7822064045 | |||
| 906861638f | |||
| 78dd453a9b | |||
| 204520d9c9 | |||
| 72b967c0ab | |||
| 781266885f | |||
| a869619fcd | |||
| 625965e129 | |||
| 91504c663b | |||
| 6cd690b737 | |||
| 3b2fe37ce1 | |||
| 8fea27e8b6 | |||
| cbea5752d1 | |||
| 34e789298b | |||
| 62c49eab5a | |||
| 2f8c81792d | |||
| 9a356cdd04 | |||
| 69ff510bac |
@@ -96,9 +96,9 @@ jobs:
|
||||
fi
|
||||
MAJOR=$(echo "$VERSION" | cut -d. -f1)
|
||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "release_tag=v${MAJOR}" >> "$GITHUB_OUTPUT"
|
||||
echo "release_tag=stable" >> "$GITHUB_OUTPUT"
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
echo "branch=version/${MAJOR}" >> "$GITHUB_OUTPUT"
|
||||
echo "branch=main" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: "Step 1b: Bump version"
|
||||
id: bump
|
||||
@@ -263,6 +263,7 @@ jobs:
|
||||
run: |
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
php /tmp/moko-platform-api/cli/badge_update.php --path . --version "${VERSION}" 2>/dev/null || true
|
||||
php /tmp/moko-platform-api/cli/version_check.php --path . --fix 2>/dev/null || true
|
||||
|
||||
- name: "Step 5: Write update stream"
|
||||
if: >-
|
||||
@@ -270,6 +271,15 @@ jobs:
|
||||
steps.platform.outputs.platform == 'joomla'
|
||||
run: |
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
|
||||
# Fetch latest updates.xml from main so preserve logic has all channels
|
||||
GA_TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
curl -sf -H "Authorization: token ${GA_TOKEN}" \
|
||||
"${API}/contents/updates.xml?ref=main" 2>/dev/null | \
|
||||
python3 -c "import sys,json,base64; print(base64.b64decode(json.load(sys.stdin)['content']).decode())" \
|
||||
> updates.xml 2>/dev/null || true
|
||||
|
||||
php /tmp/moko-platform-api/cli/updates_xml_build.php \
|
||||
--path . --version "${VERSION}" --stability stable \
|
||||
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
|
||||
@@ -297,9 +307,7 @@ jobs:
|
||||
# -- STEP 6: Create tag ---------------------------------------------------
|
||||
- name: "Step 6: Create git tag"
|
||||
if: >-
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
steps.check.outputs.tag_exists != 'true' &&
|
||||
steps.version.outputs.is_minor == 'true'
|
||||
steps.version.outputs.skip != 'true'
|
||||
run: |
|
||||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||
# Only create the major release tag if it doesn't exist yet
|
||||
@@ -453,110 +461,35 @@ jobs:
|
||||
SHA256_ZIP=$(sha256sum "/tmp/${ZIP_NAME}" | cut -d' ' -f1)
|
||||
SHA256_TAR=$(sha256sum "/tmp/${TAR_NAME}" | cut -d' ' -f1)
|
||||
|
||||
# -- Delete existing assets with same name before uploading ------
|
||||
# -- Get existing assets for cleanup --------------------------------
|
||||
ASSETS=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}/assets" 2>/dev/null || echo "[]")
|
||||
for ASSET_NAME in "$ZIP_NAME" "$TAR_NAME"; do
|
||||
|
||||
# -- Create per-file .sha256 checksum files -------------------------
|
||||
echo "${SHA256_ZIP} ${ZIP_NAME}" > "/tmp/${ZIP_NAME}.sha256"
|
||||
echo "${SHA256_TAR} ${TAR_NAME}" > "/tmp/${TAR_NAME}.sha256"
|
||||
|
||||
# -- Upload packages + checksums to release tag --------------------
|
||||
for ASSET in "${ZIP_NAME}" "${TAR_NAME}" "${ZIP_NAME}.sha256" "${TAR_NAME}.sha256"; do
|
||||
[ ! -f "/tmp/${ASSET}" ] && continue
|
||||
# Delete existing asset with same name
|
||||
ASSET_ID=$(echo "$ASSETS" | python3 -c "
|
||||
import sys,json
|
||||
assets = json.load(sys.stdin)
|
||||
for a in assets:
|
||||
if a['name'] == '${ASSET_NAME}':
|
||||
if a['name'] == '${ASSET}':
|
||||
print(a['id']); break
|
||||
" 2>/dev/null || true)
|
||||
if [ -n "$ASSET_ID" ]; then
|
||||
curl -sf -X DELETE -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}/assets/${ASSET_ID}" 2>/dev/null || true
|
||||
fi
|
||||
[ -n "$ASSET_ID" ] && curl -sf -X DELETE -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}/assets/${ASSET_ID}" 2>/dev/null || true
|
||||
# Upload
|
||||
curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @"/tmp/${ASSET}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}/assets?name=${ASSET}" > /dev/null 2>&1 || true
|
||||
done
|
||||
|
||||
# -- Upload both to release tag ----------------------------------
|
||||
curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @"/tmp/${ZIP_NAME}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}/assets?name=${ZIP_NAME}" > /dev/null 2>&1 || true
|
||||
|
||||
curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary @"/tmp/${TAR_NAME}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}/assets?name=${TAR_NAME}" > /dev/null 2>&1 || true
|
||||
|
||||
# -- Update updates.xml with both download formats ---------------
|
||||
if [ -f "updates.xml" ]; then
|
||||
ZIP_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${ZIP_NAME}"
|
||||
TAR_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${TAR_NAME}"
|
||||
|
||||
# Use Python to update only the stable entry's downloads + sha256
|
||||
export PY_ZIP_URL="$ZIP_URL" PY_TAR_URL="$TAR_URL" PY_SHA="$SHA256_ZIP"
|
||||
python3 << 'PYEOF'
|
||||
import re, os
|
||||
|
||||
with open("updates.xml") as f:
|
||||
content = f.read()
|
||||
|
||||
zip_url = os.environ["PY_ZIP_URL"]
|
||||
tar_url = os.environ["PY_TAR_URL"]
|
||||
sha = os.environ["PY_SHA"]
|
||||
|
||||
# Find the stable update block and replace its downloads + sha256
|
||||
def replace_stable(m):
|
||||
block = m.group(0)
|
||||
# Replace downloads block
|
||||
new_downloads = (
|
||||
" <downloads>\n"
|
||||
f" <downloadurl type=\"full\" format=\"zip\">{zip_url}</downloadurl>\n"
|
||||
" </downloads>"
|
||||
)
|
||||
block = re.sub(r' <downloads>.*?</downloads>', new_downloads, block, flags=re.DOTALL)
|
||||
# Add or replace sha256
|
||||
if '<sha256>' in block:
|
||||
block = re.sub(r' <sha256>.*?</sha256>', f' <sha256>{sha}</sha256>', block)
|
||||
else:
|
||||
block = block.replace('</downloads>', f'</downloads>\n <sha256>{sha}</sha256>')
|
||||
return block
|
||||
|
||||
content = re.sub(
|
||||
r' <update>.*?<tag>stable</tag>.*?</update>',
|
||||
replace_stable,
|
||||
content,
|
||||
flags=re.DOTALL
|
||||
)
|
||||
|
||||
with open("updates.xml", "w") as f:
|
||||
f.write(content)
|
||||
PYEOF
|
||||
|
||||
CURRENT_BRANCH="${{ github.ref_name }}"
|
||||
git add updates.xml
|
||||
git commit -m "chore(release): ZIP + tar.gz for ${VERSION} [skip ci]" \
|
||||
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>" || true
|
||||
git push || true
|
||||
|
||||
# Sync updates.xml to main via direct API (always runs — may be on version/XX branch)
|
||||
GA_TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
API="${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}"
|
||||
|
||||
FILE_SHA=$(curl -sf -H "Authorization: token ${GA_TOKEN}" \
|
||||
"${API}/contents/updates.xml?ref=main" | jq -r '.sha // empty')
|
||||
|
||||
if [ -n "$FILE_SHA" ]; then
|
||||
CONTENT=$(base64 -w0 updates.xml)
|
||||
curl -sf -X PUT -H "Authorization: token ${GA_TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/contents/updates.xml" \
|
||||
-d "$(jq -n \
|
||||
--arg content "$CONTENT" \
|
||||
--arg sha "$FILE_SHA" \
|
||||
--arg msg "chore: sync updates.xml ${VERSION} [skip ci]" \
|
||||
--arg branch "main" \
|
||||
'{content: $content, sha: $sha, message: $msg, branch: $branch}'
|
||||
)" > /dev/null 2>&1 \
|
||||
&& echo "updates.xml synced to main via API" \
|
||||
|| echo "WARNING: failed to sync updates.xml to main"
|
||||
else
|
||||
echo "WARNING: could not get updates.xml SHA from main"
|
||||
fi
|
||||
fi
|
||||
# updates.xml already handled by Step 5 (updates_xml_build.php with preserve logic)
|
||||
|
||||
echo "### Packages" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -567,73 +500,33 @@ jobs:
|
||||
echo "| Release | \`${RELEASE_TAG}\` | |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Download | [${ZIP_NAME}](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${ZIP_NAME}) |" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# -- STEP 8b: Update release description with changelog + SHA ----------------
|
||||
- name: "Step 8b: Update release body with changelog and SHA"
|
||||
# -- STEP 8b: Update release description with changelog ----------------------
|
||||
- name: "Step 8b: Update release body"
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
run: |
|
||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
EXT_ELEMENT="${{ steps.updates.outputs.ext_element }}"
|
||||
EXT_TYPE="${{ steps.updates.outputs.ext_type }}"
|
||||
EXT_FOLDER="${{ steps.updates.outputs.ext_folder }}"
|
||||
MOKO_CLI="/tmp/moko-platform-api/cli"
|
||||
|
||||
# Build TYPE_PREFIX to match Step 8's ZIP naming
|
||||
EXT_ELEMENT=$(echo "$EXT_ELEMENT" | sed -E 's/^(pkg_|com_|mod_|plg_[a-z]+_|tpl_|lib_)//')
|
||||
TYPE_PREFIX=""
|
||||
case "${EXT_TYPE}" in
|
||||
plugin) TYPE_PREFIX="plg_${EXT_FOLDER}_" ;;
|
||||
module) TYPE_PREFIX="mod_" ;;
|
||||
component) TYPE_PREFIX="com_" ;;
|
||||
template) TYPE_PREFIX="tpl_" ;;
|
||||
library) TYPE_PREFIX="lib_" ;;
|
||||
package) TYPE_PREFIX="pkg_" ;;
|
||||
esac
|
||||
ZIP_NAME="${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION}.zip"
|
||||
TAR_NAME="${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION}.tar.gz"
|
||||
|
||||
# Get SHA from the built files
|
||||
SHA256_ZIP=""
|
||||
[ -f "/tmp/${ZIP_NAME}" ] && SHA256_ZIP=$(sha256sum "/tmp/${ZIP_NAME}" | cut -d' ' -f1)
|
||||
SHA256_TAR=""
|
||||
[ -f "/tmp/${TAR_NAME}" ] && SHA256_TAR=$(sha256sum "/tmp/${TAR_NAME}" | cut -d' ' -f1)
|
||||
|
||||
# Extract latest changelog entry (strip the ## header to avoid duplicate)
|
||||
CHANGELOG=""
|
||||
if [ -f "CHANGELOG.md" ]; then
|
||||
CHANGELOG=$(sed -n "/^## \[*${VERSION}/,/^## \[*[0-9]/p" CHANGELOG.md | sed '$d' | sed '1d')
|
||||
[ -z "$CHANGELOG" ] && CHANGELOG=$(sed -n '/^## /,/^## /p' CHANGELOG.md | sed '$d' | sed '1d' | head -30)
|
||||
fi
|
||||
|
||||
# Build release body (single header, no duplicate from changelog)
|
||||
BODY="## ${VERSION} ($(date +%Y-%m-%d))\n\n"
|
||||
if [ -n "$CHANGELOG" ]; then
|
||||
BODY="${BODY}${CHANGELOG}\n\n"
|
||||
fi
|
||||
BODY="${BODY}---\n\n### Checksums\n\n"
|
||||
BODY="${BODY}| File | SHA-256 |\n|------|--------|\n"
|
||||
[ -n "$SHA256_ZIP" ] && BODY="${BODY}| \`${ZIP_NAME}\` | \`${SHA256_ZIP}\` |\n"
|
||||
[ -n "$SHA256_TAR" ] && BODY="${BODY}| \`${TAR_NAME}\` | \`${SHA256_TAR}\` |\n"
|
||||
|
||||
# Get release ID and update body
|
||||
RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/releases/tags/${RELEASE_TAG}" 2>/dev/null | \
|
||||
python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
||||
|
||||
if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "None" ]; then
|
||||
python3 -c "
|
||||
import json, urllib.request
|
||||
body = '''$(printf '%b' "$BODY")'''
|
||||
data = json.dumps({'body': body}).encode()
|
||||
req = urllib.request.Request(
|
||||
'${API_BASE}/releases/${RELEASE_ID}',
|
||||
data=data,
|
||||
headers={'Authorization': 'token ${{ secrets.GA_TOKEN }}', 'Content-Type': 'application/json'},
|
||||
method='PATCH'
|
||||
)
|
||||
urllib.request.urlopen(req)
|
||||
" 2>/dev/null && echo "Release body updated with changelog + SHA" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
php ${MOKO_CLI}/release_body_update.php \
|
||||
--path . --version "${VERSION}" --tag "${RELEASE_TAG}" \
|
||||
--token "${{ secrets.GA_TOKEN }}" \
|
||||
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
|
||||
2>/dev/null || {
|
||||
# Fallback: simple body update if CLI not available
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
"${API_BASE}/releases/tags/${RELEASE_TAG}" 2>/dev/null | \
|
||||
python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
||||
if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "None" ]; then
|
||||
BODY="## ${VERSION} ($(date +%Y-%m-%d))\n\nChecksum files attached as \`*.sha256\` assets."
|
||||
curl -sf -X PATCH -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}" \
|
||||
-d "{\"body\":\"${BODY}\"}" > /dev/null 2>&1
|
||||
fi
|
||||
}
|
||||
echo "Release body updated" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# -- STEP 9: Mirror to GitHub (stable only) --------------------------------
|
||||
- name: "Step 9: Mirror release to GitHub"
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: moko-platform.Automation
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Auto-create feature branch when an issue is opened
|
||||
|
||||
name: "Universal: Issue Branch"
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
issues: write
|
||||
|
||||
env:
|
||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||
|
||||
jobs:
|
||||
create-branch:
|
||||
name: Create feature branch
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Create branch and comment
|
||||
run: |
|
||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||
ISSUE_NUM="${{ github.event.issue.number }}"
|
||||
ISSUE_TITLE="${{ github.event.issue.title }}"
|
||||
|
||||
# Build slug from title: lowercase, replace non-alnum with dash, trim
|
||||
SLUG=$(echo "${ISSUE_TITLE}" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//' | cut -c1-40)
|
||||
BRANCH="feature/${ISSUE_NUM}-${SLUG}"
|
||||
|
||||
# Check dev branch exists
|
||||
DEV_EXISTS=$(curl -sf -o /dev/null -w '%{http_code}' \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
"${API}/branches/dev" 2>/dev/null || echo "000")
|
||||
|
||||
if [ "${DEV_EXISTS}" != "200" ]; then
|
||||
echo "No dev branch -- skipping"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Create branch from dev
|
||||
HTTP=$(curl -sf -o /dev/null -w '%{http_code}' -X POST \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/branches" \
|
||||
-d "{\"new_branch_name\":\"${BRANCH}\",\"old_branch_name\":\"dev\"}" 2>/dev/null || echo "000")
|
||||
|
||||
if [ "${HTTP}" = "201" ]; then
|
||||
echo "Created branch: ${BRANCH}"
|
||||
|
||||
# Comment on issue with branch link
|
||||
REPO_URL="${GITEA_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 \
|
||||
-H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/json" \
|
||||
"${API}/issues/${ISSUE_NUM}/comments" \
|
||||
-d "{\"body\":\"${BODY}\"}" > /dev/null 2>&1
|
||||
|
||||
echo "Commented on issue #${ISSUE_NUM}"
|
||||
else
|
||||
echo "Failed to create branch (HTTP ${HTTP}) -- may already exist"
|
||||
fi
|
||||
@@ -7,8 +7,8 @@
|
||||
# INGROUP: moko-platform.Release
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
# PATH: /templates/workflows/universal/pre-release.yml.template
|
||||
# VERSION: 05.00.00
|
||||
# BRIEF: Manual pre-release — builds dev/alpha/beta/rc packages from any branch
|
||||
# VERSION: 05.01.00
|
||||
# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch
|
||||
|
||||
name: "Universal: Pre-Release"
|
||||
|
||||
@@ -45,30 +45,46 @@ jobs:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GA_TOKEN }}
|
||||
|
||||
- name: Setup PHP
|
||||
- name: Setup tools
|
||||
run: |
|
||||
if ! command -v php &> /dev/null; then
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl >/dev/null 2>&1
|
||||
# Update moko-platform CLI tools if available; install PHP if missing
|
||||
if command -v moko-platform-update &> /dev/null; then
|
||||
moko-platform-update
|
||||
elif [ -d "/opt/moko-platform" ]; then
|
||||
cd /opt/moko-platform && git pull origin main --quiet 2>/dev/null || true
|
||||
else
|
||||
if ! command -v php &> /dev/null; then
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl >/dev/null 2>&1
|
||||
fi
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/moko-platform.git" \
|
||||
/tmp/moko-platform-api
|
||||
fi
|
||||
# Set MOKO_CLI to whichever path exists
|
||||
if [ -d "/opt/moko-platform/cli" ]; then
|
||||
echo "MOKO_CLI=/opt/moko-platform/cli" >> "$GITHUB_ENV"
|
||||
else
|
||||
echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
|
||||
fi
|
||||
|
||||
- name: Setup moko-platform tools
|
||||
env:
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||
run: |
|
||||
git clone --depth 1 --branch main --quiet "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" /tmp/moko-platform-api
|
||||
|
||||
- name: Detect platform
|
||||
id: platform
|
||||
run: |
|
||||
php /tmp/moko-platform-api/cli/manifest_read.php --path . --github-output
|
||||
PLATFORM=$(sed -n 's/.*<platform>\([^<]*\)<\/platform>.*/\1/p' .mokogitea/manifest.xml 2>/dev/null | head -1 | tr -d '[:space:]')
|
||||
[ -z "$PLATFORM" ] && PLATFORM="generic"
|
||||
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
|
||||
MANIFEST=$(find ./src -maxdepth 1 -name "pkg_*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||
[ -z "$MANIFEST" ] && MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" ! -path "*/packages/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||
[ -z "$MANIFEST" ] && MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||
MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1)
|
||||
echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT"
|
||||
echo "mod_file=${MOD_FILE}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Resolve metadata
|
||||
- name: Resolve metadata and bump version
|
||||
id: meta
|
||||
run: |
|
||||
STABILITY="${{ inputs.stability }}"
|
||||
MOKO_API="/tmp/moko-platform-api/cli"
|
||||
|
||||
case "$STABILITY" in
|
||||
development) SUFFIX="-dev"; TAG="development" ;;
|
||||
@@ -77,32 +93,61 @@ jobs:
|
||||
release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
|
||||
esac
|
||||
|
||||
# Read current version from manifest (priority) or README — no bump yet
|
||||
VERSION=$(php ${MOKO_API}/version_read.php --path .)
|
||||
echo "Version: ${VERSION}"
|
||||
# Patch bump via CLI tool
|
||||
php ${MOKO_CLI}/version_bump.php --path .
|
||||
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null)
|
||||
[ -z "$VERSION" ] && VERSION="00.00.01"
|
||||
TODAY=$(date +%Y-%m-%d)
|
||||
|
||||
# Ensure platform-specific manifest matches
|
||||
php ${MOKO_API}/version_set_platform.php --path . --version "${VERSION}"
|
||||
# Update platform-specific manifest
|
||||
PLATFORM="${{ steps.platform.outputs.platform }}"
|
||||
MANIFEST="${{ steps.platform.outputs.manifest }}"
|
||||
MOD_FILE="${{ steps.platform.outputs.mod_file }}"
|
||||
|
||||
# Git setup for later commits
|
||||
php ${MOKO_CLI}/version_set_platform.php \
|
||||
--path . --version "$VERSION" --branch "${{ github.ref_name }}" 2>/dev/null || true
|
||||
|
||||
# Verify version consistency across all files
|
||||
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
|
||||
|
||||
# Commit version bump
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||
git add -A
|
||||
git diff --cached --quiet || {
|
||||
git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
|
||||
git push origin HEAD 2>&1
|
||||
}
|
||||
|
||||
# Detect element from Joomla/Dolibarr manifest
|
||||
set +o pipefail
|
||||
PLATFORM="${{ steps.platform.outputs.platform }}"
|
||||
EXT_ELEMENT=$(php ${MOKO_API}/manifest_read.php --path . --field name 2>/dev/null | tr -d ' ' | tr '[:upper:]' '[:lower:]' || true)
|
||||
# For Joomla, prefer <element> tag
|
||||
if [ "$PLATFORM" = "joomla" ]; then
|
||||
MANIFEST=$(find . -maxdepth 4 -name "*.xml" ! -path "./.git/*" -print0 2>/dev/null | xargs -0 grep -l '<extension' 2>/dev/null | head -1 || true)
|
||||
if [ -n "$MANIFEST" ]; then
|
||||
ELEM=$(grep -oP "<element>\K[^<]+" "$MANIFEST" 2>/dev/null | head -1 || true)
|
||||
[ -n "$ELEM" ] && EXT_ELEMENT="$ELEM"
|
||||
fi
|
||||
fi
|
||||
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
set -o pipefail
|
||||
# Auto-detect element (platform-aware)
|
||||
EXT_ELEMENT=""
|
||||
case "$PLATFORM" in
|
||||
joomla)
|
||||
if [ -n "$MANIFEST" ]; then
|
||||
EXT_ELEMENT=$(sed -n 's/.*<element>\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" 2>/dev/null | head -1)
|
||||
if [ -z "$EXT_ELEMENT" ]; then
|
||||
EXT_ELEMENT=$(basename "$MANIFEST" .xml | tr '[:upper:]' '[:lower:]')
|
||||
case "$EXT_ELEMENT" in
|
||||
templatedetails|manifest) EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') ;;
|
||||
esac
|
||||
fi
|
||||
else
|
||||
EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
fi
|
||||
;;
|
||||
dolibarr)
|
||||
if [ -n "$MOD_FILE" ]; then
|
||||
MOD_BASENAME=$(basename "$MOD_FILE" .class.php)
|
||||
EXT_ELEMENT=$(echo "$MOD_BASENAME" | sed 's/^mod//' | tr '[:upper:]' '[:lower:]')
|
||||
else
|
||||
EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||
;;
|
||||
esac
|
||||
|
||||
ZIP_NAME="${EXT_ELEMENT}-${VERSION}${SUFFIX}.zip"
|
||||
|
||||
@@ -112,42 +157,78 @@ jobs:
|
||||
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
||||
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
|
||||
echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
|
||||
echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
|
||||
|
||||
- name: Build package
|
||||
run: |
|
||||
SOURCE_DIR="src"
|
||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||
if [ ! -d "$SOURCE_DIR" ]; then
|
||||
echo "::error::No src/ or htdocs/ directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
MANIFEST="${{ steps.meta.outputs.manifest }}"
|
||||
EXT_TYPE=""
|
||||
if [ -n "$MANIFEST" ]; then
|
||||
EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||
fi
|
||||
|
||||
EXCLUDES="sftp-config* .ftpignore *.ppk *.pem *.key .env* *.local .build-trigger"
|
||||
|
||||
mkdir -p build/package
|
||||
|
||||
REPO_ROOT=$(pwd)
|
||||
|
||||
if [ "$EXT_TYPE" = "package" ] && [ -d "${SOURCE_DIR}/packages" ]; then
|
||||
echo "=== Building Joomla PACKAGE (multi-extension) ==="
|
||||
for ext_dir in "${SOURCE_DIR}"/packages/*/; do
|
||||
[ ! -d "$ext_dir" ] && continue
|
||||
EXT_NAME=$(basename "$ext_dir")
|
||||
echo " Packaging sub-extension: ${EXT_NAME}"
|
||||
cd "$ext_dir"
|
||||
zip -r "${REPO_ROOT}/build/package/${EXT_NAME}.zip" . -x $EXCLUDES
|
||||
cd "${REPO_ROOT}"
|
||||
done
|
||||
for f in "${SOURCE_DIR}"/*.xml "${SOURCE_DIR}"/*.php; do
|
||||
[ -f "$f" ] && cp "$f" build/package/
|
||||
done
|
||||
else
|
||||
echo "=== Building standard extension ==="
|
||||
rsync -a \
|
||||
--exclude='sftp-config*' \
|
||||
--exclude='.ftpignore' \
|
||||
--exclude='*.ppk' \
|
||||
--exclude='*.pem' \
|
||||
--exclude='*.key' \
|
||||
--exclude='.env*' \
|
||||
--exclude='*.local' \
|
||||
--exclude='.build-trigger' \
|
||||
"${SOURCE_DIR}/" build/package/
|
||||
fi
|
||||
|
||||
- name: Create ZIP
|
||||
id: zip
|
||||
run: |
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
SUFFIX="${{ steps.meta.outputs.suffix }}"
|
||||
PLATFORM="${{ steps.platform.outputs.platform }}"
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
cd build/package
|
||||
zip -r "../${ZIP_NAME}" .
|
||||
cd ..
|
||||
|
||||
if [ "$PLATFORM" = "joomla" ]; then
|
||||
php /tmp/moko-platform-api/cli/joomla_build.php --path . --version "${VERSION}" --suffix "${SUFFIX}" --output build --github-output
|
||||
else
|
||||
# Generic build: zip src/ directory
|
||||
SOURCE_DIR="src"
|
||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||
[ ! -d "$SOURCE_DIR" ] && { echo "::error::No src/ or htdocs/"; exit 1; }
|
||||
EXT_ELEMENT="${{ steps.meta.outputs.ext_element }}"
|
||||
ZIP_NAME="${EXT_ELEMENT}-${VERSION}${SUFFIX}.zip"
|
||||
mkdir -p build
|
||||
cd "$SOURCE_DIR" && zip -r "../build/${ZIP_NAME}" . && cd ..
|
||||
SHA256=$(sha256sum "build/${ZIP_NAME}" | cut -d' ' -f1)
|
||||
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
|
||||
echo "zip_path=build/${ZIP_NAME}" >> "$GITHUB_OUTPUT"
|
||||
echo "sha256=${SHA256}" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
SHA256=$(sha256sum "${ZIP_NAME}" | cut -d' ' -f1)
|
||||
echo "sha256=${SHA256}" >> "$GITHUB_OUTPUT"
|
||||
echo "ZIP: ${ZIP_NAME} (SHA: ${SHA256:0:16}...)"
|
||||
|
||||
- name: Create or replace Gitea release
|
||||
id: release
|
||||
continue-on-error: true
|
||||
run: |
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
SHA256="${{ steps.zip.outputs.sha256 }}"
|
||||
ZIP_NAME="${{ steps.zip.outputs.zip_name }}"
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
EXT_ELEMENT="${{ steps.meta.outputs.ext_element }}"
|
||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
@@ -155,7 +236,7 @@ jobs:
|
||||
|
||||
BODY="## ${VERSION} ($(date +%Y-%m-%d))
|
||||
**Channel:** ${STABILITY}
|
||||
**SHA-256:** \`${SHA256}\`"
|
||||
Checksum file attached as \`${ZIP_NAME}.sha256\`."
|
||||
|
||||
# Delete existing release
|
||||
EXISTING_ID=$(curl -sS -H "Authorization: token ${TOKEN}" \
|
||||
@@ -185,76 +266,96 @@ jobs:
|
||||
curl -sS -X POST -H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
"${API}/releases/${RELEASE_ID}/assets?name=${ZIP_NAME}" \
|
||||
--data-binary "@${{ steps.zip.outputs.zip_path }}"
|
||||
--data-binary "@build/${ZIP_NAME}"
|
||||
|
||||
# Upload per-file checksum
|
||||
echo "${SHA256} ${ZIP_NAME}" > "build/${ZIP_NAME}.sha256"
|
||||
curl -sS -X POST -H "Authorization: token ${TOKEN}" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
"${API}/releases/${RELEASE_ID}/assets?name=${ZIP_NAME}.sha256" \
|
||||
--data-binary "@build/${ZIP_NAME}.sha256"
|
||||
|
||||
echo "Released: ${EXT_ELEMENT} ${VERSION} (${STABILITY})"
|
||||
|
||||
- name: "Update updates.xml"
|
||||
- name: Update updates.xml
|
||||
if: steps.platform.outputs.platform == 'joomla'
|
||||
run: |
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
SHA256="${{ steps.zip.outputs.sha256 }}"
|
||||
php /tmp/moko-platform-api/cli/updates_xml_build.php --path . --version "$VERSION" --stability "$STABILITY" --sha "$SHA256" --gitea-url "$GITEA_URL" --org "$GITEA_ORG" --repo "$GITEA_REPO"
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
|
||||
if [ ! -f "updates.xml" ]; then
|
||||
echo "No updates.xml -- skipping"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Use moko-platform CLI to build/update updates.xml (preserves other channels)
|
||||
MOKO_CLI="/tmp/moko-platform-api/cli"
|
||||
if [ -f "${MOKO_CLI}/updates_xml_build.php" ]; then
|
||||
php "${MOKO_CLI}/updates_xml_build.php" \
|
||||
--path . --version "${VERSION}" --stability "${STABILITY}" \
|
||||
--sha "${SHA256}" \
|
||||
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}"
|
||||
echo "Updated ${STABILITY} channel via CLI: version=${VERSION}"
|
||||
else
|
||||
echo "WARNING: updates_xml_build.php not found — skipping"
|
||||
fi
|
||||
|
||||
# Commit and push
|
||||
if ! git diff --quiet updates.xml 2>/dev/null; then
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
git add updates.xml
|
||||
git commit -m "chore: update $STABILITY channel $VERSION [skip ci]"
|
||||
git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]"
|
||||
git push origin HEAD 2>&1 || echo "WARNING: push failed"
|
||||
fi
|
||||
|
||||
- name: "Sync updates.xml to all branches"
|
||||
if: steps.platform.outputs.platform == 'joomla'
|
||||
run: |
|
||||
php /tmp/moko-platform-api/cli/updates_xml_sync.php --path . --current "${{ github.ref_name }}" --branches main,dev --version "${{ steps.meta.outputs.version }}" --token "${{ secrets.GA_TOKEN }}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" --gitea-url "${GITEA_URL}"
|
||||
CURRENT_BRANCH="${{ github.ref_name }}"
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
|
||||
for BRANCH in main dev; do
|
||||
[ "$BRANCH" = "$CURRENT_BRANCH" ] && continue
|
||||
echo "Syncing updates.xml -> ${BRANCH}"
|
||||
git fetch origin "${BRANCH}" 2>/dev/null || continue
|
||||
git checkout "origin/${BRANCH}" -- . 2>/dev/null || continue
|
||||
git checkout "${CURRENT_BRANCH}" -- updates.xml
|
||||
if ! git diff --quiet updates.xml 2>/dev/null; then
|
||||
git add updates.xml
|
||||
git commit -m "chore: sync updates.xml from ${CURRENT_BRANCH} [skip ci]"
|
||||
git push origin HEAD:refs/heads/${BRANCH} 2>&1 || echo "WARNING: push to ${BRANCH} failed"
|
||||
fi
|
||||
git checkout "${CURRENT_BRANCH}" 2>/dev/null
|
||||
done
|
||||
|
||||
- name: "Delete lesser pre-release channels (cascade)"
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
|
||||
# Cascade: rc → beta,alpha,dev | beta → alpha,dev | alpha → dev | dev → nothing
|
||||
case "$STABILITY" in
|
||||
release-candidate) TAGS_TO_DELETE="beta alpha development" ;;
|
||||
beta) TAGS_TO_DELETE="alpha development" ;;
|
||||
alpha) TAGS_TO_DELETE="development" ;;
|
||||
*) TAGS_TO_DELETE="" ;;
|
||||
esac
|
||||
php ${MOKO_CLI}/release_cascade.php \
|
||||
--stability "${{ steps.meta.outputs.stability }}" \
|
||||
--token "${TOKEN}" \
|
||||
--api-base "${API_BASE}"
|
||||
|
||||
[ -z "$TAGS_TO_DELETE" ] && exit 0
|
||||
|
||||
for TAG in $TAGS_TO_DELETE; do
|
||||
RELEASE_ID=$(curl -sS -H "Authorization: token ${TOKEN}" \
|
||||
"${API_BASE}/releases/tags/${TAG}" 2>/dev/null | \
|
||||
python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
||||
|
||||
if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "None" ]; then
|
||||
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \
|
||||
"${API_BASE}/releases/${RELEASE_ID}" 2>/dev/null || true
|
||||
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \
|
||||
"${API_BASE}/tags/${TAG}" 2>/dev/null || true
|
||||
echo "Deleted: ${TAG} (id: ${RELEASE_ID})"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: "Post-release version bump"
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
MOKO_API="/tmp/moko-platform-api/cli"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
|
||||
# Bump patch for next dev cycle
|
||||
BUMP_OUTPUT=$(php ${MOKO_API}/version_bump.php --path .)
|
||||
NEXT=$(echo "$BUMP_OUTPUT" | grep -oP '\d{2}\.\d{2}\.\d{2}$' || true)
|
||||
[ -z "$NEXT" ] && exit 0
|
||||
|
||||
# Update platform-specific manifest to next version
|
||||
php ${MOKO_API}/version_set_platform.php --path . --version "${NEXT}"
|
||||
|
||||
git add -A
|
||||
git diff --cached --quiet || {
|
||||
git commit -m "chore: update development channel ${VERSION} [skip ci]"
|
||||
git push origin HEAD 2>&1
|
||||
}
|
||||
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||
SHA256="${{ steps.zip.outputs.sha256 }}"
|
||||
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
|
||||
|
||||
@@ -73,12 +73,12 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GA_TOKEN }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup MokoStandards tools
|
||||
- name: Setup moko-platform tools
|
||||
env:
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }}
|
||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||
@@ -87,11 +87,15 @@ jobs:
|
||||
if ! command -v composer &> /dev/null; then
|
||||
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1
|
||||
fi
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/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
|
||||
if [ -d "/tmp/moko-platform" ]; then
|
||||
echo "moko-platform already available — skipping clone"
|
||||
else
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
||||
/tmp/moko-platform 2>/dev/null || true
|
||||
fi
|
||||
if [ -d "/tmp/moko-platform" ] && [ -f "/tmp/moko-platform/composer.json" ]; then
|
||||
cd /tmp/moko-platform && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
||||
fi
|
||||
|
||||
- name: Generate updates.xml entry
|
||||
@@ -100,14 +104,14 @@ jobs:
|
||||
BRANCH="${{ github.ref_name }}"
|
||||
REPO="${{ github.repository }}"
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
VERSION=$(php /tmp/mokostandards-api/cli/version_read.php --path . 2>/dev/null || echo "0.0.0")
|
||||
VERSION=$(php /tmp/moko-platform/cli/version_read.php --path . 2>/dev/null || echo "0.0.0")
|
||||
|
||||
# Auto-bump patch on all branches (dev, alpha, beta, rc)
|
||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||
git config --local user.name "gitea-actions[bot]"
|
||||
BUMPED=$(php /tmp/mokostandards-api/cli/version_bump.php --path . 2>/dev/null || true)
|
||||
BUMPED=$(php /tmp/moko-platform/cli/version_bump.php --path . 2>/dev/null || true)
|
||||
if [ -n "$BUMPED" ]; then
|
||||
VERSION=$(php /tmp/mokostandards-api/cli/version_read.php --path . 2>/dev/null || echo "$VERSION")
|
||||
VERSION=$(php /tmp/moko-platform/cli/version_read.php --path . 2>/dev/null || echo "$VERSION")
|
||||
git add -A
|
||||
git commit -m "chore(version): auto-bump patch ${VERSION} [skip ci]" \
|
||||
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>" 2>/dev/null || true
|
||||
@@ -442,15 +446,190 @@ jobs:
|
||||
printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json
|
||||
fi
|
||||
|
||||
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 --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
|
||||
elif [ -f "/tmp/mokostandards-api/deploy/deploy-sftp.php" ]; then
|
||||
php /tmp/mokostandards-api/deploy/deploy-sftp.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
|
||||
PLATFORM=$(php /tmp/moko-platform/cli/platform_detect.php --path . 2>/dev/null || true)
|
||||
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/moko-platform/deploy/deploy-joomla.php" ]; then
|
||||
php /tmp/moko-platform/deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
|
||||
elif [ -f "/tmp/moko-platform/deploy/deploy-sftp.php" ]; then
|
||||
php /tmp/moko-platform/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: Validate updates.xml integrity
|
||||
run: |
|
||||
ERRORS=0
|
||||
|
||||
if [ ! -f "updates.xml" ]; then
|
||||
echo "::error::updates.xml not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Well-formed XML
|
||||
if ! python3 -c "import xml.etree.ElementTree as ET; ET.parse('updates.xml')" 2>/dev/null; then
|
||||
echo "::error::updates.xml is not valid XML"
|
||||
ERRORS=$((ERRORS+1))
|
||||
fi
|
||||
|
||||
python3 << 'PYEOF'
|
||||
import xml.etree.ElementTree as ET, sys, re, os
|
||||
|
||||
tree = ET.parse("updates.xml")
|
||||
root = tree.getroot()
|
||||
updates = root.findall("update")
|
||||
errors = 0
|
||||
warnings = 0
|
||||
seen_tags = set()
|
||||
|
||||
# All 5 channels MUST be present
|
||||
REQUIRED_CHANNELS = {"stable", "rc", "beta", "alpha", "dev"}
|
||||
VALID_TAGS = REQUIRED_CHANNELS | {"development"} # accept legacy alias
|
||||
REPO = os.environ.get("GITEA_REPO", "")
|
||||
ORG = os.environ.get("GITEA_ORG", "MokoConsulting")
|
||||
REPO_BASE = f"https://git.mokoconsulting.tech/{ORG}/"
|
||||
|
||||
# Gitea release tag names per channel (Moko standard)
|
||||
RELEASE_TAG_MAP = {
|
||||
"stable": "stable",
|
||||
"rc": "release-candidate",
|
||||
"beta": "beta",
|
||||
"alpha": "alpha",
|
||||
"dev": "development",
|
||||
"development": "development",
|
||||
}
|
||||
|
||||
# Joomla update XML required fields per
|
||||
# https://docs.joomla.org/Deploying_an_Update_Server
|
||||
REQUIRED_FIELDS = ["name", "element", "type", "version", "infourl"]
|
||||
|
||||
for i, u in enumerate(updates):
|
||||
tag_el = u.find("tags/tag")
|
||||
tag = tag_el.text.strip() if tag_el is not None and tag_el.text else None
|
||||
label = f"Entry {i+1} (<tag>{tag or '?'}</tag>)"
|
||||
|
||||
# -- Required Joomla fields --
|
||||
for field in REQUIRED_FIELDS:
|
||||
el = u.find(field)
|
||||
if el is None or not (el.text or "").strip():
|
||||
print(f"::error::{label}: missing required <{field}>")
|
||||
errors += 1
|
||||
|
||||
# -- <downloads><downloadurl> --
|
||||
dl = u.find("downloads/downloadurl")
|
||||
if dl is None or not (dl.text or "").strip():
|
||||
print(f"::error::{label}: missing <downloads><downloadurl>")
|
||||
errors += 1
|
||||
else:
|
||||
dl_url = dl.text.strip()
|
||||
# Must point to org repo
|
||||
if REPO_BASE not in dl_url:
|
||||
print(f"::error::{label}: download URL not under {REPO_BASE}: {dl_url}")
|
||||
errors += 1
|
||||
# Must end in .zip
|
||||
if not dl_url.endswith(".zip"):
|
||||
print(f"::error::{label}: download URL must end in .zip: {dl_url}")
|
||||
errors += 1
|
||||
# Must use correct Gitea release tag in path
|
||||
if tag and tag in RELEASE_TAG_MAP:
|
||||
expected_tag = RELEASE_TAG_MAP[tag]
|
||||
if f"/download/{expected_tag}/" not in dl_url:
|
||||
print(f"::error::{label}: download URL should contain /download/{expected_tag}/ but got: {dl_url}")
|
||||
errors += 1
|
||||
|
||||
# -- <client> (required for Joomla to match update) --
|
||||
client = u.find("client")
|
||||
if client is None or not (client.text or "").strip():
|
||||
print(f"::error::{label}: missing <client> (required for Joomla update matching)")
|
||||
errors += 1
|
||||
|
||||
# -- <targetplatform> --
|
||||
tp = u.find("targetplatform")
|
||||
if tp is None:
|
||||
print(f"::error::{label}: missing <targetplatform>")
|
||||
errors += 1
|
||||
else:
|
||||
tp_name = tp.get("name", "")
|
||||
tp_ver = tp.get("version", "")
|
||||
if tp_name != "joomla":
|
||||
print(f"::error::{label}: targetplatform name should be 'joomla', got '{tp_name}'")
|
||||
errors += 1
|
||||
if not tp_ver:
|
||||
print(f"::error::{label}: targetplatform missing version regex")
|
||||
errors += 1
|
||||
elif "5" not in tp_ver or "6" not in tp_ver:
|
||||
print(f"::warning::{label}: targetplatform version may not cover Joomla 5+6: {tp_ver}")
|
||||
warnings += 1
|
||||
|
||||
# -- <type> must be valid Joomla type --
|
||||
type_el = u.find("type")
|
||||
if type_el is not None and type_el.text:
|
||||
valid_types = {"component", "module", "plugin", "template", "library", "package", "file"}
|
||||
if type_el.text.strip() not in valid_types:
|
||||
print(f"::error::{label}: invalid type '{type_el.text}' (expected: {valid_types})")
|
||||
errors += 1
|
||||
|
||||
# -- <version> format (XX.YY.ZZ with optional suffix) --
|
||||
ver_el = u.find("version")
|
||||
if ver_el is not None and ver_el.text:
|
||||
if not re.match(r"^\d{2}\.\d{2}\.\d{2}(-\w+)?$", ver_el.text.strip()):
|
||||
print(f"::warning::{label}: version '{ver_el.text}' does not match XX.YY.ZZ format")
|
||||
warnings += 1
|
||||
|
||||
# -- <maintainer> and <maintainerurl> --
|
||||
for field in ["maintainer", "maintainerurl"]:
|
||||
el = u.find(field)
|
||||
if el is None or not (el.text or "").strip():
|
||||
print(f"::warning::{label}: missing <{field}>")
|
||||
warnings += 1
|
||||
|
||||
# -- Valid stability tag --
|
||||
if tag is None:
|
||||
print(f"::error::{label}: missing <tags><tag>")
|
||||
errors += 1
|
||||
elif tag not in VALID_TAGS:
|
||||
print(f"::error::{label}: invalid tag '{tag}' (expected: {VALID_TAGS})")
|
||||
errors += 1
|
||||
|
||||
# -- Duplicate tag check --
|
||||
norm_tag = "dev" if tag == "development" else tag
|
||||
if norm_tag in seen_tags:
|
||||
print(f"::error::{label}: duplicate channel '{tag}'")
|
||||
errors += 1
|
||||
if norm_tag:
|
||||
seen_tags.add(norm_tag)
|
||||
|
||||
# -- All 5 channels must exist --
|
||||
missing = REQUIRED_CHANNELS - seen_tags
|
||||
if missing:
|
||||
print(f"::error::Missing required update channels: {', '.join(sorted(missing))}")
|
||||
errors += 1
|
||||
|
||||
# -- Version ordering: higher stability must not exceed dev version --
|
||||
channel_versions = {}
|
||||
for u in updates:
|
||||
tag_el = u.find("tags/tag")
|
||||
ver_el = u.find("version")
|
||||
if tag_el is not None and ver_el is not None and tag_el.text and ver_el.text:
|
||||
norm = "dev" if tag_el.text.strip() == "development" else tag_el.text.strip()
|
||||
# Strip suffix for comparison (01.00.18-dev -> 01.00.18)
|
||||
base_ver = re.sub(r"-\w+$", "", ver_el.text.strip())
|
||||
channel_versions[norm] = base_ver
|
||||
|
||||
# Cascade check: dev >= alpha >= beta >= rc >= stable
|
||||
ORDER = ["dev", "alpha", "beta", "rc", "stable"]
|
||||
for j in range(1, len(ORDER)):
|
||||
current = ORDER[j]
|
||||
previous = ORDER[j - 1]
|
||||
if current in channel_versions and previous in channel_versions:
|
||||
if channel_versions[current] > channel_versions[previous]:
|
||||
print(f"::error::{current} version ({channel_versions[current]}) is ahead of {previous} ({channel_versions[previous]})")
|
||||
errors += 1
|
||||
|
||||
# -- Summary --
|
||||
print(f"\nupdates.xml validation: {len(updates)} entries, {errors} error(s), {warnings} warning(s)")
|
||||
if errors > 0:
|
||||
sys.exit(1)
|
||||
PYEOF
|
||||
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
|
||||
@@ -31,6 +31,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- License/subscription check
|
||||
- System email template branding (DB approach)
|
||||
|
||||
## [02.06.00] - 2026-05-25
|
||||
|
||||
### Added
|
||||
- Alias offline bypass: aliases with offline=No override Joomla's global offline setting, allowing access via alias domain while main site is down
|
||||
- Block non-master users from viewing or editing MokoWaaS plugin settings
|
||||
- Master user bypasses ALL tenant restrictions (install from URL, global config, sysinfo, installer, templates)
|
||||
|
||||
### Fixed
|
||||
- Install API endpoint: extract ZIP to temp directory before passing to Joomla Installer (was passing ZIP path directly)
|
||||
- Clean up extracted temp directory on success or failure
|
||||
- Update site disabled by Joomla when protected=1 — ensureProtectedFlag() now re-enables it
|
||||
- CI: auto-release fetches updates.xml from main before building (preserves all channels)
|
||||
- CI: version_check.php --fix runs after version bump in both workflows
|
||||
- CI: checksums attached as `[filename].sha256` files instead of in release body
|
||||
- CI: auto-release cleaned up — removed duplicate asset loops, inline Python updater, dead code
|
||||
- CI: Step 8b uses `release_body_update.php` CLI instead of inline Python
|
||||
- CI: Step 6 tag creation no longer gated by `is_minor` (was never set)
|
||||
|
||||
### Changed
|
||||
- CI: auto-release uses stream tag `stable` instead of version tag `vXX`
|
||||
- CI: pre-release package build uses absolute paths (fixes relative path zip error)
|
||||
|
||||
## [02.05.00] - 2026-05-24
|
||||
|
||||
### Added
|
||||
- Joomla `protected=1` flag on all MokoWaaS extensions (framework-level disable/uninstall prevention)
|
||||
- Self-healing protected flag — restored each admin session if cleared
|
||||
- Block non-master disable via plugin list toggle (`plugins.publish`)
|
||||
- Package script sets `protected=1, locked=0` on every install/update
|
||||
- Legacy plugin entry in updates.xml for sites upgrading from standalone plugin
|
||||
|
||||
### Fixed
|
||||
- CI: auto-release workflow `pkg_pkg_` duplication in release names, ZIP filenames, and SHA256 paths
|
||||
- CI: auto-release now strips existing type prefix and uses `<packagename>` for packages
|
||||
- CI: `updates_xml_build` was cascading entries for all lower channels on stable release — now writes only current channel
|
||||
- CI: `targetplatform` regex `((5.[0-9])|(6.[0-9]))` caused Gitea 500 on XML render — simplified to `(5|6)\..*`
|
||||
- updates.xml stable entry now has correct `<tag>stable</tag>` and download URL
|
||||
- README slimmed to overview, detailed content moved to wiki
|
||||
|
||||
## [02.03.10] - 2026-05-24
|
||||
|
||||
### Added
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
DEFGROUP: Joomla.Plugin
|
||||
INGROUP: MokoWaaS
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS
|
||||
VERSION: 02.04.00
|
||||
VERSION: 02.08.03
|
||||
PATH: /README.md
|
||||
BRIEF: MokoWaaS platform plugin for Joomla
|
||||
-->
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<license>GPL-3.0-or-later</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.04.00</version>
|
||||
<version>02.08.03</version>
|
||||
<description>Minimal API-only component for MokoWaaS. Provides REST endpoints for site health, cache, updates, and backups.</description>
|
||||
<namespace path="api/src">Moko\Component\MokoWaaS\Api</namespace>
|
||||
<administration>
|
||||
|
||||
@@ -1025,6 +1025,31 @@ class MokoWaaS extends CMSPlugin
|
||||
$this->app->redirect('index.php?option=com_plugins');
|
||||
}
|
||||
}
|
||||
|
||||
// Block non-master from viewing or editing MokoWaaS plugin settings
|
||||
if ($option === 'com_plugins')
|
||||
{
|
||||
$view = $this->app->input->get('view', '');
|
||||
$layout = $this->app->input->get('layout', '');
|
||||
$extensionId = (int) $this->app->input->get('extension_id', 0);
|
||||
|
||||
if (($view === 'plugin' || $layout === 'edit') && $extensionId > 0)
|
||||
{
|
||||
$db = Factory::getDbo();
|
||||
$query = $db->getQuery(true)
|
||||
->select('COUNT(*)')
|
||||
->from($db->quoteName('#__extensions'))
|
||||
->where($db->quoteName('extension_id') . ' = ' . $extensionId)
|
||||
->where($db->quoteName('element') . ' = ' . $db->quote('mokowaas'))
|
||||
->where($db->quoteName('type') . ' = ' . $db->quote('plugin'));
|
||||
|
||||
if ((int) $db->setQuery($query)->loadResult() > 0)
|
||||
{
|
||||
$this->app->enqueueMessage('MokoWaaS settings are restricted to the master user.', 'warning');
|
||||
$this->app->redirect('index.php?option=com_plugins');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1042,6 +1067,8 @@ class MokoWaaS extends CMSPlugin
|
||||
try
|
||||
{
|
||||
$db = Factory::getDbo();
|
||||
|
||||
// Set protected=1, locked=0 on MokoWaaS extensions
|
||||
$query = $db->getQuery(true)
|
||||
->update($db->quoteName('#__extensions'))
|
||||
->set($db->quoteName('protected') . ' = 1')
|
||||
@@ -1051,6 +1078,18 @@ class MokoWaaS extends CMSPlugin
|
||||
->where($db->quoteName('protected') . ' = 0');
|
||||
$db->setQuery($query);
|
||||
$db->execute();
|
||||
|
||||
// Ensure update site stays enabled (protected extensions get their update site disabled by Joomla)
|
||||
$query = $db->getQuery(true)
|
||||
->update($db->quoteName('#__update_sites') . ' AS us')
|
||||
->join('INNER', $db->quoteName('#__update_sites_extensions') . ' AS use2 ON us.update_site_id = use2.update_site_id')
|
||||
->join('INNER', $db->quoteName('#__extensions') . ' AS e ON use2.extension_id = e.extension_id')
|
||||
->set('us.enabled = 1')
|
||||
->where('us.enabled = 0')
|
||||
->where('(' . $db->quoteName('e.element') . ' = ' . $db->quote('mokowaas')
|
||||
. ' OR ' . $db->quoteName('e.element') . ' = ' . $db->quote('pkg_mokowaas') . ')');
|
||||
$db->setQuery($query);
|
||||
$db->execute();
|
||||
}
|
||||
catch (\Throwable $e)
|
||||
{
|
||||
@@ -2747,11 +2786,36 @@ class MokoWaaS extends CMSPlugin
|
||||
|
||||
file_put_contents($tmpFile, $zipData);
|
||||
|
||||
// Extract ZIP to temp directory
|
||||
$extractDir = $this->app->getConfig()->get('tmp_path', JPATH_ROOT . '/tmp')
|
||||
. '/mokowaas_extract_' . md5($url);
|
||||
|
||||
if (is_dir($extractDir))
|
||||
{
|
||||
$this->rmdirRecursive($extractDir);
|
||||
}
|
||||
|
||||
mkdir($extractDir, 0755, true);
|
||||
|
||||
$zip = new \ZipArchive();
|
||||
|
||||
if ($zip->open($tmpFile) !== true)
|
||||
{
|
||||
@unlink($tmpFile);
|
||||
$this->sendHealthResponse(500, ['error' => 'Failed to open ZIP']);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$zip->extractTo($extractDir);
|
||||
$zip->close();
|
||||
@unlink($tmpFile);
|
||||
|
||||
// Install using Joomla's installer
|
||||
$installer = \Joomla\CMS\Installer\Installer::getInstance();
|
||||
$result = $installer->install($tmpFile);
|
||||
$result = $installer->install($extractDir);
|
||||
|
||||
@unlink($tmpFile);
|
||||
$this->rmdirRecursive($extractDir);
|
||||
|
||||
if ($result)
|
||||
{
|
||||
@@ -2774,6 +2838,11 @@ class MokoWaaS extends CMSPlugin
|
||||
{
|
||||
@unlink($tmpFile ?? '');
|
||||
|
||||
if (!empty($extractDir) && is_dir($extractDir))
|
||||
{
|
||||
$this->rmdirRecursive($extractDir);
|
||||
}
|
||||
|
||||
$this->sendHealthResponse(500, [
|
||||
'error' => 'Install exception',
|
||||
'message' => $e->getMessage(),
|
||||
@@ -2782,6 +2851,42 @@ class MokoWaaS extends CMSPlugin
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively remove a directory.
|
||||
*
|
||||
* @param string $dir Directory path
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @since 02.06.00
|
||||
*/
|
||||
protected function rmdirRecursive(string $dir): void
|
||||
{
|
||||
if (!is_dir($dir))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$items = new \RecursiveIteratorIterator(
|
||||
new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS),
|
||||
\RecursiveIteratorIterator::CHILD_FIRST
|
||||
);
|
||||
|
||||
foreach ($items as $item)
|
||||
{
|
||||
if ($item->isDir())
|
||||
{
|
||||
rmdir($item->getPathname());
|
||||
}
|
||||
else
|
||||
{
|
||||
unlink($item->getPathname());
|
||||
}
|
||||
}
|
||||
|
||||
rmdir($dir);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Site Alias handling
|
||||
// ------------------------------------------------------------------
|
||||
@@ -2944,25 +3049,33 @@ class MokoWaaS extends CMSPlugin
|
||||
}
|
||||
|
||||
// Offline: use Joomla's native offline mode for frontend requests
|
||||
if (!empty($alias->offline) && (string) $alias->offline === '1'
|
||||
&& $this->app->isClient('site'))
|
||||
if ($this->app->isClient('site'))
|
||||
{
|
||||
// Allow health API to still respond
|
||||
if ($this->app->input->get('mokowaas', '') !== '')
|
||||
if (!empty($alias->offline) && (string) $alias->offline === '1')
|
||||
{
|
||||
return;
|
||||
// Allow health API to still respond
|
||||
if ($this->app->input->get('mokowaas', '') !== '')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Set custom offline message if provided
|
||||
$message = $alias->offline_message ?? '';
|
||||
|
||||
if (!empty($message))
|
||||
{
|
||||
$this->app->getConfig()->set('offline_message', $message);
|
||||
}
|
||||
|
||||
// Enable Joomla's native offline mode
|
||||
$this->app->getConfig()->set('offline', 1);
|
||||
}
|
||||
|
||||
// Set custom offline message if provided
|
||||
$message = $alias->offline_message ?? '';
|
||||
|
||||
if (!empty($message))
|
||||
else
|
||||
{
|
||||
$this->app->getConfig()->set('offline_message', $message);
|
||||
// Alias is NOT offline — override Joomla's global offline setting
|
||||
// This allows access via the alias domain even when the main site is offline
|
||||
$this->app->getConfig()->set('offline', 0);
|
||||
}
|
||||
|
||||
// Enable Joomla's native offline mode — renders through the template's offline.php
|
||||
$this->app->getConfig()->set('offline', 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3328,12 +3441,18 @@ class MokoWaaS extends CMSPlugin
|
||||
*/
|
||||
protected function enforceAdminRestrictions()
|
||||
{
|
||||
// Master user bypasses ALL restrictions
|
||||
if ($this->isMasterUser())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$input = $this->app->input;
|
||||
$option = $input->get('option', '');
|
||||
$view = $input->get('view', '');
|
||||
$task = $input->get('task', '');
|
||||
|
||||
// Disable install-from-URL for ALL users (safety net)
|
||||
// Disable install-from-URL for non-master users
|
||||
if ($this->params->get('disable_install_url', 1)
|
||||
&& $option === 'com_installer'
|
||||
&& stripos($task, 'install') !== false
|
||||
@@ -3344,12 +3463,6 @@ class MokoWaaS extends CMSPlugin
|
||||
return;
|
||||
}
|
||||
|
||||
// Remaining restrictions only apply to non-master users
|
||||
if ($this->isMasterUser())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
$blocked = [];
|
||||
|
||||
if ($this->params->get('restrict_installer', 1))
|
||||
|
||||
@@ -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.04.00</version>
|
||||
<version>02.08.03</version>
|
||||
<description>This plugin rebrands the Joomla system interface with MokoWaaS identity. It applies language overrides and ensures consistent branding across the platform.</description>
|
||||
<namespace path=".">Moko\Plugin\System\MokoWaaS</namespace>
|
||||
<scriptfile>script.php</scriptfile>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<license>GPL-3.0-or-later</license>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||
<version>02.04.00</version>
|
||||
<version>02.08.03</version>
|
||||
<description>Joomla Web Services API routes for MokoWaaS site management — health checks, cache, updates, backups, and site info.</description>
|
||||
<namespace path="src">Moko\Plugin\WebServices\MokoWaaS</namespace>
|
||||
<files>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<extension type="package" method="upgrade">
|
||||
<name>MokoWaaS</name>
|
||||
<packagename>mokowaas</packagename>
|
||||
<version>02.04.00</version>
|
||||
<version>02.08.03</version>
|
||||
<creationDate>2026-05-23</creationDate>
|
||||
<author>Moko Consulting</author>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
|
||||
+105
-64
@@ -1,83 +1,124 @@
|
||||
<?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.04.00
|
||||
VERSION: 02.08.02
|
||||
-->
|
||||
|
||||
<updates>
|
||||
<update>
|
||||
<name>MokoWaaS</name>
|
||||
<description>MokoWaaS update</description>
|
||||
<element>mokowaas</element>
|
||||
<element>pkg_mokowaas</element>
|
||||
<type>package</type>
|
||||
<version>02.04.00-dev</version>
|
||||
<tags><tag>development</tag></tags>
|
||||
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/development</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.04.00-dev.zip</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="(5|6)\..*" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
</update>
|
||||
<update>
|
||||
<name>MokoWaaS</name>
|
||||
<description>MokoWaaS update</description>
|
||||
<element>mokowaas</element>
|
||||
<type>package</type>
|
||||
<version>02.04.00-alpha</version>
|
||||
<tags><tag>alpha</tag></tags>
|
||||
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/alpha</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/alpha/pkg_mokowaas-02.04.00-alpha.zip</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="(5|6)\..*" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
</update>
|
||||
<update>
|
||||
<name>MokoWaaS</name>
|
||||
<description>MokoWaaS update</description>
|
||||
<element>mokowaas</element>
|
||||
<type>package</type>
|
||||
<version>02.04.00-beta</version>
|
||||
<tags><tag>beta</tag></tags>
|
||||
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/beta</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/beta/pkg_mokowaas-02.04.00-beta.zip</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="(5|6)\..*" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
</update>
|
||||
<update>
|
||||
<name>MokoWaaS</name>
|
||||
<description>MokoWaaS update</description>
|
||||
<element>mokowaas</element>
|
||||
<type>package</type>
|
||||
<version>02.04.00-rc</version>
|
||||
<tags><tag>rc</tag></tags>
|
||||
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/rc</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/rc/pkg_mokowaas-02.04.00-rc.zip</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="(5|6)\..*" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
</update>
|
||||
<update>
|
||||
<name>MokoWaaS</name>
|
||||
<description>MokoWaaS update</description>
|
||||
<element>mokowaas</element>
|
||||
<type>package</type>
|
||||
<version>02.04.00</version>
|
||||
<version>02.08.02</version>
|
||||
<client>site</client>
|
||||
<tags><tag>stable</tag></tags>
|
||||
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/stable</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/stable/pkg_mokowaas-02.04.00.zip</downloadurl>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.08.02-dev.zip</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="(5|6)\..*" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
</update>
|
||||
<update>
|
||||
<name>MokoWaaS</name>
|
||||
<description>MokoWaaS update</description>
|
||||
<element>pkg_mokowaas</element>
|
||||
<type>package</type>
|
||||
<version>02.08.02</version>
|
||||
<client>site</client>
|
||||
<tags><tag>rc</tag></tags>
|
||||
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/development</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.08.02-dev.zip</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="(5|6)\..*" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
</update>
|
||||
<update>
|
||||
<name>MokoWaaS</name>
|
||||
<description>MokoWaaS update</description>
|
||||
<element>pkg_mokowaas</element>
|
||||
<type>package</type>
|
||||
<version>02.08.02</version>
|
||||
<client>site</client>
|
||||
<tags><tag>beta</tag></tags>
|
||||
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/development</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.08.02-dev.zip</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="(5|6)\..*" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
</update>
|
||||
<update>
|
||||
<name>MokoWaaS</name>
|
||||
<description>MokoWaaS update</description>
|
||||
<element>pkg_mokowaas</element>
|
||||
<type>package</type>
|
||||
<version>02.08.02</version>
|
||||
<client>site</client>
|
||||
<tags><tag>alpha</tag></tags>
|
||||
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/development</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.08.02-dev.zip</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="(5|6)\..*" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
</update>
|
||||
<update>
|
||||
<name>MokoWaaS</name>
|
||||
<description>MokoWaaS update</description>
|
||||
<element>pkg_mokowaas</element>
|
||||
<type>package</type>
|
||||
<version>02.08.02</version>
|
||||
<client>site</client>
|
||||
<tags><tag>dev</tag></tags>
|
||||
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/development</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.08.02-dev.zip</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="(5|6)\..*" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
</update>
|
||||
<update>
|
||||
<name>System - MokoWaaS</name>
|
||||
<description>MokoWaaS update (legacy plugin)</description>
|
||||
<element>mokowaas</element>
|
||||
<type>plugin</type>
|
||||
<folder>system</folder>
|
||||
<client>site</client>
|
||||
<version>02.08.02</version>
|
||||
<tags><tag>stable</tag></tags>
|
||||
<infourl title="MokoWaaS">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/development</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.08.02-dev.zip</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="(5|6)\..*" />
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
</update>
|
||||
|
||||
<update>
|
||||
<name>MokoWaaS</name>
|
||||
<description>MokoWaaS development build.</description>
|
||||
<element>pkg_mokowaas</element>
|
||||
<type>package</type>
|
||||
<version>02.10.01</version>
|
||||
<creationDate>2026-05-26</creationDate>
|
||||
<infourl title='MokoWaaS'>https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/tag/development</infourl>
|
||||
<downloads>
|
||||
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS/releases/download/development/pkg_mokowaas-02.10.01-dev.zip</downloadurl>
|
||||
</downloads>
|
||||
<sha256>6963bc1636e41267819f33ad4397b36f6dbe8c8840841f9a1276f3670c0b2672</sha256>
|
||||
<tags><tag>development</tag></tags>
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
<targetplatform name='joomla' version='(5|6).*'/>
|
||||
</update>
|
||||
|
||||
</updates>
|
||||
|
||||
Reference in New Issue
Block a user