From 20250db9e0691b1d610ecb71ffc33d03728f341a Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Tue, 23 Jun 2026 12:33:57 -0500 Subject: [PATCH] chore: sync workflows from Template-Joomla (metadata first-class fields) Pull latest workflow versions from MokoConsulting/Template-Joomla. These use the updated mokocli tools with manifest metadata as first-class fields instead of README FILE INFORMATION blocks. Updated: ci-joomla.yml, pre-release.yml, workflow-sync-trigger.yml Added: version-set.yml Removed: deploy-manual.yml, update-server.yml, composer-publish.yml (superseded by auto-release.yml pipeline) Authored-by: Moko Consulting --- .mokogitea/workflows/ci-joomla.yml | 331 ++++++++++++++++++ .mokogitea/workflows/composer-publish.yml | 76 ---- .mokogitea/workflows/deploy-manual.yml | 126 ------- .mokogitea/workflows/pre-release.yml | 16 - .mokogitea/workflows/update-server.yml | 312 ----------------- .mokogitea/workflows/version-set.yml | 130 +++++++ .../workflows/workflow-sync-trigger.yml | 7 + 7 files changed, 468 insertions(+), 530 deletions(-) delete mode 100644 .mokogitea/workflows/composer-publish.yml delete mode 100644 .mokogitea/workflows/deploy-manual.yml delete mode 100644 .mokogitea/workflows/update-server.yml create mode 100644 .mokogitea/workflows/version-set.yml diff --git a/.mokogitea/workflows/ci-joomla.yml b/.mokogitea/workflows/ci-joomla.yml index 727f661..6f184a2 100644 --- a/.mokogitea/workflows/ci-joomla.yml +++ b/.mokogitea/workflows/ci-joomla.yml @@ -164,6 +164,75 @@ jobs: echo "**Manifest validation passed.**" >> $GITHUB_STEP_SUMMARY fi + - name: Update server & packaging checks + continue-on-error: true + run: | + echo "### Update Server & Packaging" >> $GITHUB_STEP_SUMMARY + WARNINGS=0 + + # Find the extension manifest + MANIFEST="" + for XML_FILE in $(find . -maxdepth 2 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do + if grep -q "/dev/null; then + MANIFEST="$XML_FILE" + break + fi + done + + if [ -z "$MANIFEST" ]; then + echo "No manifest found — skipping." >> $GITHUB_STEP_SUMMARY + else + EXT_TYPE=$(grep -oP ']*\btype="\K[^"]+' "$MANIFEST" | head -1) + + # 1. Check exists and uses MokoGitea update server + if ! grep -q '' "$MANIFEST" 2>/dev/null; then + echo "::warning file=${MANIFEST}::Missing \`\` tag — extension will not receive OTA updates" + echo "- **Missing** \`\` — extension will not receive OTA updates" >> $GITHUB_STEP_SUMMARY + WARNINGS=$((WARNINGS + 1)) + else + SERVER_URL=$(grep -oP ']*>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1) + if [ -z "$SERVER_URL" ]; then + echo "::warning file=${MANIFEST}::\`\` is empty — no server URL defined" + echo "- **Empty** \`\` — no server URL defined" >> $GITHUB_STEP_SUMMARY + WARNINGS=$((WARNINGS + 1)) + elif ! echo "$SERVER_URL" | grep -q 'git\.mokoconsulting\.tech'; then + echo "::warning file=${MANIFEST}::Update server does not use MokoGitea engine: ${SERVER_URL}" + echo "- **Non-MokoGitea update server:** \`${SERVER_URL}\`" >> $GITHUB_STEP_SUMMARY + echo " Expected: \`https://git.mokoconsulting.tech/{org}/{repo}/updates.xml\`" >> $GITHUB_STEP_SUMMARY + WARNINGS=$((WARNINGS + 1)) + else + echo "- \`\`: MokoGitea engine ✓" >> $GITHUB_STEP_SUMMARY + fi + fi + + # 2. Check tag exists + if ! grep -q '/dev/null; then + echo "::warning file=${MANIFEST}::Missing \`\` tag — download ID authentication is not configured" + echo "- **Missing** \`\` — download ID authentication not configured" >> $GITHUB_STEP_SUMMARY + WARNINGS=$((WARNINGS + 1)) + else + echo "- \`\`: present ✓" >> $GITHUB_STEP_SUMMARY + fi + + # 3. For packages: check tag + if [ "$EXT_TYPE" = "package" ]; then + if ! grep -q '' "$MANIFEST" 2>/dev/null; then + echo "::warning file=${MANIFEST}::Package is missing \`\` — child extensions will not be removed on uninstall" + echo "- **Missing** \`\` — child extensions will remain when package is uninstalled" >> $GITHUB_STEP_SUMMARY + WARNINGS=$((WARNINGS + 1)) + else + echo "- \`\`: present ✓" >> $GITHUB_STEP_SUMMARY + fi + fi + fi + + echo "" >> $GITHUB_STEP_SUMMARY + if [ "$WARNINGS" -gt 0 ]; then + echo "**${WARNINGS} packaging warning(s).** These won't block CI but should be addressed." >> $GITHUB_STEP_SUMMARY + else + echo "**Update server & packaging checks passed.**" >> $GITHUB_STEP_SUMMARY + fi + - name: Check language files referenced in manifest run: | echo "### Language File Check" >> $GITHUB_STEP_SUMMARY @@ -647,6 +716,268 @@ jobs: echo "**Service provider check passed.**" >> $GITHUB_STEP_SUMMARY fi + - name: Script file reference check + run: | + echo "### Script File Reference" >> $GITHUB_STEP_SUMMARY + ERRORS=0 + + MANIFEST="" + for XML_FILE in $(find . -maxdepth 2 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do + if grep -q "/dev/null; then + MANIFEST="$XML_FILE" + break + fi + done + + if [ -z "$MANIFEST" ]; then + echo "No manifest found — skipping." >> $GITHUB_STEP_SUMMARY + else + MANIFEST_DIR=$(dirname "$MANIFEST") + SCRIPT_FILE=$(grep -oP '\K[^<]+' "$MANIFEST" 2>/dev/null | head -1) + if [ -z "$SCRIPT_FILE" ]; then + echo "No \`\` referenced — skipping." >> $GITHUB_STEP_SUMMARY + elif [ ! -f "${MANIFEST_DIR}/${SCRIPT_FILE}" ]; then + echo "::error file=${MANIFEST}::Manifest references \`${SCRIPT_FILE}\` but file does not exist" + echo "- **Missing** \`${SCRIPT_FILE}\` — referenced in \`\` but not found" >> $GITHUB_STEP_SUMMARY + ERRORS=$((ERRORS + 1)) + else + echo "- \`${SCRIPT_FILE}\`: present ✓" >> $GITHUB_STEP_SUMMARY + fi + fi + + echo "" >> $GITHUB_STEP_SUMMARY + if [ "${ERRORS}" -gt 0 ]; then + echo "**${ERRORS} script file issue(s).**" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo "**Script file reference check passed.**" >> $GITHUB_STEP_SUMMARY + fi + + - name: Media folder validation + run: | + echo "### Media Folder Validation" >> $GITHUB_STEP_SUMMARY + ERRORS=0 + + MANIFEST="" + for XML_FILE in $(find . -maxdepth 2 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do + if grep -q "/dev/null; then + MANIFEST="$XML_FILE" + break + fi + done + + if [ -z "$MANIFEST" ]; then + echo "No manifest found — skipping." >> $GITHUB_STEP_SUMMARY + else + MANIFEST_DIR=$(dirname "$MANIFEST") + + # Check tag and its folder/filename children + MEDIA_DEST=$(grep -oP ']*\bdestination="\K[^"]+' "$MANIFEST" 2>/dev/null | head -1) + MEDIA_FOLDER=$(grep -oP ']*\bfolder="\K[^"]+' "$MANIFEST" 2>/dev/null | head -1) + + if [ -z "$MEDIA_DEST" ] && [ -z "$MEDIA_FOLDER" ]; then + echo "No \`\` tag found — skipping." >> $GITHUB_STEP_SUMMARY + else + if [ -n "$MEDIA_FOLDER" ] && [ ! -d "${MANIFEST_DIR}/${MEDIA_FOLDER}" ]; then + echo "::error file=${MANIFEST}::\`\` references missing directory" + echo "- **Missing** media folder \`${MEDIA_FOLDER}\`" >> $GITHUB_STEP_SUMMARY + ERRORS=$((ERRORS + 1)) + else + echo "- Media folder \`${MEDIA_FOLDER:-(inline)}\`: present ✓" >> $GITHUB_STEP_SUMMARY + + # Check child references inside block + if [ -n "$MEDIA_FOLDER" ]; then + MEDIA_FOLDERS=$(sed -n '//p' "$MANIFEST" | grep -oP '\K[^<]+' 2>/dev/null || true) + for F in $MEDIA_FOLDERS; do + if [ ! -d "${MANIFEST_DIR}/${MEDIA_FOLDER}/${F}" ]; then + echo "- **Missing** media subfolder \`${MEDIA_FOLDER}/${F}\`" >> $GITHUB_STEP_SUMMARY + ERRORS=$((ERRORS + 1)) + fi + done + + MEDIA_FILES=$(sed -n '//p' "$MANIFEST" | grep -oP '\K[^<]+' 2>/dev/null || true) + for F in $MEDIA_FILES; do + if [ ! -f "${MANIFEST_DIR}/${MEDIA_FOLDER}/${F}" ]; then + echo "- **Missing** media file \`${MEDIA_FOLDER}/${F}\`" >> $GITHUB_STEP_SUMMARY + ERRORS=$((ERRORS + 1)) + fi + done + fi + fi + fi + fi + + echo "" >> $GITHUB_STEP_SUMMARY + if [ "${ERRORS}" -gt 0 ]; then + echo "**${ERRORS} media reference issue(s).**" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo "**Media folder validation passed.**" >> $GITHUB_STEP_SUMMARY + fi + + - name: Target platform check + continue-on-error: true + run: | + echo "### Target Platform Check" >> $GITHUB_STEP_SUMMARY + WARNINGS=0 + + MANIFEST="" + for XML_FILE in $(find . -maxdepth 2 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do + if grep -q "/dev/null; then + MANIFEST="$XML_FILE" + break + fi + done + + if [ -z "$MANIFEST" ]; then + echo "No manifest found — skipping." >> $GITHUB_STEP_SUMMARY + else + # Check updates.xml for targetplatform if it exists + if [ -f "updates.xml" ]; then + if ! grep -q '/dev/null; then + echo "::warning file=updates.xml::No \`\` found — Joomla updater cannot filter by compatible version" + echo "- **Missing** \`\` in updates.xml" >> $GITHUB_STEP_SUMMARY + WARNINGS=$((WARNINGS + 1)) + else + echo "- \`\` in updates.xml: present ✓" >> $GITHUB_STEP_SUMMARY + fi + fi + + # Check manifest for minimum PHP/Joomla version hints + if ! grep -qP '|targetplatform|joomla.*version' "$MANIFEST" 2>/dev/null; then + echo "::warning file=${MANIFEST}::No minimum Joomla or PHP version constraint found in manifest" + echo "- **Missing** version constraints (\`\` or \`\`)" >> $GITHUB_STEP_SUMMARY + WARNINGS=$((WARNINGS + 1)) + else + echo "- Version constraints in manifest: present ✓" >> $GITHUB_STEP_SUMMARY + fi + fi + + echo "" >> $GITHUB_STEP_SUMMARY + if [ "$WARNINGS" -gt 0 ]; then + echo "**${WARNINGS} target platform warning(s).**" >> $GITHUB_STEP_SUMMARY + else + echo "**Target platform check passed.**" >> $GITHUB_STEP_SUMMARY + fi + + - name: Changelog URL check + continue-on-error: true + run: | + echo "### Changelog URL Check" >> $GITHUB_STEP_SUMMARY + WARNINGS=0 + + MANIFEST="" + for XML_FILE in $(find . -maxdepth 2 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do + if grep -q "/dev/null; then + MANIFEST="$XML_FILE" + break + fi + done + + if [ -z "$MANIFEST" ]; then + echo "No manifest found — skipping." >> $GITHUB_STEP_SUMMARY + else + if ! grep -q '' "$MANIFEST" 2>/dev/null; then + echo "::warning file=${MANIFEST}::Missing \`\` — Joomla updater will not display changelogs" + echo "- **Missing** \`\` — Joomla 4+ shows changelogs in the update manager when this is set" >> $GITHUB_STEP_SUMMARY + WARNINGS=$((WARNINGS + 1)) + else + CHANGELOG_URL=$(grep -oP '\K[^<]+' "$MANIFEST" | head -1) + echo "- \`\`: \`${CHANGELOG_URL}\` ✓" >> $GITHUB_STEP_SUMMARY + fi + fi + + echo "" >> $GITHUB_STEP_SUMMARY + if [ "$WARNINGS" -gt 0 ]; then + echo "**${WARNINGS} changelog URL warning(s).**" >> $GITHUB_STEP_SUMMARY + else + echo "**Changelog URL check passed.**" >> $GITHUB_STEP_SUMMARY + fi + + - name: Duplicate file references check + continue-on-error: true + run: | + echo "### Duplicate File References" >> $GITHUB_STEP_SUMMARY + WARNINGS=0 + + MANIFEST="" + for XML_FILE in $(find . -maxdepth 2 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do + if grep -q "/dev/null; then + MANIFEST="$XML_FILE" + break + fi + done + + if [ -z "$MANIFEST" ]; then + echo "No manifest found — skipping." >> $GITHUB_STEP_SUMMARY + else + # Extract all and references + ALL_REFS=$(grep -oP '<(filename|folder)[^>]*>\K[^<]+' "$MANIFEST" 2>/dev/null | sort || true) + if [ -z "$ALL_REFS" ]; then + echo "No file/folder references found — skipping." >> $GITHUB_STEP_SUMMARY + else + DUPES=$(echo "$ALL_REFS" | uniq -d) + if [ -n "$DUPES" ]; then + while IFS= read -r DUP; do + COUNT=$(echo "$ALL_REFS" | grep -cx "$DUP") + echo "::warning file=${MANIFEST}::Duplicate reference: \`${DUP}\` appears ${COUNT} times (may be valid if in different sections)" + echo "- **Duplicate:** \`${DUP}\` (${COUNT}x) — check if cross-section" >> $GITHUB_STEP_SUMMARY + WARNINGS=$((WARNINGS + 1)) + done <<< "$DUPES" + else + TOTAL=$(echo "$ALL_REFS" | wc -l) + echo "All ${TOTAL} file/folder references are unique." >> $GITHUB_STEP_SUMMARY + fi + fi + fi + + echo "" >> $GITHUB_STEP_SUMMARY + if [ "$WARNINGS" -gt 0 ]; then + echo "**${WARNINGS} duplicate reference(s) found.** Review for cross-section validity." >> $GITHUB_STEP_SUMMARY + else + echo "**Duplicate file references check passed.**" >> $GITHUB_STEP_SUMMARY + fi + + - name: Empty language keys check + continue-on-error: true + run: | + echo "### Empty Language Keys" >> $GITHUB_STEP_SUMMARY + WARNINGS=0 + + LANG_FILES=$(find . -name "*.ini" -not -path "./.git/*" -not -path "./vendor/*" 2>/dev/null) + if [ -z "$LANG_FILES" ]; then + echo "No .ini language files found — skipping." >> $GITHUB_STEP_SUMMARY + else + TOTAL_FILES=0 + for FILE in $LANG_FILES; do + TOTAL_FILES=$((TOTAL_FILES + 1)) + # Find lines with KEY= but no value (empty or whitespace-only after =) + EMPTY_KEYS=$(grep -nP '^[A-Z_]+=\s*$' "$FILE" 2>/dev/null || true) + if [ -n "$EMPTY_KEYS" ]; then + COUNT=$(echo "$EMPTY_KEYS" | wc -l) + echo "::warning file=${FILE}::${COUNT} empty language key(s)" + echo "- \`${FILE}\`: ${COUNT} empty key(s)" >> $GITHUB_STEP_SUMMARY + while IFS= read -r LINE; do + LINE_NUM=$(echo "$LINE" | cut -d: -f1) + KEY=$(echo "$LINE" | cut -d: -f2 | cut -d= -f1) + echo " - Line ${LINE_NUM}: \`${KEY}\`" >> $GITHUB_STEP_SUMMARY + done <<< "$EMPTY_KEYS" + WARNINGS=$((WARNINGS + COUNT)) + fi + done + + if [ "$WARNINGS" -eq 0 ]; then + echo "All ${TOTAL_FILES} language file(s) have populated keys." >> $GITHUB_STEP_SUMMARY + fi + fi + + echo "" >> $GITHUB_STEP_SUMMARY + if [ "$WARNINGS" -gt 0 ]; then + echo "**${WARNINGS} empty language key(s) across ${TOTAL_FILES} file(s).**" >> $GITHUB_STEP_SUMMARY + else + echo "**Empty language keys check passed.**" >> $GITHUB_STEP_SUMMARY + fi + release-readiness: name: Release Readiness Check runs-on: ubuntu-latest diff --git a/.mokogitea/workflows/composer-publish.yml b/.mokogitea/workflows/composer-publish.yml deleted file mode 100644 index 03735c9..0000000 --- a/.mokogitea/workflows/composer-publish.yml +++ /dev/null @@ -1,76 +0,0 @@ -# Copyright (C) 2026 Moko Consulting -# SPDX-License-Identifier: GPL-3.0-or-later - -name: "Publish to Composer" - -on: - push: - tags: - - 'v*' - - '[0-9]*.[0-9]*.[0-9]*' - release: - types: [published] - workflow_dispatch: - -env: - GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} - -jobs: - publish: - name: Publish Package - runs-on: ubuntu-latest - if: >- - !contains(github.event.head_commit.message, '[skip ci]') && - !contains(github.event.head_commit.message, '[skip publish]') - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup PHP - 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 composer >/dev/null 2>&1 - fi - - - name: Install dependencies - run: composer install --no-dev --no-interaction --prefer-dist --quiet - - - name: Determine version - id: version - run: | - VERSION=$(php -r "echo json_decode(file_get_contents('composer.json'))->version;") - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "Package version: ${VERSION}" - - # Gitea Composer Registry — auto-publishes from tags - # The tag push itself registers the package at: - # https://git.mokoconsulting.tech/api/packages/MokoConsulting/composer - - name: Verify Gitea registry - run: | - echo "Gitea Composer registry auto-publishes from tags." - echo "Package available at: ${GITEA_URL}/api/packages/MokoConsulting/composer" - echo "Install: composer require mokoconsulting/mokocli" - - # Packagist — notify of new version - - name: Notify Packagist - if: secrets.PACKAGIST_TOKEN != '' - run: | - VERSION="${{ steps.version.outputs.version }}" - echo "Notifying Packagist of version ${VERSION}..." - curl -sf -X POST \ - -H "Content-Type: application/json" \ - -d '{"repository":{"url":"https://git.mokoconsulting.tech/MokoConsulting/mokocli"}}' \ - "https://packagist.org/api/update-package?username=mokoconsulting&apiToken=${{ secrets.PACKAGIST_TOKEN }}" \ - && echo "Packagist notified" \ - || echo "::warning::Packagist notification failed (package may not be registered yet)" - - - name: Summary - run: | - VERSION="${{ steps.version.outputs.version }}" - echo "## Composer Package Published" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Registry | Status |" >> $GITHUB_STEP_SUMMARY - echo "|----------|--------|" >> $GITHUB_STEP_SUMMARY - echo "| Gitea | \`composer require mokoconsulting/mokocli:${VERSION}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Packagist | \`composer require mokoconsulting/mokocli\` |" >> $GITHUB_STEP_SUMMARY diff --git a/.mokogitea/workflows/deploy-manual.yml b/.mokogitea/workflows/deploy-manual.yml deleted file mode 100644 index bb133ed..0000000 --- a/.mokogitea/workflows/deploy-manual.yml +++ /dev/null @@ -1,126 +0,0 @@ -# Copyright (C) 2026 Moko Consulting -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards.Deploy -# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API -# PATH: /templates/workflows/joomla/deploy-manual.yml.template -# VERSION: 04.07.00 -# BRIEF: Manual SFTP deploy to dev server for Joomla repos - -name: "Universal: Deploy to Dev (Manual)" - -on: - workflow_dispatch: - inputs: - clear_remote: - description: 'Delete all remote files before uploading' - required: false - default: 'false' - type: boolean - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -permissions: - contents: read - -jobs: - deploy: - name: SFTP Deploy to Dev - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Setup PHP - run: | - php -v && composer --version - - - name: Setup MokoStandards tools - env: - GA_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }} - MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }} - MOKO_CLONE_HOST: ${{ secrets.GA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }} - COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}' - run: | - 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 - fi - - - name: Check FTP configuration - id: check - env: - HOST: ${{ vars.DEV_FTP_HOST }} - PATH_VAR: ${{ vars.DEV_FTP_PATH }} - PORT: ${{ vars.DEV_FTP_PORT }} - run: | - if [ -z "$HOST" ] || [ -z "$PATH_VAR" ]; then - echo "DEV_FTP_HOST or DEV_FTP_PATH not configured -- cannot deploy" - echo "skip=true" >> "$GITHUB_OUTPUT" - exit 0 - fi - echo "skip=false" >> "$GITHUB_OUTPUT" - echo "host=$HOST" >> "$GITHUB_OUTPUT" - - REMOTE="${PATH_VAR%/}" - echo "remote=$REMOTE" >> "$GITHUB_OUTPUT" - - [ -z "$PORT" ] && PORT="22" - echo "port=$PORT" >> "$GITHUB_OUTPUT" - - - name: Deploy via SFTP - if: steps.check.outputs.skip != 'true' - env: - SFTP_KEY: ${{ secrets.DEV_FTP_KEY }} - SFTP_PASS: ${{ secrets.DEV_FTP_PASSWORD }} - SFTP_USER: ${{ vars.DEV_FTP_USERNAME }} - run: | - SOURCE_DIR="src" - [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" - [ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ -- nothing to deploy"; exit 0; } - - printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \ - "${{ steps.check.outputs.host }}" "${{ steps.check.outputs.port }}" "$SFTP_USER" "${{ steps.check.outputs.remote }}" \ - > /tmp/sftp-config.json - - if [ -n "$SFTP_KEY" ]; then - echo "$SFTP_KEY" > /tmp/deploy_key - chmod 600 /tmp/deploy_key - printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json - else - printf ',"password":"%s"}' "$SFTP_PASS" >> /tmp/sftp-config.json - fi - - DEPLOY_ARGS=(--path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json) - [ "${{ inputs.clear_remote }}" = "true" ] && DEPLOY_ARGS+=(--clear-remote) - - 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 "${DEPLOY_ARGS[@]}" - else - php /tmp/mokostandards-api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}" - fi - - rm -f /tmp/deploy_key /tmp/sftp-config.json - - - name: Summary - if: always() - run: | - if [ "${{ steps.check.outputs.skip }}" = "true" ]; then - echo "### Deploy Skipped -- FTP not configured" >> $GITHUB_STEP_SUMMARY - else - echo "### Manual Dev Deploy Complete" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY - echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY - echo "| Host | \`${{ steps.check.outputs.host }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Remote | \`${{ steps.check.outputs.remote }}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Clear | ${{ inputs.clear_remote }} |" >> $GITHUB_STEP_SUMMARY - fi diff --git a/.mokogitea/workflows/pre-release.yml b/.mokogitea/workflows/pre-release.yml index 4fd80eb..b34a311 100644 --- a/.mokogitea/workflows/pre-release.yml +++ b/.mokogitea/workflows/pre-release.yml @@ -88,20 +88,8 @@ jobs: php ${MOKO_CLI}/platform_detect.php --path . --github-output 2>/dev/null || true php ${MOKO_CLI}/manifest_read.php --path . --github-output - - name: Check platform eligibility (Joomla only) - id: eligibility - run: | - PLATFORM="${{ steps.platform.outputs.platform }}" - if [[ "$PLATFORM" == joomla* ]] || [[ "$PLATFORM" == "joomla" ]]; then - echo "proceed=true" >> "$GITHUB_OUTPUT" - else - echo "proceed=false" >> "$GITHUB_OUTPUT" - echo "::notice::Platform '$PLATFORM' — non-Joomla, skipping pre-release auto-bump" - fi - - name: Resolve metadata and bump version id: meta - if: steps.eligibility.outputs.proceed == 'true' run: | # Auto-detect stability from branch name on push, or use input on dispatch if [ "${{ github.event_name }}" = "push" ]; then @@ -178,7 +166,6 @@ jobs: - name: Create release id: release - if: steps.eligibility.outputs.proceed == 'true' run: | TAG="${{ steps.meta.outputs.tag }}" VERSION="${{ steps.meta.outputs.version }}" @@ -189,7 +176,6 @@ jobs: --repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease - name: Update release notes from CHANGELOG.md - if: steps.eligibility.outputs.proceed == 'true' run: | TAG="${{ steps.meta.outputs.tag }}" VERSION="${{ steps.meta.outputs.version }}" @@ -226,7 +212,6 @@ jobs: - name: Build package and upload id: package - if: steps.eligibility.outputs.proceed == 'true' run: | VERSION="${{ steps.meta.outputs.version }}" TAG="${{ steps.meta.outputs.tag }}" @@ -240,7 +225,6 @@ jobs: # No need to build, commit, or sync updates.xml from workflows - name: "Delete lesser pre-release channels (cascade)" - if: steps.eligibility.outputs.proceed == 'true' continue-on-error: true run: | API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" diff --git a/.mokogitea/workflows/update-server.yml b/.mokogitea/workflows/update-server.yml deleted file mode 100644 index 339d3f5..0000000 --- a/.mokogitea/workflows/update-server.yml +++ /dev/null @@ -1,312 +0,0 @@ -# Copyright (C) 2026 Moko Consulting -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow -# INGROUP: moko-platform.Universal -# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform -# PATH: /templates/workflows/update-server.yml -# VERSION: 05.00.00 -# BRIEF: Pre-release build + update server XML for dev/alpha/beta/rc branches -# -# Thin wrapper around moko-platform CLI tools. -# Builds packages, updates updates.xml, and optionally deploys via SFTP. -# -# Joomla filters update entries by the user's "Minimum Stability" setting. - -name: "Update Server" - -on: - push: - branches: - - 'dev' - - 'dev/**' - - 'alpha/**' - - 'beta/**' - - 'rc/**' - paths: - - 'src/**' - - 'htdocs/**' - pull_request: - types: [closed] - branches: - - 'dev' - - 'dev/**' - - 'alpha/**' - - 'beta/**' - - 'rc/**' - paths: - - 'src/**' - - 'htdocs/**' - workflow_dispatch: - inputs: - stability: - description: 'Stability tag' - required: true - default: 'development' - type: choice - options: - - development - - alpha - - beta - - rc - - stable - -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: - update-xml: - name: Update Server - runs-on: release - if: >- - github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' || github.event_name == 'push' - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - token: ${{ secrets.MOKOGITEA_TOKEN }} - fetch-depth: 0 - - - name: Setup moko-platform tools - env: - MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} - MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting - COMPOSER_AUTH: '{"http-basic":{"git.mokoconsulting.tech":{"username":"token","password":"${{ secrets.MOKOGITEA_TOKEN }}"}}}' - run: | - 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 - # Always fetch latest CLI tools — never use stale cache from previous runs - rm -rf /tmp/moko-platform - 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 - 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 - echo "MOKO_CLI=/tmp/moko-platform/cli" >> "$GITHUB_ENV" - - - name: Detect platform - id: platform - run: php ${MOKO_CLI}/manifest_read.php --path . --github-output - - - name: Resolve stability and bump version - id: meta - run: | - BRANCH="${{ github.ref_name }}" - - # Configure git for bot pushes - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" - git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" - - # Auto-bump patch version - php ${MOKO_CLI}/version_bump.php --path . 2>/dev/null || true - - VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "0.0.0") - - # Strip any existing suffix before applying stability - VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//') - - # Determine stability from branch or manual 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" - else - STABILITY="development" - fi - - # Version suffix per stability stream - case "$STABILITY" in - development) SUFFIX="-dev"; TAG="development" ;; - alpha) SUFFIX="-alpha"; TAG="alpha" ;; - beta) SUFFIX="-beta"; TAG="beta" ;; - rc) SUFFIX="-rc"; TAG="release-candidate" ;; - *) SUFFIX=""; TAG="stable" ;; - esac - - # Propagate version with stability suffix to all manifest files - php ${MOKO_CLI}/version_set_platform.php \ - --path . --version "$VERSION" --branch "$BRANCH" --stability "$STABILITY" 2>/dev/null || true - php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true - - # Re-read version (now includes suffix from version_set_platform) - if [ -n "$SUFFIX" ]; then - VERSION="${VERSION}${SUFFIX}" - fi - - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" - echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT" - echo "tag=${TAG}" >> "$GITHUB_OUTPUT" - echo "display_version=${VERSION}" >> "$GITHUB_OUTPUT" - - # Commit version bump if changed - git add -A - git diff --cached --quiet || { - git commit -m "chore(version): auto-bump ${VERSION} [skip ci]" \ - --author="gitea-actions[bot] " - git push - } - - - name: Create release and upload package - id: package - run: | - VERSION="${{ steps.meta.outputs.version }}" - TAG="${{ steps.meta.outputs.tag }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - - # Create or update Gitea release - php ${MOKO_CLI}/release_create.php \ - --path . --version "$VERSION" --tag "$TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease - - # Build package and upload - php ${MOKO_CLI}/release_package.php \ - --path . --version "$VERSION" --tag "$TAG" \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ - --repo "${GITEA_REPO}" --output /tmp || true - - - name: Update updates.xml - if: steps.platform.outputs.platform == 'joomla' - run: | - VERSION="${{ steps.meta.outputs.version }}" - STABILITY="${{ steps.meta.outputs.stability }}" - SHA256="${{ steps.package.outputs.sha256_zip }}" - - if [ ! -f "updates.xml" ]; then - echo "No updates.xml — skipping" - exit 0 - fi - - SHA_FLAG="" - [ -n "$SHA256" ] && SHA_FLAG="--sha ${SHA256}" - - php ${MOKO_CLI}/updates_xml_build.php \ - --path . --version "${VERSION}" --stability "${STABILITY}" \ - --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \ - ${SHA_FLAG} - - # Commit and push updates.xml - git add updates.xml - git diff --cached --quiet || { - git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]" - git push - } - - - name: Sync updates.xml to main - if: github.ref_name != 'main' && steps.platform.outputs.platform == 'joomla' - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - GITEA_TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" - - FILE_SHA=$(curl -sf -H "Authorization: token ${GITEA_TOKEN}" \ - "${API_BASE}/contents/updates.xml?ref=main" | python3 -c "import sys,json; print(json.load(sys.stdin).get('sha',''))" 2>/dev/null || true) - - if [ -n "$FILE_SHA" ] && [ -f "updates.xml" ]; then - python3 -c " - import base64, json, urllib.request, sys - with open('updates.xml', 'rb') as f: - content = base64.b64encode(f.read()).decode() - payload = json.dumps({ - 'content': content, - 'sha': '${FILE_SHA}', - 'message': 'chore: sync updates.xml from ${{ steps.meta.outputs.stability }} [skip ci]', - 'branch': 'main' - }).encode() - req = urllib.request.Request( - '${API_BASE}/contents/updates.xml', - data=payload, method='PUT', - headers={ - 'Authorization': 'token ${GITEA_TOKEN}', - 'Content-Type': 'application/json' - }) - try: - urllib.request.urlopen(req) - print('updates.xml synced to main') - except Exception as e: - print(f'WARNING: sync to main failed: {e}', file=sys.stderr) - " - fi - - - name: SFTP deploy to dev server - if: contains(github.ref, 'dev/') || github.ref == 'refs/heads/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 }} - run: | - # Permission check: admin or maintain role required - ACTOR="${{ github.actor }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - - PERMISSION=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ - "${API_BASE}/collaborators/${ACTOR}/permission" 2>/dev/null | \ - python3 -c "import sys,json; print(json.load(sys.stdin).get('permission','read'))" 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 ${MOKO_CLI}/platform_detect.php --path . 2>/dev/null || true) - if [ "$PLATFORM" = "waas-component" ] && [ -f "${MOKO_CLI}/../deploy/deploy-joomla.php" ]; then - php ${MOKO_CLI}/../deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json - elif [ -f "${MOKO_CLI}/../deploy/deploy-sftp.php" ]; then - php ${MOKO_CLI}/../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: | - VERSION="${{ steps.meta.outputs.version }}" - STABILITY="${{ steps.meta.outputs.stability }}" - DISPLAY="${{ steps.meta.outputs.display_version }}" - echo "## Update Server" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY - echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY - echo "| Stability | \`${STABILITY}\` |" >> $GITHUB_STEP_SUMMARY - echo "| Version | \`${DISPLAY}\` |" >> $GITHUB_STEP_SUMMARY diff --git a/.mokogitea/workflows/version-set.yml b/.mokogitea/workflows/version-set.yml new file mode 100644 index 0000000..de6c5f4 --- /dev/null +++ b/.mokogitea/workflows/version-set.yml @@ -0,0 +1,130 @@ +# Copyright (C) 2026 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Gitea.Workflow.Template +# INGROUP: MokoStandards.CI +# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla +# PATH: /.mokogitea/workflows/version-set.yml +# VERSION: 01.00.00 +# BRIEF: Set or reset the extension version across all version-bearing files + +name: "Joomla: Set Version" + +on: + workflow_dispatch: + inputs: + version: + description: "Version number (e.g. 01.00.00)" + required: true + type: string + branch: + description: "Branch to update (default: current)" + required: false + type: string + +permissions: + contents: write + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + set-version: + name: Set Version to ${{ inputs.version }} + runs-on: ubuntu-latest + + steps: + - name: Validate version format + run: | + VERSION="${{ inputs.version }}" + if ! echo "$VERSION" | grep -qP '^\d{2}\.\d{2}\.\d{2}$'; then + echo "::error::Invalid version format '${VERSION}' — expected XX.YY.ZZ (e.g. 01.00.00)" + exit 1 + fi + echo "VERSION=${VERSION}" >> "$GITHUB_ENV" + + - name: Checkout + uses: actions/checkout@v4 + with: + token: ${{ secrets.MOKOGITEA_TOKEN || secrets.GA_TOKEN || github.token }} + ref: ${{ inputs.branch || github.ref }} + fetch-depth: 1 + + - name: Update manifest version + run: | + MANIFEST="" + for XML_FILE in $(find . -maxdepth 3 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do + if grep -q "/dev/null; then + MANIFEST="$XML_FILE" + break + fi + done + + if [ -z "$MANIFEST" ]; then + echo "::warning::No Joomla extension manifest found — skipping manifest update" + else + OLD_VER=$(grep -oP '\K[^<]+' "$MANIFEST" | head -1) + sed -i "s|${OLD_VER}|${VERSION}|" "$MANIFEST" + echo "Manifest: ${OLD_VER} → ${VERSION} (${MANIFEST})" + fi + + - name: Update README.md version + run: | + if [ -f "README.md" ]; then + if grep -qP '^\s*VERSION:\s*\d' README.md; then + sed -i -E "s/(VERSION:\s*)[0-9]{2}\.[0-9]{2}\.[0-9]{2}/\1${VERSION}/" README.md + echo "README.md version updated to ${VERSION}" + else + echo "::warning::No VERSION line found in README.md — skipping" + fi + fi + + - name: Update CHANGELOG.md + run: | + if [ -f "CHANGELOG.md" ]; then + DATE=$(date +%Y-%m-%d) + # Check if this version already has an entry + if grep -q "^\#\# \[${VERSION}\]" CHANGELOG.md; then + echo "CHANGELOG.md already has entry for ${VERSION} — skipping" + else + # Insert new version entry after [Unreleased] or at the top after header + if grep -q '^\#\# \[Unreleased\]' CHANGELOG.md; then + sed -i "/^\#\# \[Unreleased\]/a\\\\n## [${VERSION}] --- ${DATE}" CHANGELOG.md + else + sed -i "/^\# Changelog/a\\\\n## [Unreleased]\n\n## [${VERSION}] --- ${DATE}" CHANGELOG.md + fi + echo "CHANGELOG.md: added entry for ${VERSION}" + fi + else + echo "::warning::No CHANGELOG.md found — skipping" + fi + + - name: Update FILE INFORMATION blocks + run: | + # Update VERSION in file header blocks (# VERSION: XX.YY.ZZ) + find . -maxdepth 1 -type f \( -name "*.yml" -o -name "*.yaml" -o -name "*.php" -o -name "*.md" \) \ + -not -path "./.git/*" -not -path "./vendor/*" -print0 2>/dev/null | \ + while IFS= read -r -d '' FILE; do + if head -20 "$FILE" | grep -qP '^\s*#?\s*VERSION:\s*\d{2}\.\d{2}\.\d{2}'; then + sed -i -E "s/(#?\s*VERSION:\s*)[0-9]{2}\.[0-9]{2}\.[0-9]{2}/\1${VERSION}/" "$FILE" + echo "Updated FILE INFORMATION VERSION in ${FILE}" + fi + done + + - name: Commit and push + run: | + git config user.name "Moko Consulting [bot]" + git config user.email "hello@mokoconsulting.tech" + git add -A + if git diff --cached --quiet; then + echo "No version changes detected — nothing to commit" + else + git commit -m "chore: set version to ${VERSION} [skip bump] + +Authored-by: Moko Consulting" + git push + echo "### Version Set" >> $GITHUB_STEP_SUMMARY + echo "Version updated to \`${VERSION}\` on branch \`${GITHUB_REF_NAME}\`" >> $GITHUB_STEP_SUMMARY + fi diff --git a/.mokogitea/workflows/workflow-sync-trigger.yml b/.mokogitea/workflows/workflow-sync-trigger.yml index 371910c..0e64b4b 100644 --- a/.mokogitea/workflows/workflow-sync-trigger.yml +++ b/.mokogitea/workflows/workflow-sync-trigger.yml @@ -45,6 +45,13 @@ jobs: echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT" echo "Platform: ${PLATFORM:-all}" + - name: Setup PHP + 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 composer >/dev/null 2>&1 + fi + - name: Clone mokocli env: MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}