diff --git a/.github/CLAUDE.md b/.github/CLAUDE.md index 45f7606..e991394 100644 --- a/.github/CLAUDE.md +++ b/.github/CLAUDE.md @@ -1,3 +1,22 @@ + + > [!IMPORTANT] > **πŸ”§ AI Self-Update Required on First Use** > @@ -19,135 +38,34 @@ > |---|---| > | `MokoCassiopeia` | The GitHub repository name (visible in the URL, `README.md` heading, or `git remote -v`) | > | `https://github.com/mokoconsulting-tech/MokoCassiopeia` | Full GitHub URL, e.g. `https://github.com/mokoconsulting-tech/` | -> | `A modern enhancement layer for Joomla’s Cassiopeia template. Moko-Cassiopeia adds Font Awesome 7, Bootstrap 5 helpers, an automatic Table of Contents (TOC) utility, and optional expansions including Google Tag Manager and Google Analytics (GA4) hooks.` | First paragraph of `README.md` body, or the GitHub repo description | > | `{{EXTENSION_NAME}}` | The `` element in `manifest.xml` at the repository root | > | `{{EXTENSION_TYPE}}` | The `type` attribute of the `` tag in `manifest.xml` (`component`, `module`, `plugin`, or `template`) | > | `{{EXTENSION_ELEMENT}}` | The `` tag in `manifest.xml`, or the filename prefix (e.g. `com_myextension`, `mod_mymodule`) | > > --- -# What This Repo Is +# MokoCassiopeia β€” GitHub Copilot Custom Instructions -**MokoCassiopeia** is a Moko Consulting **MokoWaaS** (Joomla) extension repository. +## What This Repo Is -A modern enhancement layer for Joomla’s Cassiopeia template. Moko-Cassiopeia adds Font Awesome 7, Bootstrap 5 helpers, an automatic Table of Contents (TOC) utility, and optional expansions including Google Tag Manager and Google Analytics (GA4) hooks. +This is a **Moko Consulting MokoWaaS** (Joomla) repository governed by [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards). All coding standards, workflows, and policies are defined there and enforced here via bulk sync. +Repository URL: https://github.com/mokoconsulting-tech/MokoCassiopeia Extension name: **{{EXTENSION_NAME}}** Extension type: **{{EXTENSION_TYPE}}** (`{{EXTENSION_ELEMENT}}`) -Repository URL: https://github.com/mokoconsulting-tech/MokoCassiopeia - -This repository is governed by [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards) β€” the single source of truth for coding standards, file-header policies, GitHub Actions workflows, and Terraform configuration templates across all Moko Consulting repositories. +Platform: **Joomla 4.x / MokoWaaS** --- -# Repo Structure +## Primary Language -``` -MokoCassiopeia/ -β”œβ”€β”€ manifest.xml # Joomla installer manifest (root β€” required) -β”œβ”€β”€ update.xml # Update server manifest (root β€” required) -β”œβ”€β”€ site/ # Frontend (site) code -β”‚ β”œβ”€β”€ controller.php -β”‚ β”œβ”€β”€ controllers/ -β”‚ β”œβ”€β”€ models/ -β”‚ └── views/ -β”œβ”€β”€ admin/ # Backend (admin) code -β”‚ β”œβ”€β”€ controller.php -β”‚ β”œβ”€β”€ controllers/ -β”‚ β”œβ”€β”€ models/ -β”‚ β”œβ”€β”€ views/ -β”‚ └── sql/ -β”œβ”€β”€ language/ # Language INI files -β”œβ”€β”€ media/ # CSS, JS, images -β”œβ”€β”€ docs/ # Technical documentation -β”œβ”€β”€ tests/ # Test suite -β”œβ”€β”€ .github/ -β”‚ β”œβ”€β”€ workflows/ # CI/CD workflows (synced from MokoStandards) -β”‚ β”œβ”€β”€ copilot-instructions.md -β”‚ └── CLAUDE.md # This file -β”œβ”€β”€ README.md # Version source of truth -β”œβ”€β”€ CHANGELOG.md -β”œβ”€β”€ CONTRIBUTING.md -└── LICENSE # GPL-3.0-or-later -``` +**PHP** (β‰₯ 7.4) is the primary language for this Joomla extension. JavaScript may be used for frontend enhancements. YAML uses 2-space indentation. All other text files use tabs per `.editorconfig`. --- -# Primary Language +## File Header β€” Always Required on New Files -**PHP** (β‰₯ 7.4) is the primary language for this Joomla extension. YAML uses 2-space indentation. All other text files use tabs per `.editorconfig`. - ---- - -# Version Management - -**`README.md` is the single source of truth for the repository version.** - -- **Bump the patch version on every PR** β€” increment `XX.YY.ZZ` (e.g. `01.02.03` β†’ `01.02.04`) in `README.md` before opening the PR; the `sync-version-on-merge` workflow propagates it to all `FILE INFORMATION` headers automatically on merge. -- Version format is zero-padded semver: `XX.YY.ZZ` (e.g. `01.02.03`). -- Never hardcode a version number in body text β€” use the badge or FILE INFORMATION header only. - -### Joomla Version Alignment - -Three files must **always have the same version**: - -| File | Where the version lives | -|------|------------------------| -| `README.md` | `FILE INFORMATION` block + badge | -| `manifest.xml` | `` tag | -| `update.xml` | `` in the most recent `` block | - -The `make release` command / release workflow syncs all three automatically. - ---- - -# update.xml β€” Required in Repo Root - -`update.xml` is the Joomla update server manifest. It allows Joomla installations to check for new versions of this extension via: - -```xml - - - - https://github.com/mokoconsulting-tech/MokoCassiopeia/raw/main/update.xml - - -``` - -**Rules:** -- Every release prepends a new `` block at the top β€” older entries are preserved. -- `` in `update.xml` must exactly match `` in `manifest.xml` and `README.md`. -- `` must be a publicly accessible GitHub Releases asset URL. -- `` β€” backslash is literal (Joomla regex syntax). - -Example `update.xml` entry for a new release: -```xml - - - {{EXTENSION_NAME}} - MokoCassiopeia - {{EXTENSION_ELEMENT}} - {{EXTENSION_TYPE}} - 01.02.04 - https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/tag/01.02.04 - - - https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/01.02.04/{{EXTENSION_ELEMENT}}-01.02.04.zip - - - - 7.4 - Moko Consulting - https://mokoconsulting.tech - - -``` - ---- - -# File Header Requirements - -Every new file **must** have a copyright header as its first content. JSON files, binary files, generated files, and third-party files are exempt. +Every new file needs a copyright header as its first content. **PHP:** ```php @@ -162,47 +80,141 @@ Every new file **must** have a copyright header as its first content. JSON files * DEFGROUP: MokoCassiopeia.{{EXTENSION_TYPE}} * INGROUP: MokoCassiopeia * REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia - * PATH: /site/controllers/item.php + * PATH: /path/to/file.php * VERSION: XX.YY.ZZ - * BRIEF: One-line description of file purpose + * BRIEF: One-line description of purpose */ defined('_JEXEC') or die; ``` -**Markdown / YAML / Shell / XML:** Use the appropriate comment syntax with the same fields. +**Markdown:** +```markdown + +``` + +**YAML / Shell / XML:** Use the appropriate comment syntax with the same fields. JSON files are exempt. --- -# Coding Standards +## Version Management -## Naming Conventions +**`README.md` is the single source of truth for the repository version.** -| Context | Convention | Example | -|---------|-----------|---------| -| PHP class | `PascalCase` | `ItemModel` | -| PHP method / function | `camelCase` | `getItems()` | -| PHP variable | `$snake_case` | `$item_id` | -| PHP constant | `UPPER_SNAKE_CASE` | `MAX_ITEMS` | -| PHP class file | `PascalCase.php` | `ItemModel.php` | -| YAML workflow | `kebab-case.yml` | `ci-joomla.yml` | -| Markdown doc | `kebab-case.md` | `installation-guide.md` | +- **Bump the patch version on every PR** β€” increment `XX.YY.ZZ` (e.g. `01.02.03` β†’ `01.02.04`) in `README.md` before opening the PR; the `sync-version-on-merge` workflow propagates it automatically to all badges and `FILE INFORMATION` headers on merge to `main`. +- The `VERSION: XX.YY.ZZ` field in `README.md` governs all other version references. +- Version format is zero-padded semver: `XX.YY.ZZ` (e.g. `01.02.03`). +- Never hardcode a specific version in document body text β€” use the badge or FILE INFORMATION header only. -## Commit Messages +### Joomla Version Alignment -Format: `(): ` β€” imperative, lower-case subject, no trailing period. +The version in `README.md` **must always match** the `` tag in `manifest.xml` and the latest entry in `update.xml`. The `make release` command / release workflow updates all three automatically. -Valid types: `feat` Β· `fix` Β· `docs` Β· `chore` Β· `ci` Β· `refactor` Β· `style` Β· `test` Β· `perf` Β· `revert` Β· `build` +```xml + +01.02.04 -## Branch Naming - -Format: `/[/description]` - -Approved prefixes: `dev/` Β· `rc/` Β· `version/` Β· `patch/` Β· `copilot/` Β· `dependabot/` + + + + {{EXTENSION_NAME}} + 01.02.04 + + + https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/01.02.04/{{EXTENSION_ELEMENT}}-01.02.04.zip + + + + + + +``` --- -# GitHub Actions β€” Token Usage +## Joomla Extension Structure + +``` +MokoCassiopeia/ +β”œβ”€β”€ manifest.xml # Joomla installer manifest (root β€” required) +β”œβ”€β”€ update.xml # Update server manifest (root β€” required, see below) +β”œβ”€β”€ site/ # Frontend (site) code +β”‚ β”œβ”€β”€ controller.php +β”‚ β”œβ”€β”€ controllers/ +β”‚ β”œβ”€β”€ models/ +β”‚ └── views/ +β”œβ”€β”€ admin/ # Backend (admin) code +β”‚ β”œβ”€β”€ controller.php +β”‚ β”œβ”€β”€ controllers/ +β”‚ β”œβ”€β”€ models/ +β”‚ β”œβ”€β”€ views/ +β”‚ └── sql/ +β”œβ”€β”€ language/ # Language INI files +β”œβ”€β”€ media/ # CSS, JS, images (deployed to /media/{{EXTENSION_ELEMENT}}/) +β”œβ”€β”€ docs/ # Technical documentation +β”œβ”€β”€ tests/ # Test suite +β”œβ”€β”€ .github/ +β”‚ β”œβ”€β”€ workflows/ +β”‚ β”œβ”€β”€ copilot-instructions.md # This file +β”‚ └── CLAUDE.md +β”œβ”€β”€ README.md # Version source of truth +β”œβ”€β”€ CHANGELOG.md +β”œβ”€β”€ CONTRIBUTING.md +β”œβ”€β”€ LICENSE # GPL-3.0-or-later +└── Makefile # Build automation +``` + +--- + +## update.xml β€” Required in Repo Root + +`update.xml` **must exist at the repository root**. It is the Joomla update server manifest that allows Joomla installations to check for new versions of this extension. + +The `manifest.xml` must reference it via: +```xml + + + https://github.com/mokoconsulting-tech/MokoCassiopeia/raw/main/update.xml + + +``` + +**Rules:** +- Every release must prepend a new `` block at the top of `update.xml` β€” old entries must be preserved below. +- The `` in `update.xml` must exactly match `` in `manifest.xml` and the version in `README.md`. +- The `` must be a publicly accessible direct download link (GitHub Releases asset URL). +- `` β€” the backslash is a **literal backslash character** in the XML attribute value; Joomla's update-server parser treats the value as a regular expression, so `\.` matches a literal dot and `[0-9]+` matches one or more digits. Do not double-escape it. + +--- + +## manifest.xml Rules + +- Lives at the repo root as `manifest.xml` (not inside `site/` or `admin/`). +- `` tag must be kept in sync with `README.md` version and `update.xml`. +- Must include `` block pointing to this repo's `update.xml`. +- Must include `` and `` sections. +- Joomla 4.x requires `Moko\{{EXTENSION_NAME}}` for namespaced extensions. + +--- + +## GitHub Actions β€” Token Usage Every workflow must use **`secrets.GH_TOKEN`** (the org-level Personal Access Token). @@ -217,58 +229,76 @@ env: ``` ```yaml -# ❌ Wrong β€” never use these +# ❌ Wrong β€” never use these in workflows token: ${{ github.token }} token: ${{ secrets.GITHUB_TOKEN }} ``` --- -# Keeping Documentation Current +## MokoStandards Reference -| Change type | Documentation to update | -|-------------|------------------------| -| New or renamed PHP class/method | PHPDoc block; `docs/api/` entry | -| New or changed `manifest.xml` | Sync version to `update.xml` and `README.md` | -| New release | Prepend `` to `update.xml`; update `CHANGELOG.md`; bump `README.md` | -| New or changed workflow | `docs/workflows/.md` | -| Any modified file | Update the `VERSION` field in that file's `FILE INFORMATION` block | -| **Every PR** | **Bump the patch version** β€” increment `XX.YY.ZZ` in `README.md`; `sync-version-on-merge` propagates it | - ---- - -# What NOT to Do - -- **Never commit directly to `main`** β€” all changes go through a PR. -- **Never hardcode version numbers** in body text β€” update `README.md` and let automation propagate. -- **Never let `manifest.xml`, `update.xml`, and `README.md` versions diverge.** -- **Never skip the FILE INFORMATION block** on a new source file. -- **Never use bare `catch (\Throwable $e) {}`** β€” always log or re-throw. -- **Never mix tabs and spaces** within a file β€” follow `.editorconfig`. -- **Never use `github.token` or `secrets.GITHUB_TOKEN` in workflows** β€” always use `secrets.GH_TOKEN`. -- **Never remove `defined('_JEXEC') or die;`** from web-accessible PHP files. - ---- - -# PR Checklist - -Before opening a PR, verify: - -- [ ] Patch version bumped in `README.md` (e.g. `01.02.03` β†’ `01.02.04`) -- [ ] If this is a release: `manifest.xml` version updated; `update.xml` updated with new entry -- [ ] FILE INFORMATION headers updated in modified files -- [ ] CHANGELOG.md updated -- [ ] Tests pass - ---- - -# Key Policy Documents (MokoStandards) +This repository is governed by [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards). Authoritative policies: | Document | Purpose | |----------|---------| | [file-header-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/file-header-standards.md) | Copyright-header rules for every file type | | [coding-style-guide.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/coding-style-guide.md) | Naming and formatting conventions | | [branching-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/branching-strategy.md) | Branch naming, hierarchy, and release workflow | -| [merge-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/merge-strategy.md) | Squash-merge policy and PR conventions | +| [merge-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/merge-strategy.md) | Squash-merge policy and PR title/body conventions | | [changelog-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/changelog-standards.md) | How and when to update CHANGELOG.md | -| [joomla-development-guide.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/guide/waas/joomla-development-guide.md) | MokoWaaS Joomla extension development guide | \ No newline at end of file +| [joomla-development-guide.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/guide/waas/joomla-development-guide.md) | MokoWaaS Joomla extension development guide | + +--- + +## Naming Conventions + +| Context | Convention | Example | +|---------|-----------|---------| +| PHP class | `PascalCase` | `MyController` | +| PHP method / function | `camelCase` | `getItems()` | +| PHP variable | `$snake_case` | `$item_id` | +| PHP constant | `UPPER_SNAKE_CASE` | `MAX_ITEMS` | +| PHP class file | `PascalCase.php` | `ItemModel.php` | +| YAML workflow | `kebab-case.yml` | `ci-joomla.yml` | +| Markdown doc | `kebab-case.md` | `installation-guide.md` | + +--- + +## Commit Messages + +Format: `(): ` β€” imperative, lower-case subject, no trailing period. + +Valid types: `feat` Β· `fix` Β· `docs` Β· `chore` Β· `ci` Β· `refactor` Β· `style` Β· `test` Β· `perf` Β· `revert` Β· `build` + +--- + +## Branch Naming + +Format: `/[/description]` + +Approved prefixes: `dev/` Β· `rc/` Β· `version/` Β· `patch/` Β· `copilot/` Β· `dependabot/` + +--- + +## Keeping Documentation Current + +| Change type | Documentation to update | +|-------------|------------------------| +| New or renamed PHP class/method | PHPDoc block; `docs/api/` entry | +| New or changed manifest.xml | Update `update.xml` version; bump README.md version | +| New release | Prepend `` block to `update.xml`; update CHANGELOG.md; bump README.md version | +| New or changed workflow | `docs/workflows/.md` | +| Any modified file | Update the `VERSION` field in that file's `FILE INFORMATION` block | +| **Every PR** | **Bump the patch version** β€” increment `XX.YY.ZZ` in `README.md`; `sync-version-on-merge` propagates it | + +--- + +## Key Constraints + +- Never commit directly to `main` β€” all changes go via PR, squash-merged +- Never skip the FILE INFORMATION block on a new file +- Never add `defined('_JEXEC') or die;` to CLI scripts or model tests β€” only to web-accessible PHP files +- Never hardcode version numbers in body text β€” update `README.md` and let automation propagate +- Never use `github.token` or `secrets.GITHUB_TOKEN` in workflows β€” always use `secrets.GH_TOKEN` +- Never let `manifest.xml` version, `update.xml` version, and `README.md` version go out of sync diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 3968dc4..e991394 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,3 +1,22 @@ + + > [!IMPORTANT] > **πŸ”§ AI Self-Update Required on First Use** > @@ -282,4 +301,4 @@ Approved prefixes: `dev/` Β· `rc/` Β· `version/` Β· `patch/` Β· `copilot/` Β· `d - Never add `defined('_JEXEC') or die;` to CLI scripts or model tests β€” only to web-accessible PHP files - Never hardcode version numbers in body text β€” update `README.md` and let automation propagate - Never use `github.token` or `secrets.GITHUB_TOKEN` in workflows β€” always use `secrets.GH_TOKEN` -- Never let `manifest.xml` version, `update.xml` version, and `README.md` version go out of sync \ No newline at end of file +- Never let `manifest.xml` version, `update.xml` version, and `README.md` version go out of sync diff --git a/README.md b/README.md index 3bf465f..e26e701 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,14 @@ BRIEF: Documentation for MokoCassiopeia template --> -# README - MokoCassiopeia (VERSION: 03.09.03) +# MokoCassiopeia **A Modern, Lightweight Joomla Template Based on Cassiopeia** +[![Version](https://img.shields.io/badge/version-03.09.04-green.svg)](https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/tag/v03) [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0) -[![Joomla](https://img.shields.io/badge/Joomla-4.4.x%20%7C%205.x-blue.svg)](https://www.joomla.org) -[![PHP](https://img.shields.io/badge/PHP-8.0%2B-blue.svg)](https://www.php.net) +[![Joomla](https://img.shields.io/badge/Joomla-5.x%20%7C%206.x-blue.svg)](https://www.joomla.org) +[![PHP](https://img.shields.io/badge/PHP-8.1%2B-blue.svg)](https://www.php.net) MokoCassiopeia is a modern, lightweight enhancement layer built on top of Joomla's Cassiopeia template. It adds **Font Awesome 7**, **Bootstrap 5** helpers, an automatic **Table of Contents (TOC)** utility, advanced **Dark Mode** theming, and optional integrations for **Google Tag Manager** and **Google Analytics (GA4)**β€”all while maintaining minimal core template overrides for maximum upgrade compatibility. diff --git a/scripts/README.md b/scripts/README.md deleted file mode 100644 index 0c3f452..0000000 --- a/scripts/README.md +++ /dev/null @@ -1,276 +0,0 @@ -# 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/` -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 - ---- - -## 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/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/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/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/` 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 - -.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 deleted file mode 100755 index 5e8055d..0000000 --- a/scripts/build-release.sh +++ /dev/null @@ -1,132 +0,0 @@ -#!/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/templateDetails.xml" ]; then - VERSION=$(grep -oP '\K[^<]+' "${PROJECT_ROOT}/src/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 (excluding media directory) -if [ -d "src" ]; then - rsync -av --exclude='.git*' --exclude='media' src/ "${PACKAGE_DIR}/" -else - log_error "src 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 "" diff --git a/scripts/download-google-fonts.php b/scripts/download-google-fonts.php new file mode 100644 index 0000000..98ea144 --- /dev/null +++ b/scripts/download-google-fonts.php @@ -0,0 +1,73 @@ + + * + * This file is part of a Moko Consulting project. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * # FILE INFORMATION + * DEFGROUP: Joomla.Template.Site + * INGROUP: MokoCassiopeia + * PATH: scripts/download-google-fonts.php + * VERSION: 03.09.05 + * BRIEF: Download Google Fonts (woff2) for local self-hosting + */ + +$fontsDir = __DIR__ . '/../src/media/fonts'; + +if (!is_dir($fontsDir)) { + fwrite(STDERR, "Error: Fonts directory not found: {$fontsDir}\n"); + exit(1); +} + +$fonts = [ + 'Roboto' => 'https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;700&display=swap', + 'Noto Sans' => 'https://fonts.googleapis.com/css2?family=Noto+Sans:wght@100;300;400;700&display=swap', + 'Fira Sans' => 'https://fonts.googleapis.com/css2?family=Fira+Sans:wght@100;300;400;700&display=swap', +]; + +$userAgent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; + +echo "Google Fonts Downloader for MokoCassiopeia\n"; +echo str_repeat('=', 48) . "\n"; +echo "Target: {$fontsDir}\n\n"; + +foreach ($fonts as $name => $url) { + echo "Downloading {$name}...\n"; + + $ctx = stream_context_create(['http' => ['header' => "User-Agent: {$userAgent}\r\n"]]); + $css = @file_get_contents($url, false, $ctx); + + if ($css === false) { + fwrite(STDERR, " FAIL: could not fetch CSS for {$name}\n"); + continue; + } + + preg_match_all('#https://fonts\.gstatic\.com[^)]*\.woff2#', $css, $matches); + + if (empty($matches[0])) { + fwrite(STDERR, " FAIL: no woff2 URLs found for {$name}\n"); + continue; + } + + $count = 0; + foreach ($matches[0] as $fontUrl) { + $filename = basename($fontUrl); + $dest = $fontsDir . '/' . $filename; + + $data = @file_get_contents($fontUrl, false, $ctx); + if ($data === false) { + fwrite(STDERR, " FAIL: {$filename}\n"); + continue; + } + + file_put_contents($dest, $data); + $size = round(strlen($data) / 1024, 1); + echo " OK: {$filename} ({$size} KB)\n"; + $count++; + } + + echo " {$count} file(s) downloaded\n\n"; +} + +echo "Done.\n"; diff --git a/scripts/download-google-fonts.sh b/scripts/download-google-fonts.sh deleted file mode 100755 index dc861b1..0000000 --- a/scripts/download-google-fonts.sh +++ /dev/null @@ -1,96 +0,0 @@ -#!/bin/bash -# Copyright (C) 2026 Moko Consulting -# SPDX-License-Identifier: GPL-3.0-or-later -# -# Download Google Fonts for self-hosting -# This script downloads Roboto, Noto Sans, and Fira Sans fonts - -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 - -# Target directory -FONTS_DIR="../src/media/fonts" - -echo -e "${BLUE}╔════════════════════════════════════════════════╗${NC}" -echo -e "${BLUE}β•‘ Google Fonts Downloader for MokoCassiopeia β•‘${NC}" -echo -e "${BLUE}β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•${NC}" -echo "" - -# Check if fonts directory exists -if [ ! -d "$FONTS_DIR" ]; then - echo -e "${RED}βœ— Error: Fonts directory not found: $FONTS_DIR${NC}" - exit 1 -fi - -cd "$FONTS_DIR" - -echo -e "${YELLOW}Target directory: $(pwd)${NC}" -echo "" - -# Function to download font CSS and extract font files -download_font() { - local font_name="$1" - local font_url="$2" - local display_name="$3" - - echo -e "${GREEN}Downloading $display_name...${NC}" - - # Download CSS with user agent for woff2 format - css=$(curl -s -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" "$font_url") - - if [ -z "$css" ]; then - echo -e "${RED} βœ— Failed to download CSS${NC}" - return 1 - fi - - # Extract woff2 URLs - urls=$(echo "$css" | grep -oP 'https://fonts\.gstatic\.com[^\)]*\.woff2' || true) - - if [ -z "$urls" ]; then - echo -e "${RED} βœ— No font URLs found in CSS${NC}" - return 1 - fi - - count=0 - while IFS= read -r url; do - if [ -z "$url" ]; then - continue - fi - - filename=$(basename "$url") - echo -e " β†’ Downloading ${filename}..." - - if curl -s "$url" -o "$filename"; then - size=$(du -h "$filename" | cut -f1) - echo -e "${GREEN} βœ“ Downloaded ($size)${NC}" - ((count++)) - else - echo -e "${RED} βœ— Failed${NC}" - fi - done <<< "$urls" - - echo -e "${GREEN} βœ“ Downloaded $count font files${NC}" - echo "" -} - -# Download fonts -download_font "roboto" "https://fonts.googleapis.com/css2?family=Roboto:wght@100;300;400;700&display=swap" "Roboto" -download_font "noto-sans" "https://fonts.googleapis.com/css2?family=Noto+Sans:wght@100;300;400;700&display=swap" "Noto Sans" -download_font "fira-sans" "https://fonts.googleapis.com/css2?family=Fira+Sans:wght@100;300;400;700&display=swap" "Fira Sans" - -echo -e "${GREEN}╔════════════════════════════════════════════╗${NC}" -echo -e "${GREEN}β•‘ βœ“ All fonts downloaded successfully! β•‘${NC}" -echo -e "${GREEN}β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•${NC}" -echo "" -echo -e "Font files saved to: ${BLUE}$(pwd)${NC}" -echo "" -echo "Next steps:" -echo "1. Verify font files are present" -echo "2. Update templateDetails.xml font options (if needed)" -echo "3. Remove Google Fonts CDN preconnect links from PHP templates" diff --git a/scripts/minify.js b/scripts/minify.js deleted file mode 100644 index b6116e8..0000000 --- a/scripts/minify.js +++ /dev/null @@ -1,188 +0,0 @@ -#!/usr/bin/env node -/* Copyright (C) 2026 Moko Consulting - * - * This file is part of a Moko Consulting project. - * - * SPDX-License-Identifier: GPL-3.0-or-later - * - * # FILE INFORMATION - * DEFGROUP: Joomla.Template.Site - * INGROUP: MokoCassiopeia - * REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia - * PATH: ./scripts/minify.js - * VERSION: 03.09.03 - * BRIEF: Generates .min.css and .min.js files from the Joomla asset manifest - */ - -'use strict'; - -const fs = require('fs'); -const path = require('path'); - -const CleanCSS = require('clean-css'); -const { minify: terserMinify } = require('terser'); - -// --------------------------------------------------------------------------- -// Config -// --------------------------------------------------------------------------- - -const ROOT = path.resolve(__dirname, '..'); -const SRC_MEDIA = path.join(ROOT, 'src', 'media'); -const ASSET_JSON = path.join(ROOT, 'src', 'joomla.asset.json'); - -// URI prefix used in the manifest β€” maps to SRC_MEDIA on disk. -// e.g. "media/templates/site/mokocassiopeia/css/template.css" -const URI_PREFIX = 'media/templates/site/mokocassiopeia/'; - -// --------------------------------------------------------------------------- -// Helpers -// --------------------------------------------------------------------------- - -/** - * Resolve a manifest URI to an absolute disk path under src/media/. - * - * @param {string} uri e.g. "media/templates/site/mokocassiopeia/css/foo.css" - * @returns {string|null} - */ -function uriToPath(uri) { - if (!uri.startsWith(URI_PREFIX)) return null; - return path.join(SRC_MEDIA, uri.slice(URI_PREFIX.length)); -} - -/** - * Return true if the filename looks like an already-minified file or belongs - * to a vendor bundle we don't own. - */ -function isVendorOrUserFile(filePath) { - const rel = filePath.replace(SRC_MEDIA + path.sep, ''); - return rel.startsWith('vendor' + path.sep) - || path.basename(filePath).startsWith('user.'); -} - -// --------------------------------------------------------------------------- -// Pair detection -// --------------------------------------------------------------------------- - -/** - * Read the asset manifest and return an array of { src, dest, type } pairs - * where dest is a minified version of src that doesn't already exist or is - * older than src. - * - * Pairing logic: for every non-.min asset, check whether the manifest also - * contains a corresponding .min asset. If so, that's our pair. - */ -function detectPairs(assets) { - // Build a lookup of all URIs in the manifest. - const uriSet = new Set(assets.map(a => a.uri)); - - const pairs = []; - - for (const asset of assets) { - const { uri, type } = asset; - if (type !== 'style' && type !== 'script') continue; - - // Skip already-minified entries. - if (/\.min\.(css|js)$/.test(uri)) continue; - - // Derive the expected .min URI. - const minUri = uri.replace(/\.(css|js)$/, '.min.$1'); - if (!uriSet.has(minUri)) continue; - - const srcPath = uriToPath(uri); - const destPath = uriToPath(minUri); - if (!srcPath || !destPath) continue; - - if (isVendorOrUserFile(srcPath)) continue; - - if (!fs.existsSync(srcPath)) { - console.warn(` [skip] source missing: ${srcPath}`); - continue; - } - - pairs.push({ src: srcPath, dest: destPath, type }); - } - - return pairs; -} - -// --------------------------------------------------------------------------- -// Minifiers -// --------------------------------------------------------------------------- - -async function minifyCSS(srcPath, destPath) { - const source = fs.readFileSync(srcPath, 'utf8'); - const result = new CleanCSS({ level: 2, returnPromise: true }); - const output = await result.minify(source); - - if (output.errors && output.errors.length) { - throw new Error(output.errors.join('\n')); - } - - fs.mkdirSync(path.dirname(destPath), { recursive: true }); - fs.writeFileSync(destPath, output.styles, 'utf8'); - - const srcSize = Buffer.byteLength(source, 'utf8'); - const destSize = Buffer.byteLength(output.styles, 'utf8'); - const saving = (100 - (destSize / srcSize * 100)).toFixed(1); - - return { srcSize, destSize, saving }; -} - -async function minifyJS(srcPath, destPath) { - const source = fs.readFileSync(srcPath, 'utf8'); - const result = await terserMinify(source, { - compress: { drop_console: false }, - mangle: true, - format: { comments: false } - }); - - if (!result.code) throw new Error('terser returned no output'); - - fs.mkdirSync(path.dirname(destPath), { recursive: true }); - fs.writeFileSync(destPath, result.code, 'utf8'); - - const srcSize = Buffer.byteLength(source, 'utf8'); - const destSize = Buffer.byteLength(result.code, 'utf8'); - const saving = (100 - (destSize / srcSize * 100)).toFixed(1); - - return { srcSize, destSize, saving }; -} - -// --------------------------------------------------------------------------- -// Main -// --------------------------------------------------------------------------- - -(async () => { - const manifest = JSON.parse(fs.readFileSync(ASSET_JSON, 'utf8')); - const pairs = detectPairs(manifest.assets); - - if (pairs.length === 0) { - console.log('No pairs found β€” nothing to minify.'); - return; - } - - console.log(`\nMinifying ${pairs.length} file(s)...\n`); - - let ok = 0, fail = 0; - - for (const { src, dest, type } of pairs) { - const label = path.relative(ROOT, src); - process.stdout.write(` ${label} ... `); - - try { - const stats = type === 'style' - ? await minifyCSS(src, dest) - : await minifyJS(src, dest); - - const kb = n => (n / 1024).toFixed(1) + ' kB'; - console.log(`${kb(stats.srcSize)} β†’ ${kb(stats.destSize)} (${stats.saving}% saved)`); - ok++; - } catch (err) { - console.error(`FAILED\n ${err.message}`); - fail++; - } - } - - console.log(`\nDone. ${ok} succeeded, ${fail} failed.\n`); - if (fail > 0) process.exit(1); -})(); diff --git a/src/index.php b/src/index.php index 4fc14e0..5a5abf0 100644 --- a/src/index.php +++ b/src/index.php @@ -448,7 +448,10 @@ $wa->useScript('user.js'); // js/user.js countModules('search', true)) : ?> -