From 89b9f34dbf0c2df2eeea96ab2cf9db672d879eb7 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 21 May 2026 21:00:56 -0500 Subject: [PATCH 01/16] fix(ci): repair corrupted YAML in workflow files Python replacement introduced control char (0x01) instead of backreference (\1) in sed commands. Fixed both workflows. Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .mokogitea/workflows/auto-release.yml | 2 +- .mokogitea/workflows/pre-release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 46ce4b2..6bbc44e 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -80,7 +80,7 @@ jobs: id: platform run: | # Read platform from manifest.xml element; fallback to generic - PLATFORM=$(sed -n 's/.*\([^<]*\)<\/platform>.*//p' .mokogitea/manifest.xml 2>/dev/null | head -1 | tr -d '[:space:]') + PLATFORM=$(sed -n 's/.*\([^<]*\)<\/platform>.*//p' .mokogitea/manifest.xml 2>/dev/null | head -1 | tr -d '[:space:]') [ -z "$PLATFORM" ] && PLATFORM="generic" echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT" echo "Platform detected: ${PLATFORM}" diff --git a/.mokogitea/workflows/pre-release.yml b/.mokogitea/workflows/pre-release.yml index 3ddd113..23c3db9 100644 --- a/.mokogitea/workflows/pre-release.yml +++ b/.mokogitea/workflows/pre-release.yml @@ -55,7 +55,7 @@ jobs: - name: Detect platform id: platform run: | - tr -d '[:space:]')| tr -d '[:space:]') + PLATFORM=$(sed -n 's/.*\([^<]*\)<\/platform>.*/\1/p' .mokogitea/manifest.xml 2>/dev/null | head -1 | tr -d '[:space:]') [ -z "$PLATFORM" ] && PLATFORM="generic" echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT" # For packages: prefer pkg_*.xml in src/; fallback to any manifest -- 2.52.0 From da618990728dbd097ec21e1caa8a59767f0216d4 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 21 May 2026 21:28:50 -0500 Subject: [PATCH 02/16] chore(ci): replace inline bash with moko-platform CLI calls Replace ~400 lines of inline bash in auto-release.yml and pre-release.yml with calls to moko-platform CLI scripts: - version_bump.php, changelog_promote.php, badge_update.php - updates_xml_build.php (with stability suffixes) - package_build.php, release_manage.php, release_cascade.php - version_set_platform.php (with --stability flag) Authored-by: Moko Consulting Co-Authored-By: Claude Opus 4.6 (1M context) --- .mokogitea/workflows/auto-release.yml | 626 +++++--------------------- .mokogitea/workflows/pre-release.yml | 383 ++++------------ 2 files changed, 197 insertions(+), 812 deletions(-) diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 6bbc44e..7049eb3 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -129,67 +129,21 @@ jobs: if: steps.version.outputs.skip != 'true' id: bump run: | - CURRENT=$(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) + CLI="/tmp/moko-platform-api/cli" + CURRENT=$(php $CLI/version_read.php --path . 2>/dev/null) [ -z "$CURRENT" ] && { echo "skip=true" >> "$GITHUB_OUTPUT"; exit 0; } - MAJOR=$((10#$(echo "$CURRENT" | cut -d. -f1))) - MINOR=$((10#$(echo "$CURRENT" | cut -d. -f2))) - - # Minor bump, reset patch. Rollover if minor > 99 - MINOR=$((MINOR + 1)) - if [ $MINOR -gt 99 ]; then - MINOR=0 - MAJOR=$((MAJOR + 1)) - fi - - VERSION=$(printf "%02d.%02d.00" $MAJOR $MINOR) + # Minor bump via CLI (updates README.md in-place) + BUMP_OUT=$(php $CLI/version_bump.php --path . --minor) + VERSION=$(php $CLI/version_read.php --path . 2>/dev/null) TODAY=$(date +%Y-%m-%d) + echo "Stable bump: ${BUMP_OUT}" - echo "Stable bump: ${CURRENT} → ${VERSION} (minor)" + # Set platform-specific version (Joomla XML, Dolibarr mod*.class.php) + php $CLI/version_set_platform.php --path . --version "$VERSION" --stability stable --branch main - # Update README.md - sed -i "s/VERSION:[[:space:]]*${CURRENT}/VERSION: ${VERSION}/" README.md - - # Update platform-specific manifest - PLATFORM="${{ steps.platform.outputs.platform }}" - MANIFEST="${{ steps.platform.outputs.manifest }}" - MOD_FILE="${{ steps.platform.outputs.mod_file }}" - case "$PLATFORM" in - joomla) - if [ -n "$MANIFEST" ]; then - MANIFEST_VER=$(sed -n 's/.*\([^<]*\)<\/version>.*/\1/p' "$MANIFEST" | head -1) - [ -n "$MANIFEST_VER" ] && sed -i "s|${MANIFEST_VER}|${VERSION}|" "$MANIFEST" - sed -i "s|[^<]*|${TODAY}|" "$MANIFEST" - fi - # For packages: also bump version in all sub-extension manifests - if [ -d "src/packages" ]; then - for SUB_MANIFEST in $(find src/packages -maxdepth 2 -name "*.xml" -exec grep -l '/dev/null); do - SUB_VER=$(sed -n 's/.*\([^<]*\)<\/version>.*/\1/p' "$SUB_MANIFEST" | head -1) - if [ -n "$SUB_VER" ]; then - sed -i "s|${SUB_VER}|${VERSION}|" "$SUB_MANIFEST" - sed -i "s|[^<]*|${TODAY}|" "$SUB_MANIFEST" - echo " Bumped sub-extension: $(basename $SUB_MANIFEST) ${SUB_VER} → ${VERSION}" - fi - done - fi - ;; - dolibarr) - if [ -n "$MOD_FILE" ]; then - sed -i "s/\$this->version = '[^']*'/\$this->version = '${VERSION}'/" "$MOD_FILE" - fi - echo "${VERSION}" > update.txt - ;; - *) ;; - esac - - # Promote [Unreleased] section in CHANGELOG.md to new version - if [ -f "CHANGELOG.md" ] && grep -qi "Unreleased" CHANGELOG.md; then - sed -i "s|## \[Unreleased\]|## [${VERSION}] --- ${TODAY}|" CHANGELOG.md - sed -i "s|## Unreleased|## [${VERSION}] --- ${TODAY}|" CHANGELOG.md - sed -i "2i ## [Unreleased]" CHANGELOG.md - sed -i "3i \\ " CHANGELOG.md - echo "CHANGELOG promoted to [${VERSION}]" - fi + # Promote [Unreleased] in CHANGELOG.md + php $CLI/changelog_promote.php --path . --version "$VERSION" --date "$TODAY" 2>/dev/null || true # Commit and push git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" @@ -202,8 +156,9 @@ jobs: } # Override version output for rest of pipeline + MAJOR=$(echo "$VERSION" | cut -d. -f1) echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "major=$(printf "%02d" $MAJOR)" >> "$GITHUB_OUTPUT" + echo "major=${MAJOR}" >> "$GITHUB_OUTPUT" - name: Check if already released if: steps.version.outputs.skip != 'true' @@ -358,11 +313,7 @@ jobs: steps.check.outputs.already_released != 'true' run: | VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - find . -name "*.md" ! -path "./.git/*" ! -path "./vendor/*" | while read -r f; do - if grep -q '\[VERSION:' "$f" 2>/dev/null; then - sed -i "s/\[VERSION:[[:space:]]*[0-9]\{2\}\.[0-9]\{2\}\.[0-9]\{2\}\]/[VERSION: ${VERSION}]/" "$f" - fi - done + php /tmp/moko-platform-api/cli/badge_update.php --path . --version "$VERSION" # -- STEP 5: Write updates.xml (Joomla update server) --------------------- - name: "Step 5: Write update stream" @@ -372,143 +323,18 @@ jobs: steps.check.outputs.already_released != 'true' run: | VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - REPO="${{ github.repository }}" + CLI="/tmp/moko-platform-api/cli" - # -- Parse extension metadata from XML manifest ---------------- - MANIFEST=$(find ./src -maxdepth 1 -name "pkg_*.xml" -exec grep -l '/dev/null | head -1) - [ -z "$MANIFEST" ] && MANIFEST=$(find . -maxdepth 2 -name "*.xml" ! -path "*/packages/*" -exec grep -l '/dev/null | head -1) - if [ -z "$MANIFEST" ]; then - echo "Warning: No Joomla XML manifest found — skipping updates.xml" >> $GITHUB_STEP_SUMMARY - exit 0 - fi - - # Extract fields using sed (portable — no grep -P) - EXT_NAME=$(sed -n 's/.*\([^<]*\)<\/name>.*/\1/p' "$MANIFEST" | head -1) - EXT_TYPE=$(sed -n 's/.*]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1) - EXT_ELEMENT=$(sed -n 's/.*\([^<]*\)<\/element>.*/\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) - - # If EXT_NAME is a language key (e.g. PLG_SYSTEM_MOKOJGDPC), resolve from .ini - if echo "$EXT_NAME" | grep -qE '^[A-Z_]+$'; then - INI_NAME=$(find . -name "*.sys.ini" -path "*/en-GB/*" -exec grep -h "^${EXT_NAME}=" {} \; 2>/dev/null | head -1 | cut -d'"' -f2) - [ -z "$INI_NAME" ] && INI_NAME=$(find . -name "*.sys.ini" -exec grep -h "^${EXT_NAME}=" {} \; 2>/dev/null | head -1 | cut -d'"' -f2) - [ -n "$INI_NAME" ] && EXT_NAME="$INI_NAME" - fi - - # Fallbacks - [ -z "$EXT_NAME" ] && EXT_NAME="${{ github.event.repository.name }}" - [ -z "$EXT_TYPE" ] && EXT_TYPE="component" - - # Derive element if not in manifest: - # 1. plugin="xxx" attribute (plugins) - # 2. module="xxx" attribute (modules) - # 3. XML filename (components, packages) - # 4. Repo name fallback (templates, anything else) - if [ -z "$EXT_ELEMENT" ]; then - EXT_ELEMENT=$(sed -n 's/.*plugin="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1) - fi - if [ -z "$EXT_ELEMENT" ]; then - EXT_ELEMENT=$(sed -n 's/.*module="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1) - fi - if [ -z "$EXT_ELEMENT" ]; then - FNAME=$(basename "$MANIFEST" .xml | tr '[:upper:]' '[:lower:]') - # If filename is generic (templateDetails, manifest), use repo name - case "$FNAME" in - templatedetails|manifest) EXT_ELEMENT=$(echo "${{ github.event.repository.name }}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') ;; - *) EXT_ELEMENT="$FNAME" ;; - esac - fi - # Final fallback - [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${{ github.event.repository.name }}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') - - # Save for Steps 7, 8, 8b - echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT" - echo "ext_name=${EXT_NAME}" >> "$GITHUB_OUTPUT" - echo "ext_type=${EXT_TYPE}" >> "$GITHUB_OUTPUT" - echo "ext_folder=${EXT_FOLDER}" >> "$GITHUB_OUTPUT" - - # Build client tag: plugins and frontend modules need site - CLIENT_TAG="" - if [ -n "$EXT_CLIENT" ]; then - CLIENT_TAG="${EXT_CLIENT}" - elif [ "$EXT_TYPE" = "module" ] || [ "$EXT_TYPE" = "plugin" ]; then - CLIENT_TAG="site" - fi - - # Build folder tag for plugins (required for Joomla to match the update) - FOLDER_TAG="" - if [ -n "$EXT_FOLDER" ] && [ "$EXT_TYPE" = "plugin" ]; then - FOLDER_TAG="${EXT_FOLDER}" - fi - - # Build targetplatform (fallback to Joomla 5 if not in manifest) - if [ -z "$TARGET_PLATFORM" ]; then - TARGET_PLATFORM=$(printf '' "/") - fi - - # Build php_minimum tag - PHP_TAG="" - if [ -n "$PHP_MINIMUM" ]; then - PHP_TAG="${PHP_MINIMUM}" - fi - - # Build TYPE_PREFIX for download URL - 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 - - DOWNLOAD_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/stable/${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION}.zip" - INFO_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/stable" - - # -- Build update entry for a given stability tag - build_entry() { - local TAG_NAME="$1" - printf '%s\n' ' ' - printf '%s\n' " ${EXT_NAME}" - printf '%s\n' " ${EXT_NAME} update" - printf '%s\n' " ${EXT_ELEMENT}" - printf '%s\n' " ${EXT_TYPE}" - printf '%s\n' " ${VERSION}" - [ -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' " ${TARGET_PLATFORM}" - [ -n "$PHP_TAG" ] && printf '%s\n' " ${PHP_TAG}" - printf '%s\n' ' Moko Consulting' - printf '%s\n' ' https://mokoconsulting.tech' - printf '%s\n' ' ' - } - - # -- Write updates.xml with cascading channels - # Stable release updates ALL channels (development, alpha, beta, rc, stable) - { - printf '%s\n' "" - printf '%s\n' "" - printf '%s\n' "" - printf '%s\n' '' - build_entry "development" - build_entry "alpha" - build_entry "beta" - build_entry "rc" - build_entry "stable" - printf '%s\n' '' - } > updates.xml + # Generate updates.xml with all stability channels + suffixed versions + # Also exports ext_element, ext_name, ext_type, ext_folder to GITHUB_OUTPUT + php $CLI/updates_xml_build.php \ + --path . \ + --version "$VERSION" \ + --stability stable \ + --gitea-url "${GITEA_URL}" \ + --org "${GITEA_ORG}" \ + --repo "${GITEA_REPO}" \ + --github-output echo "updates.xml: ${VERSION} (all channels updated to stable)" >> $GITHUB_STEP_SUMMARY @@ -558,336 +384,117 @@ jobs: VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" RELEASE_TAG="${{ steps.version.outputs.release_tag }}" BRANCH="${{ steps.version.outputs.branch }}" - MAJOR="${{ steps.version.outputs.major }}" + CLI="/tmp/moko-platform-api/cli" API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - # Reuse metadata from Step 5 (single source of truth) - EXT_ELEMENT="${{ steps.updates.outputs.ext_element }}" + # Reuse metadata from Step 5 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 + TYPE_PREFIX="${{ steps.updates.outputs.type_prefix }}" + EXT_ELEMENT="${{ steps.updates.outputs.ext_element }}" [ -z "$EXT_NAME" ] && EXT_NAME="${GITEA_REPO}" + [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') - NOTES=$(php /tmp/moko-platform-api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null) + RELEASE_NAME="${EXT_NAME} ${VERSION} (${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION})" + NOTES=$(php $CLI/release_notes.php --path . --version "$VERSION" 2>/dev/null) [ -z "$NOTES" ] && NOTES="Release ${VERSION}" - # Build release name: "Pretty Name VERSION (type_element-VERSION)" - 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})" + php $CLI/release_manage.php \ + --action create \ + --tag "$RELEASE_TAG" \ + --name "$RELEASE_NAME" \ + --body "## ${VERSION} ($(date +%Y-%m-%d))\n${NOTES}" \ + --target "$BRANCH" \ + --token "${{ secrets.GA_TOKEN }}" \ + --api-base "$API_BASE" - # 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" + # -- STEP 8: Build package, upload, and update checksums ------------------- + - name: "Step 8: Build package and upload" if: >- steps.version.outputs.skip != 'true' run: | VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" RELEASE_TAG="${{ steps.version.outputs.release_tag }}" - REPO="${{ github.repository }}" + CLI="/tmp/moko-platform-api/cli" API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - # All ZIPs upload to the major release tag (vXX) - RELEASE_JSON=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \ - "${API_BASE}/releases/tags/${RELEASE_TAG}" 2>/dev/null || 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 + # Build ZIP + tar.gz via CLI (handles single and multi-extension packages) + php $CLI/package_build.php --path . --version "$VERSION" --output-dir /tmp --github-output - # Find extension element name from manifest - MANIFEST=$(find ./src -maxdepth 1 -name "pkg_*.xml" -exec grep -l '/dev/null | head -1) - [ -z "$MANIFEST" ] && MANIFEST=$(find . -maxdepth 2 -name "*.xml" ! -path "*/packages/*" -exec grep -l '/dev/null | head -1 || true) - [ -z "$MANIFEST" ] && exit 0 + # Read outputs from package_build + ZIP_NAME="${{ steps.updates.outputs.type_prefix }}${{ steps.updates.outputs.ext_element }}-${VERSION}.zip" + TAR_NAME="${{ steps.updates.outputs.type_prefix }}${{ steps.updates.outputs.ext_element }}-${VERSION}.tar.gz" - # 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>.*/\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/.*]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1) - EXT_FOLDER=$(sed -n 's/.*]*group="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1) - 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" + # Upload assets to release (handles dedup automatically) + php $CLI/release_manage.php \ + --action upload \ + --tag "$RELEASE_TAG" \ + --files "/tmp/${ZIP_NAME},/tmp/${TAR_NAME}" \ + --token "${{ secrets.GA_TOKEN }}" \ + --api-base "$API_BASE" - # -- Build install packages from src/ ---------------------------- - SOURCE_DIR="src" - [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" - [ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ — skipping package"; exit 0; } - - EXCLUDES=".ftpignore sftp-config* *.ppk *.pem *.key .env*" - - if [ "$EXT_TYPE" = "package" ] && [ -d "${SOURCE_DIR}/packages" ]; then - echo "=== Building Joomla PACKAGE (multi-extension) ===" - PKG_STAGE=$(mktemp -d) - - # ZIP each sub-extension - for ext_dir in "${SOURCE_DIR}"/packages/*/; do - [ ! -d "$ext_dir" ] && continue - SUB_NAME=$(basename "$ext_dir") - echo " Packaging sub-extension: ${SUB_NAME}" - (cd "$ext_dir" && zip -r "${PKG_STAGE}/${SUB_NAME}.zip" . -x $EXCLUDES) - done - - # Copy package-level files (manifest, script, etc.) - for f in "${SOURCE_DIR}"/*.xml "${SOURCE_DIR}"/*.php; do - [ -f "$f" ] && cp "$f" "${PKG_STAGE}/" - done - - # Create ZIP and tar.gz from staged package - (cd "$PKG_STAGE" && zip -r "/tmp/${ZIP_NAME}" .) - tar -czf "/tmp/${TAR_NAME}" -C "$PKG_STAGE" . - - rm -rf "$PKG_STAGE" - echo "Package contents built with sub-extension ZIPs" - else - # Standard extension: flat ZIP from src/ - cd "$SOURCE_DIR" - zip -r "/tmp/${ZIP_NAME}" . -x $EXCLUDES - cd .. - - tar -czf "/tmp/${TAR_NAME}" -C "$SOURCE_DIR" \ - --exclude='.ftpignore' --exclude='sftp-config*' \ - --exclude='*.ppk' --exclude='*.pem' --exclude='*.key' --exclude='.env*' . - fi - - 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 ---------------------------------- + # Regenerate updates.xml with SHA-256 from built package SHA256_ZIP=$(sha256sum "/tmp/${ZIP_NAME}" | cut -d' ' -f1) - SHA256_TAR=$(sha256sum "/tmp/${TAR_NAME}" | cut -d' ' -f1) + php $CLI/updates_xml_build.php \ + --path . \ + --version "$VERSION" \ + --stability stable \ + --sha "$SHA256_ZIP" \ + --gitea-url "${GITEA_URL}" \ + --org "${GITEA_ORG}" \ + --repo "${GITEA_REPO}" - # -- Delete existing assets with same name before uploading ------ - ASSETS=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \ - "${API_BASE}/releases/${RELEASE_ID}/assets" 2>/dev/null || echo "[]") - for ASSET_NAME in "$ZIP_NAME" "$TAR_NAME"; do - ASSET_ID=$(echo "$ASSETS" | python3 -c " - import sys,json - assets = json.load(sys.stdin) - for a in assets: - if a['name'] == '${ASSET_NAME}': - print(a['id']); break - " 2>/dev/null || true) - if [ -n "$ASSET_ID" ]; then - curl -sf -X DELETE -H "Authorization: token ${{ secrets.GA_TOKEN }}" \ - "${API_BASE}/releases/${RELEASE_ID}/assets/${ASSET_ID}" 2>/dev/null || true - fi - done + # Commit updated updates.xml + git add updates.xml + git commit -m "chore(release): ZIP + tar.gz for ${VERSION} [skip ci]" \ + --author="gitea-actions[bot] " || true + git push || true - # -- Upload both to release tag ---------------------------------- - curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \ - -H "Content-Type: application/octet-stream" \ - --data-binary @"/tmp/${ZIP_NAME}" \ - "${API_BASE}/releases/${RELEASE_ID}/assets?name=${ZIP_NAME}" > /dev/null 2>&1 || true - - curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \ - -H "Content-Type: application/octet-stream" \ - --data-binary @"/tmp/${TAR_NAME}" \ - "${API_BASE}/releases/${RELEASE_ID}/assets?name=${TAR_NAME}" > /dev/null 2>&1 || true - - # -- Update updates.xml with both download formats --------------- - if [ -f "updates.xml" ]; then - ZIP_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${ZIP_NAME}" - TAR_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${TAR_NAME}" - - # Use Python to update only the stable entry's downloads + sha256 - export PY_ZIP_URL="$ZIP_URL" PY_TAR_URL="$TAR_URL" PY_SHA="$SHA256_ZIP" - python3 << 'PYEOF' - import re, os - - with open("updates.xml") as f: - content = f.read() - - zip_url = os.environ["PY_ZIP_URL"] - tar_url = os.environ["PY_TAR_URL"] - sha = os.environ["PY_SHA"] - - # Find the stable update block and replace its downloads + sha256 - def replace_stable(m): - block = m.group(0) - # Replace downloads block - new_downloads = ( - " \n" - f" {zip_url}\n" - " " - ) - block = re.sub(r' .*?', new_downloads, block, flags=re.DOTALL) - # Add or replace sha256 - if '' in block: - block = re.sub(r' .*?', f' {sha}', block) - else: - block = block.replace('', f'\n {sha}') - return block - - content = re.sub( - r' .*?stable.*?', - replace_stable, - content, - flags=re.DOTALL - ) - - with open("updates.xml", "w") as f: - f.write(content) - PYEOF - - CURRENT_BRANCH="${{ github.ref_name }}" - git add updates.xml - git commit -m "chore(release): ZIP + tar.gz for ${VERSION} [skip ci]" \ - --author="gitea-actions[bot] " || true - git push || true - - # Sync updates.xml to main via direct API (always runs — may be on version/XX branch) - GA_TOKEN="${{ secrets.GA_TOKEN }}" - API="${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}" - - FILE_SHA=$(curl -sf -H "Authorization: token ${GA_TOKEN}" \ - "${API}/contents/updates.xml?ref=main" | jq -r '.sha // empty') - - if [ -n "$FILE_SHA" ]; then - CONTENT=$(base64 -w0 updates.xml) - curl -sf -X PUT -H "Authorization: token ${GA_TOKEN}" \ - -H "Content-Type: application/json" \ - "${API}/contents/updates.xml" \ - -d "$(jq -n \ - --arg content "$CONTENT" \ - --arg sha "$FILE_SHA" \ - --arg msg "chore: sync updates.xml ${VERSION} [skip ci]" \ - --arg branch "main" \ - '{content: $content, sha: $sha, message: $msg, branch: $branch}' - )" > /dev/null 2>&1 \ - && echo "updates.xml synced to main via API" \ - || echo "WARNING: failed to sync updates.xml to main" - else - echo "WARNING: could not get updates.xml SHA from main" - fi + # Sync updates.xml to main via API (may be on version/XX branch) + GA_TOKEN="${{ secrets.GA_TOKEN }}" + API="${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}" + FILE_SHA=$(curl -sf -H "Authorization: token ${GA_TOKEN}" \ + "${API}/contents/updates.xml?ref=main" | jq -r '.sha // empty') + if [ -n "$FILE_SHA" ]; then + CONTENT=$(base64 -w0 updates.xml) + curl -sf -X PUT -H "Authorization: token ${GA_TOKEN}" \ + -H "Content-Type: application/json" \ + "${API}/contents/updates.xml" \ + -d "$(jq -n \ + --arg content "$CONTENT" \ + --arg sha "$FILE_SHA" \ + --arg msg "chore: sync updates.xml ${VERSION} [skip ci]" \ + --arg branch "main" \ + '{content: $content, sha: $sha, message: $msg, branch: $branch}' + )" > /dev/null 2>&1 \ + && echo "updates.xml synced to main via API" \ + || echo "WARNING: failed to sync updates.xml to main" fi - 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 + SHA ---------------- - - name: "Step 8b: Update release body with changelog and SHA" - if: steps.version.outputs.skip != 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - RELEASE_TAG="${{ steps.version.outputs.release_tag }}" - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - EXT_ELEMENT="${{ steps.updates.outputs.ext_element }}" - EXT_TYPE="${{ steps.updates.outputs.ext_type }}" - EXT_FOLDER="${{ steps.updates.outputs.ext_folder }}" - - # Build TYPE_PREFIX to match Step 8's ZIP naming - 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" - - # Get SHA from the built files - SHA256_ZIP="" - [ -f "/tmp/${ZIP_NAME}" ] && SHA256_ZIP=$(sha256sum "/tmp/${ZIP_NAME}" | cut -d' ' -f1) + # Build release body with changelog + SHA + NOTES=$(php $CLI/release_notes.php --path . --version "$VERSION" 2>/dev/null) SHA256_TAR="" [ -f "/tmp/${TAR_NAME}" ] && SHA256_TAR=$(sha256sum "/tmp/${TAR_NAME}" | cut -d' ' -f1) - # Extract latest changelog entry (strip the ## header to avoid duplicate) - CHANGELOG="" - if [ -f "CHANGELOG.md" ]; then - CHANGELOG=$(sed -n "/^## \[*${VERSION}/,/^## \[*[0-9]/p" CHANGELOG.md | sed '$d' | sed '1d') - [ -z "$CHANGELOG" ] && CHANGELOG=$(sed -n '/^## /,/^## /p' CHANGELOG.md | sed '$d' | sed '1d' | head -30) - fi - - # Build release body (single header, no duplicate from changelog) - BODY="## ${VERSION} ($(date +%Y-%m-%d))\n\n" - if [ -n "$CHANGELOG" ]; then - BODY="${BODY}${CHANGELOG}\n\n" - fi - BODY="${BODY}---\n\n### Checksums\n\n" + BODY="## ${VERSION} ($(date +%Y-%m-%d))\n\n${NOTES}\n\n---\n\n### Checksums\n\n" BODY="${BODY}| File | SHA-256 |\n|------|--------|\n" - [ -n "$SHA256_ZIP" ] && BODY="${BODY}| \`${ZIP_NAME}\` | \`${SHA256_ZIP}\` |\n" + BODY="${BODY}| \`${ZIP_NAME}\` | \`${SHA256_ZIP}\` |\n" [ -n "$SHA256_TAR" ] && BODY="${BODY}| \`${TAR_NAME}\` | \`${SHA256_TAR}\` |\n" - # Get release ID and update body - RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \ - "${API_BASE}/releases/tags/${RELEASE_TAG}" 2>/dev/null | \ - python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) + printf '%b' "$BODY" > /tmp/release_body.md + php $CLI/release_manage.php \ + --action update-body \ + --tag "$RELEASE_TAG" \ + --body-file /tmp/release_body.md \ + --token "${{ secrets.GA_TOKEN }}" \ + --api-base "$API_BASE" - if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "None" ]; then - python3 -c " - import json, urllib.request - body = '''$(printf '%b' "$BODY")''' - data = json.dumps({'body': body}).encode() - req = urllib.request.Request( - '${API_BASE}/releases/${RELEASE_ID}', - data=data, - headers={'Authorization': 'token ${{ secrets.GA_TOKEN }}', 'Content-Type': 'application/json'}, - method='PATCH' - ) - urllib.request.urlopen(req) - " 2>/dev/null && echo "Release body updated with changelog + SHA" >> $GITHUB_STEP_SUMMARY - fi + echo "### Packages" >> $GITHUB_STEP_SUMMARY + echo "| Package | SHA-256 |" >> $GITHUB_STEP_SUMMARY + echo "|---------|---------|" >> $GITHUB_STEP_SUMMARY + echo "| \`${ZIP_NAME}\` | \`${SHA256_ZIP}\` |" >> $GITHUB_STEP_SUMMARY + echo "| \`${TAR_NAME}\` | \`${SHA256_TAR}\` |" >> $GITHUB_STEP_SUMMARY # -- STEP 9: Mirror to GitHub (stable only) -------------------------------- - name: "Step 9: Mirror release to GitHub" @@ -950,33 +557,14 @@ jobs: || echo "WARNING: GitHub mirror push failed" # -- Clean up lesser pre-releases (cascade) --------------------------------- - # stable → deletes all | rc → beta,alpha,dev | beta → alpha,dev | alpha → dev - name: "Delete lesser pre-release channels" if: steps.version.outputs.skip != 'true' continue-on-error: true run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - TOKEN="${{ secrets.GA_TOKEN }}" - - # Stable deletes all pre-release channels - TAGS_TO_DELETE="development alpha beta release-candidate" - - DELETED=0 - for TAG in $TAGS_TO_DELETE; do - RELEASE_ID=$(curl -sS -H "Authorization: token ${TOKEN}" \ - "${API_BASE}/releases/tags/${TAG}" 2>/dev/null | \ - python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) - - if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "None" ]; then - curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \ - "${API_BASE}/releases/${RELEASE_ID}" 2>/dev/null || true - curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \ - "${API_BASE}/tags/${TAG}" 2>/dev/null || true - echo "Deleted: ${TAG} (id: ${RELEASE_ID})" - DELETED=$((DELETED + 1)) - fi - done - echo "Cleaned up ${DELETED} pre-release channel(s)" >> $GITHUB_STEP_SUMMARY + php /tmp/moko-platform-api/cli/release_cascade.php \ + --stability stable \ + --token "${{ secrets.GA_TOKEN }}" \ + --api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" # -- STEP 11: Reset dev branch from main ------------------------------------ - name: "Step 11: Delete and recreate dev branch from main" diff --git a/.mokogitea/workflows/pre-release.yml b/.mokogitea/workflows/pre-release.yml index 23c3db9..234a949 100644 --- a/.mokogitea/workflows/pre-release.yml +++ b/.mokogitea/workflows/pre-release.yml @@ -5,9 +5,9 @@ # FILE INFORMATION # DEFGROUP: Gitea.Workflow # INGROUP: moko-platform.Release -# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards +# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # PATH: /templates/workflows/universal/pre-release.yml.template -# VERSION: 05.00.00 +# VERSION: 05.01.00 # BRIEF: Manual pre-release — builds dev/alpha/beta/rc packages from any branch name: "Universal: Pre-Release" @@ -45,30 +45,33 @@ jobs: fetch-depth: 0 token: ${{ secrets.GA_TOKEN }} - - name: Setup PHP + - name: Setup moko-platform tools + env: + MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }} + MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting + COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN }}"}}' 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 >/dev/null 2>&1 + 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 + git clone --depth 1 --branch main --quiet \ + "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ + /tmp/moko-platform-api + cd /tmp/moko-platform-api + composer install --no-dev --no-interaction --quiet - name: Detect platform id: platform run: | PLATFORM=$(sed -n 's/.*\([^<]*\)<\/platform>.*/\1/p' .mokogitea/manifest.xml 2>/dev/null | head -1 | tr -d '[:space:]') + [ -z "$PLATFORM" ] && PLATFORM=$(sed -n 's/.*\([^<]*\)<\/platform>.*/\1/p' .gitea/manifest.xml 2>/dev/null | head -1 | tr -d '[:space:]') [ -z "$PLATFORM" ] && PLATFORM="generic" echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT" - # For packages: prefer pkg_*.xml in src/; fallback to any manifest - MANIFEST=$(find ./src -maxdepth 1 -name "pkg_*.xml" -exec grep -l '/dev/null | head -1) - [ -z "$MANIFEST" ] && MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" ! -path "*/packages/*" -exec grep -l '/dev/null | head -1) - [ -z "$MANIFEST" ] && MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1) - MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1) - echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT" - echo "mod_file=${MOD_FILE}" >> "$GITHUB_OUTPUT" - - name: Resolve metadata + - name: Resolve metadata and bump version id: meta run: | + CLI="/tmp/moko-platform-api/cli" STABILITY="${{ inputs.stability }}" case "$STABILITY" in @@ -78,66 +81,16 @@ jobs: release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;; esac - # Read and bump patch version (with rollover) - CURRENT=$(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) + # Bump patch version via CLI + CURRENT=$(php $CLI/version_read.php --path . 2>/dev/null) [ -z "$CURRENT" ] && CURRENT="00.00.00" - - MAJOR=$(echo "$CURRENT" | cut -d. -f1) - MINOR=$(echo "$CURRENT" | cut -d. -f2) - PATCH=$(echo "$CURRENT" | cut -d. -f3) - - # Patch bump with rollover: ZZ=99 → bump minor, YY=99 → bump major - NEW_PATCH=$((10#$PATCH + 1)) - NEW_MINOR=$((10#$MINOR)) - NEW_MAJOR=$((10#$MAJOR)) - - if [ $NEW_PATCH -gt 99 ]; then - NEW_PATCH=0 - NEW_MINOR=$((NEW_MINOR + 1)) - fi - if [ $NEW_MINOR -gt 99 ]; then - NEW_MINOR=0 - NEW_MAJOR=$((NEW_MAJOR + 1)) - fi - - VERSION=$(printf "%02d.%02d.%02d" $NEW_MAJOR $NEW_MINOR $NEW_PATCH) - TODAY=$(date +%Y-%m-%d) - + php $CLI/version_bump.php --path . + VERSION=$(php $CLI/version_read.php --path . 2>/dev/null) echo "Bumping: ${CURRENT} → ${VERSION} (patch)" - # Update README.md - sed -i "s/VERSION:[[:space:]]*${CURRENT}/VERSION: ${VERSION}/" README.md - - # Update platform-specific manifest - PLATFORM="${{ steps.platform.outputs.platform }}" - MANIFEST="${{ steps.platform.outputs.manifest }}" - MOD_FILE="${{ steps.platform.outputs.mod_file }}" - case "$PLATFORM" in - joomla) - if [ -n "$MANIFEST" ]; then - MANIFEST_VER=$(sed -n 's/.*\([^<]*\)<\/version>.*/\1/p' "$MANIFEST" | head -1) - sed -i "s|${MANIFEST_VER}|${VERSION}|" "$MANIFEST" - sed -i "s|[^<]*|${TODAY}|" "$MANIFEST" - fi - # For packages: also bump version in all sub-extension manifests - if [ -d "src/packages" ]; then - for SUB_MANIFEST in $(find src/packages -maxdepth 2 -name "*.xml" -exec grep -l '/dev/null); do - SUB_VER=$(sed -n 's/.*\([^<]*\)<\/version>.*/\1/p' "$SUB_MANIFEST" | head -1) - if [ -n "$SUB_VER" ]; then - sed -i "s|${SUB_VER}|${VERSION}|" "$SUB_MANIFEST" - sed -i "s|[^<]*|${TODAY}|" "$SUB_MANIFEST" - echo " Bumped sub-extension: $(basename $SUB_MANIFEST) ${SUB_VER} → ${VERSION}" - fi - done - fi - ;; - dolibarr) - if [ -n "$MOD_FILE" ]; then - sed -i "s/\$this->version = '[^']*'/\$this->version = '${VERSION}'/" "$MOD_FILE" - fi - ;; - *) ;; - esac + # Set platform-specific version with stability suffix + php $CLI/version_set_platform.php \ + --path . --version "$VERSION" --stability "$STABILITY" --branch "${{ github.ref_name }}" # Commit version bump git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" @@ -145,229 +98,90 @@ jobs: git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" git add -A git diff --cached --quiet || { - git commit -m "chore(version): bump ${CURRENT} → ${VERSION} [skip ci]" + git commit -m "chore(version): bump ${CURRENT} → ${VERSION}${SUFFIX} [skip ci]" git push origin HEAD 2>&1 } - # Auto-detect element (platform-aware) - case "$PLATFORM" in - joomla) - MANIFEST="${{ steps.platform.outputs.manifest }}" - EXT_ELEMENT="" - if [ -n "$MANIFEST" ]; then - EXT_ELEMENT=$(sed -n 's/.*\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" 2>/dev/null | head -1) - if [ -z "$EXT_ELEMENT" ]; then - EXT_ELEMENT=$(basename "$MANIFEST" .xml | tr '[:upper:]' '[:lower:]') - case "$EXT_ELEMENT" in - templatedetails|manifest) EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') ;; - esac - fi - else - EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') - fi - ;; - dolibarr) - MOD_FILE="${{ steps.platform.outputs.mod_file }}" - if [ -n "$MOD_FILE" ]; then - MOD_BASENAME=$(basename "$MOD_FILE" .class.php) - EXT_ELEMENT=$(echo "$MOD_BASENAME" | sed 's/^mod//' | tr '[:upper:]' '[:lower:]') - else - EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') - fi - ;; - *) - EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') - ;; - esac - - ZIP_NAME="${EXT_ELEMENT}-${VERSION}${SUFFIX}.zip" - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT" echo "tag=${TAG}" >> "$GITHUB_OUTPUT" - echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT" - echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT" - echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT" - - echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ===" - name: Build package + id: package run: | - SOURCE_DIR="src" - [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" - if [ ! -d "$SOURCE_DIR" ]; then - echo "::error::No src/ or htdocs/ directory" - exit 1 - fi - - MANIFEST="${{ steps.meta.outputs.manifest }}" - EXT_TYPE="" - if [ -n "$MANIFEST" ]; then - EXT_TYPE=$(sed -n 's/.*]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1) - fi - - EXCLUDES="sftp-config* .ftpignore *.ppk *.pem *.key .env* *.local .build-trigger" - - mkdir -p build/package - - if [ "$EXT_TYPE" = "package" ] && [ -d "${SOURCE_DIR}/packages" ]; then - echo "=== Building Joomla PACKAGE (multi-extension) ===" - - # 1) ZIP each sub-extension in src/packages/ - for ext_dir in "${SOURCE_DIR}"/packages/*/; do - [ ! -d "$ext_dir" ] && continue - EXT_NAME=$(basename "$ext_dir") - echo " Packaging sub-extension: ${EXT_NAME}" - cd "$ext_dir" - zip -r "../../build/package/${EXT_NAME}.zip" . -x $EXCLUDES - cd "$OLDPWD" - done - - # 2) Copy package-level files (manifest, script, etc.) - for f in "${SOURCE_DIR}"/*.xml "${SOURCE_DIR}"/*.php; do - [ -f "$f" ] && cp "$f" build/package/ - done - - echo "Package contents:" - ls -la build/package/ - else - echo "=== Building standard Joomla extension ===" - rsync -a \ - --exclude='sftp-config*' \ - --exclude='.ftpignore' \ - --exclude='*.ppk' \ - --exclude='*.pem' \ - --exclude='*.key' \ - --exclude='.env*' \ - --exclude='*.local' \ - --exclude='.build-trigger' \ - "${SOURCE_DIR}/" build/package/ - fi - - - name: Create ZIP - id: zip - run: | - ZIP_NAME="${{ steps.meta.outputs.zip_name }}" - cd build/package - zip -r "../${ZIP_NAME}" . - cd .. - - SHA256=$(sha256sum "${ZIP_NAME}" | cut -d' ' -f1) - echo "sha256=${SHA256}" >> "$GITHUB_OUTPUT" - echo "ZIP: ${ZIP_NAME} (SHA: ${SHA256:0:16}...)" - - - name: Create or replace Gitea release - id: release - run: | - TAG="${{ steps.meta.outputs.tag }}" + CLI="/tmp/moko-platform-api/cli" VERSION="${{ steps.meta.outputs.version }}" + SUFFIX="${{ steps.meta.outputs.suffix }}" + + # Build ZIP + tar.gz via CLI (handles type prefix, excludes, multi-extension packages) + php $CLI/package_build.php \ + --path . \ + --version "${VERSION}${SUFFIX}" \ + --output-dir build \ + --github-output + + - name: Create release and upload + run: | + CLI="/tmp/moko-platform-api/cli" + VERSION="${{ steps.meta.outputs.version }}" + SUFFIX="${{ steps.meta.outputs.suffix }}" + TAG="${{ steps.meta.outputs.tag }}" STABILITY="${{ steps.meta.outputs.stability }}" - SHA256="${{ steps.zip.outputs.sha256 }}" - ZIP_NAME="${{ steps.meta.outputs.zip_name }}" - EXT_ELEMENT="${{ steps.meta.outputs.ext_element }}" - TOKEN="${{ secrets.GA_TOKEN }}" - API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - BRANCH=$(git branch --show-current) + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + EXT_ELEMENT="${{ steps.package.outputs.ext_element }}" + [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') - BODY="## ${VERSION} ($(date +%Y-%m-%d)) - **Channel:** ${STABILITY} - **SHA-256:** \`${SHA256}\`" - - # Delete existing release - EXISTING_ID=$(curl -sS -H "Authorization: token ${TOKEN}" \ - "${API}/releases/tags/${TAG}" | jq -r '.id // empty' 2>/dev/null) - if [ -n "$EXISTING_ID" ]; then - curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \ - "${API}/releases/${EXISTING_ID}" 2>/dev/null || true - curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \ - "${API}/tags/${TAG}" 2>/dev/null || true - fi + SHA256="${{ steps.package.outputs.sha256_zip }}" + ZIP_PATH="${{ steps.package.outputs.zip_path }}" + TAR_PATH="${{ steps.package.outputs.tar_path }}" # Create release - RELEASE_ID=$(curl -sS -X POST -H "Authorization: token ${TOKEN}" \ - -H "Content-Type: application/json" \ - "${API}/releases" \ - -d "$(jq -n \ - --arg tag "$TAG" \ - --arg target "$BRANCH" \ - --arg name "${EXT_ELEMENT} ${VERSION} (${STABILITY})" \ - --arg body "$BODY" \ - '{tag_name: $tag, target_commitish: $target, name: $name, body: $body, prerelease: true}' - )" | jq -r '.id') + php $CLI/release_manage.php \ + --action create \ + --tag "$TAG" \ + --name "${EXT_ELEMENT} ${VERSION}${SUFFIX} (${STABILITY})" \ + --body "## ${VERSION}${SUFFIX} ($(date +%Y-%m-%d))\n**Channel:** ${STABILITY}\n**SHA-256:** \`${SHA256}\`" \ + --target "${{ github.ref_name }}" \ + --token "${{ secrets.GA_TOKEN }}" \ + --api-base "$API_BASE" - echo "release_id=${RELEASE_ID}" >> "$GITHUB_OUTPUT" - - # Upload ZIP - curl -sS -X POST -H "Authorization: token ${TOKEN}" \ - -H "Content-Type: application/octet-stream" \ - "${API}/releases/${RELEASE_ID}/assets?name=${ZIP_NAME}" \ - --data-binary "@build/${ZIP_NAME}" - - echo "Released: ${EXT_ELEMENT} ${VERSION} (${STABILITY})" + # Upload assets + FILES="${ZIP_PATH}" + [ -f "$TAR_PATH" ] && FILES="${FILES},${TAR_PATH}" + php $CLI/release_manage.php \ + --action upload \ + --tag "$TAG" \ + --files "$FILES" \ + --token "${{ secrets.GA_TOKEN }}" \ + --api-base "$API_BASE" - name: Update updates.xml if: steps.platform.outputs.platform == 'joomla' run: | - STABILITY="${{ steps.meta.outputs.stability }}" + CLI="/tmp/moko-platform-api/cli" VERSION="${{ steps.meta.outputs.version }}" - SHA256="${{ steps.zip.outputs.sha256 }}" - ZIP_NAME="${{ steps.meta.outputs.zip_name }}" - TAG="${{ steps.meta.outputs.tag }}" - DATE=$(date +%Y-%m-%d) + STABILITY="${{ steps.meta.outputs.stability }}" + SHA256="${{ steps.package.outputs.sha256_zip }}" - if [ ! -f "updates.xml" ]; then - echo "No updates.xml — skipping" - exit 0 - fi + # Map stability names + case "$STABILITY" in + release-candidate) CLI_STABILITY="rc" ;; + *) CLI_STABILITY="$STABILITY" ;; + esac - export PY_STABILITY="$STABILITY" PY_VERSION="$VERSION" PY_SHA256="$SHA256" \ - PY_ZIP_NAME="$ZIP_NAME" PY_TAG="$TAG" PY_DATE="$DATE" \ - PY_GITEA_ORG="$GITEA_ORG" PY_GITEA_REPO="$GITEA_REPO" - python3 << 'PYEOF' - import re, os + # Generate updates.xml with stability-suffixed versions + php $CLI/updates_xml_build.php \ + --path . \ + --version "$VERSION" \ + --stability "$CLI_STABILITY" \ + --sha "$SHA256" \ + --gitea-url "${GITEA_URL}" \ + --org "${GITEA_ORG}" \ + --repo "${GITEA_REPO}" - stability = os.environ["PY_STABILITY"] - version = os.environ["PY_VERSION"] - sha256 = os.environ["PY_SHA256"] - zip_name = os.environ["PY_ZIP_NAME"] - tag = os.environ["PY_TAG"] - date = os.environ["PY_DATE"] - gitea_org = os.environ["PY_GITEA_ORG"] - gitea_repo = os.environ["PY_GITEA_REPO"] - download_url = f"https://git.mokoconsulting.tech/{gitea_org}/{gitea_repo}/releases/download/{tag}/{zip_name}" - - with open("updates.xml", "r") as f: - content = f.read() - - # Map stability to XML tag name - tag_map = {"development": "development", "alpha": "alpha", "beta": "beta", "release-candidate": "rc"} - xml_tag = tag_map.get(stability, stability) - - pattern = r"((?:(?!).)*?" + re.escape(xml_tag) + r".*?)" - match = re.search(pattern, content, re.DOTALL) - if match: - block = match.group(1) - updated = re.sub(r"[^<]*", f"{version}", block) - updated = re.sub(r"[^<]*", f"{date}", updated) - if "" in updated: - updated = re.sub(r"[^<]*", f"{sha256}", updated) - else: - updated = updated.replace("", f"\n {sha256}") - updated = re.sub(r"(]*>)[^<]*()", rf"\g<1>{download_url}\g<2>", updated) - content = content.replace(block, updated) - print(f"Updated {xml_tag} channel: version={version}") - else: - print(f"WARNING: No {xml_tag} block in updates.xml") - - with open("updates.xml", "w") as f: - f.write(content) - PYEOF - - # Commit and push to current branch + # Commit and push if ! git diff --quiet updates.xml 2>/dev/null; then - git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" - git config --local user.name "gitea-actions[bot]" git add updates.xml git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]" git push origin HEAD 2>&1 || echo "WARNING: push failed" @@ -380,10 +194,8 @@ jobs: git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" git config --local user.name "gitea-actions[bot]" - # Sync updates.xml to main and dev (whichever isn't current) for BRANCH in main dev; do [ "$BRANCH" = "$CURRENT_BRANCH" ] && continue - echo "Syncing updates.xml → ${BRANCH}" git fetch origin "${BRANCH}" 2>/dev/null || continue git checkout "origin/${BRANCH}" -- . 2>/dev/null || continue @@ -399,30 +211,15 @@ jobs: - name: "Delete lesser pre-release channels (cascade)" continue-on-error: true run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - TOKEN="${{ secrets.GA_TOKEN }}" STABILITY="${{ steps.meta.outputs.stability }}" - # Cascade: rc → beta,alpha,dev | beta → alpha,dev | alpha → dev | dev → nothing + # Map workflow stability names to CLI names case "$STABILITY" in - release-candidate) TAGS_TO_DELETE="beta alpha development" ;; - beta) TAGS_TO_DELETE="alpha development" ;; - alpha) TAGS_TO_DELETE="development" ;; - *) TAGS_TO_DELETE="" ;; + release-candidate) CLI_STABILITY="rc" ;; + *) CLI_STABILITY="$STABILITY" ;; esac - [ -z "$TAGS_TO_DELETE" ] && exit 0 - - for TAG in $TAGS_TO_DELETE; do - RELEASE_ID=$(curl -sS -H "Authorization: token ${TOKEN}" \ - "${API_BASE}/releases/tags/${TAG}" 2>/dev/null | \ - python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) - - if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "None" ]; then - curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \ - "${API_BASE}/releases/${RELEASE_ID}" 2>/dev/null || true - curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \ - "${API_BASE}/tags/${TAG}" 2>/dev/null || true - echo "Deleted: ${TAG} (id: ${RELEASE_ID})" - fi - done + php /tmp/moko-platform-api/cli/release_cascade.php \ + --stability "$CLI_STABILITY" \ + --token "${{ secrets.GA_TOKEN }}" \ + --api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" -- 2.52.0 From 22bc47196023bd0e6249fc4298642f66f37a7e12 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 26 May 2026 04:13:18 +0000 Subject: [PATCH 03/16] sync: update cascade-dev.yml from MokoJoomGallery [skip ci] -- 2.52.0 From 2d40037a8c0e037b04b1b485a8d35391813aae70 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 26 May 2026 04:13:19 +0000 Subject: [PATCH 04/16] sync: update cleanup.yml from MokoJoomGallery [skip ci] -- 2.52.0 From 052537360b861c36fd6ddfc00bd48eba5ec57b98 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 26 May 2026 04:13:19 +0000 Subject: [PATCH 05/16] sync: update gitleaks.yml from MokoJoomGallery [skip ci] -- 2.52.0 From 8c2b4d2f290855f81f560d1867540ed9292e1a30 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 26 May 2026 04:13:19 +0000 Subject: [PATCH 06/16] sync: update notify.yml from MokoJoomGallery [skip ci] -- 2.52.0 From f2bf61fe2ea84db25d9affebec2d0b508683d72e Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 26 May 2026 04:13:20 +0000 Subject: [PATCH 07/16] sync: update security-audit.yml from MokoJoomGallery [skip ci] -- 2.52.0 From 523362395b6f59eeccba78b55b70c4263a1c5da6 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 26 May 2026 04:13:20 +0000 Subject: [PATCH 08/16] sync: update auto-release.yml from MokoJoomGallery [skip ci] --- .mokogitea/workflows/auto-release.yml | 158 +++++++++++++++++--------- 1 file changed, 102 insertions(+), 56 deletions(-) diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index e3e1c85..5c589e0 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -53,7 +53,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@v4 with: token: ${{ secrets.GA_TOKEN }} fetch-depth: 0 @@ -79,37 +79,86 @@ jobs: - name: Detect platform id: platform run: | - php /tmp/moko-platform-api/cli/manifest_read.php --path . --github-output - MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1 || true) - MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1 || true) + # Read platform from manifest.xml element; fallback to generic + PLATFORM=$(sed -n 's/.*\([^<]*\)<\/platform>.*//p' .mokogitea/manifest.xml 2>/dev/null | head -1 | tr -d '[:space:]') + [ -z "$PLATFORM" ] && PLATFORM="generic" + echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT" + echo "Platform detected: ${PLATFORM}" + # For packages: prefer pkg_*.xml in src/; fallback to any manifest + MANIFEST=$(find ./src -maxdepth 1 -name "pkg_*.xml" -exec grep -l '/dev/null | head -1) + [ -z "$MANIFEST" ] && MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" ! -path "*/packages/*" -exec grep -l '/dev/null | head -1) + [ -z "$MANIFEST" ] && MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1) + MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1) echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT" echo "mod_file=${MOD_FILE}" >> "$GITHUB_OUTPUT" - - name: "Step 1: Read version" + # -- STEP 1: Read version ----------------------------------------------- + - name: "Step 1: Read version from README.md" id: version run: | - VERSION=$(php /tmp/moko-platform-api/cli/version_read.php --path .) + VERSION=$(php /tmp/moko-platform-api/cli/version_read.php --path . 2>/dev/null) if [ -z "$VERSION" ]; then - echo "::error::No VERSION in README.md" + echo "No VERSION in README.md — skipping release" echo "skip=true" >> "$GITHUB_OUTPUT" exit 0 fi + # Derive major.minor for branch naming (patches update existing branch) + MINOR=$(echo "$VERSION" | awk -F. '{printf "%s.%s", $1, $2}') + PATCH=$(echo "$VERSION" | awk -F. '{print $3}') + + MAJOR=$(echo "$VERSION" | awk -F. '{print $1}') + MINOR_NUM=$(echo "$VERSION" | awk -F. '{print $2}') + + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "branch=version/${MAJOR}" >> "$GITHUB_OUTPUT" + echo "minor=$MINOR" >> "$GITHUB_OUTPUT" + echo "major=$MAJOR" >> "$GITHUB_OUTPUT" + echo "release_tag=stable" >> "$GITHUB_OUTPUT" + echo "stability=stable" >> "$GITHUB_OUTPUT" + echo "skip=false" >> "$GITHUB_OUTPUT" + if [ "$PATCH" = "00" ] || [ "$PATCH" = "01" ]; then + echo "is_minor=true" >> "$GITHUB_OUTPUT" + echo "Version: $VERSION (first release for this minor — full pipeline)" + else + echo "is_minor=false" >> "$GITHUB_OUTPUT" + echo "Version: $VERSION (patch — platform version + badges only)" + fi + + # -- STEP 1b: Bump minor version (stable = minor bump, reset patch) ------ + - name: "Step 1b: Bump minor version for stable release" + if: steps.version.outputs.skip != 'true' + id: bump + run: | + CLI="/tmp/moko-platform-api/cli" + CURRENT=$(php $CLI/version_read.php --path . 2>/dev/null) + [ -z "$CURRENT" ] && { echo "skip=true" >> "$GITHUB_OUTPUT"; exit 0; } + + # Minor bump via CLI (updates README.md in-place) + BUMP_OUT=$(php $CLI/version_bump.php --path . --minor) + VERSION=$(php $CLI/version_read.php --path . 2>/dev/null) + TODAY=$(date +%Y-%m-%d) + echo "Stable bump: ${BUMP_OUT}" + + # Set platform-specific version (Joomla XML, Dolibarr mod*.class.php) + php $CLI/version_set_platform.php --path . --version "$VERSION" --stability stable --branch main + + # Promote [Unreleased] in CHANGELOG.md + php $CLI/changelog_promote.php --path . --version "$VERSION" --date "$TODAY" 2>/dev/null || true + + # Commit and push + 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 diff --cached --quiet || { + git commit -m "chore(version): bump ${CURRENT} → ${VERSION} [skip ci]" + git push origin HEAD:main 2>&1 + } + + # Override version output for rest of pipeline MAJOR=$(echo "$VERSION" | cut -d. -f1) echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "release_tag=v${MAJOR}" >> "$GITHUB_OUTPUT" - echo "skip=false" >> "$GITHUB_OUTPUT" - echo "branch=version/${MAJOR}" >> "$GITHUB_OUTPUT" - - - name: "Step 1b: Bump version" - id: bump - if: steps.version.outputs.skip != '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}" + echo "major=${MAJOR}" >> "$GITHUB_OUTPUT" - name: Check if already released if: steps.version.outputs.skip != 'true' @@ -259,22 +308,37 @@ jobs: # -- STEP 4: Update version badges ---------------------------------------- - name: "Step 4: Update version badges" - if: steps.version.outputs.skip != 'true' - run: | - VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - php /tmp/moko-platform-api/cli/badge_update.php --path . --version "${VERSION}" 2>/dev/null || true - - - name: "Step 5: Write update stream" if: >- steps.version.outputs.skip != 'true' && - steps.platform.outputs.platform == 'joomla' + steps.check.outputs.already_released != 'true' run: | VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - php /tmp/moko-platform-api/cli/updates_xml_build.php \ - --path . --version "${VERSION}" --stability stable \ - --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \ + php /tmp/moko-platform-api/cli/badge_update.php --path . --version "$VERSION" + + # -- STEP 5: Write updates.xml (Joomla update server) --------------------- + - name: "Step 5: Write update stream" + id: updates + if: >- + steps.version.outputs.skip != 'true' && + steps.check.outputs.already_released != 'true' + run: | + VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" + CLI="/tmp/moko-platform-api/cli" + + # Generate updates.xml with all stability channels + suffixed versions + # Also exports ext_element, ext_name, ext_type, ext_folder to GITHUB_OUTPUT + php $CLI/updates_xml_build.php \ + --path . \ + --version "$VERSION" \ + --stability stable \ + --gitea-url "${GITEA_URL}" \ + --org "${GITEA_ORG}" \ + --repo "${GITEA_REPO}" \ --github-output + echo "updates.xml: ${VERSION} (all channels updated to stable)" >> $GITHUB_STEP_SUMMARY + + # -- Commit all changes --------------------------------------------------- - name: Commit release changes if: >- steps.version.outputs.skip != 'true' && @@ -358,9 +422,9 @@ jobs: # Build ZIP + tar.gz via CLI (handles single and multi-extension packages) php $CLI/package_build.php --path . --version "$VERSION" --output-dir /tmp --github-output - # Find extension element name from manifest - MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '/dev/null | head -1 || true) - [ -z "$MANIFEST" ] && exit 0 + # Read outputs from package_build + ZIP_NAME="${{ steps.updates.outputs.type_prefix }}${{ steps.updates.outputs.ext_element }}-${VERSION}.zip" + TAR_NAME="${{ steps.updates.outputs.type_prefix }}${{ steps.updates.outputs.ext_element }}-${VERSION}.tar.gz" # Upload assets to release (handles dedup automatically) php $CLI/release_manage.php \ @@ -370,26 +434,7 @@ jobs: --token "${{ secrets.GA_TOKEN }}" \ --api-base "$API_BASE" - # -- 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 ---------------------------------- + # Regenerate updates.xml with SHA-256 from built package SHA256_ZIP=$(sha256sum "/tmp/${ZIP_NAME}" | cut -d' ' -f1) php $CLI/updates_xml_build.php \ --path . \ @@ -513,14 +558,15 @@ jobs: # -- Clean up lesser pre-releases (cascade) --------------------------------- - name: "Delete lesser pre-release channels" + if: steps.version.outputs.skip != 'true' continue-on-error: true run: | php /tmp/moko-platform-api/cli/release_cascade.php \ --stability stable \ --token "${{ secrets.GA_TOKEN }}" \ - --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \ - --gitea-url "${GITEA_URL}" 2>/dev/null || true + --api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + # -- STEP 11: Reset dev branch from main ------------------------------------ - name: "Step 11: Delete and recreate dev branch from main" if: steps.version.outputs.skip != 'true' continue-on-error: true -- 2.52.0 From 8c5c5365a3eb450c0c72e71267d725350c048bfb Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 26 May 2026 04:13:21 +0000 Subject: [PATCH 09/16] sync: update ci-joomla.yml from MokoJoomGallery [skip ci] --- .mokogitea/workflows/ci-joomla.yml | 82 ++++++++++++++++++++++++------ 1 file changed, 66 insertions(+), 16 deletions(-) diff --git a/.mokogitea/workflows/ci-joomla.yml b/.mokogitea/workflows/ci-joomla.yml index af1bacc..0c6f5ea 100644 --- a/.mokogitea/workflows/ci-joomla.yml +++ b/.mokogitea/workflows/ci-joomla.yml @@ -35,25 +35,32 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + 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 php -v && composer --version - - name: Clone MokoStandards + - name: Setup moko-platform 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_TOKEN: ${{ secrets.GA_TOKEN || github.token }} MOKO_CLONE_HOST: ${{ secrets.GA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }} run: | - git clone --depth 1 --branch main --quiet \ - "https://${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ - /tmp/mokostandards-api + if [ -d "/tmp/moko-platform" ] || [ -d "/opt/moko-platform" ]; then + echo "moko-platform already available on runner — skipping clone" + else + 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 || echo "moko-platform clone skipped — continuing without it" + fi - name: Install dependencies env: - COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}' + COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || secrets.GA_TOKEN || github.token }}"}}' run: | if [ -f "composer.json" ]; then composer install \ @@ -124,8 +131,8 @@ jobs: echo "Manifest is well-formed XML." >> $GITHUB_STEP_SUMMARY fi - # Check required tags: name, version, author, namespace (Joomla 5+) - for TAG in name version author namespace; do + # Check required tags: name, version, author + for TAG in name version author; do if ! grep -q "<${TAG}>" "$MANIFEST" 2>/dev/null; then echo "Missing required tag: \`<${TAG}>\`" >> $GITHUB_STEP_SUMMARY ERRORS=$((ERRORS + 1)) @@ -133,6 +140,19 @@ jobs: echo "Found required tag: \`<${TAG}>\`" >> $GITHUB_STEP_SUMMARY fi done + + # Namespace is required for components/plugins but not packages + EXT_TYPE=$(grep -oP ']*\btype="\K[^"]+' "$MANIFEST" | head -1) + if [ "$EXT_TYPE" != "package" ]; then + if ! grep -q "/dev/null; then + echo "Missing required tag: \`\` (required for Joomla 5+ ${EXT_TYPE} extensions)" >> $GITHUB_STEP_SUMMARY + ERRORS=$((ERRORS + 1)) + else + echo "Found required tag: \`\`" >> $GITHUB_STEP_SUMMARY + fi + else + echo "Package extension — \`\` not required." >> $GITHUB_STEP_SUMMARY + fi fi if [ "${ERRORS}" -gt 0 ]; then @@ -232,7 +252,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@v4 - name: Validate release readiness run: | @@ -338,15 +358,19 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@v4 - name: Setup PHP ${{ matrix.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 php -v && composer --version - name: Install dependencies env: - COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}' + COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || secrets.GA_TOKEN || github.token }}"}}' run: | if [ -f "composer.json" ]; then composer install \ @@ -384,14 +408,19 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@v4 - name: Setup PHP - run: php -v && composer --version + 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 + php -v && composer --version - name: Install dependencies env: - COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}' + COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || secrets.GA_TOKEN || github.token }}"}}' run: | if [ -f "composer.json" ]; then composer install --no-interaction --prefer-dist --optimize-autoloader @@ -448,3 +477,24 @@ jobs: echo '```' >> $GITHUB_STEP_SUMMARY fi exit $EXIT + + pre-release: + name: Build RC Pre-Release + runs-on: ubuntu-latest + needs: [lint-and-validate, test] + if: github.event_name == 'pull_request' + + steps: + - name: Trigger pre-release build + env: + GA_TOKEN: ${{ secrets.GA_TOKEN }} + REPO: ${{ github.repository }} + BRANCH: ${{ github.head_ref }} + run: | + curl -s -X POST \ + "${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${REPO}/actions/workflows/pre-release.yml/dispatches" \ + -H "Authorization: token ${GA_TOKEN}" \ + -H "Content-Type: application/json" \ + -d "{\"ref\":\"${BRANCH}\",\"inputs\":{\"stability\":\"release-candidate\"}}" + echo "### Pre-Release" >> $GITHUB_STEP_SUMMARY + echo "Triggered RC build on branch \`${BRANCH}\`" >> $GITHUB_STEP_SUMMARY -- 2.52.0 From cabb6940e0118f87d356dabd69eda3e5a4825b3d Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 26 May 2026 04:13:21 +0000 Subject: [PATCH 10/16] sync: update pr-check.yml from MokoJoomGallery [skip ci] --- .mokogitea/workflows/pr-check.yml | 43 +++++++++++++------------------ 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/.mokogitea/workflows/pr-check.yml b/.mokogitea/workflows/pr-check.yml index 7ebc767..3e436cf 100644 --- a/.mokogitea/workflows/pr-check.yml +++ b/.mokogitea/workflows/pr-check.yml @@ -193,31 +193,24 @@ jobs: echo "Source: ${FILE_COUNT} files" [ "$FILE_COUNT" -gt 0 ] || { echo "::error::Source directory is empty"; exit 1; } - # ── Changelog Gate ──────────────────────────────────────────────────── - changelog: - name: Changelog Updated + # ── Pre-Release RC Build ───────────────────────────────────────────────── + pre-release: + name: Build RC Package runs-on: ubuntu-latest - if: github.base_ref == 'main' + needs: [branch-policy, validate] + steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Check CHANGELOG.md was updated + - name: Trigger RC pre-release + env: + GA_TOKEN: ${{ secrets.GA_TOKEN }} + REPO: ${{ github.repository }} + BRANCH: ${{ github.head_ref }} + GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} run: | - BASE="${{ github.event.pull_request.base.sha }}" - HEAD="${{ github.event.pull_request.head.sha }}" - - if git diff --name-only "$BASE" "$HEAD" | grep -q "^CHANGELOG.md$"; then - echo "CHANGELOG.md updated" - else - # Allow [skip changelog] in PR title or body - PR_TITLE="${{ github.event.pull_request.title }}" - PR_BODY="${{ github.event.pull_request.body }}" - if echo "$PR_TITLE $PR_BODY" | grep -qi "\[skip changelog\]"; then - echo "::warning::Changelog skip requested via [skip changelog]" - exit 0 - fi - echo "::error::CHANGELOG.md must be updated before merging to main. Add [skip changelog] to the PR title to bypass." - exit 1 - fi + curl -s -X POST \ + "${GITEA_URL}/api/v1/repos/${REPO}/actions/workflows/pre-release.yml/dispatches" \ + -H "Authorization: token ${GA_TOKEN}" \ + -H "Content-Type: application/json" \ + -d "{\"ref\":\"${BRANCH}\",\"inputs\":{\"stability\":\"release-candidate\"}}" + echo "### Pre-Release" >> $GITHUB_STEP_SUMMARY + echo "Triggered RC build on branch \`${BRANCH}\`" >> $GITHUB_STEP_SUMMARY -- 2.52.0 From 2d662d3453754a44670c9caaf5fc0157db19fd5b Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 26 May 2026 04:13:21 +0000 Subject: [PATCH 11/16] sync: update repo-health.yml from MokoJoomGallery [skip ci] --- .mokogitea/workflows/repo-health.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.mokogitea/workflows/repo-health.yml b/.mokogitea/workflows/repo-health.yml index e5e1c73..87bc666 100644 --- a/.mokogitea/workflows/repo-health.yml +++ b/.mokogitea/workflows/repo-health.yml @@ -153,7 +153,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -248,7 +248,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@v4 with: fetch-depth: 0 @@ -362,7 +362,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@v4 with: fetch-depth: 0 -- 2.52.0 From 60fbd66feeb7328b1e3c4d2e88f24e5e3b5773f1 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 26 May 2026 04:13:22 +0000 Subject: [PATCH 12/16] sync: update update-server.yml from MokoJoomGallery [skip ci] --- .mokogitea/workflows/update-server.yml | 34 ++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/.mokogitea/workflows/update-server.yml b/.mokogitea/workflows/update-server.yml index 7c1a341..d7087f6 100644 --- a/.mokogitea/workflows/update-server.yml +++ b/.mokogitea/workflows/update-server.yml @@ -73,12 +73,12 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + uses: actions/checkout@v4 with: token: ${{ secrets.GA_TOKEN }} fetch-depth: 0 - - name: Setup MokoStandards tools + - name: Setup moko-platform tools env: MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }} MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting @@ -87,11 +87,15 @@ jobs: 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 - git clone --depth 1 --branch main --quiet \ - "https://${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.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 + if [ -d "/tmp/moko-platform" ]; then + echo "moko-platform already available — skipping clone" + else + 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 + fi + 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 - name: Generate updates.xml entry @@ -100,14 +104,14 @@ jobs: BRANCH="${{ github.ref_name }}" REPO="${{ github.repository }}" API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - VERSION=$(php /tmp/mokostandards-api/cli/version_read.php --path . 2>/dev/null || echo "0.0.0") + VERSION=$(php /tmp/moko-platform/cli/version_read.php --path . 2>/dev/null || echo "0.0.0") # Auto-bump patch on all branches (dev, alpha, beta, rc) git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" git config --local user.name "gitea-actions[bot]" - BUMPED=$(php /tmp/mokostandards-api/cli/version_bump.php --path . 2>/dev/null || true) + BUMPED=$(php /tmp/moko-platform/cli/version_bump.php --path . 2>/dev/null || true) if [ -n "$BUMPED" ]; then - VERSION=$(php /tmp/mokostandards-api/cli/version_read.php --path . 2>/dev/null || echo "$VERSION") + VERSION=$(php /tmp/moko-platform/cli/version_read.php --path . 2>/dev/null || echo "$VERSION") git add -A git commit -m "chore(version): auto-bump patch ${VERSION} [skip ci]" \ --author="gitea-actions[bot] " 2>/dev/null || true @@ -442,11 +446,11 @@ jobs: printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json fi - 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 --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json - elif [ -f "/tmp/mokostandards-api/deploy/deploy-sftp.php" ]; then - php /tmp/mokostandards-api/deploy/deploy-sftp.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json + PLATFORM=$(php /tmp/moko-platform/cli/platform_detect.php --path . 2>/dev/null || true) + if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/moko-platform/deploy/deploy-joomla.php" ]; then + php /tmp/moko-platform/deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json + elif [ -f "/tmp/moko-platform/deploy/deploy-sftp.php" ]; then + php /tmp/moko-platform/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 -- 2.52.0 From 2357aacc212ec9312fbe802df7b36b5141ce841e Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 26 May 2026 04:13:22 +0000 Subject: [PATCH 13/16] sync: update pre-release.yml from MokoJoomGallery [skip ci] --- .mokogitea/workflows/pre-release.yml | 211 ++++++++++++++++----------- 1 file changed, 123 insertions(+), 88 deletions(-) diff --git a/.mokogitea/workflows/pre-release.yml b/.mokogitea/workflows/pre-release.yml index fa8228d..46cce5d 100644 --- a/.mokogitea/workflows/pre-release.yml +++ b/.mokogitea/workflows/pre-release.yml @@ -51,9 +51,8 @@ jobs: MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN }}"}}' 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 >/dev/null 2>&1 + 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 git clone --depth 1 --branch main --quiet \ "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ @@ -61,24 +60,19 @@ jobs: cd /tmp/moko-platform-api composer install --no-dev --no-interaction --quiet - - name: Setup moko-platform tools - env: - MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }} - MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting - run: | - git clone --depth 1 --branch main --quiet "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" /tmp/moko-platform-api - - name: Detect platform id: platform run: | - php /tmp/moko-platform-api/cli/manifest_read.php --path . --github-output + PLATFORM=$(sed -n 's/.*\([^<]*\)<\/platform>.*/\1/p' .mokogitea/manifest.xml 2>/dev/null | head -1 | tr -d '[:space:]') + [ -z "$PLATFORM" ] && PLATFORM=$(sed -n 's/.*\([^<]*\)<\/platform>.*/\1/p' .gitea/manifest.xml 2>/dev/null | head -1 | tr -d '[:space:]') + [ -z "$PLATFORM" ] && PLATFORM="generic" + echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT" - name: Resolve metadata and bump version id: meta run: | CLI="/tmp/moko-platform-api/cli" STABILITY="${{ inputs.stability }}" - MOKO_API="/tmp/moko-platform-api/cli" case "$STABILITY" in development) SUFFIX="-dev"; TAG="development" ;; @@ -87,14 +81,16 @@ jobs: release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;; esac - # Bump patch version - BUMP_OUTPUT=$(php ${MOKO_API}/version_bump.php --path .) - VERSION=$(echo "$BUMP_OUTPUT" | grep -oP '\d{2}\.\d{2}\.\d{2}$' || true) - [ -z "$VERSION" ] && VERSION=$(php ${MOKO_API}/version_read.php --path .) - echo "Version: ${VERSION}" + # Bump patch version via CLI + CURRENT=$(php $CLI/version_read.php --path . 2>/dev/null) + [ -z "$CURRENT" ] && CURRENT="00.00.00" + php $CLI/version_bump.php --path . + VERSION=$(php $CLI/version_read.php --path . 2>/dev/null) + echo "Bumping: ${CURRENT} → ${VERSION} (patch)" - # Update platform-specific manifest - php ${MOKO_API}/version_set_platform.php --path . --version "${VERSION}" + # Set platform-specific version with stability suffix + php $CLI/version_set_platform.php \ + --path . --version "$VERSION" --stability "$STABILITY" --branch "${{ github.ref_name }}" # Commit version bump git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" @@ -102,72 +98,80 @@ jobs: git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" git add -A git diff --cached --quiet || { - git commit -m "chore(version): bump to ${VERSION} [skip ci]" + git commit -m "chore(version): bump ${CURRENT} → ${VERSION}${SUFFIX} [skip ci]" git push origin HEAD 2>&1 } - # Detect element from Joomla/Dolibarr manifest - PLATFORM="${{ steps.platform.outputs.platform }}" - EXT_ELEMENT=$(php ${MOKO_API}/manifest_read.php --path . --field name 2>/dev/null | tr -d ' ' | tr '[:upper:]' '[:lower:]' || true) - # For Joomla, prefer tag - if [ "$PLATFORM" = "joomla" ]; then - MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1 || true) - if [ -n "$MANIFEST" ]; then - ELEM=$(grep -oP "\K[^<]+" "$MANIFEST" 2>/dev/null | head -1) - [ -n "$ELEM" ] && EXT_ELEMENT="$ELEM" - fi - fi - [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') - - ZIP_NAME="${EXT_ELEMENT}-${VERSION}${SUFFIX}.zip" - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT" echo "tag=${TAG}" >> "$GITHUB_OUTPUT" - echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT" - echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT" - - echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ===" - name: Build package - id: zip + id: package run: | VERSION="${{ steps.meta.outputs.version }}" SUFFIX="${{ steps.meta.outputs.suffix }}" PLATFORM="${{ steps.platform.outputs.platform }}" + SRC_DIR="src" + [ ! -d "$SRC_DIR" ] && SRC_DIR="htdocs" + BUILD_DIR="build" + mkdir -p "$BUILD_DIR" - if [ "$PLATFORM" = "joomla" ]; then - php /tmp/moko-platform-api/cli/joomla_build.php --path . --version "${VERSION}" --suffix "${SUFFIX}" --output build --github-output + # Detect extension type from manifest + PKG_MANIFEST=$(find "$SRC_DIR" -maxdepth 1 -name "pkg_*.xml" | head -1) + + if [ -n "$PKG_MANIFEST" ] && grep -q 'type="package"' "$PKG_MANIFEST" 2>/dev/null; then + # --- Joomla Package Extension: zip sub-extensions individually --- + EXT_NAME=$(grep -oP '\K[^<]+' "$PKG_MANIFEST" | head -1) + [ -z "$EXT_NAME" ] && EXT_NAME=$(basename "$PKG_MANIFEST" .xml | sed 's/^pkg_//') + PACKAGE_NAME="pkg_${EXT_NAME}-${VERSION}${SUFFIX}.zip" + + mkdir -p "$BUILD_DIR/packages" + ABS_BUILD="$(cd "$BUILD_DIR" && pwd)" + + # Zip each sub-extension + for EXT_DIR in "$SRC_DIR"/packages/*/; do + DIR_NAME=$(basename "$EXT_DIR") + [ "$DIR_NAME" = "index.html" ] && continue + [ ! -d "$EXT_DIR" ] && continue + (cd "$EXT_DIR" && zip -qr "$ABS_BUILD/packages/${DIR_NAME}.zip" . -x "*/index.html") + done + + # Assemble outer package + cp "$PKG_MANIFEST" "$BUILD_DIR/" + [ -f "$SRC_DIR/script.php" ] && cp "$SRC_DIR/script.php" "$BUILD_DIR/" + [ -d "$SRC_DIR/language" ] && cp -r "$SRC_DIR/language" "$BUILD_DIR/" + + (cd "$BUILD_DIR" && zip -qr "../$PACKAGE_NAME" .) + mv "$PACKAGE_NAME" "$BUILD_DIR/" else - # Generic build: zip src/ directory - SOURCE_DIR="src" - [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" - [ ! -d "$SOURCE_DIR" ] && { echo "::error::No src/ or htdocs/"; exit 1; } - EXT_ELEMENT="${{ steps.meta.outputs.ext_element }}" - ZIP_NAME="${EXT_ELEMENT}-${VERSION}${SUFFIX}.zip" - mkdir -p build - cd "$SOURCE_DIR" && zip -r "../build/${ZIP_NAME}" . && cd .. - SHA256=$(sha256sum "build/${ZIP_NAME}" | cut -d' ' -f1) - echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT" - echo "zip_path=build/${ZIP_NAME}" >> "$GITHUB_OUTPUT" - echo "sha256=${SHA256}" >> "$GITHUB_OUTPUT" + # --- Single extension: use moko-platform CLI --- + CLI="/tmp/moko-platform-api/cli" + php $CLI/package_build.php \ + --path . \ + --version "${VERSION}${SUFFIX}" \ + --output-dir "$BUILD_DIR" \ + --github-output && exit 0 + + # Fallback: simple zip of source + EXT_NAME=$(grep -oP '\K[^<]+' $(find "$SRC_DIR" -maxdepth 2 -name "*.xml" | head -1) | head -1 | tr -d ' ') + PACKAGE_NAME="${EXT_NAME}-${VERSION}${SUFFIX}.zip" + (cd "$SRC_DIR" && zip -qr "../$BUILD_DIR/$PACKAGE_NAME" . -x "*/index.html") fi - - name: Create or replace Gitea release - id: release - continue-on-error: true - run: | - TAG="${{ steps.meta.outputs.tag }}" - VERSION="${{ steps.meta.outputs.version }}" - SUFFIX="${{ steps.meta.outputs.suffix }}" - - # Build ZIP + tar.gz via CLI (handles type prefix, excludes, multi-extension packages) - php $CLI/package_build.php \ - --path . \ - --version "${VERSION}${SUFFIX}" \ - --output-dir build \ - --github-output + # Set outputs + ZIP_PATH="$BUILD_DIR/$PACKAGE_NAME" + if [ -f "$ZIP_PATH" ]; then + SHA256=$(sha256sum "$ZIP_PATH" | cut -d' ' -f1) + echo "zip_path=$ZIP_PATH" >> "$GITHUB_OUTPUT" + echo "sha256_zip=$SHA256" >> "$GITHUB_OUTPUT" + echo "ext_element=pkg_${EXT_NAME}" >> "$GITHUB_OUTPUT" + echo "ZIP: $PACKAGE_NAME ($(stat -c%s "$ZIP_PATH") bytes, sha256: $SHA256)" + else + echo "::error::Package build failed — no ZIP produced" + exit 1 + fi - name: Create release and upload run: | @@ -176,12 +180,9 @@ jobs: SUFFIX="${{ steps.meta.outputs.suffix }}" TAG="${{ steps.meta.outputs.tag }}" STABILITY="${{ steps.meta.outputs.stability }}" - SHA256="${{ steps.zip.outputs.sha256 }}" - ZIP_NAME="${{ steps.zip.outputs.zip_name }}" - EXT_ELEMENT="${{ steps.meta.outputs.ext_element }}" - TOKEN="${{ secrets.GA_TOKEN }}" - API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - BRANCH=$(git branch --show-current) + API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + EXT_ELEMENT="${{ steps.package.outputs.ext_element }}" + [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') SHA256="${{ steps.package.outputs.sha256_zip }}" ZIP_PATH="${{ steps.package.outputs.zip_path }}" @@ -197,33 +198,67 @@ jobs: --token "${{ secrets.GA_TOKEN }}" \ --api-base "$API_BASE" - echo "release_id=${RELEASE_ID}" >> "$GITHUB_OUTPUT" + # Upload assets + FILES="${ZIP_PATH}" + [ -f "$TAR_PATH" ] && FILES="${FILES},${TAR_PATH}" + php $CLI/release_manage.php \ + --action upload \ + --tag "$TAG" \ + --files "$FILES" \ + --token "${{ secrets.GA_TOKEN }}" \ + --api-base "$API_BASE" - # Upload ZIP - curl -sS -X POST -H "Authorization: token ${TOKEN}" \ - -H "Content-Type: application/octet-stream" \ - "${API}/releases/${RELEASE_ID}/assets?name=${ZIP_NAME}" \ - --data-binary "@${{ steps.zip.outputs.zip_path }}" - - echo "Released: ${EXT_ELEMENT} ${VERSION} (${STABILITY})" - - - name: "Update updates.xml" + - name: Update updates.xml if: steps.platform.outputs.platform == 'joomla' run: | + CLI="/tmp/moko-platform-api/cli" VERSION="${{ steps.meta.outputs.version }}" STABILITY="${{ steps.meta.outputs.stability }}" - SHA256="${{ steps.zip.outputs.sha256 }}" - php /tmp/moko-platform-api/cli/updates_xml_build.php --path . --version "$VERSION" --stability "$STABILITY" --sha "$SHA256" --gitea-url "$GITEA_URL" --org "$GITEA_ORG" --repo "$GITEA_REPO" + SHA256="${{ steps.package.outputs.sha256_zip }}" + + # Map stability names + case "$STABILITY" in + release-candidate) CLI_STABILITY="rc" ;; + *) CLI_STABILITY="$STABILITY" ;; + esac + + # Generate updates.xml with stability-suffixed versions + php $CLI/updates_xml_build.php \ + --path . \ + --version "$VERSION" \ + --stability "$CLI_STABILITY" \ + --sha "$SHA256" \ + --gitea-url "${GITEA_URL}" \ + --org "${GITEA_ORG}" \ + --repo "${GITEA_REPO}" + + # Commit and push if ! git diff --quiet updates.xml 2>/dev/null; then git add updates.xml - git commit -m "chore: update $STABILITY channel $VERSION [skip ci]" + git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]" git push origin HEAD 2>&1 || echo "WARNING: push failed" fi - name: "Sync updates.xml to all branches" if: steps.platform.outputs.platform == 'joomla' run: | - php /tmp/moko-platform-api/cli/updates_xml_sync.php --path . --current "${{ github.ref_name }}" --branches main,dev --version "${{ steps.meta.outputs.version }}" --token "${{ secrets.GA_TOKEN }}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" --gitea-url "${GITEA_URL}" + CURRENT_BRANCH="${{ github.ref_name }}" + git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" + git config --local user.name "gitea-actions[bot]" + + for BRANCH in main dev; do + [ "$BRANCH" = "$CURRENT_BRANCH" ] && continue + echo "Syncing updates.xml → ${BRANCH}" + git fetch origin "${BRANCH}" 2>/dev/null || continue + git checkout "origin/${BRANCH}" -- . 2>/dev/null || continue + git checkout "${CURRENT_BRANCH}" -- updates.xml + if ! git diff --quiet updates.xml 2>/dev/null; then + git add updates.xml + git commit -m "chore: sync updates.xml from ${CURRENT_BRANCH} [skip ci]" + git push origin HEAD:refs/heads/${BRANCH} 2>&1 || echo "WARNING: push to ${BRANCH} failed" + fi + git checkout "${CURRENT_BRANCH}" 2>/dev/null + done - name: "Delete lesser pre-release channels (cascade)" continue-on-error: true -- 2.52.0 From 8c80907b3d3471d0baa4878dd7b58d0ab9c438fe Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 26 May 2026 04:13:23 +0000 Subject: [PATCH 14/16] sync: update Makefile from MokoJoomGallery [skip ci] --- Makefile | 241 ++++++++----------------------------------------------- 1 file changed, 33 insertions(+), 208 deletions(-) diff --git a/Makefile b/Makefile index a30e5e0..3ebe6a2 100644 --- a/Makefile +++ b/Makefile @@ -2,18 +2,15 @@ # Copyright (C) 2026 Moko Consulting # SPDX-License-Identifier: GPL-3.0-or-later # -# This is a reference Makefile for building Joomla extensions. -# Copy this to your repository root as "Makefile" and customize as needed. -# -# Supports: Modules, Plugins, Components, Packages, Templates +# MokoJoomGallery — Photo gallery management for Joomla # ============================================================================== # CONFIGURATION - Customize these for your extension # ============================================================================== # Extension Configuration -EXTENSION_NAME := mokoexample -EXTENSION_TYPE := module +EXTENSION_NAME := mokojoomgallery +EXTENSION_TYPE := package # Options: module, plugin, component, package, template EXTENSION_VERSION := 1.0.0 @@ -26,7 +23,7 @@ PLUGIN_GROUP := system # Options: system, content, user, authentication, etc. # Directories -SRC_DIR := . +SRC_DIR := src BUILD_DIR := build DIST_DIR := dist DOCS_DIR := docs @@ -69,11 +66,6 @@ help: ## Show this help message @echo "$(COLOR_GREEN)Available targets:$(COLOR_RESET)" @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " $(COLOR_BLUE)%-20s$(COLOR_RESET) %s\n", $$1, $$2}' @echo "" - @echo "$(COLOR_YELLOW)Quick Start:$(COLOR_RESET)" - @echo " 1. make install-deps # Install dependencies" - @echo " 2. make build # Build extension package" - @echo " 3. make test # Run tests" - @echo "" .PHONY: install-deps install-deps: ## Install all dependencies (Composer + npm) @@ -82,22 +74,6 @@ install-deps: ## Install all dependencies (Composer + npm) $(COMPOSER) install; \ echo "$(COLOR_GREEN)✓ Composer dependencies installed$(COLOR_RESET)"; \ fi - @if [ -f "package.json" ]; then \ - $(NPM) install; \ - echo "$(COLOR_GREEN)✓ npm dependencies installed$(COLOR_RESET)"; \ - fi - -.PHONY: update-deps -update-deps: ## Update all dependencies - @echo "$(COLOR_BLUE)Updating dependencies...$(COLOR_RESET)" - @if [ -f "composer.json" ]; then \ - $(COMPOSER) update; \ - echo "$(COLOR_GREEN)✓ Composer dependencies updated$(COLOR_RESET)"; \ - fi - @if [ -f "package.json" ]; then \ - $(NPM) update; \ - echo "$(COLOR_GREEN)✓ npm dependencies updated$(COLOR_RESET)"; \ - fi .PHONY: lint lint: ## Run PHP linter (syntax check) @@ -115,39 +91,10 @@ phpcs: ## Run PHP CodeSniffer (Joomla standards) echo "$(COLOR_YELLOW)⚠ PHP CodeSniffer not installed. Run: make install-deps$(COLOR_RESET)"; \ fi -.PHONY: phpcbf -phpcbf: ## Fix coding standards automatically - @echo "$(COLOR_BLUE)Running PHP Code Beautifier...$(COLOR_RESET)" - @if [ -f "$(PHPCBF)" ]; then \ - $(PHPCBF) --standard=$(PHPCS_STANDARD) --extensions=php --ignore=vendor,node_modules,$(BUILD_DIR) .; \ - echo "$(COLOR_GREEN)✓ Code formatting applied$(COLOR_RESET)"; \ - else \ - echo "$(COLOR_YELLOW)⚠ PHP Code Beautifier not installed. Run: make install-deps$(COLOR_RESET)"; \ - fi - .PHONY: validate validate: lint phpcs ## Run all validation checks @echo "$(COLOR_GREEN)✓ All validation checks passed$(COLOR_RESET)" -.PHONY: test -test: ## Run PHPUnit tests - @echo "$(COLOR_BLUE)Running tests...$(COLOR_RESET)" - @if [ -f "$(PHPUNIT)" ] && [ -f "phpunit.xml" ]; then \ - $(PHPUNIT); \ - else \ - echo "$(COLOR_YELLOW)⚠ PHPUnit not configured$(COLOR_RESET)"; \ - fi - -.PHONY: test-coverage -test-coverage: ## Run tests with coverage report - @echo "$(COLOR_BLUE)Running tests with coverage...$(COLOR_RESET)" - @if [ -f "$(PHPUNIT)" ] && [ -f "phpunit.xml" ]; then \ - $(PHPUNIT) --coverage-html $(BUILD_DIR)/coverage; \ - echo "$(COLOR_GREEN)✓ Coverage report: $(BUILD_DIR)/coverage/index.html$(COLOR_RESET)"; \ - else \ - echo "$(COLOR_YELLOW)⚠ PHPUnit not configured$(COLOR_RESET)"; \ - fi - .PHONY: clean clean: ## Clean build artifacts @echo "$(COLOR_BLUE)Cleaning build artifacts...$(COLOR_RESET)" @@ -169,124 +116,39 @@ minify: ## Minify CSS/JS assets fi .PHONY: build -build: clean validate minify ## Build extension package - @echo "$(COLOR_BLUE)Building Joomla extension package...$(COLOR_RESET)" - @mkdir -p $(DIST_DIR) $(BUILD_DIR) - - # Determine package prefix based on extension type - @case "$(EXTENSION_TYPE)" in \ - module) \ - PACKAGE_PREFIX="mod_$(EXTENSION_NAME)"; \ - BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \ - ;; \ - plugin) \ - PACKAGE_PREFIX="plg_$(PLUGIN_GROUP)_$(EXTENSION_NAME)"; \ - BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \ - ;; \ - component) \ - PACKAGE_PREFIX="com_$(EXTENSION_NAME)"; \ - BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \ - ;; \ - package) \ - PACKAGE_PREFIX="pkg_$(EXTENSION_NAME)"; \ - BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \ - ;; \ - template) \ - PACKAGE_PREFIX="tpl_$(EXTENSION_NAME)"; \ - BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \ - ;; \ - *) \ - echo "$(COLOR_RED)✗ Unknown extension type: $(EXTENSION_TYPE)$(COLOR_RESET)"; \ - exit 1; \ - ;; \ - esac; \ - \ - mkdir -p "$$BUILD_TARGET"; \ - \ - echo "Building $$PACKAGE_PREFIX..."; \ - \ - rsync -av --progress \ - --exclude='$(BUILD_DIR)' \ - --exclude='$(DIST_DIR)' \ - --exclude='.git*' \ - --exclude='vendor/' \ - --exclude='node_modules/' \ - --exclude='tests/' \ - --exclude='Makefile' \ - --exclude='composer.json' \ - --exclude='composer.lock' \ - --exclude='package.json' \ - --exclude='package-lock.json' \ - --exclude='phpunit.xml' \ - --exclude='*.md' \ - --exclude='.editorconfig' \ - . "$$BUILD_TARGET/"; \ - \ - cd $(BUILD_DIR) && $(ZIP) -r "../$(DIST_DIR)/$${PACKAGE_PREFIX}-$(EXTENSION_VERSION).zip" "$${PACKAGE_PREFIX}"; \ - \ - echo "$(COLOR_GREEN)✓ Package created: $(DIST_DIR)/$${PACKAGE_PREFIX}-$(EXTENSION_VERSION).zip$(COLOR_RESET)" +build: clean minify ## Build extension package + @echo "$(COLOR_BLUE)Building Joomla package extension...$(COLOR_RESET)" + @mkdir -p $(DIST_DIR) $(BUILD_DIR)/packages + + @# --- Build each sub-extension as a separate ZIP --- + @for EXT_DIR in $(SRC_DIR)/packages/*/; do \ + EXT_NAME=$$(basename "$$EXT_DIR"); \ + [ "$$EXT_NAME" = "index.html" ] && continue; \ + echo " Packaging $$EXT_NAME..."; \ + cd "$$EXT_DIR" && $(ZIP) -r "$(CURDIR)/$(BUILD_DIR)/packages/$${EXT_NAME}.zip" . \ + -x "*.git*" -x "*/index.html" 2>/dev/null; \ + cd "$(CURDIR)"; \ + done + + @# --- Build the outer package ZIP --- + @echo " Assembling pkg_$(EXTENSION_NAME)..." + @cp $(SRC_DIR)/pkg_mokojoomgallery.xml $(BUILD_DIR)/pkg_mokojoomgallery.xml + @cp $(SRC_DIR)/script.php $(BUILD_DIR)/script.php + @[ -d "$(SRC_DIR)/language" ] && cp -r $(SRC_DIR)/language $(BUILD_DIR)/language || true + @cd $(BUILD_DIR) && $(ZIP) -r "$(CURDIR)/$(DIST_DIR)/pkg_$(EXTENSION_NAME)-$(EXTENSION_VERSION).zip" \ + pkg_mokojoomgallery.xml script.php language/ packages/ + + @echo "$(COLOR_GREEN)✓ Package created: $(DIST_DIR)/pkg_$(EXTENSION_NAME)-$(EXTENSION_VERSION).zip$(COLOR_RESET)" + @echo " Contents:" + @unzip -l "$(DIST_DIR)/pkg_$(EXTENSION_NAME)-$(EXTENSION_VERSION).zip" | tail -n +4 | head -20 .PHONY: package package: build ## Alias for build @echo "$(COLOR_GREEN)✓ Package ready for distribution$(COLOR_RESET)" -.PHONY: install-local -install-local: build ## Install to local Joomla (upload via admin) - @echo "$(COLOR_BLUE)Package ready for installation$(COLOR_RESET)" - @case "$(EXTENSION_TYPE)" in \ - module) PACKAGE="mod_$(EXTENSION_NAME)";; \ - plugin) PACKAGE="plg_$(PLUGIN_GROUP)_$(EXTENSION_NAME)";; \ - component) PACKAGE="com_$(EXTENSION_NAME)";; \ - package) PACKAGE="pkg_$(EXTENSION_NAME)";; \ - template) PACKAGE="tpl_$(EXTENSION_NAME)";; \ - esac; \ - echo "$(COLOR_YELLOW)Upload $(DIST_DIR)/$${PACKAGE}-$(EXTENSION_VERSION).zip via Joomla Administrator$(COLOR_RESET)"; \ - echo "Admin URL: $(JOOMLA_ROOT) → Extensions → Install" - -.PHONY: dev-install -dev-install: ## Create symlink for development (Joomla 4+) - @echo "$(COLOR_BLUE)Creating development symlink...$(COLOR_RESET)" - @if [ ! -d "$(JOOMLA_ROOT)" ]; then \ - echo "$(COLOR_RED)✗ Joomla root not found at $(JOOMLA_ROOT)$(COLOR_RESET)"; \ - echo "Update JOOMLA_ROOT in Makefile"; \ - exit 1; \ - fi - - @case "$(EXTENSION_TYPE)" in \ - module) \ - if [ "$(MODULE_TYPE)" = "admin" ]; then \ - TARGET="$(JOOMLA_ROOT)/administrator/modules/mod_$(EXTENSION_NAME)"; \ - else \ - TARGET="$(JOOMLA_ROOT)/modules/mod_$(EXTENSION_NAME)"; \ - fi; \ - ;; \ - plugin) \ - TARGET="$(JOOMLA_ROOT)/plugins/$(PLUGIN_GROUP)/$(EXTENSION_NAME)"; \ - ;; \ - component) \ - echo "$(COLOR_YELLOW)⚠ Components require complex symlink setup$(COLOR_RESET)"; \ - echo "Manual setup recommended for component development"; \ - exit 1; \ - ;; \ - *) \ - echo "$(COLOR_RED)✗ dev-install not supported for $(EXTENSION_TYPE)$(COLOR_RESET)"; \ - exit 1; \ - ;; \ - esac; \ - \ - rm -rf "$$TARGET"; \ - ln -s "$(PWD)" "$$TARGET"; \ - echo "$(COLOR_GREEN)✓ Development symlink created at $$TARGET$(COLOR_RESET)" - -.PHONY: watch -watch: ## Watch for changes and rebuild - @echo "$(COLOR_BLUE)Watching for changes...$(COLOR_RESET)" - @echo "$(COLOR_YELLOW)Press Ctrl+C to stop$(COLOR_RESET)" - @while true; do \ - inotifywait -r -e modify,create,delete --exclude '($(BUILD_DIR)|$(DIST_DIR)|vendor|node_modules)' . 2>/dev/null || \ - (echo "$(COLOR_YELLOW)⚠ inotifywait not installed. Install: apt-get install inotify-tools$(COLOR_RESET)" && sleep 5); \ - make build; \ - done +.PHONY: release +release: validate build ## Create a release (validate + build) + @echo "$(COLOR_GREEN)✓ Release package ready$(COLOR_RESET)" .PHONY: version version: ## Display version information @@ -294,40 +156,6 @@ version: ## Display version information @echo " Name: $(EXTENSION_NAME)" @echo " Type: $(EXTENSION_TYPE)" @echo " Version: $(EXTENSION_VERSION)" - @if [ "$(EXTENSION_TYPE)" = "module" ]; then \ - echo " Module: $(MODULE_TYPE)"; \ - fi - @if [ "$(EXTENSION_TYPE)" = "plugin" ]; then \ - echo " Group: $(PLUGIN_GROUP)"; \ - fi - -.PHONY: docs -docs: ## Generate documentation - @echo "$(COLOR_BLUE)Generating documentation...$(COLOR_RESET)" - @mkdir -p $(DOCS_DIR) - @echo "$(COLOR_YELLOW)⚠ Documentation generation not configured$(COLOR_RESET)" - @echo "Consider adding phpDocumentor or similar" - -.PHONY: release -release: validate test build ## Create a release (validate + test + build) - @echo "$(COLOR_GREEN)✓ Release package ready$(COLOR_RESET)" - @echo "" - @echo "$(COLOR_BLUE)Release Checklist:$(COLOR_RESET)" - @echo " [ ] Update CHANGELOG.md" - @echo " [ ] Update version in XML manifest" - @echo " [ ] Test installation in clean Joomla" - @echo " [ ] Tag release in git: git tag v$(EXTENSION_VERSION)" - @echo " [ ] Push tags: git push --tags" - @echo " [ ] Create GitHub release" - @echo "" - @case "$(EXTENSION_TYPE)" in \ - module) PACKAGE="mod_$(EXTENSION_NAME)";; \ - plugin) PACKAGE="plg_$(PLUGIN_GROUP)_$(EXTENSION_NAME)";; \ - component) PACKAGE="com_$(EXTENSION_NAME)";; \ - package) PACKAGE="pkg_$(EXTENSION_NAME)";; \ - template) PACKAGE="tpl_$(EXTENSION_NAME)";; \ - esac; \ - echo "$(COLOR_GREEN)Package: $(DIST_DIR)/$${PACKAGE}-$(EXTENSION_VERSION).zip$(COLOR_RESET)" .PHONY: security-check security-check: ## Run security checks on dependencies @@ -335,12 +163,9 @@ security-check: ## Run security checks on dependencies @if [ -f "composer.json" ]; then \ $(COMPOSER) audit || echo "$(COLOR_YELLOW)⚠ Vulnerabilities found$(COLOR_RESET)"; \ fi - @if [ -f "package.json" ]; then \ - $(NPM) audit || echo "$(COLOR_YELLOW)⚠ Vulnerabilities found$(COLOR_RESET)"; \ - fi .PHONY: all -all: install-deps validate test build ## Run complete build pipeline +all: install-deps validate build ## Run complete build pipeline @echo "$(COLOR_GREEN)✓ Complete build pipeline finished$(COLOR_RESET)" # Default target -- 2.52.0 From 32fea2a7c69a6e4c649286f2fa714f0f7711ebb1 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 26 May 2026 04:13:23 +0000 Subject: [PATCH 15/16] sync: update phpstan.neon from MokoJoomGallery [skip ci] -- 2.52.0 From 18807888a2e02d404f30d9c59d5da6d4cf4ca407 Mon Sep 17 00:00:00 2001 From: Jonathan Miller <1+jmiller@noreply.git.mokoconsulting.tech> Date: Tue, 26 May 2026 04:13:23 +0000 Subject: [PATCH 16/16] sync: update composer.json from MokoJoomGallery [skip ci] --- composer.json | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 8b13789..10afe3a 100644 --- a/composer.json +++ b/composer.json @@ -1 +1,27 @@ - +{ + "name": "mokoconsulting/mokojoomgallery", + "description": "Photo gallery management for Joomla — galleries, images, thumbnails, lightbox, and frontend display", + "type": "joomla-package", + "version": "01.00.00", + "license": "GPL-3.0-or-later", + "authors": [ + { + "name": "Moko Consulting", + "email": "hello@mokoconsulting.tech", + "homepage": "https://mokoconsulting.tech" + } + ], + "require": { + "php": ">=8.1" + }, + "require-dev": { + "squizlabs/php_codesniffer": "^3.7", + "phpstan/phpstan": "^1.10", + "joomla/coding-standards": "3.0.x-dev" + }, + "minimum-stability": "dev", + "prefer-stable": true, + "config": { + "sort-packages": true + } +} -- 2.52.0