From 4374e3b382372fa087159c6d02ff30e2eb324637 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 23 Apr 2026 13:07:59 -0500 Subject: [PATCH] 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) --- .github/workflows/dev-release.yml | 310 ---------------------------- .github/workflows/update-server.yml | 19 +- 2 files changed, 15 insertions(+), 314 deletions(-) delete mode 100644 .github/workflows/dev-release.yml diff --git a/.github/workflows/dev-release.yml b/.github/workflows/dev-release.yml deleted file mode 100644 index e1049de6..00000000 --- a/.github/workflows/dev-release.yml +++ /dev/null @@ -1,310 +0,0 @@ -# Copyright (C) 2026 Moko Consulting -# -# 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 '/dev/null | head -1) - if [ -z "$MANIFEST" ]; then - echo "No Joomla XML manifest found" && exit 1 - fi - - EXT_NAME=$(sed -n 's/.*\([^<]*\)<\/name>.*/\1/p' "$MANIFEST" | head -1) - EXT_ELEMENT=$(sed -n 's/.*\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" | head -1) - EXT_TYPE=$(sed -n 's/.*]*type="\([^"]*\)".*/\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) - TARGET_PLATFORM=$(sed -n 's/.*\(\).*/\1/p' "$MANIFEST" | head -1) - PHP_MINIMUM=$(sed -n 's/.*\([^<]*\)<\/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="${EXT_CLIENT}" - elif [ "$EXT_TYPE" = "module" ] || [ "$EXT_TYPE" = "plugin" ]; then - CLIENT_TAG="site" - fi - - FOLDER_TAG="" - if [ -n "$EXT_FOLDER" ] && [ "$EXT_TYPE" = "plugin" ]; then - FOLDER_TAG="${EXT_FOLDER}" - fi - - if [ -z "$TARGET_PLATFORM" ]; then - TARGET_PLATFORM='' - fi - - PHP_TAG="" - if [ -n "$PHP_MINIMUM" ]; then - PHP_TAG="${PHP_MINIMUM}" - fi - - # -- Build a single update entry for a given channel tag - build_entry() { - local TAG_NAME="$1" - printf '%s\n' ' ' - printf '%s\n' " ${EXT_NAME}" - printf '%s\n' " ${EXT_NAME} (${TAG_NAME})" - printf '%s\n' " ${EXT_ELEMENT}" - printf '%s\n' " ${EXT_TYPE}" - printf '%s\n' " ${VERSION}-dev" - [ -n "$CLIENT_TAG" ] && printf '%s\n' " ${CLIENT_TAG}" - [ -n "$FOLDER_TAG" ] && printf '%s\n' " ${FOLDER_TAG}" - printf '%s\n' " ${TAG_NAME}" - printf '%s\n' " ${INFO_URL}" - printf '%s\n' ' ' - printf '%s\n' " ${DOWNLOAD_URL}" - printf '%s\n' ' ' - printf '%s\n' " ${SHA256}" - printf '%s\n' " ${TARGET_PLATFORM}" - [ -n "$PHP_TAG" ] && printf '%s\n' " ${PHP_TAG}" - printf '%s\n' ' Moko Consulting' - printf '%s\n' ' https://mokoconsulting.tech' - printf '%s\n' ' ' - } - - # -- Cascade: dev stream writes ONLY the development channel - { - printf '%s\n' "" - printf '%s\n' "" - printf '%s\n' "" - printf '%s\n' '' - build_entry "development" - printf '%s\n' '' - } > /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 diff --git a/.github/workflows/update-server.yml b/.github/workflows/update-server.yml index 7f079c5a..e3b62e30 100644 --- a/.github/workflows/update-server.yml +++ b/.github/workflows/update-server.yml @@ -13,16 +13,27 @@ # Writes updates.xml with multiple entries: # - stable on push to main (from auto-release) # - rc on push to rc/** -# - development on push to dev/** +# - development 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 }}