diff --git a/.github/workflows/joomla_testing.yml b/.github/workflows/joomla_testing.yml index 8c6a9b9..800f4b0 100644 --- a/.github/workflows/joomla_testing.yml +++ b/.github/workflows/joomla_testing.yml @@ -56,23 +56,46 @@ jobs: uses: actions/setup-node@v4 with: node-version: '18' + cache: 'npm' + cache-dependency-path: '**/package-lock.json' + + - name: Cache Joomla Downloads + uses: actions/cache@v4 + with: + path: /tmp/joomla-cache + key: joomla-${{ matrix.joomla-version }} + restore-keys: | + joomla-${{ matrix.joomla-version }} - name: Download Joomla ${{ matrix.joomla-version }} run: | mkdir -p /tmp/joomla - cd /tmp/joomla + mkdir -p /tmp/joomla-cache - # Download appropriate Joomla version + # Define ZIP file based on version if [ "${{ matrix.joomla-version }}" = "4.4" ]; then - wget -q https://downloads.joomla.org/cms/joomla4/4-4-9/Joomla_4-4-9-Stable-Full_Package.zip - unzip -q Joomla_4-4-9-Stable-Full_Package.zip + ZIP_FILE="Joomla_4-4-9-Stable-Full_Package.zip" + ZIP_URL="https://downloads.joomla.org/cms/joomla4/4-4-9/${ZIP_FILE}" elif [ "${{ matrix.joomla-version }}" = "5.0" ]; then - wget -q https://downloads.joomla.org/cms/joomla5/5-0-3/Joomla_5-0-3-Stable-Full_Package.zip - unzip -q Joomla_5-0-3-Stable-Full_Package.zip + ZIP_FILE="Joomla_5-0-3-Stable-Full_Package.zip" + ZIP_URL="https://downloads.joomla.org/cms/joomla5/5-0-3/${ZIP_FILE}" else - wget -q https://downloads.joomla.org/cms/joomla5/5-1-4/Joomla_5-1-4-Stable-Full_Package.zip - unzip -q Joomla_5-1-4-Stable-Full_Package.zip + ZIP_FILE="Joomla_5-1-4-Stable-Full_Package.zip" + ZIP_URL="https://downloads.joomla.org/cms/joomla5/5-1-4/${ZIP_FILE}" fi + + # Use cached ZIP if available, otherwise download + if [ -f "/tmp/joomla-cache/${ZIP_FILE}" ]; then + echo "Using cached Joomla package: ${ZIP_FILE}" + cp "/tmp/joomla-cache/${ZIP_FILE}" "/tmp/joomla/" + else + echo "Downloading Joomla package: ${ZIP_FILE}" + wget -q "${ZIP_URL}" -O "/tmp/joomla/${ZIP_FILE}" + cp "/tmp/joomla/${ZIP_FILE}" "/tmp/joomla-cache/" + fi + + cd /tmp/joomla + unzip -q "${ZIP_FILE}" - name: Configure Joomla run: | @@ -211,6 +234,16 @@ jobs: coverage: xdebug tools: composer:v2 + - name: Cache Composer packages + uses: actions/cache@v4 + with: + path: ~/.composer + key: ${{ runner.os }}-composer-codeception-8.1-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer-codeception-8.1- + ${{ runner.os }}-composer-codeception- + ${{ runner.os }}-composer- + - name: Install Codeception run: | composer global require codeception/codeception diff --git a/.github/workflows/php_quality.yml b/.github/workflows/php_quality.yml index 22b70a2..6d9bd49 100644 --- a/.github/workflows/php_quality.yml +++ b/.github/workflows/php_quality.yml @@ -38,6 +38,16 @@ jobs: coverage: none tools: cs2pr + - name: Cache Composer packages + uses: actions/cache@v4 + with: + path: ~/.composer + key: ${{ runner.os }}-composer-phpcs-${{ matrix.php-version }}-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer-phpcs-${{ matrix.php-version }}- + ${{ runner.os }}-composer-phpcs- + ${{ runner.os }}-composer- + - name: Install PHP_CodeSniffer run: | composer global require squizlabs/php_codesniffer @@ -82,6 +92,16 @@ jobs: extensions: mbstring, xml, ctype, json, zip coverage: none + - name: Cache Composer packages + uses: actions/cache@v4 + with: + path: ~/.composer + key: ${{ runner.os }}-composer-phpstan-${{ matrix.php-version }}-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer-phpstan-${{ matrix.php-version }}- + ${{ runner.os }}-composer-phpstan- + ${{ runner.os }}-composer- + - name: Install PHPStan run: | composer global require phpstan/phpstan @@ -119,6 +139,16 @@ jobs: extensions: mbstring, xml, ctype, json, zip coverage: none + - name: Cache Composer packages + uses: actions/cache@v4 + with: + path: ~/.composer + key: ${{ runner.os }}-composer-phpcompat-8.3-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-composer-phpcompat-8.3- + ${{ runner.os }}-composer-phpcompat- + ${{ runner.os }}-composer- + - name: Install dependencies run: | composer global require squizlabs/php_codesniffer diff --git a/.gitignore b/.gitignore index 71f055d..90279ad 100644 --- a/.gitignore +++ b/.gitignore @@ -33,7 +33,11 @@ System Volume Information/ *.lnk Icon? .idea/ -.vscode/ +# .vscode/ - Allow specific VS Code config files +.vscode/* +!.vscode/tasks.json +!.vscode/settings.json.example +!.vscode/extensions.json *.code-workspace *.sublime-project *.sublime-workspace diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..071585d --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,171 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "Validate All", + "type": "shell", + "command": "./scripts/run/validate_all.sh", + "group": { + "kind": "test", + "isDefault": true + }, + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + }, + { + "label": "Validate Required", + "type": "shell", + "command": "make validate-required", + "group": "test", + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + }, + { + "label": "Run Tests", + "type": "shell", + "command": "codecept run", + "group": "test", + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + }, + { + "label": "PHP CodeSniffer", + "type": "shell", + "command": "phpcs --standard=phpcs.xml src/", + "group": "test", + "presentation": { + "reveal": "always", + "panel": "shared" + }, + "problemMatcher": { + "owner": "php", + "fileLocation": "relative", + "pattern": { + "regexp": "^(.+):(\\d+):(\\d+):\\s+(error|warning)\\s+-\\s+(.+)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + } + }, + { + "label": "PHP CodeSniffer - Auto Fix", + "type": "shell", + "command": "phpcbf --standard=phpcs.xml src/", + "group": "none", + "presentation": { + "reveal": "always", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "PHPStan", + "type": "shell", + "command": "phpstan analyse --configuration=phpstan.neon", + "group": "test", + "presentation": { + "reveal": "always", + "panel": "shared" + }, + "problemMatcher": { + "owner": "php", + "fileLocation": "relative", + "pattern": { + "regexp": "^(.+):(\\d+):\\s+(.+)$", + "file": 1, + "line": 2, + "message": 3 + } + } + }, + { + "label": "Create Package", + "type": "shell", + "command": "./scripts/release/package_extension.sh dist", + "group": "build", + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + }, + { + "label": "Smoke Test", + "type": "shell", + "command": "./scripts/run/smoke_test.sh", + "group": "test", + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + }, + { + "label": "Script Health Check", + "type": "shell", + "command": "./scripts/run/script_health.sh", + "group": "test", + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + }, + { + "label": "Check Code Quality", + "type": "shell", + "command": "make quality", + "group": "test", + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + }, + { + "label": "Fix Permissions", + "type": "shell", + "command": "make fix-permissions", + "group": "none", + "presentation": { + "reveal": "always", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Clean", + "type": "shell", + "command": "make clean", + "group": "none", + "presentation": { + "reveal": "always", + "panel": "shared" + }, + "problemMatcher": [] + }, + { + "label": "Install Git Hooks", + "type": "shell", + "command": "./scripts/git/install-hooks.sh", + "group": "none", + "presentation": { + "reveal": "always", + "panel": "new" + }, + "problemMatcher": [] + } + ] +} diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..634f298 --- /dev/null +++ b/Makefile @@ -0,0 +1,193 @@ +# Makefile for Moko Cassiopeia Development +# Copyright (C) 2025 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later + +.PHONY: help install validate test quality package clean dev-setup + +# Default target +.DEFAULT_GOAL := help + +# Version detection +VERSION := $(shell grep -oP '\K[^<]+' src/templates/templateDetails.xml 2>/dev/null || echo "unknown") + +## help: Show this help message +help: + @echo "Moko Cassiopeia Development Makefile" + @echo "" + @echo "Version: $(VERSION)" + @echo "" + @echo "Available targets:" + @sed -n 's/^##//p' $(MAKEFILE_LIST) | column -t -s ':' | sed -e 's/^/ /' + +## install: Install development dependencies +install: + @echo "Installing development dependencies..." + @command -v composer >/dev/null 2>&1 || { echo "Error: composer not found. Please install composer first."; exit 1; } + composer global require squizlabs/php_codesniffer + composer global require phpstan/phpstan + composer global require phpcompatibility/php-compatibility + composer global require codeception/codeception + phpcs --config-set installed_paths ~/.composer/vendor/phpcompatibility/php-compatibility + @echo "✓ Dependencies installed" + +## validate: Run all validation scripts +validate: + @echo "Running validation scripts..." + @./scripts/run/validate_all.sh + +## validate-required: Run only required validation scripts +validate-required: + @echo "Running required validations..." + @./scripts/validate/manifest.sh + @./scripts/validate/xml_wellformed.sh + @./scripts/validate/workflows.sh + @echo "✓ Required validations passed" + +## test: Run all tests +test: + @echo "Running tests..." + @command -v codecept >/dev/null 2>&1 || { echo "Error: codecept not found. Run 'make install' first."; exit 1; } + codecept run + +## test-unit: Run unit tests only +test-unit: + @echo "Running unit tests..." + @command -v codecept >/dev/null 2>&1 || { echo "Error: codecept not found. Run 'make install' first."; exit 1; } + codecept run unit + +## test-acceptance: Run acceptance tests only +test-acceptance: + @echo "Running acceptance tests..." + @command -v codecept >/dev/null 2>&1 || { echo "Error: codecept not found. Run 'make install' first."; exit 1; } + codecept run acceptance + +## quality: Run code quality checks +quality: + @echo "Running code quality checks..." + @$(MAKE) phpcs + @$(MAKE) phpstan + @$(MAKE) phpcompat + +## phpcs: Run PHP_CodeSniffer +phpcs: + @echo "Running PHP_CodeSniffer..." + @command -v phpcs >/dev/null 2>&1 || { echo "Error: phpcs not found. Run 'make install' first."; exit 1; } + phpcs --standard=phpcs.xml src/ + +## phpcs-fix: Auto-fix PHPCS violations +phpcs-fix: + @echo "Auto-fixing PHPCS violations..." + @command -v phpcbf >/dev/null 2>&1 || { echo "Error: phpcbf not found. Run 'make install' first."; exit 1; } + phpcbf --standard=phpcs.xml src/ + +## phpstan: Run PHPStan static analysis +phpstan: + @echo "Running PHPStan..." + @command -v phpstan >/dev/null 2>&1 || { echo "Error: phpstan not found. Run 'make install' first."; exit 1; } + phpstan analyse --configuration=phpstan.neon + +## phpcompat: Check PHP 8.0+ compatibility +phpcompat: + @echo "Checking PHP 8.0+ compatibility..." + @command -v phpcs >/dev/null 2>&1 || { echo "Error: phpcs not found. Run 'make install' first."; exit 1; } + phpcs --standard=PHPCompatibility --runtime-set testVersion 8.0- src/ + +## package: Create distribution package +package: + @echo "Creating distribution package..." + @./scripts/release/package_extension.sh dist $(VERSION) + @echo "✓ Package created: dist/moko-cassiopeia-$(VERSION)-*.zip" + +## smoke-test: Run smoke tests +smoke-test: + @echo "Running smoke tests..." + @./scripts/run/smoke_test.sh + +## script-health: Check script health +script-health: + @echo "Checking script health..." + @./scripts/run/script_health.sh + +## version-check: Display current version information +version-check: + @echo "Version Information:" + @echo " Manifest: $(VERSION)" + @echo " Latest CHANGELOG entry:" + @grep -m 1 "^## \[" CHANGELOG.md || echo " (not found)" + +## fix-permissions: Fix script executable permissions +fix-permissions: + @echo "Fixing script permissions..." + @find scripts -type f -name "*.sh" -exec chmod +x {} \; + @echo "✓ Permissions fixed" + +## clean: Remove generated files and caches +clean: + @echo "Cleaning generated files..." + @rm -rf dist/ + @rm -rf tests/_output/ + @rm -rf .phpunit.cache/ + @find . -type f -name "*.log" -delete + @find . -type f -name ".DS_Store" -delete + @echo "✓ Cleaned" + +## dev-setup: Complete development environment setup +dev-setup: + @echo "Setting up development environment..." + @$(MAKE) install + @$(MAKE) fix-permissions + @echo "" + @echo "✓ Development environment ready!" + @echo "" + @echo "Quick start:" + @echo " make validate - Run all validations" + @echo " make test - Run tests" + @echo " make quality - Check code quality" + @echo " make package - Create distribution package" + +## format: Format code (PHPCS auto-fix) +format: phpcs-fix + +## check: Quick check (required validations + quality) +check: + @echo "Running quick checks..." + @$(MAKE) validate-required + @$(MAKE) quality + @echo "✓ All checks passed" + +## all: Run everything (validate, test, quality, package) +all: + @echo "Running complete build pipeline..." + @$(MAKE) validate + @$(MAKE) test + @$(MAKE) quality + @$(MAKE) package + @echo "✓ Complete pipeline successful" + +## watch: Watch for changes and run validations (requires entr) +watch: + @echo "Watching for changes... (Ctrl+C to stop)" + @command -v entr >/dev/null 2>&1 || { echo "Error: entr not found. Install with: apt-get install entr"; exit 1; } + @find src -type f | entr -c make validate-required + +## list-scripts: List all available scripts +list-scripts: + @echo "Available validation scripts:" + @find scripts/validate -type f -name "*.sh" -exec basename {} \; | sort + @echo "" + @echo "Available fix scripts:" + @find scripts/fix -type f -name "*.sh" -exec basename {} \; | sort + @echo "" + @echo "Available run scripts:" + @find scripts/run -type f -name "*.sh" -exec basename {} \; | sort + +## docs: Open documentation +docs: + @echo "Documentation files:" + @echo " README.md - Project overview" + @echo " docs/JOOMLA_DEVELOPMENT.md - Development guide" + @echo " docs/WORKFLOW_GUIDE.md - Workflow reference" + @echo " scripts/README.md - Scripts documentation" + @echo " CHANGELOG.md - Version history" + @echo " CONTRIBUTING.md - Contribution guide" diff --git a/docs/QUICK_START.md b/docs/QUICK_START.md new file mode 100644 index 0000000..00031ff --- /dev/null +++ b/docs/QUICK_START.md @@ -0,0 +1,352 @@ +# Quick Start Guide - Moko Cassiopeia Development + +Get up and running with Moko Cassiopeia development in minutes. + +## Prerequisites + +Before you begin, ensure you have: + +- **Git** - For version control +- **PHP 8.0+** - Required runtime +- **Composer** - PHP dependency manager +- **Make** (optional) - For convenient commands +- **Code Editor** - VS Code recommended (tasks pre-configured) + +## 5-Minute Setup + +### 1. Clone the Repository + +```bash +git clone https://github.com/mokoconsulting-tech/moko-cassiopeia.git +cd moko-cassiopeia +``` + +### 2. Install Development Dependencies + +```bash +# Using Make (recommended) +make dev-setup + +# Or manually +composer global require squizlabs/php_codesniffer +composer global require phpstan/phpstan +composer global require phpcompatibility/php-compatibility +composer global require codeception/codeception +``` + +### 3. Install Git Hooks (Optional but Recommended) + +```bash +./scripts/git/install-hooks.sh +``` + +### 4. Validate Everything Works + +```bash +# Quick validation +make validate-required + +# Or comprehensive validation +make validate +``` + +## Common Tasks + +### Development Workflow + +```bash +# 1. Make your changes +vim src/templates/index.php + +# 2. Validate locally +make validate-required + +# 3. Check code quality +make quality + +# 4. Commit +git add -A +git commit -m "feat: add new feature" +# (pre-commit hook runs automatically) + +# 5. Push +git push origin your-branch +``` + +### Testing + +```bash +# Run all tests +make test + +# Run unit tests only +make test-unit + +# Run acceptance tests only +make test-acceptance +``` + +### Code Quality + +```bash +# Check everything +make quality + +# PHP CodeSniffer only +make phpcs + +# Auto-fix PHPCS issues +make phpcs-fix + +# PHPStan only +make phpstan + +# PHP compatibility check +make phpcompat +``` + +### Creating a Release Package + +```bash +# Package with auto-detected version +make package + +# Or specify directory and version +./scripts/release/package_extension.sh dist 03.05.00 + +# Check package contents +ls -lh dist/ +unzip -l dist/moko-cassiopeia-*.zip +``` + +## VS Code Integration + +If using VS Code, press `Ctrl+Shift+P` (or `Cmd+Shift+P` on Mac) and type "Run Task" to see available tasks: + +- **Validate All** - Run all validation scripts (default test task) +- **Validate Required** - Run only required validations +- **PHP CodeSniffer** - Check code style +- **PHP CodeSniffer - Auto Fix** - Fix code style issues +- **PHPStan** - Static analysis +- **Run Tests** - Execute all tests +- **Create Package** - Build distribution ZIP +- **Install Git Hooks** - Set up pre-commit hooks + +## Available Make Commands + +Run `make help` to see all available commands: + +```bash +make help # Show all commands +make dev-setup # Complete environment setup +make validate # Run all validations +make test # Run all tests +make quality # Check code quality +make package # Create distribution package +make clean # Remove generated files +make check # Quick check (validate + quality) +make all # Complete build pipeline +``` + +## Project Structure + +``` +moko-cassiopeia/ +├── src/ # Joomla template source +│ ├── templates/ # Template files +│ ├── media/ # Assets (CSS, JS, images) +│ ├── language/ # Language files +│ └── administrator/ # Admin files +├── scripts/ # Automation scripts +│ ├── validate/ # Validation scripts +│ ├── fix/ # Fix/update scripts +│ ├── release/ # Release scripts +│ ├── run/ # Execution scripts +│ ├── git/ # Git hooks +│ └── lib/ # Shared libraries +├── tests/ # Test suites +├── docs/ # Documentation +├── .github/workflows/ # CI/CD workflows +├── Makefile # Make commands +└── README.md # Project overview +``` + +## Next Steps + +### Learning the Workflow + +1. **Read the Workflow Guide**: [docs/WORKFLOW_GUIDE.md](./WORKFLOW_GUIDE.md) +2. **Review Joomla Development**: [docs/JOOMLA_DEVELOPMENT.md](./JOOMLA_DEVELOPMENT.md) +3. **Check Scripts Documentation**: [scripts/README.md](../scripts/README.md) + +### Creating Your First Feature + +1. **Create a version branch** via GitHub Actions: + - Go to Actions → Create version branch + - Enter version (e.g., 03.06.00) + - Select branch prefix: `dev/` + - Run workflow + +2. **Checkout the branch**: + ```bash + git fetch origin + git checkout dev/03.06.00 + ``` + +3. **Make changes and test**: + ```bash + # Edit files + vim src/templates/index.php + + # Validate + make validate-required + + # Check quality + make quality + ``` + +4. **Commit and push**: + ```bash + git add -A + git commit -m "feat: your feature description" + git push origin dev/03.06.00 + ``` + +5. **Watch CI**: Check GitHub Actions for automated testing + +### Understanding the Release Process + +``` +Development → RC → Stable → Production + (dev/) (rc/) (version/) (main) +``` + +1. **dev/X.Y.Z** - Active development +2. **rc/X.Y.Z** - Release candidate testing +3. **version/X.Y.Z** - Stable release +4. **main** - Production (auto-merged from version/) + +Use the Release Pipeline workflow to promote between stages. + +## Troubleshooting + +### Scripts Not Executable + +```bash +make fix-permissions +# Or manually: +chmod +x scripts/**/*.sh +``` + +### PHPStan/PHPCS Not Found + +```bash +make install +# Or manually: +composer global require squizlabs/php_codesniffer phpstan/phpstan +``` + +### Pre-commit Hook Fails + +```bash +# Run manually to see details +./scripts/git/pre-commit.sh + +# Quick mode (skip some checks) +./scripts/git/pre-commit.sh --quick + +# Skip quality checks +./scripts/git/pre-commit.sh --skip-quality + +# Bypass hook (not recommended) +git commit --no-verify +``` + +### CI Workflow Fails + +1. Check the workflow logs in GitHub Actions +2. Run the same checks locally: + ```bash + ./scripts/validate/manifest.sh + ./scripts/validate/php_syntax.sh + make quality + ``` + +### Need Help? + +- **Documentation**: Check [docs/](../docs/) directory +- **Issues**: Open an issue on GitHub +- **Contributing**: See [CONTRIBUTING.md](../CONTRIBUTING.md) + +## Best Practices + +### Before Committing + +```bash +# Always validate first +make validate-required + +# Check quality for PHP changes +make quality + +# Run tests if you changed functionality +make test +``` + +### Code Style + +- Follow PSR-12 standards +- Use `make phpcs-fix` to auto-fix issues +- Add SPDX license headers to new files +- Keep functions small and focused + +### Documentation + +- Update docs when changing workflows +- Add comments for complex logic +- Update CHANGELOG.md with changes +- Keep README.md current + +### Version Management + +- Use semantic versioning: Major.Minor.Patch (03.05.00) +- Update CHANGELOG.md with all changes +- Follow the version hierarchy: dev → rc → version → main +- Never skip stages in the release process + +## Useful Resources + +- [Joomla Documentation](https://docs.joomla.org/) +- [PSR-12 Coding Standard](https://www.php-fig.org/psr/psr-12/) +- [Semantic Versioning](https://semver.org/) +- [Conventional Commits](https://www.conventionalcommits.org/) + +## Quick Reference Card + +```bash +# Setup +make dev-setup # Initial setup +./scripts/git/install-hooks.sh # Install hooks + +# Development +make validate-required # Quick validation +make quality # Code quality +make test # Run tests + +# Building +make package # Create ZIP + +# Maintenance +make clean # Clean generated files +make fix-permissions # Fix script permissions + +# Help +make help # Show all commands +./scripts/run/validate_all.sh --help # Script help +``` + +--- + +**Document Version:** 1.0.0 +**Last Updated:** 2026-01-04 +**Get Started:** Run `make dev-setup` now! diff --git a/docs/WORKFLOW_GUIDE.md b/docs/WORKFLOW_GUIDE.md new file mode 100644 index 0000000..b1c9c0c --- /dev/null +++ b/docs/WORKFLOW_GUIDE.md @@ -0,0 +1,472 @@ +# Workflow Guide - Moko Cassiopeia + +Quick reference guide for GitHub Actions workflows and common development tasks. + +## Table of Contents + +- [Overview](#overview) +- [Workflow Quick Reference](#workflow-quick-reference) +- [Common Development Tasks](#common-development-tasks) +- [Troubleshooting](#troubleshooting) +- [Best Practices](#best-practices) + +## Overview + +This repository uses GitHub Actions for continuous integration, testing, quality checks, and deployment. All workflows are located in `.github/workflows/`. + +### Workflow Execution Model + +``` +┌─────────────────┐ +│ Code Changes │ +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ CI Pipeline │ ← Validation, Testing, Quality +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ Version Branch │ ← Create dev/X.Y.Z branch +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ Release Pipeline│ ← dev → rc → version → main +└────────┬────────┘ + │ + ▼ +┌─────────────────┐ +│ Distribution │ ← ZIP package + GitHub Release +└─────────────────┘ +``` + +## Workflow Quick Reference + +### Continuous Integration (ci.yml) + +**Trigger:** Automatic on push/PR to main, dev/*, rc/*, version/* branches + +**Purpose:** Validates code quality and repository structure + +**What it does:** +- ✅ Validates Joomla manifest XML +- ✅ Checks XML well-formedness +- ✅ Validates GitHub Actions workflows +- ✅ Runs PHP syntax checks +- ✅ Checks for secrets in code +- ✅ Validates license headers +- ✅ Verifies version alignment + +**When to check:** After every commit + +**How to view results:** +```bash +# Via GitHub CLI +gh run list --workflow=ci.yml --limit 5 +gh run view --log +``` + +### PHP Quality Checks (php_quality.yml) + +**Trigger:** Automatic on push/PR to main, dev/*, rc/*, version/* branches + +**Purpose:** Ensures PHP code quality and compatibility + +**What it does:** +- 🔍 PHPStan static analysis (level 5) +- 📏 PHP_CodeSniffer with PSR-12 standards +- ✔️ PHP 8.0+ compatibility checks + +**Matrix:** PHP 8.0, 8.1, 8.2, 8.3 + +**When to check:** Before committing PHP changes + +**How to run locally:** +```bash +# Install tools +composer global require squizlabs/php_codesniffer phpstan/phpstan + +# Run checks +phpcs --standard=phpcs.xml src/ +phpstan analyse --configuration=phpstan.neon +``` + +### Joomla Testing (joomla_testing.yml) + +**Trigger:** Automatic on push/PR to main, dev/*, rc/* branches + +**Purpose:** Tests template compatibility with Joomla versions + +**What it does:** +- 📦 Downloads and installs Joomla (4.4, 5.0, 5.1) +- 🔧 Installs template into Joomla +- ✅ Validates template installation +- 🧪 Runs Codeception tests + +**Matrix:** Joomla 4.4/5.0/5.1 × PHP 8.0/8.1/8.2/8.3 + +**When to check:** Before releasing new versions + +**How to test locally:** +```bash +# See docs/JOOMLA_DEVELOPMENT.md for local testing setup +codecept run +``` + +### Version Branch Creation (version_branch.yml) + +**Trigger:** Manual workflow dispatch + +**Purpose:** Creates a new version branch and bumps version numbers + +**What it does:** +- 🏷️ Creates dev/*, rc/*, or version/* branch +- 📝 Updates version in all files +- 📅 Updates manifest dates +- 📋 Moves CHANGELOG unreleased to version +- ✅ Validates version hierarchy + +**When to use:** Starting work on a new version + +**How to run:** +1. Go to Actions → Create version branch +2. Click "Run workflow" +3. Enter version (e.g., 03.05.00) +4. Select branch prefix (dev/, rc/, or version/) +5. Click "Run workflow" + +**Example:** +```yaml +new_version: 03.06.00 +branch_prefix: dev/ +version_text: beta +``` + +### Release Pipeline (release_pipeline.yml) + +**Trigger:** Manual workflow dispatch or release event + +**Purpose:** Promotes branches through release stages and creates distributions + +**What it does:** +- 🔄 Promotes branches: dev → rc → version → main +- 📅 Normalizes dates in manifest and CHANGELOG +- 📦 Builds distributable ZIP package +- 🚀 Uploads to SFTP server +- 🏷️ Creates Git tag +- 📋 Creates GitHub Release +- 🔒 Attests build provenance + +**When to use:** Promoting a version through release stages + +**How to run:** +1. Go to Actions → Release Pipeline +2. Click "Run workflow" +3. Select classification (auto/rc/stable) +4. Click "Run workflow" + +**Release flow:** +``` +dev/X.Y.Z → rc/X.Y.Z → version/X.Y.Z → main + (dev) (RC) (stable) (production) +``` + +### Deploy to Staging (deploy_staging.yml) + +**Trigger:** Manual workflow dispatch + +**Purpose:** Deploys template to staging/development environments + +**What it does:** +- ✅ Validates deployment prerequisites +- 📦 Builds deployment package +- 🚀 Uploads via SFTP to environment +- 📝 Creates deployment summary + +**When to use:** Testing in staging before production release + +**How to run:** +1. Go to Actions → Deploy to Staging +2. Click "Run workflow" +3. Select environment (staging/development/preview) +4. Optionally specify version +5. Click "Run workflow" + +**Required secrets:** +- `STAGING_HOST` - SFTP hostname +- `STAGING_USER` - SFTP username +- `STAGING_KEY` - SSH private key (or `STAGING_PASSWORD`) +- `STAGING_PATH` - Remote deployment path + +### Repository Health (repo_health.yml) + +**Trigger:** Manual workflow dispatch (admin only) + +**Purpose:** Comprehensive repository health and configuration checks + +**What it does:** +- 🔐 Validates release configuration +- 🌐 Tests SFTP connectivity +- 📂 Checks scripts governance +- 📄 Validates required artifacts +- 🔍 Extended checks (SPDX, ShellCheck, etc.) + +**When to use:** Before major releases or when debugging deployment issues + +**How to run:** +1. Go to Actions → Repo Health +2. Click "Run workflow" +3. Select profile (all/release/scripts/repo) +4. Click "Run workflow" + +**Profiles:** +- `all` - Run all checks +- `release` - Release configuration and SFTP only +- `scripts` - Scripts governance only +- `repo` - Repository health only + +## Common Development Tasks + +### Starting a New Feature + +```bash +# 1. Create a new version branch via GitHub Actions +# Actions → Create version branch → dev/X.Y.Z + +# 2. Clone and checkout the new branch +git fetch origin +git checkout dev/X.Y.Z + +# 3. Make your changes +vim src/templates/index.php + +# 4. Validate locally +./scripts/validate/php_syntax.sh +./scripts/validate/manifest.sh + +# 5. Commit and push +git add -A +git commit -m "feat: add new feature" +git push origin dev/X.Y.Z +``` + +### Running All Validations Locally + +```bash +# Run comprehensive validation suite +./scripts/run/validate_all.sh + +# Run with verbose output +./scripts/run/validate_all.sh -v + +# Run smoke tests +./scripts/run/smoke_test.sh +``` + +### Creating a Release Package + +```bash +# Package with auto-detected version +./scripts/release/package_extension.sh + +# Package with specific version +./scripts/release/package_extension.sh dist 03.05.00 + +# Verify package contents +unzip -l dist/moko-cassiopeia-*.zip +``` + +### Updating Version Numbers + +```bash +# Via GitHub Actions (recommended) +# Actions → Create version branch + +# Or manually with scripts +./scripts/fix/versions.sh 03.05.00 +``` + +### Updating CHANGELOG + +```bash +# Add new version entry +./scripts/release/update_changelog.sh 03.05.00 + +# Update release dates +./scripts/release/update_dates.sh 2025-01-15 03.05.00 +``` + +## Troubleshooting + +### CI Failures + +#### PHP Syntax Errors + +```bash +# Check specific file +php -l src/templates/index.php + +# Run validation script +./scripts/validate/php_syntax.sh +``` + +#### Manifest Validation Failed + +```bash +# Validate manifest XML +./scripts/validate/manifest.sh + +# Check XML well-formedness +./scripts/validate/xml_wellformed.sh +``` + +#### Version Alignment Issues + +```bash +# Check version in manifest matches CHANGELOG +./scripts/validate/version_alignment.sh + +# Fix versions +./scripts/fix/versions.sh 03.05.00 +``` + +### Workflow Failures + +#### "Permission denied" on scripts + +```bash +# Fix script permissions +chmod +x scripts/**/*.sh +``` + +#### "Branch already exists" + +```bash +# Check existing branches +git branch -r | grep dev/ + +# Delete remote branch if needed (carefully!) +git push origin --delete dev/03.05.00 +``` + +#### "Missing required secrets" + +Go to repository Settings → Secrets and variables → Actions, and add: +- `FTP_HOST` +- `FTP_USER` +- `FTP_KEY` or `FTP_PASSWORD` +- `FTP_PATH` + +#### SFTP Connection Failed + +1. Verify credentials in repo_health workflow: + - Actions → Repo Health → profile: release + +2. Check SSH key format (OpenSSH, not PuTTY PPK) + +3. Verify server allows connections from GitHub IPs + +### Quality Check Failures + +#### PHPStan Errors + +```bash +# Run locally to see full output +phpstan analyse --configuration=phpstan.neon + +# Generate baseline to ignore existing issues +phpstan analyse --configuration=phpstan.neon --generate-baseline +``` + +#### PHPCS Violations + +```bash +# Check violations +phpcs --standard=phpcs.xml src/ + +# Auto-fix where possible +phpcbf --standard=phpcs.xml src/ + +# Show specific error codes +phpcs --standard=phpcs.xml --report=source src/ +``` + +#### Joomla Testing Failed + +1. Check PHP/Joomla version matrix compatibility +2. Review MySQL connection errors +3. Verify template manifest structure +4. Check template file paths + +## Best Practices + +### Version Management + +1. **Always use version branches:** dev/X.Y.Z, rc/X.Y.Z, version/X.Y.Z +2. **Follow hierarchy:** dev → rc → version → main +3. **Update CHANGELOG:** Document all changes in Unreleased section +4. **Semantic versioning:** Major.Minor.Patch (03.05.00) + +### Code Quality + +1. **Run validations locally** before pushing +2. **Fix PHPStan warnings** at level 5 +3. **Follow PSR-12** coding standards +4. **Add SPDX license headers** to new files +5. **Keep functions small** and well-documented + +### Workflow Usage + +1. **Use CI for every commit** - automated validation +2. **Run repo_health before releases** - comprehensive checks +3. **Test on staging first** - never deploy directly to production +4. **Monitor workflow runs** - fix failures promptly +5. **Review workflow logs** - understand what changed + +### Release Process + +1. **Create dev branch** → Work on features +2. **Promote to rc** → Release candidate testing +3. **Promote to version** → Stable release +4. **Merge to main** → Production (auto-merged via PR) +5. **Create GitHub Release** → Public distribution + +### Security + +1. **Never commit secrets** - use GitHub Secrets +2. **Use SSH keys** for SFTP (not passwords) +3. **Scan for secrets** - runs automatically in CI +4. **Keep dependencies updated** - security patches +5. **Review security advisories** - GitHub Dependabot + +### Documentation + +1. **Update docs with code** - keep in sync +2. **Document workflow changes** - update this guide +3. **Add examples** - show, don't just tell +4. **Link to relevant docs** - cross-reference +5. **Keep README current** - first impression matters + +## Quick Links + +- [Main README](../README.md) - Project overview +- [Joomla Development Guide](./JOOMLA_DEVELOPMENT.md) - Testing and quality +- [Scripts README](../scripts/README.md) - Script documentation +- [CHANGELOG](../CHANGELOG.md) - Version history +- [CONTRIBUTING](../CONTRIBUTING.md) - Contribution guidelines + +## Getting Help + +1. **Check workflow logs** - Most issues have clear error messages +2. **Review this guide** - Common solutions documented +3. **Run validation scripts** - Identify specific issues +4. **Open an issue** - For bugs or questions +5. **Contact maintainers** - For access or configuration issues + +--- + +**Document Version:** 1.0.0 +**Last Updated:** 2026-01-04 +**Maintained by:** Moko Consulting Engineering diff --git a/scripts/git/install-hooks.sh b/scripts/git/install-hooks.sh new file mode 100755 index 0000000..c2307bb --- /dev/null +++ b/scripts/git/install-hooks.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# Install Git hooks for Moko Cassiopeia +# Copyright (C) 2025 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Moko-Cassiopeia.Scripts +# INGROUP: Scripts.Git +# REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia +# FILE: ./scripts/git/install-hooks.sh +# VERSION: 01.00.00 +# BRIEF: Install Git hooks for local development + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +REPO_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)" + +echo "Installing Git hooks..." +echo "" + +# Create .git/hooks directory if it doesn't exist +mkdir -p "${REPO_ROOT}/.git/hooks" + +# Install pre-commit hook +PRE_COMMIT_HOOK="${REPO_ROOT}/.git/hooks/pre-commit" +cat > "${PRE_COMMIT_HOOK}" <<'EOF' +#!/usr/bin/env bash +# Pre-commit hook - installed by scripts/git/install-hooks.sh + +SCRIPT_DIR="$(git rev-parse --show-toplevel)/scripts/git" + +if [ -f "${SCRIPT_DIR}/pre-commit.sh" ]; then + exec "${SCRIPT_DIR}/pre-commit.sh" "$@" +else + echo "Error: pre-commit.sh not found in ${SCRIPT_DIR}" + exit 1 +fi +EOF + +chmod +x "${PRE_COMMIT_HOOK}" + +echo "✓ Installed pre-commit hook" +echo "" +echo "The pre-commit hook will run automatically before each commit." +echo "" +echo "Options:" +echo " - Skip hook: git commit --no-verify" +echo " - Quick mode: ./scripts/git/pre-commit.sh --quick" +echo " - Skip quality checks: ./scripts/git/pre-commit.sh --skip-quality" +echo "" +echo "To uninstall hooks:" +echo " rm .git/hooks/pre-commit" +echo "" +echo "Done!" diff --git a/scripts/git/pre-commit.sh b/scripts/git/pre-commit.sh new file mode 100755 index 0000000..18ed667 --- /dev/null +++ b/scripts/git/pre-commit.sh @@ -0,0 +1,270 @@ +#!/usr/bin/env bash +# Pre-commit hook script for Moko Cassiopeia +# Copyright (C) 2025 Moko Consulting +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: Moko-Cassiopeia.Scripts +# INGROUP: Scripts.Git +# REPO: https://github.com/mokoconsulting-tech/moko-cassiopeia +# FILE: ./scripts/git/pre-commit.sh +# VERSION: 01.00.00 +# BRIEF: Pre-commit hook for local validation + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +. "${SCRIPT_DIR}/lib/common.sh" + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +log_success() { + echo -e "${GREEN}✓${NC} $*" +} + +log_warning() { + echo -e "${YELLOW}⚠${NC} $*" +} + +log_error() { + echo -e "${RED}✗${NC} $*" +} + +log_header() { + echo "" + echo "================================" + echo "$*" + echo "================================" +} + +# Parse arguments +SKIP_TESTS=false +SKIP_QUALITY=false +QUICK_MODE=false + +while [[ $# -gt 0 ]]; do + case $1 in + --skip-tests) + SKIP_TESTS=true + shift + ;; + --skip-quality) + SKIP_QUALITY=true + shift + ;; + --quick) + QUICK_MODE=true + shift + ;; + *) + echo "Unknown option: $1" + echo "Usage: pre-commit.sh [--skip-tests] [--skip-quality] [--quick]" + exit 1 + ;; + esac +done + +log_header "Pre-commit Validation" + +# Get list of staged files +STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR) + +if [ -z "$STAGED_FILES" ]; then + log_warning "No staged files to check" + exit 0 +fi + +echo "Checking staged files:" +echo "$STAGED_FILES" | sed 's/^/ - /' +echo "" + +# Track failures +FAILURES=0 + +# Check 1: PHP Syntax +log_header "Checking PHP Syntax" +PHP_FILES=$(echo "$STAGED_FILES" | grep '\.php$' || true) + +if [ -n "$PHP_FILES" ]; then + while IFS= read -r file; do + if [ -f "$file" ]; then + if php -l "$file" > /dev/null 2>&1; then + log_success "PHP syntax OK: $file" + else + log_error "PHP syntax error: $file" + php -l "$file" + FAILURES=$((FAILURES + 1)) + fi + fi + done <<< "$PHP_FILES" +else + echo " No PHP files to check" +fi + +# Check 2: XML Well-formedness +log_header "Checking XML Files" +XML_FILES=$(echo "$STAGED_FILES" | grep '\.xml$' || true) + +if [ -n "$XML_FILES" ]; then + while IFS= read -r file; do + if [ -f "$file" ]; then + if xmllint --noout "$file" 2>/dev/null; then + log_success "XML well-formed: $file" + else + log_error "XML malformed: $file" + xmllint --noout "$file" || true + FAILURES=$((FAILURES + 1)) + fi + fi + done <<< "$XML_FILES" +else + echo " No XML files to check" +fi + +# Check 3: YAML Syntax +log_header "Checking YAML Files" +YAML_FILES=$(echo "$STAGED_FILES" | grep -E '\.(yml|yaml)$' || true) + +if [ -n "$YAML_FILES" ]; then + while IFS= read -r file; do + if [ -f "$file" ]; then + if python3 -c "import yaml; yaml.safe_load(open('$file'))" 2>/dev/null; then + log_success "YAML valid: $file" + else + log_error "YAML invalid: $file" + python3 -c "import yaml; yaml.safe_load(open('$file'))" || true + FAILURES=$((FAILURES + 1)) + fi + fi + done <<< "$YAML_FILES" +else + echo " No YAML files to check" +fi + +# Check 4: Trailing Whitespace +log_header "Checking for Trailing Whitespace" +TEXT_FILES=$(echo "$STAGED_FILES" | grep -vE '\.(png|jpg|jpeg|gif|svg|ico|zip|gz|woff|woff2|ttf)$' || true) + +if [ -n "$TEXT_FILES" ]; then + TRAILING_WS=$(echo "$TEXT_FILES" | xargs grep -n '[[:space:]]$' 2>/dev/null || true) + if [ -n "$TRAILING_WS" ]; then + log_warning "Files with trailing whitespace found:" + echo "$TRAILING_WS" | sed 's/^/ /' + echo "" + echo " Run: sed -i 's/[[:space:]]*$//' to fix" + else + log_success "No trailing whitespace" + fi +else + echo " No text files to check" +fi + +# Check 5: SPDX License Headers (if not quick mode) +if [ "$QUICK_MODE" = false ]; then + log_header "Checking SPDX License Headers" + SOURCE_FILES=$(echo "$STAGED_FILES" | grep -E '\.(php|sh|js|ts|css)$' || true) + + if [ -n "$SOURCE_FILES" ]; then + MISSING_SPDX="" + while IFS= read -r file; do + if [ -f "$file" ]; then + if ! head -n 20 "$file" | grep -q 'SPDX-License-Identifier:'; then + MISSING_SPDX="${MISSING_SPDX} - ${file}\n" + fi + fi + done <<< "$SOURCE_FILES" + + if [ -n "$MISSING_SPDX" ]; then + log_warning "Files missing SPDX license header:" + echo -e "$MISSING_SPDX" + else + log_success "All source files have SPDX headers" + fi + else + echo " No source files to check" + fi +fi + +# Check 6: No Secrets +log_header "Checking for Secrets" +if [ -x "${SCRIPT_DIR}/validate/no_secrets.sh" ]; then + if "${SCRIPT_DIR}/validate/no_secrets.sh" > /dev/null 2>&1; then + log_success "No secrets detected" + else + log_error "Potential secrets detected!" + "${SCRIPT_DIR}/validate/no_secrets.sh" || true + FAILURES=$((FAILURES + 1)) + fi +else + echo " Secret scanner not available" +fi + +# Check 7: PHP_CodeSniffer (if not skipped) +if [ "$SKIP_QUALITY" = false ] && command -v phpcs >/dev/null 2>&1; then + log_header "Running PHP_CodeSniffer" + PHP_FILES=$(echo "$STAGED_FILES" | grep '\.php$' || true) + + if [ -n "$PHP_FILES" ]; then + if echo "$PHP_FILES" | xargs phpcs --standard=phpcs.xml -q 2>/dev/null; then + log_success "PHPCS passed" + else + log_warning "PHPCS found issues (non-blocking)" + echo "$PHP_FILES" | xargs phpcs --standard=phpcs.xml --report=summary || true + fi + else + echo " No PHP files to check" + fi +else + if [ "$SKIP_QUALITY" = true ]; then + echo " Skipping PHPCS (--skip-quality)" + else + echo " PHPCS not available (install with: composer global require squizlabs/php_codesniffer)" + fi +fi + +# Check 8: PHPStan (if not skipped and not quick mode) +if [ "$SKIP_QUALITY" = false ] && [ "$QUICK_MODE" = false ] && command -v phpstan >/dev/null 2>&1; then + log_header "Running PHPStan" + PHP_FILES=$(echo "$STAGED_FILES" | grep '\.php$' || true) + + if [ -n "$PHP_FILES" ]; then + if phpstan analyse --configuration=phpstan.neon --no-progress > /dev/null 2>&1; then + log_success "PHPStan passed" + else + log_warning "PHPStan found issues (non-blocking)" + phpstan analyse --configuration=phpstan.neon --no-progress || true + fi + else + echo " No PHP files to check" + fi +else + if [ "$SKIP_QUALITY" = true ]; then + echo " Skipping PHPStan (--skip-quality)" + elif [ "$QUICK_MODE" = true ]; then + echo " Skipping PHPStan (--quick mode)" + else + echo " PHPStan not available (install with: composer global require phpstan/phpstan)" + fi +fi + +# Summary +log_header "Pre-commit Summary" + +if [ $FAILURES -gt 0 ]; then + log_error "Pre-commit checks failed with $FAILURES error(s)" + echo "" + echo "To commit anyway, use: git commit --no-verify" + echo "To run quick checks only: ./scripts/git/pre-commit.sh --quick" + echo "To skip quality checks: ./scripts/git/pre-commit.sh --skip-quality" + exit 1 +else + log_success "All pre-commit checks passed!" + echo "" + echo "Tip: Use 'make validate' for comprehensive validation" + exit 0 +fi