From da618990728dbd097ec21e1caa8a59767f0216d4 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 21 May 2026 21:28:50 -0500 Subject: [PATCH] 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}"