feat: centralized auto-bump workflow for all version bumps
Generic: Repo Health / Site Health (push) Has been cancelled
Generic: Repo Health / Access control (push) Has been cancelled
Universal: Auto Version Bump / Patch Bump (push) Has been cancelled
Universal: Cascade Main → Dev / Cascade main → branches (push) Has been cancelled
Platform: moko-platform CI / Gate 1: Code Quality (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled

This commit is contained in:
Jonathan Miller
2026-05-26 17:22:42 -05:00
8 changed files with 611 additions and 771 deletions
+94
View File
@@ -0,0 +1,94 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: moko-platform.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /.mokogitea/workflows/auto-bump.yml
# VERSION: 09.02.00
# BRIEF: Auto patch-bump version on every push to dev
name: "Universal: Auto Version Bump"
on:
push:
branches:
- dev
- main
env:
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
permissions:
contents: write
jobs:
bump:
name: Patch Bump
runs-on: release
if: >-
!contains(github.event.head_commit.message, '[skip ci]') &&
!contains(github.event.head_commit.message, '[skip bump]')
steps:
- name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
token: ${{ secrets.GA_TOKEN }}
fetch-depth: 1
- name: Setup moko-platform tools
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
if [ -d "/opt/moko-platform/cli" ]; then
echo "MOKO_CLI=/opt/moko-platform/cli" >> "$GITHUB_ENV"
else
git clone --depth 1 --branch main --quiet \
"https://x-access-token:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/moko-platform.git" \
/tmp/moko-platform-api
cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
fi
- name: Bump version
run: |
BRANCH="${{ github.ref_name }}"
# main = minor bump, dev = patch bump
if [ "$BRANCH" = "main" ]; then
BUMP_TYPE="--minor"
BUMP_LABEL="minor"
else
BUMP_TYPE=""
BUMP_LABEL="patch"
fi
BUMP=$(php ${MOKO_CLI}/version_bump.php --path . $BUMP_TYPE 2>&1) || true
echo "$BUMP"
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null) || true
[ -z "$VERSION" ] && { echo "No version found — skipping"; exit 0; }
# Propagate to platform manifests
php ${MOKO_CLI}/version_set_platform.php \
--path . --version "$VERSION" --branch "$BRANCH" 2>/dev/null || true
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
# Commit if anything changed
if git diff --quiet && git diff --cached --quiet; then
echo "No version changes to commit"
exit 0
fi
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]"
git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
git add -A
git commit -m "chore(version): ${BUMP_LABEL} bump to ${VERSION} [skip ci]" \
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>"
git push origin "$BRANCH"
echo "Bumped to ${VERSION} (${BUMP_LABEL})" >> $GITHUB_STEP_SUMMARY
+51 -329
View File
@@ -31,6 +31,15 @@ on:
branches: branches:
- main - main
workflow_dispatch: workflow_dispatch:
inputs:
action:
description: 'Action to perform'
required: false
type: choice
default: release
options:
- release
- promote-rc
env: env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
@@ -47,8 +56,8 @@ jobs:
name: Promote Pre-Release to RC name: Promote Pre-Release to RC
runs-on: release runs-on: release
if: >- if: >-
github.event.action == 'opened' && (github.event.action == 'opened' && github.event.pull_request.draft == true) ||
github.event.pull_request.draft == true (github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc')
steps: steps:
- name: Checkout repository - name: Checkout repository
@@ -100,7 +109,8 @@ jobs:
name: Build & Release Pipeline name: Build & Release Pipeline
runs-on: release runs-on: release
if: >- if: >-
github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch' github.event.pull_request.merged == true ||
(github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc')
steps: steps:
- name: Checkout repository - name: Checkout repository
@@ -170,18 +180,7 @@ jobs:
echo "::notice::No RC release — full build pipeline" echo "::notice::No RC release — full build pipeline"
fi fi
- name: "Step 1b: Bump version" # Version bump handled by auto-bump.yml (minor on main, patch on dev)
id: bump
if: >-
steps.version.outputs.skip != 'true' &&
steps.rc.outputs.promote != 'true'
run: |
MOKO_API="/tmp/moko-platform-api/cli"
BUMP=$(php ${MOKO_API}/version_bump.php --path . --minor)
VERSION=$(echo "$BUMP" | grep -oP '\d{2}\.\d{2}\.\d{2}$' || true)
[ -z "$VERSION" ] && VERSION=$(php ${MOKO_API}/version_read.php --path .)
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "Bumped to: ${VERSION}"
- name: Check if already released - name: Check if already released
if: steps.version.outputs.skip != 'true' if: steps.version.outputs.skip != 'true'
@@ -208,96 +207,9 @@ jobs:
steps.version.outputs.skip != 'true' && steps.version.outputs.skip != 'true' &&
steps.check.outputs.already_released != 'true' steps.check.outputs.already_released != 'true'
run: | run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
ERRORS=0 php /tmp/moko-platform-api/cli/release_validate.php \
--path . --version "$VERSION" --output-summary --github-output || true
PLATFORM="${{ steps.platform.outputs.platform }}"
MANIFEST="${{ steps.platform.outputs.manifest }}"
MOD_FILE="${{ steps.platform.outputs.mod_file }}"
echo "## Pre-Release Sanity Checks (${PLATFORM})" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# -- Version drift check (must pass before release) --------
README_VER=$(sed -n 's/.*VERSION:[[:space:]]*\([0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\).*/\1/p' README.md 2>/dev/null | head -1)
if [ "$README_VER" != "$VERSION" ]; then
echo "- Version drift: README says \`${README_VER}\` but releasing \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
ERRORS=$((ERRORS+1))
else
echo "- Version consistent: \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
fi
# Check CHANGELOG version matches
CL_VER=$(sed -n 's/.*VERSION:[[:space:]]*\([0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\).*/\1/p' CHANGELOG.md 2>/dev/null | head -1)
if [ -n "$CL_VER" ] && [ "$CL_VER" != "$VERSION" ]; then
echo "- CHANGELOG drift: \`${CL_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
ERRORS=$((ERRORS+1))
fi
# Check composer.json version if present
if [ -f "composer.json" ]; then
COMP_VER=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' composer.json 2>/dev/null | head -1)
if [ -n "$COMP_VER" ] && [ "$COMP_VER" != "$VERSION" ]; then
echo "- composer.json drift: \`${COMP_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
ERRORS=$((ERRORS+1))
fi
fi
# Common checks
if [ ! -f "LICENSE" ]; then
echo "- Missing LICENSE file" >> $GITHUB_STEP_SUMMARY
ERRORS=$((ERRORS+1))
else
echo "- LICENSE present" >> $GITHUB_STEP_SUMMARY
fi
if [ ! -d "src" ] && [ ! -d "htdocs" ]; then
echo "- Warning: No src/ or htdocs/ directory" >> $GITHUB_STEP_SUMMARY
else
echo "- Source directory present" >> $GITHUB_STEP_SUMMARY
fi
# -- Platform-specific checks --------
case "$PLATFORM" in
joomla)
if [ -n "$MANIFEST" ]; then
XML_VER=$(sed -n 's/.*<version>\([^<]*\)<\/version>.*/\1/p' "$MANIFEST" 2>/dev/null | head -1)
if [ -n "$XML_VER" ] && [ "$XML_VER" != "$VERSION" ]; then
echo "- Manifest drift: \`${XML_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
ERRORS=$((ERRORS+1))
else
echo "- Manifest version: \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
fi
TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" 2>/dev/null)
echo "- Extension type: ${TYPE:-unknown}" >> $GITHUB_STEP_SUMMARY
else
echo "- No Joomla XML manifest (WaaS site)" >> $GITHUB_STEP_SUMMARY
fi ;;
dolibarr)
if [ -n "$MOD_FILE" ]; then
MOD_VER=$(sed -n "s/.*\\\$this->version = '\([^']*\)'.*/\1/p" "$MOD_FILE" 2>/dev/null | head -1)
if [ -n "$MOD_VER" ] && [ "$MOD_VER" != "$VERSION" ]; then
echo "- Module drift: \`${MOD_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
ERRORS=$((ERRORS+1))
else
echo "- Module version: \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
fi
else
echo "- No mod*.class.php found" >> $GITHUB_STEP_SUMMARY
ERRORS=$((ERRORS+1))
fi
if [ ! -f "update.txt" ]; then
echo "- Missing update.txt" >> $GITHUB_STEP_SUMMARY
ERRORS=$((ERRORS+1))
fi ;;
*) echo "- Generic platform no manifest checks" >> $GITHUB_STEP_SUMMARY ;;
esac
echo "" >> $GITHUB_STEP_SUMMARY
if [ "$ERRORS" -gt 0 ]; then
echo "**${ERRORS} error(s) — release may be incomplete**" >> $GITHUB_STEP_SUMMARY
else
echo "**All sanity checks passed**" >> $GITHUB_STEP_SUMMARY
fi
# -- STEP 2: Create or update version/XX.YY archive branch --------------- # -- STEP 2: Create or update version/XX.YY archive branch ---------------
# Always runs — every version change on main archives to version/XX.YY # Always runs — every version change on main archives to version/XX.YY
@@ -306,7 +218,7 @@ jobs:
run: | run: |
BRANCH="${{ steps.version.outputs.branch }}" BRANCH="${{ steps.version.outputs.branch }}"
IS_MINOR="${{ steps.version.outputs.is_minor }}" IS_MINOR="${{ steps.version.outputs.is_minor }}"
PATCH="${{ steps.bump.outputs.version || steps.version.outputs.version }}" PATCH="${{ steps.version.outputs.version }}"
PATCH_NUM=$(echo "$PATCH" | awk -F. '{print $3}') PATCH_NUM=$(echo "$PATCH" | awk -F. '{print $3}')
# Check if branch exists # Check if branch exists
@@ -325,7 +237,7 @@ jobs:
steps.version.outputs.skip != 'true' && steps.version.outputs.skip != 'true' &&
steps.check.outputs.already_released != 'true' steps.check.outputs.already_released != 'true'
run: | run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
php /tmp/moko-platform-api/cli/version_set_platform.php \ php /tmp/moko-platform-api/cli/version_set_platform.php \
--path . --version "$VERSION" --branch main --path . --version "$VERSION" --branch main
@@ -333,7 +245,7 @@ jobs:
- name: "Step 4: Update version badges" - name: "Step 4: Update version badges"
if: steps.version.outputs.skip != 'true' if: steps.version.outputs.skip != 'true'
run: | run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
php /tmp/moko-platform-api/cli/badge_update.php --path . --version "${VERSION}" 2>/dev/null || true php /tmp/moko-platform-api/cli/badge_update.php --path . --version "${VERSION}" 2>/dev/null || true
php /tmp/moko-platform-api/cli/version_check.php --path . --fix 2>/dev/null || true php /tmp/moko-platform-api/cli/version_check.php --path . --fix 2>/dev/null || true
@@ -342,7 +254,7 @@ jobs:
steps.version.outputs.skip != 'true' && steps.version.outputs.skip != 'true' &&
steps.platform.outputs.platform == 'joomla' steps.platform.outputs.platform == 'joomla'
run: | run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
# Fetch latest updates.xml from main so preserve logic has all channels # Fetch latest updates.xml from main so preserve logic has all channels
GA_TOKEN="${{ secrets.GA_TOKEN }}" GA_TOKEN="${{ secrets.GA_TOKEN }}"
@@ -366,7 +278,7 @@ jobs:
echo "No changes to commit" echo "No changes to commit"
exit 0 exit 0
fi fi
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]" git config --local user.name "gitea-actions[bot]"
# Set push URL with token for branch-protected repos # Set push URL with token for branch-protected repos
@@ -413,187 +325,34 @@ jobs:
steps.version.outputs.skip != 'true' && steps.version.outputs.skip != 'true' &&
steps.rc.outputs.promote != 'true' steps.rc.outputs.promote != 'true'
run: | run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
RELEASE_TAG="${{ steps.version.outputs.release_tag }}" RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
BRANCH="${{ steps.version.outputs.branch }}"
MAJOR="${{ steps.version.outputs.major }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
php /tmp/moko-platform-api/cli/release_create.php \
--path . --version "$VERSION" --tag "$RELEASE_TAG" \
--token "${{ secrets.GA_TOKEN }}" --api-base "$API_BASE" \
--repo "${GITEA_REPO}" --branch main
echo "Release created: ${VERSION}" >> $GITHUB_STEP_SUMMARY
# Reuse metadata from Step 5 (single source of truth) # -- STEP 8: Build packages and upload to release ----------------------------
EXT_ELEMENT="${{ steps.updates.outputs.ext_element }}" - name: "Step 8: Build package and upload"
EXT_NAME="${{ steps.updates.outputs.ext_name }}"
EXT_TYPE="${{ steps.updates.outputs.ext_type }}"
EXT_FOLDER="${{ steps.updates.outputs.ext_folder }}"
# Fallbacks if Step 5 was skipped
if [ -z "$EXT_ELEMENT" ]; then
EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
fi
[ -z "$EXT_NAME" ] && EXT_NAME="${GITEA_REPO}"
NOTES=$(php /tmp/moko-platform-api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null)
[ -z "$NOTES" ] && NOTES="Release ${VERSION}"
# Build release name: "Pretty Name VERSION (type_element-VERSION)"
# Strip existing type prefix to prevent duplication
EXT_ELEMENT=$(echo "$EXT_ELEMENT" | sed -E 's/^(pkg_|com_|mod_|plg_[a-z]+_|tpl_|lib_)//')
TYPE_PREFIX=""
case "${EXT_TYPE}" in
plugin) TYPE_PREFIX="plg_${EXT_FOLDER}_" ;;
module) TYPE_PREFIX="mod_" ;;
component) TYPE_PREFIX="com_" ;;
template) TYPE_PREFIX="tpl_" ;;
library) TYPE_PREFIX="lib_" ;;
package) TYPE_PREFIX="pkg_" ;;
esac
RELEASE_NAME="${EXT_NAME} ${VERSION} (${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION})"
# Delete existing release if present (overwrite, not append)
EXISTING=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
"${API_BASE}/releases/tags/${RELEASE_TAG}" 2>/dev/null || true)
EXISTING_ID=$(echo "$EXISTING" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('id',''))" 2>/dev/null || true)
if [ -n "$EXISTING_ID" ]; then
curl -sS -X DELETE -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
"${API_BASE}/releases/${EXISTING_ID}" 2>/dev/null || true
curl -sS -X DELETE -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
"${API_BASE}/tags/${RELEASE_TAG}" 2>/dev/null || true
echo "Deleted previous stable release (id: ${EXISTING_ID})"
fi
# Create fresh release
curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
-H "Content-Type: application/json" \
"${API_BASE}/releases" \
-d "$(python3 -c "import json; print(json.dumps({
'tag_name': '${RELEASE_TAG}',
'name': '${RELEASE_NAME}',
'body': '''## ${VERSION} ($(date +%Y-%m-%d))\n${NOTES}''',
'target_commitish': '${BRANCH}'
}))")"
echo "Release created: ${RELEASE_NAME}" >> $GITHUB_STEP_SUMMARY
# -- STEP 8: Build Joomla install ZIP + SHA-256 checksum ------------------
- name: "Step 8: Build package and update checksum"
if: >- if: >-
steps.version.outputs.skip != 'true' && steps.version.outputs.skip != 'true' &&
steps.rc.outputs.promote != 'true' steps.rc.outputs.promote != 'true'
run: | run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
RELEASE_TAG="${{ steps.version.outputs.release_tag }}" RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
REPO="${{ github.repository }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
php /tmp/moko-platform-api/cli/release_package.php \
# All ZIPs upload to the major release tag (vXX) --path . --version "$VERSION" --tag "$RELEASE_TAG" \
RELEASE_JSON=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \ --token "${{ secrets.GA_TOKEN }}" --api-base "$API_BASE" \
"${API_BASE}/releases/tags/${RELEASE_TAG}" 2>/dev/null || true) --repo "${GITEA_REPO}" --output /tmp || true
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
echo "No release ${RELEASE_TAG} found — skipping ZIP upload"
exit 0
fi
# Find extension element name from manifest
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1 || true)
[ -z "$MANIFEST" ] && exit 0
# Reuse element from Step 5, with same fallback chain
EXT_ELEMENT="${{ steps.updates.outputs.ext_element }}"
if [ -z "$EXT_ELEMENT" ]; then
EXT_ELEMENT=$(sed -n 's/.*<element>\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" 2>/dev/null | head -1)
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(sed -n 's/.*plugin="\([^"]*\)".*/\1/p' "$MANIFEST" 2>/dev/null | head -1)
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(basename "$MANIFEST" .xml | tr '[:upper:]' '[:lower:]')
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
fi
# ZIP name: type_folder_element-VERSION (e.g. plg_system_mokojgdpc-01.01.00.zip)
EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
EXT_FOLDER=$(sed -n 's/.*<extension[^>]*group="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
# For packages, prefer <packagename> over filename-derived element
if [ "$EXT_TYPE" = "package" ]; then
PKG_NAME=$(sed -n 's/.*<packagename>\([^<]*\)<\/packagename>.*/\1/p' "$MANIFEST" 2>/dev/null | head -1)
[ -n "$PKG_NAME" ] && EXT_ELEMENT="$PKG_NAME"
fi
# Strip existing type prefix to prevent duplication (e.g. pkg_mokowaas → mokowaas)
EXT_ELEMENT=$(echo "$EXT_ELEMENT" | sed -E 's/^(pkg_|com_|mod_|plg_[a-z]+_|tpl_|lib_)//')
TYPE_PREFIX=""
case "${EXT_TYPE}" in
plugin) TYPE_PREFIX="plg_${EXT_FOLDER}_" ;;
module) TYPE_PREFIX="mod_" ;;
component) TYPE_PREFIX="com_" ;;
template) TYPE_PREFIX="tpl_" ;;
library) TYPE_PREFIX="lib_" ;;
package) TYPE_PREFIX="pkg_" ;;
esac
ZIP_NAME="${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION}.zip"
TAR_NAME="${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION}.tar.gz"
# -- Build install packages from src/ ----------------------------
SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
[ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/"; exit 0; }
# ZIP package (type-aware via moko-platform PHP API)
php /tmp/moko-platform-api/cli/joomla_build.php --path . --version "${VERSION}" --output /tmp
# Match the expected ZIP_NAME for upload
BUILT_ZIP=$(ls /tmp/${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION}.zip 2>/dev/null | head -1 || true)
if [ -n "$BUILT_ZIP" ] && [ "$BUILT_ZIP" != "/tmp/${ZIP_NAME}" ]; then
mv "$BUILT_ZIP" "/tmp/${ZIP_NAME}"
fi
# tar.gz package (flat source archive)
tar -czf "/tmp/${TAR_NAME}" -C "$SOURCE_DIR" --exclude='.ftpignore' --exclude='sftp-config*' --exclude='*.ppk' --exclude='*.pem' --exclude='*.key' --exclude='.env*' .
ZIP_SIZE=$(stat -c%s "/tmp/${ZIP_NAME}" 2>/dev/null || stat -f%z "/tmp/${ZIP_NAME}" 2>/dev/null || echo "unknown")
TAR_SIZE=$(stat -c%s "/tmp/${TAR_NAME}" 2>/dev/null || stat -f%z "/tmp/${TAR_NAME}" 2>/dev/null || echo "unknown")
# -- Calculate SHA-256 for both ----------------------------------
SHA256_ZIP=$(sha256sum "/tmp/${ZIP_NAME}" | cut -d' ' -f1)
SHA256_TAR=$(sha256sum "/tmp/${TAR_NAME}" | cut -d' ' -f1)
# -- Get existing assets for cleanup --------------------------------
ASSETS=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
"${API_BASE}/releases/${RELEASE_ID}/assets" 2>/dev/null || echo "[]")
# -- Create per-file .sha256 checksum files -------------------------
echo "${SHA256_ZIP} ${ZIP_NAME}" > "/tmp/${ZIP_NAME}.sha256"
echo "${SHA256_TAR} ${TAR_NAME}" > "/tmp/${TAR_NAME}.sha256"
# -- Upload packages + checksums to release tag --------------------
for ASSET in "${ZIP_NAME}" "${TAR_NAME}" "${ZIP_NAME}.sha256" "${TAR_NAME}.sha256"; do
[ ! -f "/tmp/${ASSET}" ] && continue
# Delete existing asset with same name
ASSET_ID=$(echo "$ASSETS" | python3 -c "
import sys,json
assets = json.load(sys.stdin)
for a in assets:
if a['name'] == '${ASSET}':
print(a['id']); break
" 2>/dev/null || true)
[ -n "$ASSET_ID" ] && curl -sf -X DELETE -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
"${API_BASE}/releases/${RELEASE_ID}/assets/${ASSET_ID}" 2>/dev/null || true
# Upload
curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
-H "Content-Type: application/octet-stream" \
--data-binary @"/tmp/${ASSET}" \
"${API_BASE}/releases/${RELEASE_ID}/assets?name=${ASSET}" > /dev/null 2>&1 || true
done
# updates.xml already handled by Step 5 (updates_xml_build.php with preserve logic)
echo "### Packages" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Package | Size | SHA-256 |" >> $GITHUB_STEP_SUMMARY
echo "|---------|------|---------|" >> $GITHUB_STEP_SUMMARY
echo "| \`${ZIP_NAME}\` | ${ZIP_SIZE} | \`${SHA256_ZIP}\` |" >> $GITHUB_STEP_SUMMARY
echo "| \`${TAR_NAME}\` | ${TAR_SIZE} | \`${SHA256_TAR}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Release | \`${RELEASE_TAG}\` | |" >> $GITHUB_STEP_SUMMARY
echo "| Download | [${ZIP_NAME}](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${ZIP_NAME}) |" >> $GITHUB_STEP_SUMMARY
# -- STEP 8b: Update release description with changelog ---------------------- # -- STEP 8b: Update release description with changelog ----------------------
- name: "Step 8b: Update release body" - name: "Step 8b: Update release body"
if: steps.version.outputs.skip != 'true' if: steps.version.outputs.skip != 'true'
run: | run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
RELEASE_TAG="${{ steps.version.outputs.release_tag }}" RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
MOKO_CLI="/tmp/moko-platform-api/cli" MOKO_CLI="/tmp/moko-platform-api/cli"
@@ -621,44 +380,19 @@ jobs:
- name: "Step 9: Mirror release to GitHub" - name: "Step 9: Mirror release to GitHub"
if: >- if: >-
steps.version.outputs.skip != 'true' && steps.version.outputs.skip != 'true' &&
steps.version.outputs.stability == 'stable' &&
secrets.GH_TOKEN != '' secrets.GH_TOKEN != ''
continue-on-error: true continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GH_TOKEN }}
run: | run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
RELEASE_TAG="${{ steps.version.outputs.release_tag }}" RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
MAJOR="${{ steps.version.outputs.major }}"
BRANCH="${{ steps.version.outputs.branch }}"
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
NOTES=$(php /tmp/moko-platform-api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null || true) php /tmp/moko-platform-api/cli/release_mirror.php \
[ -z "$NOTES" ] && NOTES="Release ${VERSION}" --version "$VERSION" --tag "$RELEASE_TAG" \
echo "$NOTES" > /tmp/release_notes.md --token "${{ secrets.GA_TOKEN }}" --api-base "$API_BASE" \
--gh-token "${{ secrets.GH_TOKEN }}" --gh-repo "$GH_REPO" \
EXISTING=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" "${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}/releases/tags/$RELEASE_TAG" 2>/dev/null | jq -r ".tag_name // empty" || true) --branch main 2>&1 || true
echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY
if [ -z "$EXISTING" ]; then
gh release create "$RELEASE_TAG" \
--repo "$GH_REPO" \
--title "v${MAJOR} (latest: ${VERSION})" \
--notes-file /tmp/release_notes.md \
--target "$BRANCH" || true
else
gh release edit "$RELEASE_TAG" \
--repo "$GH_REPO" \
--title "v${MAJOR} (latest: ${VERSION})" || true
fi
# Upload assets to GitHub mirror
for PKG in /tmp/${EXT_ELEMENT:-pkg}-${VERSION}.*; do
if [ -f "$PKG" ]; then
_RELID=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" "${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}/releases/tags/$RELEASE_TAG" 2>/dev/null | jq -r ".id // empty")
[ -n "$_RELID" ] && curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" -H "Content-Type: application/octet-stream" "${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}/releases/${_RELID}/assets?name=$(basename $PKG)" --data-binary "@$PKG" > /dev/null 2>&1 || true
fi
done
echo "GitHub mirror updated: ${GH_REPO} ${RELEASE_TAG}" >> $GITHUB_STEP_SUMMARY
# -- STEP 10: Sync main branch to GitHub mirror ---------------------------- # -- STEP 10: Sync main branch to GitHub mirror ----------------------------
- name: "Step 10: Push main to GitHub mirror" - name: "Step 10: Push main to GitHub mirror"
@@ -682,7 +416,7 @@ jobs:
- name: "Delete lesser pre-release channels" - name: "Delete lesser pre-release channels"
continue-on-error: true continue-on-error: true
run: | run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
php /tmp/moko-platform-api/cli/release_cascade.php \ php /tmp/moko-platform-api/cli/release_cascade.php \
--stability stable \ --stability stable \
@@ -711,32 +445,20 @@ jobs:
# -- Dolibarr post-release: Reset dev version ----------------------------- # -- Dolibarr post-release: Reset dev version -----------------------------
- name: "Dolibarr: Reset dev version" - name: "Post-release: Reset dev version"
if: >- if: steps.version.outputs.skip != 'true'
steps.version.outputs.skip != 'true' &&
steps.platform.outputs.platform == 'dolibarr' &&
steps.platform.outputs.mod_file != ''
continue-on-error: true continue-on-error: true
run: | run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
TOKEN="${{ secrets.GA_TOKEN }}" php /tmp/moko-platform-api/cli/version_reset_dev.php \
MOD_FILE="${{ steps.platform.outputs.mod_file }}" --token "${{ secrets.GA_TOKEN }}" --api-base "${API_BASE}" \
ENCODED_PATH=$(echo "$MOD_FILE" | sed 's|^\./||' | python3 -c "import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read().strip()))") --branch dev --path . 2>&1 || true
FILE_RESP=$(curl -sf -H "Authorization: token ${TOKEN}" "${API_BASE}/contents/${ENCODED_PATH}?ref=dev" 2>/dev/null || true)
FILE_SHA=$(echo "$FILE_RESP" | python3 -c "import sys,json; print(json.load(sys.stdin).get('sha',''))" 2>/dev/null || true)
FILE_CONTENT=$(echo "$FILE_RESP" | python3 -c "import sys,json,base64; print(base64.b64decode(json.load(sys.stdin).get('content','')).decode())" 2>/dev/null || true)
if [ -n "$FILE_SHA" ] && [ -n "$FILE_CONTENT" ]; then
UPDATED=$(echo "$FILE_CONTENT" | sed "s/\$this->version = '[^']*'/\$this->version = 'development'/")
ENCODED=$(echo "$UPDATED" | base64 -w0)
curl -sf -X PUT -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/contents/${ENCODED_PATH}" \
-d "$(jq -n --arg content \"$ENCODED\" --arg sha \"$FILE_SHA\" --arg msg \"chore(version): reset dev version [skip ci]\" --arg branch \"dev\" '{content:$content,sha:$sha,message:$msg,branch:$branch}')" > /dev/null 2>&1 || true
fi
# -- Summary -------------------------------------------------------------- # -- Summary --------------------------------------------------------------
- name: Pipeline Summary - name: Pipeline Summary
if: always() if: always()
run: | run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
PLATFORM="${{ steps.platform.outputs.platform }}" PLATFORM="${{ steps.platform.outputs.platform }}"
if [ "${{ steps.version.outputs.skip }}" = "true" ]; then if [ "${{ steps.version.outputs.skip }}" = "true" ]; then
echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY
+1 -2
View File
@@ -92,8 +92,7 @@ jobs:
release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;; release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
esac esac
# Patch bump via CLI tool # Read current version (bump already handled by push workflow)
php ${MOKO_CLI}/version_bump.php --path .
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null) VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null)
[ -z "$VERSION" ] && VERSION="00.00.01" [ -z "$VERSION" ] && VERSION="00.00.01"
+4
View File
@@ -18,6 +18,10 @@ Version format: `XX.YY.ZZ` (zero-padded semver).
## [Unreleased] ## [Unreleased]
### Added
- `auto-bump.yml`: auto patch-bump version on every push to dev
- Removed redundant bump from pre-release.yml (handled by auto-bump)
## [09.02.00] - 2026-05-26 ## [09.02.00] - 2026-05-26
### Added ### Added
+1 -1
View File
@@ -6,7 +6,7 @@ DEFGROUP: MokoStandards.Root
INGROUP: MokoStandards INGROUP: MokoStandards
REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
PATH: /README.md PATH: /README.md
VERSION: 09.02.00 VERSION: 09.02.01
BRIEF: Project overview and documentation BRIEF: Project overview and documentation
--> -->
+9 -4
View File
@@ -413,10 +413,15 @@ if ($isJoomlaPackage) {
} }
} }
// Copy language directory (package-level language files referenced in manifest) // Include top-level directories (e.g. language/) that aren't packages/
if (is_dir("{$sourceDir}/language")) { $topLevelDirs = glob("{$sourceDir}/*", GLOB_ONLYDIR) ?: [];
addDirToZip($zip, "{$sourceDir}/language", 'language', $excludePatterns); foreach ($topLevelDirs as $tlDir) {
echo " Language files added from language/\n"; $dirName = basename($tlDir);
if ($dirName === 'packages') {
continue;
}
addDirToZip($zip, $tlDir, $dirName, $excludePatterns);
echo " Included dir: {$dirName}/\n";
} }
$zip->close(); $zip->close();
+12 -6
View File
@@ -30,7 +30,7 @@ $mokoManifest = "{$root}/.mokogitea/manifest.xml";
$mokoContent = ''; $mokoContent = '';
if (file_exists($mokoManifest)) { if (file_exists($mokoManifest)) {
$mokoContent = file_get_contents($mokoManifest); $mokoContent = file_get_contents($mokoManifest);
if (preg_match('|<version>(\d{2}\.\d{2}\.\d{2})</version>|', $mokoContent, $m)) { if (preg_match('|<version>(\d{2}\.\d{2}\.\d{2})(?:-([a-z]+))?</version>|', $mokoContent, $m)) {
$mokoVersion = $m[1]; $mokoVersion = $m[1];
} }
} }
@@ -56,15 +56,17 @@ $manifestFiles = array_merge(
glob("{$root}/*.xml") ?: [] glob("{$root}/*.xml") ?: []
); );
$manifestSuffix = '';
foreach ($manifestFiles as $xmlFile) { foreach ($manifestFiles as $xmlFile) {
$xmlContent = file_get_contents($xmlFile); $xmlContent = file_get_contents($xmlFile);
if (strpos($xmlContent, '<extension') === false && strpos($xmlContent, '<version>') === false) { if (strpos($xmlContent, '<extension') === false && strpos($xmlContent, '<version>') === false) {
continue; continue;
} }
if (preg_match('|<version>(\d{2}\.\d{2}\.\d{2})(?:-[a-z]+)?</version>|', $xmlContent, $xm)) { if (preg_match('|<version>(\d{2}\.\d{2}\.\d{2})(?:-([a-z]+))?</version>|', $xmlContent, $xm)) {
$candidate = $xm[1]; $candidate = $xm[1];
if ($manifestVersion === null || version_compare($candidate, $manifestVersion, '>')) { if ($manifestVersion === null || version_compare($candidate, $manifestVersion, '>')) {
$manifestVersion = $candidate; $manifestVersion = $candidate;
$manifestSuffix = isset($xm[2]) ? $xm[2] : '';
} }
} }
} }
@@ -106,10 +108,14 @@ switch ($type) {
$new = sprintf('%02d.%02d.%02d', $major, $minor, $patch); $new = sprintf('%02d.%02d.%02d', $major, $minor, $patch);
// -- Update .mokogitea/manifest.xml (canonical target) -- // -- Determine suffix to preserve (from whichever source had the version) --
$suffix = !empty($manifestSuffix) ? $manifestSuffix : '';
$newFull = $suffix !== '' ? "{$new}-{$suffix}" : $new;
// -- Update .mokogitea/manifest.xml (canonical target, no suffix) --
if (file_exists($mokoManifest) && !empty($mokoContent)) { if (file_exists($mokoManifest) && !empty($mokoContent)) {
$updated = preg_replace( $updated = preg_replace(
'|<version>\d{2}\.\d{2}\.\d{2}</version>|', '|<version>\d{2}\.\d{2}\.\d{2}(?:-[a-z]+)?</version>|',
"<version>{$new}</version>", "<version>{$new}</version>",
$mokoContent, $mokoContent,
1 1
@@ -146,7 +152,7 @@ foreach ($xmlPatterns as $pattern) {
} }
$newContent = preg_replace( $newContent = preg_replace(
'|<version>\d{2}\.\d{2}\.\d{2}(?:-[a-z]+)?</version>|', '|<version>\d{2}\.\d{2}\.\d{2}(?:-[a-z]+)?</version>|',
"<version>{$new}</version>", "<version>{$newFull}</version>",
$content $content
); );
if ($newContent !== $content) { if ($newContent !== $content) {
@@ -190,5 +196,5 @@ if (file_exists($pyprojectFile)) {
} }
} }
echo "{$old} -> {$new}\n"; echo "{$old} -> {$newFull}\n";
exit(0); exit(0);
+11 -1
View File
@@ -54,6 +54,7 @@ if (file_exists($readme)) {
// -- 3. Fallback: Joomla manifest XML -- // -- 3. Fallback: Joomla manifest XML --
$manifestVersion = null; $manifestVersion = null;
$manifestVersionSuffix = '';
$manifestFiles = array_merge( $manifestFiles = array_merge(
glob("{$root}/src/pkg_*.xml") ?: [], glob("{$root}/src/pkg_*.xml") ?: [],
glob("{$root}/src/*.xml") ?: [], glob("{$root}/src/*.xml") ?: [],
@@ -66,10 +67,12 @@ foreach ($manifestFiles as $xmlFile) {
if (strpos($xmlContent, '<extension') === false && strpos($xmlContent, '<version>') === false) { if (strpos($xmlContent, '<extension') === false && strpos($xmlContent, '<version>') === false) {
continue; continue;
} }
if (preg_match('|<version>(\d{2}\.\d{2}\.\d{2})(?:-[a-z]+)?</version>|', $xmlContent, $xm)) { if (preg_match('|<version>(\d{2}\.\d{2}\.\d{2})(-[a-z]+)?</version>|', $xmlContent, $xm)) {
$candidate = $xm[1]; $candidate = $xm[1];
$candidateSuffix = isset($xm[2]) ? $xm[2] : '';
if ($manifestVersion === null || version_compare($candidate, $manifestVersion, '>')) { if ($manifestVersion === null || version_compare($candidate, $manifestVersion, '>')) {
$manifestVersion = $candidate; $manifestVersion = $candidate;
$manifestVersionSuffix = $candidateSuffix;
} }
} }
} }
@@ -103,9 +106,11 @@ $candidates = array_filter([
]); ]);
$version = null; $version = null;
$versionSource = '';
foreach ($candidates as $candidate) { foreach ($candidates as $candidate) {
if ($version === null || version_compare($candidate, $version, '>')) { if ($version === null || version_compare($candidate, $version, '>')) {
$version = $candidate; $version = $candidate;
$versionSource = ($candidate === $manifestVersion) ? 'manifest' : 'other';
} }
} }
@@ -114,6 +119,11 @@ if ($version === null) {
exit(1); exit(1);
} }
// Append suffix if the version came from a Joomla manifest with a suffix
if ($versionSource === 'manifest' && !empty($manifestVersionSuffix)) {
$version .= $manifestVersionSuffix;
}
// -- Backfill: if manifest.xml exists but lacks <version>, insert it -- // -- Backfill: if manifest.xml exists but lacks <version>, insert it --
if (file_exists($mokoManifest)) { if (file_exists($mokoManifest)) {
$content = file_get_contents($mokoManifest); $content = file_get_contents($mokoManifest);