From 3aa6ab2a58ed291c8bd71de90d19d501ac481fad Mon Sep 17 00:00:00 2001
From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com>
Date: Thu, 9 Apr 2026 10:51:29 -0500
Subject: [PATCH] chore: update .github/workflows/update-server.yml from
MokoStandards
---
.github/workflows/update-server.yml | 290 ++++++++++------------------
1 file changed, 102 insertions(+), 188 deletions(-)
diff --git a/.github/workflows/update-server.yml b/.github/workflows/update-server.yml
index 83c8e0d..c217389 100644
--- a/.github/workflows/update-server.yml
+++ b/.github/workflows/update-server.yml
@@ -6,8 +6,8 @@
# DEFGROUP: GitHub.Workflow
# INGROUP: MokoStandards.Joomla
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
-# PATH: /templates/workflows/joomla/update-server.yml.template
-# VERSION: 04.06.00
+# PATH: /templates/workflows/joomla/update-server.yml
+# VERSION: 04.05.13
# BRIEF: Update Joomla update server XML feed with stable/rc/dev entries
#
# Writes updates.xml with multiple entries:
@@ -20,12 +20,9 @@
name: Update Joomla Update Server XML Feed
on:
- pull_request:
- types: [closed]
+ push:
branches:
- 'dev/**'
- - 'alpha/**'
- - 'beta/**'
- 'rc/**'
paths:
- 'src/**'
@@ -33,14 +30,12 @@ on:
workflow_dispatch:
inputs:
stability:
- description: 'Stability tag'
+ description: 'Stability tag (development, rc, stable)'
required: true
default: 'development'
type: choice
options:
- development
- - alpha
- - beta
- rc
- stable
@@ -54,8 +49,6 @@ jobs:
update-xml:
name: Update updates.xml
runs-on: ubuntu-latest
- if: >-
- github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
steps:
- name: Checkout repository
@@ -82,64 +75,33 @@ jobs:
REPO="${{ github.repository }}"
VERSION=$(php /tmp/mokostandards/api/cli/version_read.php --path . 2>/dev/null || echo "0.0.0")
- # Auto-bump patch on alpha/beta/rc branches (not dev — dev bumps manually)
- if [[ "$BRANCH" != dev/* ]]; then
- git config --local user.email "github-actions[bot]@users.noreply.github.com"
- git config --local user.name "github-actions[bot]"
- BUMPED=$(php /tmp/mokostandards/api/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")
- git add -A
- git commit -m "chore(version): auto-bump patch ${VERSION} [skip ci]" \
- --author="github-actions[bot] " 2>/dev/null || true
- git push 2>/dev/null || true
- fi
- fi
-
# Determine stability from branch or 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"
elif [[ "$BRANCH" == dev/* ]]; then
STABILITY="development"
else
STABILITY="stable"
fi
- # Parse manifest (portable — no grep -P)
+ # Parse manifest
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '/dev/null | head -1)
if [ -z "$MANIFEST" ]; then
echo "No Joomla manifest found — skipping"
exit 0
fi
- # Extract fields using sed (works on all runners)
- EXT_NAME=$(sed -n 's/.*\([^<]*\)<\/name>.*/\1/p' "$MANIFEST" | head -1)
- EXT_TYPE=$(sed -n 's/.*]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
- EXT_ELEMENT=$(sed -n 's/.*\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" | head -1)
- EXT_CLIENT=$(sed -n 's/.*]*client="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
- EXT_FOLDER=$(sed -n 's/.*]*group="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
- EXT_VERSION=$(sed -n 's/.*\([^<]*\)<\/version>.*/\1/p' "$MANIFEST" | head -1)
- TARGET_PLATFORM=$(sed -n 's/.*\(\).*/\1/p' "$MANIFEST" | head -1)
- PHP_MINIMUM=$(sed -n 's/.*\([^<]*\)<\/php_minimum>.*/\1/p' "$MANIFEST" | head -1)
-
- # Fallbacks
- [ -z "$EXT_NAME" ] && EXT_NAME="${{ github.event.repository.name }}"
- [ -z "$EXT_TYPE" ] && EXT_TYPE="component"
-
- # Templates and modules don't have — derive from
- if [ -z "$EXT_ELEMENT" ]; then
- EXT_ELEMENT=$(echo "$EXT_NAME" | tr '[:upper:]' '[:lower:]' | tr -d ' ')
- fi
-
- # Use manifest version if README version is empty
- [ "$VERSION" = "0.0.0" ] && [ -n "$EXT_VERSION" ] && VERSION="$EXT_VERSION"
+ EXT_NAME=$(grep -oP '\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "${{ github.event.repository.name }}")
+ EXT_TYPE=$(grep -oP ']+type="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "component")
+ EXT_ELEMENT=$(grep -oP '\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || basename "$MANIFEST" .xml)
+ EXT_CLIENT=$(grep -oP ']+client="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "")
+ EXT_FOLDER=$(grep -oP ']+group="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "")
+ TARGET_PLATFORM=$(grep -oP '' "$MANIFEST" 2>/dev/null | head -1 || echo "")
+ PHP_MINIMUM=$(grep -oP '\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "")
+ [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(basename "$MANIFEST" .xml)
[ -z "$TARGET_PLATFORM" ] && TARGET_PLATFORM=$(printf '' "/")
CLIENT_TAG=""
@@ -154,117 +116,127 @@ jobs:
# Version suffix for non-stable
DISPLAY_VERSION="$VERSION"
- case "$STABILITY" in
- development) DISPLAY_VERSION="${VERSION}-dev" ;;
- alpha) DISPLAY_VERSION="${VERSION}-alpha" ;;
- beta) DISPLAY_VERSION="${VERSION}-beta" ;;
- rc) DISPLAY_VERSION="${VERSION}-rc" ;;
- esac
+ [ "$STABILITY" = "rc" ] && DISPLAY_VERSION="${VERSION}-rc"
+ [ "$STABILITY" = "development" ] && DISPLAY_VERSION="${VERSION}-dev"
MAJOR=$(echo "$VERSION" | awk -F. '{print $1}')
-
- # Each stability level has its own release tag
- case "$STABILITY" in
- development) RELEASE_TAG="development" ;;
- alpha) RELEASE_TAG="alpha" ;;
- beta) RELEASE_TAG="beta" ;;
- rc) RELEASE_TAG="release-candidate" ;;
- *) RELEASE_TAG="v${MAJOR}" ;;
- esac
-
+ RELEASE_TAG="v${MAJOR}"
PACKAGE_NAME="${EXT_ELEMENT}-${DISPLAY_VERSION}.zip"
DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}"
INFO_URL="https://github.com/${REPO}"
- # ── Build install packages (ZIP + tar.gz) ───────────────────
+ # ── Build install-ready ZIP ─────────────────────────────────
SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
if [ -d "$SOURCE_DIR" ]; then
- EXCLUDES=".ftpignore sftp-config* *.ppk *.pem *.key .env*"
- TAR_NAME="${EXT_ELEMENT}-${DISPLAY_VERSION}.tar.gz"
-
cd "$SOURCE_DIR"
- zip -r "/tmp/${PACKAGE_NAME}" . -x $EXCLUDES
+ zip -r "/tmp/${PACKAGE_NAME}" .
cd ..
- tar -czf "/tmp/${TAR_NAME}" -C "$SOURCE_DIR" \
- --exclude='.ftpignore' --exclude='sftp-config*' \
- --exclude='*.ppk' --exclude='*.pem' --exclude='*.key' --exclude='.env*' .
SHA256=$(sha256sum "/tmp/${PACKAGE_NAME}" | cut -d' ' -f1)
- # Ensure release exists
+ # Ensure draft release exists for this major
gh release view "$RELEASE_TAG" --json tagName > /dev/null 2>&1 || \
- gh release create "$RELEASE_TAG" --title "${RELEASE_TAG} (${DISPLAY_VERSION})" --notes "${STABILITY} release" --prerelease --target main 2>/dev/null || true
+ gh release create "$RELEASE_TAG" --title "v${MAJOR}" --notes "Development release" --draft --target main 2>/dev/null || true
- # Upload both formats
+ # Upload ZIP to the major release
gh release upload "$RELEASE_TAG" "/tmp/${PACKAGE_NAME}" --clobber 2>/dev/null || true
- gh release upload "$RELEASE_TAG" "/tmp/${TAR_NAME}" --clobber 2>/dev/null || true
- echo "Packages: ${PACKAGE_NAME} + ${TAR_NAME} (SHA: ${SHA256})" >> $GITHUB_STEP_SUMMARY
+ echo "Package: ${PACKAGE_NAME} (SHA: ${SHA256})" >> $GITHUB_STEP_SUMMARY
else
SHA256=""
fi
# ── Build the new entry ───────────────────────────────────────
- NEW_ENTRY=""
- NEW_ENTRY="${NEW_ENTRY} \n"
- NEW_ENTRY="${NEW_ENTRY} ${EXT_NAME}\n"
- NEW_ENTRY="${NEW_ENTRY} ${EXT_NAME} (${STABILITY})\n"
- NEW_ENTRY="${NEW_ENTRY} ${EXT_ELEMENT}\n"
- NEW_ENTRY="${NEW_ENTRY} ${EXT_TYPE}\n"
- NEW_ENTRY="${NEW_ENTRY} ${DISPLAY_VERSION}\n"
- [ -n "$CLIENT_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${CLIENT_TAG}\n"
- [ -n "$FOLDER_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${FOLDER_TAG}\n"
- NEW_ENTRY="${NEW_ENTRY} \n"
- NEW_ENTRY="${NEW_ENTRY} ${STABILITY}\n"
- NEW_ENTRY="${NEW_ENTRY} \n"
- NEW_ENTRY="${NEW_ENTRY} ${INFO_URL}\n"
- NEW_ENTRY="${NEW_ENTRY} \n"
- TAR_URL="https://github.com/${REPO}/releases/download/${RELEASE_TAG}/${EXT_ELEMENT}-${DISPLAY_VERSION}.tar.gz"
- NEW_ENTRY="${NEW_ENTRY} ${DOWNLOAD_URL}\n"
- NEW_ENTRY="${NEW_ENTRY} ${TAR_URL}\n"
- NEW_ENTRY="${NEW_ENTRY} \n"
- [ -n "$SHA256" ] && NEW_ENTRY="${NEW_ENTRY} sha256:${SHA256}\n"
- NEW_ENTRY="${NEW_ENTRY} ${TARGET_PLATFORM}\n"
- [ -n "$PHP_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${PHP_TAG}\n"
- NEW_ENTRY="${NEW_ENTRY} Moko Consulting\n"
- NEW_ENTRY="${NEW_ENTRY} https://mokoconsulting.tech\n"
- NEW_ENTRY="${NEW_ENTRY} "
-
- # ── Write new entry to temp file ───────────────────────────────
- printf '%b' "$NEW_ENTRY" > /tmp/new_entry.xml
+ NEW_ENTRY=$(cat <
+ ${EXT_NAME}
+ ${EXT_NAME} (${STABILITY})
+ ${EXT_ELEMENT}
+ ${EXT_TYPE}
+ ${DISPLAY_VERSION}
+ $([ -n "$CLIENT_TAG" ] && echo " ${CLIENT_TAG}")
+ $([ -n "$FOLDER_TAG" ] && echo " ${FOLDER_TAG}")
+
+ ${STABILITY}
+
+ ${INFO_URL}
+
+ ${DOWNLOAD_URL}
+
+ $([ -n "$SHA256" ] && echo " sha256:${SHA256}")
+ ${TARGET_PLATFORM}
+ $([ -n "$PHP_TAG" ] && echo " ${PHP_TAG}")
+ Moko Consulting
+ https://mokoconsulting.tech
+
+XMLEOF
+)
# ── Merge into updates.xml ─────────────────────────────────────
if [ ! -f "updates.xml" ]; then
+ # Create fresh
printf '%s\n' '' > updates.xml
printf '%s\n' '' >> updates.xml
- cat /tmp/new_entry.xml >> updates.xml
- printf '\n%s\n' '' >> updates.xml
+ echo "$NEW_ENTRY" >> updates.xml
+ printf '%s\n' '' >> updates.xml
else
- # Remove existing entry for this stability, insert new one
- printf 'import re\nstability = "%s"\n' "${STABILITY}" > /tmp/merge_xml.py
- printf 'with open("updates.xml") as f: content = f.read()\n' >> /tmp/merge_xml.py
- printf 'with open("/tmp/new_entry.xml") as f: new_entry = f.read()\n' >> /tmp/merge_xml.py
- printf 'pattern = r" .*?" + re.escape(stability) + r".*?\\n?"\n' >> /tmp/merge_xml.py
- printf 'content = re.sub(pattern, "", content, flags=re.DOTALL)\n' >> /tmp/merge_xml.py
- printf 'content = content.replace("", new_entry + "\\n")\n' >> /tmp/merge_xml.py
- printf 'content = re.sub(r"\\n{3,}", "\\n\\n", content)\n' >> /tmp/merge_xml.py
- printf 'with open("updates.xml", "w") as f: f.write(content)\n' >> /tmp/merge_xml.py
- python3 /tmp/merge_xml.py 2>/dev/null || {
- # Fallback: rebuild keeping other stability entries
+ # Remove existing entry for this stability, add new one
+ # Use python for reliable XML manipulation
+ python3 -c "
+import re, sys
+
+with open('updates.xml', 'r') as f:
+ content = f.read()
+
+# Remove existing entry with this stability tag
+pattern = r' .*?${STABILITY}.*?\n?'
+content = re.sub(pattern, '', content, flags=re.DOTALL)
+
+# Insert new entry before
+new_entry = '''${NEW_ENTRY}'''
+content = content.replace('', new_entry + '\n')
+
+# Clean up empty lines
+content = re.sub(r'\n{3,}', '\n\n', content)
+
+with open('updates.xml', 'w') as f:
+ f.write(content)
+" 2>/dev/null || {
+ # Fallback: just rewrite the whole file if python fails
+ # Keep existing stable entry if present
+ STABLE_ENTRY=""
+ if [ "$STABILITY" != "stable" ] && grep -q 'stable' updates.xml; then
+ STABLE_ENTRY=$(sed -n '//,/<\/update>/{ /stable<\/tag>/,/<\/update>/p; //,/stable<\/tag>/p }' updates.xml | sort -u)
+ fi
+ RC_ENTRY=""
+ if [ "$STABILITY" != "rc" ] && grep -q 'rc' updates.xml; then
+ RC_ENTRY=$(python3 -c "
+import re
+with open('updates.xml') as f: c = f.read()
+m = re.search(r'(.*?rc.*?)', c, re.DOTALL)
+if m: print(m.group(1))
+" 2>/dev/null || true)
+ fi
+ DEV_ENTRY=""
+ if [ "$STABILITY" != "development" ] && grep -q 'development' updates.xml; then
+ DEV_ENTRY=$(python3 -c "
+import re
+with open('updates.xml') as f: c = f.read()
+m = re.search(r'(.*?development.*?)', c, re.DOTALL)
+if m: print(m.group(1))
+" 2>/dev/null || true)
+ fi
+
{
printf '%s\n' ''
printf '%s\n' ''
- for TAG in stable rc development; do
- [ "$TAG" = "${STABILITY}" ] && continue
- if grep -q "${TAG}" updates.xml 2>/dev/null; then
- sed -n "//,/<\/update>/{ /${TAG}<\/tag>/p; }" updates.xml
- fi
- done
- cat /tmp/new_entry.xml
- printf '\n%s\n' ''
- } > /tmp/updates_new.xml
- mv /tmp/updates_new.xml updates.xml
+ [ -n "$STABLE_ENTRY" ] && echo "$STABLE_ENTRY"
+ [ -n "$RC_ENTRY" ] && echo "$RC_ENTRY"
+ [ -n "$DEV_ENTRY" ] && echo "$DEV_ENTRY"
+ echo "$NEW_ENTRY"
+ printf '%s\n' ''
+ } > updates.xml
}
fi
@@ -278,64 +250,6 @@ jobs:
git push
}
- - name: SFTP deploy to dev server
- if: contains(github.ref, '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 }}
- GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
- run: |
- # ── Permission check: admin or maintain role required ──────
- ACTOR="${{ github.actor }}"
- REPO="${{ github.repository }}"
- PERMISSION=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" \
- --jq '.permission' 2>/dev/null || \
- gh api "repos/${REPO}/collaborators/${ACTOR}" \
- --jq '.role' 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 /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
- fi
- rm -f /tmp/deploy_key /tmp/sftp-config.json
- echo "SFTP deploy to dev complete" >> $GITHUB_STEP_SUMMARY
-
- - name: Summary
- if: always()
- run: |
echo "## Joomla Update Server" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY