Add release workflows and build scripts from MokoStandards

Co-authored-by: jmiller-moko <230051081+jmiller-moko@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-02-27 04:31:30 +00:00
parent 5ddc9fb093
commit 63a7b9dbb4
4 changed files with 779 additions and 0 deletions

147
.github/workflows/auto-update-sha.yml vendored Normal file
View File

@@ -0,0 +1,147 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# 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|<version>.*</version>|<version>${TAG}</version>|" updates.xml
# Update creation date
sed -i "s|<creationDate>.*</creationDate>|<creationDate>${DATE}</creationDate>|" updates.xml
# Update download URL
sed -i "s|<downloadurl type='full' format='zip'>.*</downloadurl>|<downloadurl type='full' format='zip'>https://github.com/${{ github.repository }}/releases/download/${TAG}/mokocassiopeia-src-${TAG}.zip</downloadurl>|" updates.xml
# Update or add SHA-256 hash
if grep -q "<sha256>" updates.xml; then
sed -i "s|<sha256>.*</sha256>|<sha256>sha256:${SHA256}</sha256>|" updates.xml
else
# Add SHA-256 after downloadurl
sed -i "/<\/downloadurl>/a\ <sha256>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

210
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,210 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# 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 <https://www.gnu.org/licenses/>.
#
# 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>${VERSION}<\/version>/g" src/templates/templateDetails.xml
# Update version in updates.xml
sed -i "s/<version>.*<\/version>/<version>${VERSION}<\/version>/g" updates.xml
# Update creation date to today
DATE=$(date +%Y-%m-%d)
sed -i "s/<creationDate>.*<\/creationDate>/<creationDate>${DATE}<\/creationDate>/g" src/templates/templateDetails.xml
sed -i "s/<creationDate>.*<\/creationDate>/<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

290
scripts/README.md Normal file
View File

@@ -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

132
scripts/build-release.sh Executable file
View File

@@ -0,0 +1,132 @@
#!/usr/bin/env bash
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# 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 '<version>\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 ""