diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8d15727..11fb262 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,27 +4,13 @@ # # SPDX-License-Identifier: GPL-3.0-or-later # -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# # FILE INFORMATION -# DEFGROUP: GitHub.Workflow +# DEFGROUP: Gitea.Workflow # INGROUP: MokoCassiopeia.Release # REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoCassiopeia # PATH: /.github/workflows/release.yml -# VERSION: 03.09.03 -# BRIEF: Automated release workflow for MokoCassiopeia Joomla template -# NOTE: Creates release packages and publishes to GitHub Releases +# VERSION: 03.09.16 +# BRIEF: Joomla release — build ZIP, publish to Gitea, mirror to GitHub name: Create Release @@ -35,7 +21,7 @@ on: workflow_dispatch: inputs: version: - description: 'Release version (e.g., 03.08.03)' + description: 'Release version (e.g., 03.09.16)' required: true type: string prerelease: @@ -43,15 +29,26 @@ on: required: false type: boolean default: false + stability: + description: 'Stability tag (development, alpha, beta, rc, stable)' + required: false + type: string + default: 'development' permissions: contents: write +env: + GITEA_URL: https://git.mokoconsulting.tech + GITEA_ORG: MokoConsulting + GITEA_REPO: MokoCassiopeia + EXT_ELEMENT: mokocassiopeia + jobs: build: name: Build Release Package runs-on: ubuntu-latest - + steps: - name: Checkout repository uses: actions/checkout@v4 @@ -65,16 +62,38 @@ jobs: extensions: mbstring, xml, zip tools: composer:v2 - - name: Get version - id: version + - name: Get version and stability + id: meta run: | if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then VERSION="${{ inputs.version }}" + STABILITY="${{ inputs.stability }}" + PRERELEASE="${{ inputs.prerelease }}" else VERSION=${GITHUB_REF#refs/tags/} + STABILITY="stable" + PRERELEASE="false" fi - echo "version=${VERSION}" >> $GITHUB_OUTPUT - echo "Building version: ${VERSION}" + + # Derive suffix and tag from stability + case "$STABILITY" in + development) SUFFIX="-dev"; TAG_NAME="development" ;; + alpha) SUFFIX="-alpha"; TAG_NAME="alpha" ;; + beta) SUFFIX="-beta"; TAG_NAME="beta" ;; + rc) SUFFIX="-rc"; TAG_NAME="release-candidate" ;; + stable) SUFFIX=""; TAG_NAME="v${VERSION%%.*}" ;; + *) SUFFIX="-dev"; TAG_NAME="development" ;; + esac + + ZIP_NAME="${EXT_ELEMENT}-${VERSION}${SUFFIX}.zip" + + echo "version=${VERSION}" >> "$GITHUB_OUTPUT" + echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" + echo "prerelease=${PRERELEASE}" >> "$GITHUB_OUTPUT" + echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT" + echo "tag_name=${TAG_NAME}" >> "$GITHUB_OUTPUT" + echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT" + echo "Building: ${ZIP_NAME} (${STABILITY})" - name: Install dependencies run: | @@ -82,129 +101,226 @@ jobs: composer install --no-dev --optimize-autoloader fi - - name: Update version in manifest files - run: | - VERSION="${{ steps.version.outputs.version }}" - # Update version in templateDetails.xml - sed -i "s/.*<\/version>/${VERSION}<\/version>/g" src/templateDetails.xml - # Update version in updates.xml - sed -i "s/.*<\/version>/${VERSION}<\/version>/g" updates.xml - # Update creation date to today - DATE=$(date +%Y-%m-%d) - sed -i "s/.*<\/creationDate>/${DATE}<\/creationDate>/g" src/templateDetails.xml - sed -i "s/.*<\/creationDate>/${DATE}<\/creationDate>/g" updates.xml - - - name: Create package structure + - name: Create package run: | mkdir -p build/package - - # Copy template files from src (excluding media directory) - rsync -av --exclude='media' src/ build/package/ - - # Copy media files from src/media to package/media directory - mkdir -p build/package/media - rsync -av src/media/ build/package/media/ + rsync -av \ + --exclude='sftp-config*' \ + --exclude='.ftpignore' \ + --exclude='*.ppk' \ + --exclude='*.pem' \ + --exclude='*.key' \ + --exclude='.env*' \ + --exclude='*.local' \ + src/ build/package/ - - name: Create source ZIP package + - name: Build ZIP + id: zip run: | + ZIP_NAME="${{ steps.meta.outputs.zip_name }}" cd build/package - VERSION="${{ steps.version.outputs.version }}" - ZIP_NAME="mokocassiopeia-src-${VERSION}.zip" zip -r "../${ZIP_NAME}" . - cd ../.. - echo "ZIP_NAME=${ZIP_NAME}" >> $GITHUB_ENV - echo "Created package: ${ZIP_NAME}" + cd .. - - name: Generate checksums + SHA256=$(sha256sum "${ZIP_NAME}" | cut -d' ' -f1) + SIZE=$(stat -c%s "${ZIP_NAME}") + + echo "sha256=${SHA256}" >> "$GITHUB_OUTPUT" + echo "size=${SIZE}" >> "$GITHUB_OUTPUT" + echo "SHA-256: ${SHA256}" + echo "Size: ${SIZE} bytes" + + # ── Gitea Release (PRIMARY) ────────────────────────────────────── + - name: "Gitea: Delete existing release" run: | - cd build - sha256sum "${ZIP_NAME}" > "${ZIP_NAME}.sha256" - md5sum "${ZIP_NAME}" > "${ZIP_NAME}.md5" - - # Extract just the hash for updates.xml - SHA256_HASH=$(sha256sum "${ZIP_NAME}" | cut -d' ' -f1) - echo "SHA256_HASH=${SHA256_HASH}" >> $GITHUB_ENV - echo "SHA-256: ${SHA256_HASH}" + TAG="${{ steps.meta.outputs.tag_name }}" + TOKEN="${{ secrets.GITEA_TOKEN }}" + API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - - name: Upload build artifacts - uses: actions/upload-artifact@v4 - with: - name: release-package - path: | - build/*.zip - build/*.sha256 - build/*.md5 + # Find and delete existing release by tag + RELEASE_ID=$(curl -sf -H "Authorization: token ${TOKEN}" \ + "${API}/releases/tags/${TAG}" 2>/dev/null | jq -r '.id // empty') - release: - name: Create GitHub Release - runs-on: ubuntu-latest - needs: build - - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - - name: Download build artifacts - uses: actions/download-artifact@v4 - with: - name: release-package - path: ./artifacts - - - name: Get version - id: version - run: | - if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then - VERSION="${{ inputs.version }}" - else - VERSION=${GITHUB_REF#refs/tags/} + if [ -n "$RELEASE_ID" ]; then + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ + "${API}/releases/${RELEASE_ID}" || true + echo "Deleted existing release id=${RELEASE_ID}" fi - echo "version=${VERSION}" >> $GITHUB_OUTPUT - - name: Extract changelog - id: changelog + # Delete existing tag + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ + "${API}/tags/${TAG}" 2>/dev/null || true + + - name: "Gitea: Create release" + id: gitea_release run: | + TAG="${{ steps.meta.outputs.tag_name }}" + VERSION="${{ steps.meta.outputs.version }}" + STABILITY="${{ steps.meta.outputs.stability }}" + PRERELEASE="${{ steps.meta.outputs.prerelease }}" + SHA256="${{ steps.zip.outputs.sha256 }}" + TOKEN="${{ secrets.GITEA_TOKEN }}" + API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + + # Build release body + BODY="## ${EXT_ELEMENT} ${VERSION} (${STABILITY}) + + ### SHA-256 + \`${SHA256}\`" + + # Extract changelog if available if [ -f "CHANGELOG.md" ]; then - # Extract changelog for this version - VERSION="${{ steps.version.outputs.version }}" - awk "/## \[${VERSION}\]/,/## \[/{if(/## \[${VERSION}\]/)print;else if(/## \[/)exit;else print}" CHANGELOG.md > release_notes.md - - if [ ! -s release_notes.md ]; then - echo "No specific changelog found for version ${VERSION}" > release_notes.md - echo "" >> release_notes.md - echo "Please refer to the full CHANGELOG.md for details." >> release_notes.md + NOTES=$(awk "/## \[${VERSION}\]/,/## \[/{if(/## \[${VERSION}\]/)next;if(/## \[/)exit;print}" CHANGELOG.md) + if [ -n "$NOTES" ]; then + BODY="## ${EXT_ELEMENT} ${VERSION} (${STABILITY}) + + ${NOTES} + + ### SHA-256 + \`${SHA256}\`" fi - else - echo "Release version ${{ steps.version.outputs.version }}" > release_notes.md fi - - name: Create Release - uses: softprops/action-gh-release@v1 - with: - tag_name: ${{ steps.version.outputs.version }} - name: Release ${{ steps.version.outputs.version }} - body_path: release_notes.md - draft: false - prerelease: ${{ inputs.prerelease || false }} - files: | - artifacts/*.zip - artifacts/*.sha256 - artifacts/*.md5 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + IS_PRE="true" + if [ "$STABILITY" = "stable" ]; then + IS_PRE="false" + fi - - name: Release summary + RESULT=$(curl -sf -X POST -H "Authorization: token ${TOKEN}" \ + -H "Content-Type: application/json" \ + "${API}/releases" \ + -d "$(jq -n \ + --arg tag "$TAG" \ + --arg target "${{ github.ref_name }}" \ + --arg name "${EXT_ELEMENT} ${VERSION} ${STABILITY^}" \ + --arg body "$BODY" \ + --argjson pre "$IS_PRE" \ + '{tag_name: $tag, target_commitish: $target, name: $name, body: $body, prerelease: $pre}' + )") + + RELEASE_ID=$(echo "$RESULT" | jq -r '.id') + echo "release_id=${RELEASE_ID}" >> "$GITHUB_OUTPUT" + echo "Gitea release created: id=${RELEASE_ID}, tag=${TAG}" + + - name: "Gitea: Upload ZIP" run: | - echo "### Release Created Successfully" >> $GITHUB_STEP_SUMMARY + RELEASE_ID="${{ steps.gitea_release.outputs.release_id }}" + ZIP_NAME="${{ steps.meta.outputs.zip_name }}" + TOKEN="${{ secrets.GITEA_TOKEN }}" + API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" + + curl -sf -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 "Uploaded ${ZIP_NAME} to Gitea release ${RELEASE_ID}" + + # ── GitHub Mirror (BACKUP) ─────────────────────────────────────── + - name: "GitHub: Mirror release (backup)" + if: ${{ secrets.GH_MIRROR_TOKEN != '' }} + continue-on-error: true + run: | + TAG="${{ steps.meta.outputs.tag_name }}" + VERSION="${{ steps.meta.outputs.version }}" + STABILITY="${{ steps.meta.outputs.stability }}" + ZIP_NAME="${{ steps.meta.outputs.zip_name }}" + SHA256="${{ steps.zip.outputs.sha256 }}" + TOKEN="${{ secrets.GH_MIRROR_TOKEN }}" + GH_REPO="mokoconsulting-tech/${GITEA_REPO}" + GH_API="https://api.github.com/repos/${GH_REPO}" + + IS_PRE="true" + [ "$STABILITY" = "stable" ] && IS_PRE="false" + + # Delete existing release by tag + EXISTING=$(curl -sf -H "Authorization: token ${TOKEN}" \ + "${GH_API}/releases/tags/${TAG}" 2>/dev/null | jq -r '.id // empty') + if [ -n "$EXISTING" ]; then + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ + "${GH_API}/releases/${EXISTING}" || true + fi + + # Delete tag + curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ + "${GH_API}/git/refs/tags/${TAG}" 2>/dev/null || true + + # Create release + RELEASE_ID=$(curl -sf -X POST -H "Authorization: token ${TOKEN}" \ + -H "Content-Type: application/json" \ + "${GH_API}/releases" \ + -d "$(jq -n \ + --arg tag "$TAG" \ + --arg target "${{ github.sha }}" \ + --arg name "${EXT_ELEMENT} ${VERSION} ${STABILITY^} (mirror)" \ + --arg body "Mirror of Gitea release. SHA-256: \`${SHA256}\`" \ + --argjson pre "$IS_PRE" \ + '{tag_name: $tag, target_commitish: $target, name: $name, body: $body, prerelease: $pre}' + )" | jq -r '.id') + + # Upload ZIP + if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "null" ]; then + curl -sf -X POST \ + -H "Authorization: token ${TOKEN}" \ + -H "Content-Type: application/octet-stream" \ + "https://uploads.github.com/repos/${GH_REPO}/releases/${RELEASE_ID}/assets?name=${ZIP_NAME}" \ + --data-binary "@build/${ZIP_NAME}" + echo "GitHub mirror: uploaded ${ZIP_NAME}" + fi + + # ── Update updates.xml ────────────────────────────────────────── + - name: "Update updates.xml SHA-256" + run: | + TAG="${{ steps.meta.outputs.tag_name }}" + STABILITY="${{ steps.meta.outputs.stability }}" + SHA256="${{ steps.zip.outputs.sha256 }}" + + if [ -f "updates.xml" ] && [ -n "$SHA256" ]; then + # Update the SHA for the matching stability channel + python3 -c " + import re, sys + tag_map = {'development':'development','alpha':'alpha','beta':'beta','rc':'rc','stable':'stable'} + tag = tag_map.get('${STABILITY}', 'development') + with open('updates.xml', 'r') as f: + content = f.read() + # Find the update block with matching tag and replace its sha256 + pattern = r'(' + re.escape(tag) + r'.*?)[^<]*()' + content = re.sub(pattern, r'\g<1>sha256:${SHA256}\g<2>', content, flags=re.DOTALL) + with open('updates.xml', 'w') as f: + f.write(content) + print(f'Updated SHA for {tag} channel') + " + fi + + - name: "Commit updates.xml" + run: | + if git diff --quiet updates.xml 2>/dev/null; then + echo "No changes to updates.xml" + exit 0 + fi + 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} SHA-256 for ${{ steps.meta.outputs.version }} [skip ci]" \ + --author="gitea-actions[bot] " + git push || true + + - name: Summary + run: | + VERSION="${{ steps.meta.outputs.version }}" + STABILITY="${{ steps.meta.outputs.stability }}" + ZIP_NAME="${{ steps.meta.outputs.zip_name }}" + SHA256="${{ steps.zip.outputs.sha256 }}" + TAG="${{ steps.meta.outputs.tag_name }}" + + echo "### Release Created" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY - echo "- Version: ${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY - echo "- Repository: $GITHUB_REPOSITORY" >> $GITHUB_STEP_SUMMARY - echo "- Tag: ${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY - echo "- Pre-release: ${{ inputs.prerelease || false }}" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "Package files:" >> $GITHUB_STEP_SUMMARY - ls -lh artifacts/ >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Next Steps" >> $GITHUB_STEP_SUMMARY - echo "1. Verify the release at: https://github.com/$GITHUB_REPOSITORY/releases/tag/${{ steps.version.outputs.version }}" >> $GITHUB_STEP_SUMMARY - echo "2. Update updates.xml with the SHA-256 hash from the .sha256 file" >> $GITHUB_STEP_SUMMARY - echo "3. Test the installation package" >> $GITHUB_STEP_SUMMARY + echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY + echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY + echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Stability | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY + echo "| Tag | \`${TAG}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY + echo "| SHA-256 | \`${SHA256}\` |" >> $GITHUB_STEP_SUMMARY + echo "| Gitea | [Release](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${TAG}) |" >> $GITHUB_STEP_SUMMARY