fix: use updated update-server template with push triggers + bare dev support
Repo Health / Access control (push) Failing after 1s
Repo Health / Release configuration (push) Has been skipped
Repo Health / Scripts governance (push) Has been skipped
Repo Health / Repository health (push) Has been skipped

- Replace dev-release.yml with fixed update-server.yml that supports
  push triggers and bare 'dev' branch (synced from MokoStandards-API)
- Template now handles cascade channels for all release streams

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jonathan Miller
2026-04-23 13:07:59 -05:00
parent d252f7c358
commit 4374e3b382
2 changed files with 15 additions and 314 deletions
-310
View File
@@ -1,310 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoWaaS
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoWaaS
# PATH: /.github/workflows/dev-release.yml
# VERSION: 02.01.18
# BRIEF: Dev channel release — build ZIP, upload to Gitea, sync updates.xml to main
#
# +========================================================================+
# | DEV RELEASE PIPELINE |
# +========================================================================+
# | |
# | Triggers on push to dev (src/** or README.md changes): |
# | |
# | 1. Read version from README.md |
# | 2. Parse extension metadata from XML manifest |
# | 3. Build ZIP from src/ |
# | 4. Upload to Gitea "development" release |
# | 5. Write updates.xml (development channel only) |
# | 6. Sync updates.xml to main via Gitea API |
# | |
# | Cascade: dev writes ONLY the development channel. |
# | Higher streams (alpha, beta, rc, stable) cascade downward: |
# | alpha → development, alpha |
# | beta → development, alpha, beta |
# | rc → development, alpha, beta, rc |
# | stable → development, alpha, beta, rc, stable |
# | |
# +========================================================================+
name: Dev Release
on:
push:
branches: [dev]
paths:
- 'src/**'
- 'README.md'
workflow_dispatch:
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:
dev-release:
name: Dev Release
runs-on: release
steps:
- name: Checkout dev branch
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
token: ${{ secrets.GA_TOKEN }}
ref: dev
# -- Read version ----------------------------------------------------------
- name: Read version from README.md
id: version
run: |
VERSION=$(sed -n 's/.*VERSION:[[:space:]]*\([0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\).*/\1/p' README.md | head -1)
if [ -z "$VERSION" ]; then
echo "No VERSION in README.md — skipping"
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "skip=false" >> "$GITHUB_OUTPUT"
# -- Parse manifest --------------------------------------------------------
- name: Parse extension metadata
if: steps.version.outputs.skip != 'true'
id: manifest
run: |
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
if [ -z "$MANIFEST" ]; then
echo "No Joomla XML manifest found" && exit 1
fi
EXT_NAME=$(sed -n 's/.*<name>\([^<]*\)<\/name>.*/\1/p' "$MANIFEST" | head -1)
EXT_ELEMENT=$(sed -n 's/.*<element>\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" | head -1)
EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
EXT_CLIENT=$(sed -n 's/.*<extension[^>]*client="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
EXT_FOLDER=$(sed -n 's/.*<extension[^>]*group="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
TARGET_PLATFORM=$(sed -n 's/.*\(<targetplatform[^/]*\/>\).*/\1/p' "$MANIFEST" | head -1)
PHP_MINIMUM=$(sed -n 's/.*<php_minimum>\([^<]*\)<\/php_minimum>.*/\1/p' "$MANIFEST" | head -1)
[ -z "$EXT_NAME" ] && EXT_NAME="${{ github.event.repository.name }}"
[ -z "$EXT_TYPE" ] && EXT_TYPE="component"
if [ -z "$EXT_ELEMENT" ]; then
EXT_ELEMENT=$(basename "$MANIFEST" .xml | tr '[:upper:]' '[:lower:]')
fi
echo "name=$EXT_NAME" >> "$GITHUB_OUTPUT"
echo "element=$EXT_ELEMENT" >> "$GITHUB_OUTPUT"
echo "type=$EXT_TYPE" >> "$GITHUB_OUTPUT"
echo "client=$EXT_CLIENT" >> "$GITHUB_OUTPUT"
echo "folder=$EXT_FOLDER" >> "$GITHUB_OUTPUT"
echo "target_platform=$TARGET_PLATFORM" >> "$GITHUB_OUTPUT"
echo "php_minimum=$PHP_MINIMUM" >> "$GITHUB_OUTPUT"
# -- Build ZIP -------------------------------------------------------------
- name: Build ZIP package
if: steps.version.outputs.skip != 'true'
id: build
run: |
VERSION="${{ steps.version.outputs.version }}"
ELEMENT="${{ steps.manifest.outputs.element }}"
ZIP_NAME="${ELEMENT}-${VERSION}.zip"
SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
[ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/"; exit 1; }
cd "$SOURCE_DIR"
zip -r "/tmp/${ZIP_NAME}" . -x "*.git*" ".ftpignore" "sftp-config*" "*.ppk" "*.pem" "*.key" ".env*"
cd ..
SHA256=$(sha256sum "/tmp/${ZIP_NAME}" | cut -d' ' -f1)
SIZE=$(stat -c%s "/tmp/${ZIP_NAME}" 2>/dev/null || stat -f%z "/tmp/${ZIP_NAME}")
echo "zip_name=$ZIP_NAME" >> "$GITHUB_OUTPUT"
echo "sha256=$SHA256" >> "$GITHUB_OUTPUT"
echo "size=$SIZE" >> "$GITHUB_OUTPUT"
# -- Upload to Gitea -------------------------------------------------------
- name: Update Gitea development release
if: steps.version.outputs.skip != 'true'
run: |
VERSION="${{ steps.version.outputs.version }}"
ZIP_NAME="${{ steps.build.outputs.zip_name }}"
SHA256="${{ steps.build.outputs.sha256 }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
AUTH="Authorization: token ${{ secrets.GA_TOKEN }}"
# Find or create the development release
RELEASE_JSON=$(curl -sf -H "$AUTH" "${API_BASE}/releases/tags/development" 2>/dev/null || echo "")
RELEASE_ID=$(echo "$RELEASE_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
if [ -z "$RELEASE_ID" ]; then
RELEASE_JSON=$(curl -sf -X POST -H "$AUTH" -H "Content-Type: application/json" \
"${API_BASE}/releases" \
-d "$(python3 -c "import json; print(json.dumps({
'tag_name': 'development',
'name': 'development (${VERSION}-dev)',
'body': '## ${VERSION} (development)\n\n### SHA-256\n\`${SHA256}\`',
'target_commitish': 'dev',
'prerelease': True
}))")")
RELEASE_ID=$(echo "$RELEASE_JSON" | python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
else
curl -sf -X PATCH -H "$AUTH" -H "Content-Type: application/json" \
"${API_BASE}/releases/${RELEASE_ID}" \
-d "$(python3 -c "import json; print(json.dumps({
'name': 'development (${VERSION}-dev)',
'body': '## ${VERSION} (development)\n\n### SHA-256\n\`${SHA256}\`',
'target_commitish': 'dev',
'prerelease': True
}))")" > /dev/null
fi
# Delete all existing assets
ASSETS=$(curl -sf -H "$AUTH" "${API_BASE}/releases/${RELEASE_ID}/assets" 2>/dev/null || echo "[]")
echo "$ASSETS" | python3 -c "
import sys, json
for a in json.load(sys.stdin):
print(a['id'])
" 2>/dev/null | while read -r AID; do
curl -sf -X DELETE -H "$AUTH" "${API_BASE}/releases/${RELEASE_ID}/assets/${AID}" 2>/dev/null || true
done
# Upload new ZIP
curl -sf -X POST -H "$AUTH" \
-H "Content-Type: application/octet-stream" \
--data-binary @"/tmp/${ZIP_NAME}" \
"${API_BASE}/releases/${RELEASE_ID}/assets?name=${ZIP_NAME}" > /dev/null
# -- Write & sync updates.xml ----------------------------------------------
- name: Sync updates.xml to main
if: steps.version.outputs.skip != 'true'
run: |
VERSION="${{ steps.version.outputs.version }}"
ZIP_NAME="${{ steps.build.outputs.zip_name }}"
SHA256="${{ steps.build.outputs.sha256 }}"
EXT_NAME="${{ steps.manifest.outputs.name }}"
EXT_ELEMENT="${{ steps.manifest.outputs.element }}"
EXT_TYPE="${{ steps.manifest.outputs.type }}"
EXT_CLIENT="${{ steps.manifest.outputs.client }}"
EXT_FOLDER="${{ steps.manifest.outputs.folder }}"
TARGET_PLATFORM="${{ steps.manifest.outputs.target_platform }}"
PHP_MINIMUM="${{ steps.manifest.outputs.php_minimum }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
AUTH="Authorization: token ${{ secrets.GA_TOKEN }}"
DOWNLOAD_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/development/${ZIP_NAME}"
INFO_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/development"
# Build optional tags
CLIENT_TAG=""
if [ -n "$EXT_CLIENT" ]; then
CLIENT_TAG="<client>${EXT_CLIENT}</client>"
elif [ "$EXT_TYPE" = "module" ] || [ "$EXT_TYPE" = "plugin" ]; then
CLIENT_TAG="<client>site</client>"
fi
FOLDER_TAG=""
if [ -n "$EXT_FOLDER" ] && [ "$EXT_TYPE" = "plugin" ]; then
FOLDER_TAG="<folder>${EXT_FOLDER}</folder>"
fi
if [ -z "$TARGET_PLATFORM" ]; then
TARGET_PLATFORM='<targetplatform name="joomla" version="((5.[0-9])|(6.[0-9]))" />'
fi
PHP_TAG=""
if [ -n "$PHP_MINIMUM" ]; then
PHP_TAG="<php_minimum>${PHP_MINIMUM}</php_minimum>"
fi
# -- Build a single update entry for a given channel tag
build_entry() {
local TAG_NAME="$1"
printf '%s\n' ' <update>'
printf '%s\n' " <name>${EXT_NAME}</name>"
printf '%s\n' " <description>${EXT_NAME} (${TAG_NAME})</description>"
printf '%s\n' " <element>${EXT_ELEMENT}</element>"
printf '%s\n' " <type>${EXT_TYPE}</type>"
printf '%s\n' " <version>${VERSION}-dev</version>"
[ -n "$CLIENT_TAG" ] && printf '%s\n' " ${CLIENT_TAG}"
[ -n "$FOLDER_TAG" ] && printf '%s\n' " ${FOLDER_TAG}"
printf '%s\n' " <tags><tag>${TAG_NAME}</tag></tags>"
printf '%s\n' " <infourl title=\"${EXT_NAME}\">${INFO_URL}</infourl>"
printf '%s\n' ' <downloads>'
printf '%s\n' " <downloadurl type=\"full\" format=\"zip\">${DOWNLOAD_URL}</downloadurl>"
printf '%s\n' ' </downloads>'
printf '%s\n' " <sha256>${SHA256}</sha256>"
printf '%s\n' " ${TARGET_PLATFORM}"
[ -n "$PHP_TAG" ] && printf '%s\n' " ${PHP_TAG}"
printf '%s\n' ' <maintainer>Moko Consulting</maintainer>'
printf '%s\n' ' <maintainerurl>https://mokoconsulting.tech</maintainerurl>'
printf '%s\n' ' </update>'
}
# -- Cascade: dev stream writes ONLY the development channel
{
printf '%s\n' "<?xml version='1.0' encoding='UTF-8'?>"
printf '%s\n' "<!-- Copyright (C) $(date +%Y) Moko Consulting <hello@mokoconsulting.tech>"
printf '%s\n' " SPDX-License-Identifier: GPL-3.0-or-later"
printf '%s\n' " VERSION: ${VERSION}"
printf '%s\n' " -->"
printf '%s\n' ""
printf '%s\n' '<updates>'
build_entry "development"
printf '%s\n' '</updates>'
} > /tmp/updates.xml
# -- Push updates.xml to main via Gitea API
FILE_SHA=$(curl -sf -H "$AUTH" \
"${API_BASE}/contents/updates.xml?ref=main" | python3 -c "import sys,json; print(json.load(sys.stdin).get('sha',''))" 2>/dev/null || true)
CONTENT=$(base64 -w0 /tmp/updates.xml)
if [ -n "$FILE_SHA" ]; then
curl -sf -X PUT -H "$AUTH" -H "Content-Type: application/json" \
"${API_BASE}/contents/updates.xml" \
-d "$(python3 -c "import json; print(json.dumps({
'content': '${CONTENT}',
'sha': '${FILE_SHA}',
'message': 'chore: sync updates.xml ${VERSION}-dev [skip ci]',
'branch': 'main'
}))")" > /dev/null \
&& echo "updates.xml synced to main (development channel)" \
|| echo "WARNING: failed to sync updates.xml to main"
else
curl -sf -X POST -H "$AUTH" -H "Content-Type: application/json" \
"${API_BASE}/contents/updates.xml" \
-d "$(python3 -c "import json; print(json.dumps({
'content': '${CONTENT}',
'message': 'chore: add updates.xml ${VERSION}-dev [skip ci]',
'branch': 'main'
}))")" > /dev/null \
&& echo "updates.xml created on main" \
|| echo "WARNING: failed to create updates.xml on main"
fi
# -- Summary ---------------------------------------------------------------
- name: Summary
if: always()
run: |
VERSION="${{ steps.version.outputs.version }}"
if [ "${{ steps.version.outputs.skip }}" = "true" ]; then
echo "## Dev Release Skipped" >> $GITHUB_STEP_SUMMARY
else
echo "## Dev Release — ${VERSION}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Detail | Value |" >> $GITHUB_STEP_SUMMARY
echo "|--------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Version | \`${VERSION}-dev\` |" >> $GITHUB_STEP_SUMMARY
echo "| Channels | \`development\` |" >> $GITHUB_STEP_SUMMARY
echo "| Asset | \`${{ steps.build.outputs.zip_name }}\` (${{ steps.build.outputs.size }} bytes) |" >> $GITHUB_STEP_SUMMARY
echo "| SHA-256 | \`${{ steps.build.outputs.sha256 }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| updates.xml | synced to main |" >> $GITHUB_STEP_SUMMARY
fi
+15 -4
View File
@@ -13,16 +13,27 @@
# Writes updates.xml with multiple <update> entries:
# - <tag>stable</tag> on push to main (from auto-release)
# - <tag>rc</tag> on push to rc/**
# - <tag>development</tag> on push to dev/**
# - <tag>development</tag> on push to dev or dev/**
#
# Joomla filters by user's "Minimum Stability" setting.
name: Update Joomla Update Server XML Feed
on:
push:
branches:
- 'dev'
- 'dev/**'
- 'alpha/**'
- 'beta/**'
- 'rc/**'
paths:
- 'src/**'
- 'htdocs/**'
pull_request:
types: [closed]
branches:
- 'dev'
- 'dev/**'
- 'alpha/**'
- 'beta/**'
@@ -58,7 +69,7 @@ jobs:
name: Update updates.xml
runs-on: release
if: >-
github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' || github.event_name == 'push'
steps:
- name: Checkout repository
@@ -112,7 +123,7 @@ jobs:
STABILITY="beta"
elif [[ "$BRANCH" == alpha/* ]]; then
STABILITY="alpha"
elif [[ "$BRANCH" == dev/* ]]; then
elif [[ "$BRANCH" == dev/* ]] || [[ "$BRANCH" == "dev" ]]; then
STABILITY="development"
else
STABILITY="stable"
@@ -389,7 +400,7 @@ jobs:
done
- name: SFTP deploy to dev server
if: contains(github.ref, 'dev/')
if: contains(github.ref, 'dev/') || github.ref == 'refs/heads/dev'
env:
DEV_HOST: ${{ vars.DEV_FTP_HOST }}
DEV_PATH: ${{ vars.DEV_FTP_PATH }}