fix: use updated update-server template with push triggers + bare dev support
- 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:
@@ -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
|
||||
@@ -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 }}
|
||||
|
||||
Reference in New Issue
Block a user