diff --git a/.github/workflows/auto-update-sha.yml b/.github/workflows/auto-update-sha.yml new file mode 100644 index 0000000..2aa5fb6 --- /dev/null +++ b/.github/workflows/auto-update-sha.yml @@ -0,0 +1,147 @@ +# Copyright (C) 2026 Moko Consulting +# SPDX-License-Identifier: GPL-3.0-or-later +# FILE INFORMATION +# DEFGROUP: GitHub.Workflow +# INGROUP: MokoCassiopeia.Automation +# REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia +# PATH: /.github/workflows/auto-update-sha.yml +# VERSION: 01.00.00 +# BRIEF: Automatically update SHA-256 hash in updates.xml after release +# NOTE: Ensures updates.xml stays synchronized with release packages + +name: Auto-Update SHA Hash + +on: + release: + types: [published] + workflow_dispatch: + inputs: + tag: + description: 'Release tag to update SHA for (e.g., 03.08.03)' + required: true + type: string + +permissions: + contents: write + +jobs: + update-sha: + name: Update SHA-256 Hash in updates.xml + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: main + + - name: Get release tag + id: tag + run: | + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + TAG="${{ inputs.tag }}" + else + TAG="${{ github.event.release.tag_name }}" + fi + echo "tag=${TAG}" >> $GITHUB_OUTPUT + echo "Processing release: ${TAG}" + + - name: Download release package + run: | + TAG="${{ steps.tag.outputs.tag }}" + PACKAGE_NAME="mokocassiopeia-src-${TAG}.zip" + DOWNLOAD_URL="https://github.com/${{ github.repository }}/releases/download/${TAG}/${PACKAGE_NAME}" + + echo "Downloading: ${DOWNLOAD_URL}" + curl -L -o "${PACKAGE_NAME}" "${DOWNLOAD_URL}" + + if [ ! -f "${PACKAGE_NAME}" ]; then + echo "Error: Failed to download package" + exit 1 + fi + + echo "PACKAGE_NAME=${PACKAGE_NAME}" >> $GITHUB_ENV + + - name: Calculate SHA-256 hash + id: sha + run: | + SHA256_HASH=$(sha256sum "${PACKAGE_NAME}" | cut -d' ' -f1) + echo "sha256=${SHA256_HASH}" >> $GITHUB_OUTPUT + echo "SHA-256 Hash: ${SHA256_HASH}" + + - name: Update updates.xml + run: | + TAG="${{ steps.tag.outputs.tag }}" + SHA256="${{ steps.sha.outputs.sha256 }}" + DATE=$(date +%Y-%m-%d) + + # Update version + sed -i "s|.*|${TAG}|" updates.xml + + # Update creation date + sed -i "s|.*|${DATE}|" updates.xml + + # Update download URL + sed -i "s|.*|https://github.com/${{ github.repository }}/releases/download/${TAG}/mokocassiopeia-src-${TAG}.zip|" updates.xml + + # Update or add SHA-256 hash + if grep -q "" updates.xml; then + sed -i "s|.*|sha256:${SHA256}|" updates.xml + else + # Add SHA-256 after downloadurl + sed -i "/<\/downloadurl>/a\ sha256:${SHA256}<\/sha256>" updates.xml + fi + + echo "Updated updates.xml with:" + echo " Version: ${TAG}" + echo " Date: ${DATE}" + echo " SHA-256: ${SHA256}" + + - name: Check for changes + id: changes + run: | + if git diff --quiet updates.xml; then + echo "has_changes=false" >> $GITHUB_OUTPUT + echo "No changes to updates.xml" + else + echo "has_changes=true" >> $GITHUB_OUTPUT + echo "Changes detected in updates.xml" + git diff updates.xml + fi + + - name: Commit and push changes + if: steps.changes.outputs.has_changes == 'true' + run: | + TAG="${{ steps.tag.outputs.tag }}" + + git config --local user.email "github-actions[bot]@users.noreply.github.com" + git config --local user.name "github-actions[bot]" + + git add updates.xml + git commit -m "chore: Update SHA-256 hash for release ${TAG} + +Auto-generated by auto-update-sha workflow +SHA-256: ${{ steps.sha.outputs.sha256 }}" + + git push origin main + + echo "Successfully updated updates.xml with SHA-256 hash for release ${TAG}" + + - name: Summary + if: steps.changes.outputs.has_changes == 'true' + run: | + echo "### SHA-256 Hash Updated Successfully" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "- Release: ${{ steps.tag.outputs.tag }}" >> $GITHUB_STEP_SUMMARY + echo "- SHA-256: \`${{ steps.sha.outputs.sha256 }}\`" >> $GITHUB_STEP_SUMMARY + echo "- File: updates.xml" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "The Joomla update server will now provide the correct package hash." >> $GITHUB_STEP_SUMMARY + + - name: Summary (no changes) + if: steps.changes.outputs.has_changes == 'false' + run: | + echo "### No Updates Needed" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "updates.xml already contains the correct SHA-256 hash for release ${{ steps.tag.outputs.tag }}" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e1ab9b2 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,210 @@ +# Copyright (C) 2026 Moko Consulting +# +# This file is part of a Moko Consulting project. +# +# 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 +# INGROUP: MokoCassiopeia.Release +# REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia +# PATH: /.github/workflows/release.yml +# VERSION: 01.00.00 +# BRIEF: Automated release workflow for MokoCassiopeia Joomla template +# NOTE: Creates release packages and publishes to GitHub Releases + +name: Create Release + +on: + push: + tags: + - '[0-9][0-9].[0-9][0-9].[0-9][0-9]' + workflow_dispatch: + inputs: + version: + description: 'Release version (e.g., 03.08.03)' + required: true + type: string + prerelease: + description: 'Mark as pre-release' + required: false + type: boolean + default: false + +permissions: + contents: write + +jobs: + build: + name: Build Release Package + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.1' + extensions: mbstring, xml, zip + tools: composer:v2 + + - name: Get version + id: version + run: | + if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then + VERSION="${{ inputs.version }}" + else + VERSION=${GITHUB_REF#refs/tags/} + fi + echo "version=${VERSION}" >> $GITHUB_OUTPUT + echo "Building version: ${VERSION}" + + - name: Install dependencies + run: | + if [ -f "composer.json" ]; then + 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/templates/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/templates/templateDetails.xml + sed -i "s/.*<\/creationDate>/${DATE}<\/creationDate>/g" updates.xml + + - name: Create package structure + run: | + mkdir -p build/package + + # Copy template files from src/templates + rsync -av src/templates/ build/package/ + + # Copy media files from src/media to media directory + mkdir -p build/package/media + rsync -av src/media/ build/package/media/ + + - name: Create source ZIP package + run: | + 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}" + + - name: Generate checksums + 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}" + + - name: Upload build artifacts + uses: actions/upload-artifact@v4 + with: + name: release-package + path: | + build/*.zip + build/*.sha256 + build/*.md5 + + 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/} + fi + echo "version=${VERSION}" >> $GITHUB_OUTPUT + + - name: Extract changelog + id: changelog + run: | + 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 + 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 }} + + - name: Release summary + run: | + echo "### Release Created Successfully" >> $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 diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..0deddfd --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,290 @@ +# Scripts — MokoCassiopeia + +This directory contains utility scripts for building, releasing, and managing the MokoCassiopeia Joomla template. + +## Available Scripts + +### build-release.sh + +**Purpose**: Build a release package for MokoCassiopeia template. + +**Usage**: +```bash +# Build with auto-detected version from templateDetails.xml +./scripts/build-release.sh + +# Build with specific version +./scripts/build-release.sh 03.08.03 +``` + +**What it does**: +1. Creates a `build/` directory +2. Copies template files from `src/templates/` +3. Copies media files from `src/media/` to `media/` +4. Creates a ZIP package: `mokocassiopeia-src-{version}.zip` +5. Generates SHA-256 and MD5 checksums +6. Outputs package location and checksums + +**Output**: +- `build/mokocassiopeia-src-{version}.zip` - Installation package +- `build/mokocassiopeia-src-{version}.zip.sha256` - SHA-256 checksum +- `build/mokocassiopeia-src-{version}.zip.md5` - MD5 checksum + +**Requirements**: +- `rsync` for file copying +- `zip` for package creation +- `sha256sum` and `md5sum` for checksums + +--- + +### create-client-fork.sh + +**Purpose**: Create a customized client fork of MokoCassiopeia. + +**Usage**: +```bash +./scripts/create-client-fork.sh +``` + +See the script documentation for details on creating client-specific forks. + +--- + +## Automated Workflows + +The repository includes GitHub Actions workflows that automate the build and release process: + +### `.github/workflows/release.yml` + +**Purpose**: Automated release creation when tags are pushed. + +**Triggers**: +- Push of version tag (e.g., `03.08.03`) +- Manual workflow dispatch with version input + +**Process**: +1. Checks out repository +2. Sets up PHP environment +3. Installs dependencies (if composer.json exists) +4. Updates version numbers in manifest files +5. Creates package structure +6. Builds ZIP package +7. Generates checksums +8. Creates GitHub Release with artifacts + +**Usage**: +```bash +# Create and push a tag +git tag 03.08.04 +git push origin 03.08.04 + +# Or use GitHub UI to run manually +``` + +--- + +### `.github/workflows/auto-update-sha.yml` + +**Purpose**: Automatically update SHA-256 hash in `updates.xml` after a release is published. + +**Triggers**: +- GitHub Release published +- Manual workflow dispatch with tag input + +**Process**: +1. Downloads the release package +2. Calculates SHA-256 hash +3. Updates `updates.xml` with: + - New version number + - Current date + - Download URL + - SHA-256 hash +4. Commits and pushes changes to main branch + +**Benefits**: +- Ensures `updates.xml` always has correct SHA-256 hash +- Enables Joomla update server functionality +- Reduces manual update errors +- Automates security verification + +--- + +## Release Process + +### Manual Release (Local Build) + +1. **Update version numbers**: + ```bash + # Update these files manually: + # - src/templates/templateDetails.xml + # - updates.xml + # - CHANGELOG.md + ``` + +2. **Build package**: + ```bash + ./scripts/build-release.sh 03.08.04 + ``` + +3. **Test package**: + - Install ZIP in Joomla test environment + - Verify all features work correctly + +4. **Create GitHub Release**: + - Go to GitHub Releases + - Click "Create a new release" + - Upload the ZIP, SHA256, and MD5 files + - Add release notes from CHANGELOG.md + +5. **Update updates.xml**: + - Copy SHA-256 hash from `.sha256` file + - Update `updates.xml` with new hash + - Commit and push changes + +--- + +### Automated Release (GitHub Actions) + +1. **Update version numbers**: + ```bash + # Update these files in a branch: + # - src/templates/templateDetails.xml + # - CHANGELOG.md + + git checkout -b release/03.08.04 + # Make changes + git commit -m "chore: Prepare release 03.08.04" + git push origin release/03.08.04 + ``` + +2. **Create and merge PR**: + - Create PR from release branch + - Review changes + - Merge to main + +3. **Create and push tag**: + ```bash + git checkout main + git pull + git tag 03.08.04 + git push origin 03.08.04 + ``` + +4. **Automated process**: + - GitHub Actions builds package automatically + - Creates GitHub Release with artifacts + - `auto-update-sha` workflow updates `updates.xml` + +5. **Verify**: + - Check GitHub Release is created + - Verify `updates.xml` has correct SHA-256 + - Test Joomla update server + +--- + +## Development Workflow + +### Testing Local Builds + +```bash +# Build current version +./scripts/build-release.sh + +# Install in Joomla +# Navigate to Extensions > Manage > Install > Upload Package File +# Select: build/mokocassiopeia-src-{version}.zip +``` + +### Pre-Release Checklist + +- [ ] All code changes merged to main +- [ ] Version numbers updated: + - [ ] `src/templates/templateDetails.xml` + - [ ] `CHANGELOG.md` +- [ ] CHANGELOG.md updated with release notes +- [ ] Tests passing +- [ ] Documentation updated +- [ ] Local build tested in Joomla + +--- + +## Troubleshooting + +### Build Fails + +**Problem**: `rsync: command not found` +```bash +# Ubuntu/Debian +sudo apt-get install rsync + +# macOS +brew install rsync +``` + +**Problem**: `zip: command not found` +```bash +# Ubuntu/Debian +sudo apt-get install zip + +# macOS (usually pre-installed) +brew install zip +``` + +### GitHub Actions Fails + +**Problem**: Release workflow fails on tag push + +Check: +1. Tag format matches pattern: `[0-9][0-9].[0-9][0-9].[0-9][0-9]` +2. Repository has write permissions for GITHUB_TOKEN +3. `src/templates/` and `src/media/` directories exist + +**Problem**: auto-update-sha fails + +Check: +1. Release package was published successfully +2. Workflow has write permissions +3. Package naming matches expected format + +--- + +## File Structure + +``` +scripts/ +├── README.md # This file +├── build-release.sh # Local build script +└── create-client-fork.sh # Client fork creation script + +.github/workflows/ +├── release.yml # Automated release workflow +└── auto-update-sha.yml # SHA hash update workflow +``` + +--- + +## Contributing + +When adding new scripts: + +1. Add GPL-3.0-or-later license header +2. Include FILE INFORMATION block +3. Add usage documentation in this README +4. Make scripts executable: `chmod +x scripts/your-script.sh` +5. Test thoroughly before committing + +--- + +## Support + +- **Issues**: [GitHub Issues](https://github.com/mokoconsulting-tech/MokoCassiopeia/issues) +- **Email**: hello@mokoconsulting.tech +- **Documentation**: [docs/](../docs/) + +--- + +## License + +All scripts are licensed under GPL-3.0-or-later. + +Copyright (C) 2026 Moko Consulting diff --git a/scripts/build-release.sh b/scripts/build-release.sh new file mode 100755 index 0000000..0490745 --- /dev/null +++ b/scripts/build-release.sh @@ -0,0 +1,132 @@ +#!/usr/bin/env bash +# Copyright (C) 2026 Moko Consulting +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Build.Scripts +# INGROUP: MokoCassiopeia.Build +# REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia +# PATH: /scripts/build-release.sh +# VERSION: 01.00.00 +# BRIEF: Build release package for MokoCassiopeia template +# USAGE: ./scripts/build-release.sh [version] + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Script directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +# Functions +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Check if version is provided +if [ -z "$1" ]; then + # Try to extract version from templateDetails.xml + if [ -f "${PROJECT_ROOT}/src/templates/templateDetails.xml" ]; then + VERSION=$(grep -oP '\K[^<]+' "${PROJECT_ROOT}/src/templates/templateDetails.xml" | head -1) + log_info "Detected version: ${VERSION}" + else + log_error "Please provide version as argument: ./build-release.sh 03.08.03" + exit 1 + fi +else + VERSION="$1" +fi + +log_info "Building MokoCassiopeia release package" +log_info "Version: ${VERSION}" + +# Change to project root +cd "${PROJECT_ROOT}" + +# Create build directory +BUILD_DIR="${PROJECT_ROOT}/build" +PACKAGE_DIR="${BUILD_DIR}/package" +rm -rf "${BUILD_DIR}" +mkdir -p "${PACKAGE_DIR}" + +log_info "Creating package structure..." + +# Copy template files from src/templates +if [ -d "src/templates" ]; then + rsync -av --exclude='.git*' src/templates/ "${PACKAGE_DIR}/" +else + log_error "src/templates directory not found!" + exit 1 +fi + +# Copy media files from src/media +if [ -d "src/media" ]; then + mkdir -p "${PACKAGE_DIR}/media" + rsync -av --exclude='.git*' src/media/ "${PACKAGE_DIR}/media/" +else + log_warning "src/media directory not found, skipping media files" +fi + +log_info "Package structure created" + +# Create ZIP package +cd "${PACKAGE_DIR}" +ZIP_NAME="mokocassiopeia-src-${VERSION}.zip" +log_info "Creating ZIP package: ${ZIP_NAME}" + +zip -r "../${ZIP_NAME}" . -q + +if [ $? -ne 0 ]; then + log_error "Failed to create ZIP package" + exit 1 +fi + +cd "${BUILD_DIR}" +log_success "Created: ${ZIP_NAME}" + +# Generate checksums +log_info "Generating checksums..." +sha256sum "${ZIP_NAME}" > "${ZIP_NAME}.sha256" +md5sum "${ZIP_NAME}" > "${ZIP_NAME}.md5" + +# Extract just the hash +SHA256_HASH=$(sha256sum "${ZIP_NAME}" | cut -d' ' -f1) + +log_success "SHA-256: ${SHA256_HASH}" +log_success "MD5: $(md5sum "${ZIP_NAME}" | cut -d' ' -f1)" + +# Show file info +FILE_SIZE=$(du -h "${ZIP_NAME}" | cut -f1) +log_info "Package size: ${FILE_SIZE}" + +# Summary +echo "" +log_success "Build completed successfully!" +echo "" +echo "Package: ${BUILD_DIR}/${ZIP_NAME}" +echo "SHA-256: ${BUILD_DIR}/${ZIP_NAME}.sha256" +echo "MD5: ${BUILD_DIR}/${ZIP_NAME}.md5" +echo "" +echo "Next steps:" +echo "1. Test the package installation in Joomla" +echo "2. Create a GitHub release with this package" +echo "3. Update updates.xml with the new version and SHA-256 hash" +echo ""