From 1dcbfe12195fca8e0cbb15b726463c8ee85691fd Mon Sep 17 00:00:00 2001 From: Jonathan Miller <230051081+jmiller-moko@users.noreply.github.com> Date: Tue, 24 Mar 2026 11:22:08 -0500 Subject: [PATCH] chore: Sync MokoStandards workflows and configurations (#85) * chore: update LICENSE from MokoStandards * chore: add update.xml from MokoStandards * chore: update phpstan.neon from MokoStandards * chore: add Makefile from MokoStandards * chore: update .gitignore from MokoStandards * chore: add composer.json from MokoStandards * chore: add .moko-standards from MokoStandards * chore: update .github/copilot.yml from MokoStandards * chore: update .github/copilot-instructions.md from MokoStandards * chore: update .github/CLAUDE.md from MokoStandards * chore: add .github/workflows/codeql-analysis.yml from MokoStandards * chore: add .github/workflows/standards-compliance.yml from MokoStandards * chore: add .github/workflows/enterprise-firewall-setup.yml from MokoStandards * chore: add .github/workflows/deploy-dev.yml from MokoStandards * chore: add .github/ISSUE_TEMPLATE/config.yml from MokoStandards * chore: add .github/ISSUE_TEMPLATE/adr.md from MokoStandards * chore: add .github/ISSUE_TEMPLATE/bug_report.md from MokoStandards * chore: add .github/ISSUE_TEMPLATE/documentation.md from MokoStandards * chore: add .github/ISSUE_TEMPLATE/enterprise_support.md from MokoStandards * chore: add .github/ISSUE_TEMPLATE/feature_request.md from MokoStandards * chore: add .github/ISSUE_TEMPLATE/firewall-request.md from MokoStandards * chore: add .github/ISSUE_TEMPLATE/question.md from MokoStandards * chore: add .github/ISSUE_TEMPLATE/request-license.md from MokoStandards * chore: add .github/ISSUE_TEMPLATE/rfc.md from MokoStandards * chore: add .github/ISSUE_TEMPLATE/security.md from MokoStandards * chore: add .github/ISSUE_TEMPLATE/joomla_issue.md from MokoStandards --- .github/CLAUDE.md | 711 ++--- .github/ISSUE_TEMPLATE/adr.md | 125 + .github/ISSUE_TEMPLATE/bug_report.md | 63 + .github/ISSUE_TEMPLATE/config.yml | 18 + .github/ISSUE_TEMPLATE/documentation.md | 67 + .github/ISSUE_TEMPLATE/enterprise_support.md | 100 + .github/ISSUE_TEMPLATE/feature_request.md | 66 + .github/ISSUE_TEMPLATE/firewall-request.md | 203 ++ .github/ISSUE_TEMPLATE/joomla_issue.md | 102 + .github/ISSUE_TEMPLATE/question.md | 86 + .github/ISSUE_TEMPLATE/request-license.md | 120 + .github/ISSUE_TEMPLATE/rfc.md | 141 + .github/ISSUE_TEMPLATE/security.md | 66 + .github/copilot-instructions.md | 625 ++-- .github/copilot.yml | 55 + .github/workflows/codeql-analysis.yml | 115 + .github/workflows/deploy-dev.yml | 587 ++++ .../workflows/enterprise-firewall-setup.yml | 758 +++++ .github/workflows/standards-compliance.yml | 2534 +++++++++++++++++ .gitignore | 111 +- .moko-standards | 20 + LICENSE | 19 +- Makefile | 333 +++ composer.json | 55 + phpstan.neon | 57 +- update.xml | 34 + 26 files changed, 6228 insertions(+), 943 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/adr.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/documentation.md create mode 100644 .github/ISSUE_TEMPLATE/enterprise_support.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/firewall-request.md create mode 100644 .github/ISSUE_TEMPLATE/joomla_issue.md create mode 100644 .github/ISSUE_TEMPLATE/question.md create mode 100644 .github/ISSUE_TEMPLATE/request-license.md create mode 100644 .github/ISSUE_TEMPLATE/rfc.md create mode 100644 .github/ISSUE_TEMPLATE/security.md create mode 100644 .github/workflows/codeql-analysis.yml create mode 100644 .github/workflows/deploy-dev.yml create mode 100644 .github/workflows/enterprise-firewall-setup.yml create mode 100644 .github/workflows/standards-compliance.yml create mode 100644 .moko-standards create mode 100644 Makefile create mode 100644 composer.json create mode 100644 update.xml diff --git a/.github/CLAUDE.md b/.github/CLAUDE.md index bd84bb2..45f7606 100644 --- a/.github/CLAUDE.md +++ b/.github/CLAUDE.md @@ -1,545 +1,274 @@ +> [!IMPORTANT] +> **πŸ”§ AI Self-Update Required on First Use** +> +> This file may contain unfilled `{{TOKEN}}` placeholders. The MokoStandards bulk-sync system +> replaces them automatically at sync time; if you can still see them, sync has not yet run for +> this repository β€” or this is a brand-new repo. +> +> **On your very first interaction in this repository, before answering any other question:** +> +> 1. Check whether any `{{TOKEN}}` placeholders remain in this file. +> 2. If they do, locate the values as described in the table below and replace every occurrence +> in **both** `.github/copilot-instructions.md` and `.github/CLAUDE.md`. +> 3. After replacing all tokens, **delete this entire notice block** (from `> [!IMPORTANT]` +> through the closing `> ---` line) in both files. +> 4. Tell the developer: *"I've self-updated the AI context files with this repository's +> values β€” all set."* +> +> | Placeholder | Where to find the value | +> |---|---| +> | `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 is a modern, lightweight Joomla template that extends Cassiopeia (Joomla's default template) with enhanced features while maintaining maximum upgrade compatibility. It is used by Joomla 4.4.x and 5.x site administrators who want Font Awesome 7, Bootstrap 5 helpers, dark mode theming, and table of contents features without breaking core Joomla upgrades. This is NOT a standalone theme framework, NOT a template builder, and NOT a general-purpose UI libraryβ€”it is specifically a Joomla site template extension that must be installed via Joomla's Extension Manager. Repository: https://github.com/mokoconsulting-tech/MokoCassiopeia +**MokoCassiopeia** is a Moko Consulting **MokoWaaS** (Joomla) extension repository. + +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. + +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. + +--- # Repo Structure ``` MokoCassiopeia/ -β”œβ”€β”€ .github/ # GitHub workflows (CI, testing, release), Copilot instructions -β”œβ”€β”€ docs/ # Comprehensive documentation (guides, philosophy, CSS reference) -β”œβ”€β”€ scripts/ # Build scripts (build-release.sh) -β”œβ”€β”€ src/ # MAIN WORKING DIRECTORY: template source files -β”‚ β”œβ”€β”€ index.php # Main template rendering file -β”‚ β”œβ”€β”€ templateDetails.xml # Joomla manifest (metadata, params, files) -β”‚ β”œβ”€β”€ joomla.asset.json # Web Asset Manager registration -β”‚ β”œβ”€β”€ language/ # Frontend language files (en-GB, en-US .ini and .sys.ini) -β”‚ β”œβ”€β”€ html/ # Alternative layout overrides (never replace defaults!) -β”‚ └── media/ # CSS, JS, images, fonts, vendor libraries -β”œβ”€β”€ templates/ # Color scheme templates for client customization -β”œβ”€β”€ tests/ # Codeception acceptance and unit tests -β”œβ”€β”€ phpcs.xml # PHP_CodeSniffer configuration (PSR-12 + Joomla standards) -└── phpstan.neon # PHPStan static analysis configuration +β”œβ”€β”€ 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 ``` +--- + +# Primary Language + +**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 -## Header Format - -All source files MUST include this standardized copyright header: - -### PHP Files (Full Header) +Every new file **must** have a copyright header as its first content. JSON files, binary files, generated files, and third-party files are exempt. +**PHP:** ```php - - This file is part of a Moko Consulting project. - - SPDX-License-Identifier: GPL-3.0-or-later - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see . - - - # FILE INFORMATION - DEFGROUP: Joomla.Template.Site - INGROUP: MokoCassiopeia.Template - REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia - PATH: ./templates/mokocassiopeia/filename.php - VERSION: XX.YY.ZZ - BRIEF: Brief description of file purpose + * + * This file is part of a Moko Consulting project. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * FILE INFORMATION + * DEFGROUP: MokoCassiopeia.{{EXTENSION_TYPE}} + * INGROUP: MokoCassiopeia + * REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia + * PATH: /site/controllers/item.php + * VERSION: XX.YY.ZZ + * BRIEF: One-line description of file purpose */ defined('_JEXEC') or die; ``` -### JavaScript Files +**Markdown / YAML / Shell / XML:** Use the appropriate comment syntax with the same fields. -```javascript -/* 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 - PATH: ./media/templates/site/mokocassiopeia/js/filename.js - VERSION: XX.YY.ZZ - BRIEF: Brief description of file purpose - */ - -(function (win, doc) { - "use strict"; - // Implementation -})(window, document); -``` - -### CSS Files - -```css -/* 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.Styles - PATH: ./media/templates/site/mokocassiopeia/css/filename.css - VERSION: XX.YY.ZZ - BRIEF: Brief description of file purpose - */ -``` - -### Markdown Documentation - -```markdown - -``` - -## FILE INFORMATION Block Fields - -- **DEFGROUP**: Top-level group (always `Joomla.Template.Site`) -- **INGROUP**: Subgroup (e.g., `MokoCassiopeia.Template`, `MokoCassiopeia.Documentation`) -- **REPO**: GitHub repository URL (required in docs, optional in code) -- **PATH** or **FILE**: Relative path from repository root -- **VERSION**: Semantic version `XX.YY.ZZ` -- **BRIEF**: One-line description of file purpose - -## Exempt Files - -These file types DO NOT require headers: -- `joomla.asset.json` and other JSON data files (metadata in `x-header` field instead) -- Binary files (images, fonts, compiled assets) -- Third-party vendor libraries in `src/media/vendor/` -- Generated files (minified CSS/JS with `.min.` in name) -- Empty placeholder files - -## When to Use Full vs Minimal Header - -- **Full header with GPL notice**: Required in all PHP files (use example above) -- **Minimal header without GPL notice**: JavaScript and CSS files (shorter version shown above) -- **Markdown format**: All documentation files in `docs/` +--- # Coding Standards -## Indentation - -From `.editorconfig`: -- **Default**: Tabs with 2-space visual width -- **PHP**: Tabs (PSR-12 uses tabs, Joomla uses tabs) -- **JavaScript**: Tabs with 2-space visual width -- **CSS**: Tabs with 2-space visual width -- **JSON/YAML**: Tabs with 2-space visual width -- **Markdown**: Spaces (for compatibility), trim_trailing_whitespace = false -- **Makefiles**: Always tabs - -## Line Length - -From `phpcs.xml`: -- **General**: No strict limit (Generic.Files.LineLength excluded) -- **Practical guideline**: Keep lines under 120 characters when reasonable -- **Long lines accepted for**: URLs, array definitions, Joomla HTML helpers - ## Naming Conventions -### PHP (Joomla Standards + PSR-12) +| 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` | -- **Classes**: `PascalCase` (e.g., `TemplateHelper`) -- **Methods**: `camelCase` (e.g., `getThemeOptions()`) -- **Variables**: `$camelCase` or `$snake_case` (Joomla prefers snake_case for local vars) -- **Constants**: `UPPER_SNAKE_CASE` (e.g., `THEME_LIGHT_MODE`) -- **Files**: Lowercase with hyphens for layouts (e.g., `mobile.php`, `toc-left.php`) -- **Private properties**: Prefix with underscore (e.g., `$_privateData`) +## Commit Messages -### JavaScript (ES6+) +Format: `(): ` β€” imperative, lower-case subject, no trailing period. -- **Functions**: `camelCase` (e.g., `applyTheme()`) -- **Constants**: `UPPER_SNAKE_CASE` or `camelCase` (e.g., `storageKey`) -- **Classes**: `PascalCase` (e.g., `ThemeController`) -- **Files**: Lowercase with hyphens (e.g., `template.js`, `menu-metismenu.js`) +Valid types: `feat` Β· `fix` Β· `docs` Β· `chore` Β· `ci` Β· `refactor` Β· `style` Β· `test` Β· `perf` Β· `revert` Β· `build` -### CSS +## Branch Naming -- **Classes**: Kebab-case (e.g., `.theme-toggle`, `.fab-button`) -- **IDs**: Kebab-case (e.g., `#main-content`) -- **CSS Variables**: Kebab-case with double hyphen prefix (e.g., `--color-primary`, `--body-bg`) -- **Files**: Lowercase with dots and descriptive names (e.g., `light.standard.css`, `dark.custom.css`, `light.custom.css`) +Format: `/[/description]` -### General File Naming +Approved prefixes: `dev/` Β· `rc/` Β· `version/` Β· `patch/` Β· `copilot/` Β· `dependabot/` -- **Alternative layouts**: Descriptive name + `.php` (e.g., `mobile.php`, `mainmenu.php`, `toc-left.php`) -- **NEVER use**: `default.php` (replaces core layoutβ€”FORBIDDEN) -- **Scripts**: Lowercase with hyphens (e.g., `build-release.sh`) +--- -## Primary Language +# GitHub Actions β€” Token Usage -- **PHP**: All new template logic, Joomla integration code -- **JavaScript**: All new client-side functionality (ES6+ syntax) -- **CSS**: All new styling (CSS variables for theming) -- **Bash**: Build and automation scripts (`.sh` files) +Every workflow must use **`secrets.GH_TOKEN`** (the org-level Personal Access Token). -# Language-Specific Requirements +```yaml +# βœ… Correct +- uses: actions/checkout@v4 + with: + token: ${{ secrets.GH_TOKEN }} -## PHP - -### Type Hints - -Required for all function signatures in PHP 8.0+ code: - -```php -public function getThemeOption(string $key, mixed $default = null): mixed -{ - return $this->params->get($key, $default); -} +env: + GH_TOKEN: ${{ secrets.GH_TOKEN }} ``` -### Docstring Format - -Use DocBlocks for all classes, methods, and functions: - -```php -/** - * Apply theme settings to document - * - * @param string $theme Theme name (light|dark) - * @param object $params Template parameters - * - * @return void - * - * @since 3.6.0 - */ -public function applyTheme(string $theme, object $params): void -{ - // Implementation -} +```yaml +# ❌ Wrong β€” never use these +token: ${{ github.token }} +token: ${{ secrets.GITHUB_TOKEN }} ``` -Required DocBlock sections: -- Brief description (first line) -- `@param` for each parameter (type, name, description) -- `@return` for return value -- `@since` for version introduced -- `@throws` if exceptions are thrown +--- -### Script Structure +# Keeping Documentation Current -All PHP files must follow this pattern: +| 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 | -```php - section -- Update paths to be relative from package root -- Ensures proper installation to JOOMLA_ROOT/language/ - -Refs: #42 -``` - -``` -Add dark mode color variables for custom themes - -- Add --color-primary-dark, --color-secondary-dark -- Update theme toggle to respect custom colors -- Document new variables in CSS_VARIABLES.md -``` - -**Bad examples:** - -``` -Update files -``` - -``` -WIP -``` - -``` -Fixed bug -``` - -## Commit Rules - -- Keep subject line under 50 characters -- Use imperative mood ("Add feature" not "Added feature") -- Capitalize first letter -- No period at end of subject -- Blank line between subject and body -- Wrap body at 72 characters -- Explain WHAT and WHY, not HOW (code shows HOW) - -# Running Validation - -Before committing, run these commands from repository root: - -```bash -# 1. PHP CodeSniffer (code style) -phpcs --standard=phpcs.xml src/ - -# 2. PHPStan (static analysis) -phpstan analyse --configuration=phpstan.neon - -# 3. XML validation (template manifest) -xmllint --noout src/templateDetails.xml - -# 4. Check file headers (if script exists) -# ./scripts/validate-headers.sh - -# 5. Run tests (if Codeception installed) -codecept run -``` - -If you don't have tools installed: - -```bash -# Install PHP quality tools globally -composer global require "squizlabs/php_codesniffer:^3.0" --with-all-dependencies -composer global require "phpstan/phpstan:^1.0" --with-all-dependencies - -# Install Codeception -composer global require "codeception/codeception" --with-all-dependencies -``` - -**Note:** CI runs these automatically on push, but running locally saves time. - -# Contribution Workflow - -1. **Fork** the repository on GitHub -2. **Clone** your fork locally: `git clone https://github.com/YOUR-USERNAME/MokoCassiopeia.git` -3. **Create branch** from current development branch: `git checkout -b feature/your-feature-name` - - Branch naming: `feature/description`, `fix/issue-description`, `docs/topic` -4. **Make changes** in the `src/` directory (this is the working copy) -5. **Run validation** (see commands above) -6. **Commit changes** with descriptive messages -7. **Push to your fork**: `git push origin feature/your-feature-name` -8. **Open Pull Request** on GitHub targeting the appropriate base branch - -## Branch Strategy - -- **main**: Stable releases only (protected) -- **version/XX.YY**: Version-specific branches (protected) -- **dev/XX.YY.ZZ**: Active development branches -- **rc/XX.YY.ZZ**: Release candidate branches -- **feature/**, **fix/**, **docs/**: Working branches - -## Merge Strategy - -- Pull requests use **squash merge** to main/version branches -- Commits go to development branches first, then promoted through rc β†’ version β†’ main -- NEVER commit directly to protected branches (main, version/*) - -# PR Checklist - -Before opening a pull request: - -- [ ] Code follows Joomla coding standards (PSR-12 + Joomla) -- [ ] All files have proper copyright headers -- [ ] PHP files have `defined('_JEXEC') or die;` at top -- [ ] No `default.php` layout files (use alternative layout names) -- [ ] Language files: metadata in `.sys.ini`, runtime strings in `.ini` -- [ ] Assets registered in `joomla.asset.json` (not inline in template) -- [ ] CSS uses CSS variables for colors (supports light/dark themes) -- [ ] PHP CodeSniffer passes: `phpcs --standard=phpcs.xml src/` -- [ ] PHPStan passes: `phpstan analyse --configuration=phpstan.neon` -- [ ] Tested in Joomla 4.4.x AND 5.x -- [ ] Tested in light mode AND dark mode -- [ ] Documentation updated if adding features -- [ ] No hardcoded absolute paths (use Joomla path constants) -- [ ] No credentials, API keys, or secrets in code -- [ ] Version numbers NOT added to revision history tables -- [ ] Alternative layouts documented with activation instructions +--- # What NOT to Do -## Absolutely Forbidden +- **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. -- ❌ **NEVER create `default.php` layout files** - This replaces core layouts and breaks upgrades. Use alternative layout names like `mobile.php`, `mainmenu.php` -- ❌ **NEVER commit without copyright headers** - All source files must have proper headers -- ❌ **NEVER add `folder` attribute to `` section in templateDetails.xml** - Language file paths must be relative from package root -- ❌ **NEVER put template metadata in `.ini` files** - Template name and description belong ONLY in `.sys.ini` files -- ❌ **NEVER hardcode absolute paths** - Use Joomla path constants (`JPATH_ROOT`, `JPATH_SITE`, etc.) -- ❌ **NEVER commit credentials or API keys** - Use Joomla's configuration management -- ❌ **NEVER modify vendor libraries** - Third-party code in `src/media/vendor/` is immutable -- ❌ **NEVER remove `defined('_JEXEC') or die;`** - This is a critical security check +--- -## Strong Discouragements +# PR Checklist -- ⚠️ Avoid committing without running `phpcs` and `phpstan` -- ⚠️ Avoid adding version numbers to revision history tables (use VERSION metadata instead) -- ⚠️ Avoid inline styles or scripts (register assets in `joomla.asset.json`) -- ⚠️ Avoid hardcoded color values (use CSS variables: `--color-primary`, etc.) -- ⚠️ Avoid direct commits to `main` or `version/*` branches (use PRs) -- ⚠️ Avoid changing more than one concern per commit -- ⚠️ Avoid long lines when reasonable (aim for 120 chars) +Before opening a PR, verify: -## Common Mistakes +- [ ] 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 -- Using spaces instead of tabs (check `.editorconfig`) -- Forgetting `SPDX-License-Identifier: GPL-3.0-or-later` in headers -- Mixing frontend strings (.ini) and metadata strings (.sys.ini) -- Creating layout overrides that replace defaults instead of providing alternatives -- Not testing in both Joomla 4.x and 5.x -- Not testing light/dark mode theme switching +--- -# Key Policy Documents +# Key Policy Documents (MokoStandards) -Must-read before contributing: - -1. **[OVERRIDE_PHILOSOPHY.md](./docs/OVERRIDE_PHILOSOPHY.md)** - CRITICAL: Explains why we NEVER replace default layouts and how alternative layouts work -2. **[QUICK_START.md](./docs/QUICK_START.md)** - 5-minute setup guide for first-time contributors -3. **[JOOMLA_DEVELOPMENT.md](./docs/JOOMLA_DEVELOPMENT.md)** - Complete development guide: testing, quality checks, deployment -4. **[WORKFLOW_GUIDE.md](./docs/WORKFLOW_GUIDE.md)** - Git workflow, branching strategy, CI/CD pipelines -5. **[CSS_VARIABLES.md](./docs/CSS_VARIABLES.md)** - Complete reference for theme customization -6. **[CONTRIBUTING.md](./CONTRIBUTING.md)** - Formal contribution guidelines and governance -7. **[SECURITY.md](./SECURITY.md)** - Security reporting procedures (never report publicly) -8. **[MODULE_OVERRIDES.md](./docs/MODULE_OVERRIDES.md)** - Documentation of all alternative layouts and activation instructions - -## Related Documentation - -- **README.md** - Overview, features, installation, configuration -- **CHANGELOG.md** - Version history and release notes -- **GOVERNANCE.md** - Project governance model and decision-making -- **CODE_OF_CONDUCT.md** - Community standards and expectations +| 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 | +| [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 diff --git a/.github/ISSUE_TEMPLATE/adr.md b/.github/ISSUE_TEMPLATE/adr.md new file mode 100644 index 0000000..6fea768 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/adr.md @@ -0,0 +1,125 @@ +--- +name: Architecture Decision Record (ADR) +about: Propose or document an architectural decision +title: '[ADR] ' +labels: 'architecture, decision' +assignees: '' + +--- + + + +## ADR Number +ADR-XXXX + +## Status +- [ ] Proposed +- [ ] Accepted +- [ ] Deprecated +- [ ] Superseded by ADR-XXXX + +## Context +Describe the issue or problem that motivates this decision. + +## Decision +State the architecture decision and provide rationale. + +## Consequences +### Positive +- List positive consequences + +### Negative +- List negative consequences or trade-offs + +### Neutral +- List neutral aspects + +## Alternatives Considered +### Alternative 1 +- Description +- Pros +- Cons +- Why not chosen + +### Alternative 2 +- Description +- Pros +- Cons +- Why not chosen + +## Implementation Plan +1. Step 1 +2. Step 2 +3. Step 3 + +## Stakeholders +- **Decision Makers**: @user1, @user2 +- **Consulted**: @user3, @user4 +- **Informed**: team-name + +## Technical Details +### Architecture Diagram +``` +[Add diagram or link] +``` + +### Dependencies +- Dependency 1 +- Dependency 2 + +### Impact Analysis +- **Performance**: [Impact description] +- **Security**: [Impact description] +- **Scalability**: [Impact description] +- **Maintainability**: [Impact description] + +## Testing Strategy +- [ ] Unit tests +- [ ] Integration tests +- [ ] Performance tests +- [ ] Security tests + +## Documentation +- [ ] Architecture documentation updated +- [ ] API documentation updated +- [ ] Developer guide updated +- [ ] Runbook created + +## Migration Path +Describe how to migrate from current state to new architecture. + +## Rollback Plan +Describe how to rollback if issues occur. + +## Timeline +- **Proposal Date**: +- **Decision Date**: +- **Implementation Start**: +- **Expected Completion**: + +## References +- Related ADRs: +- External resources: +- RFCs: + +## Review Checklist +- [ ] Aligns with enterprise architecture principles +- [ ] Security implications reviewed +- [ ] Performance implications reviewed +- [ ] Cost implications reviewed +- [ ] Compliance requirements met +- [ ] Team consensus achieved diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..c57ce5b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,63 @@ +--- +name: Bug Report +about: Report a bug or issue with the project +title: '[BUG] ' +labels: 'bug' +assignees: '' + +--- + + + +## Bug Description +A clear and concise description of what the bug is. + +## Steps to Reproduce +1. Go to '...' +2. Click on '...' +3. Scroll down to '...' +4. See error + +## Expected Behavior +A clear and concise description of what you expected to happen. + +## Actual Behavior +A clear and concise description of what actually happened. + +## Screenshots +If applicable, add screenshots to help explain your problem. + +## Environment +- **Project**: [e.g., MokoDoliTools, moko-cassiopeia] +- **Version**: [e.g., 1.2.3] +- **Platform**: [e.g., Dolibarr 18.0, Joomla 5.0] +- **PHP Version**: [e.g., 8.1] +- **Database**: [e.g., MySQL 8.0, PostgreSQL 14] +- **Browser** (if applicable): [e.g., Chrome 120, Firefox 121] +- **OS**: [e.g., Ubuntu 22.04, Windows 11] + +## Additional Context +Add any other context about the problem here. + +## Possible Solution +If you have suggestions on how to fix the issue, please describe them here. + +## Checklist +- [ ] I have searched for similar issues before creating this one +- [ ] I have provided all the requested information +- [ ] I have tested this on the latest stable version +- [ ] I have checked the documentation and couldn't find a solution diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..7edc8bc --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,18 @@ +--- +blank_issues_enabled: true +contact_links: + - name: πŸ’Ό Enterprise Support + url: https://mokoconsulting.tech/enterprise + about: Enterprise-level support and consultation services + - name: πŸ’¬ Ask a Question + url: https://mokoconsulting.tech/ + about: Get help or ask questions through our website + - name: πŸ“š MokoStandards Documentation + url: https://github.com/mokoconsulting-tech/MokoStandards + about: View our coding standards and best practices + - name: πŸ”’ Report a Security Vulnerability + url: https://github.com/mokoconsulting-tech/.github-private/security/advisories/new + about: Report security vulnerabilities privately (for critical issues) + - name: πŸ’‘ Community Discussions + url: https://github.com/orgs/mokoconsulting-tech/discussions + about: Join community discussions and Q&A diff --git a/.github/ISSUE_TEMPLATE/documentation.md b/.github/ISSUE_TEMPLATE/documentation.md new file mode 100644 index 0000000..133e8b6 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation.md @@ -0,0 +1,67 @@ +--- +name: Documentation Issue +about: Report an issue with documentation +title: '[DOCS] ' +labels: 'documentation' +assignees: '' + +--- + + + +## Documentation Issue + +**Location**: + + +## Issue Type + +- [ ] Typo or grammar error +- [ ] Outdated information +- [ ] Missing documentation +- [ ] Unclear explanation +- [ ] Broken links +- [ ] Missing examples +- [ ] Other (specify below) + +## Description + + +## Current Content + +``` +Current text here +``` + +## Suggested Improvement + +``` +Suggested text here +``` + +## Additional Context + + +## Standards Alignment +- [ ] Follows MokoStandards documentation guidelines +- [ ] Uses en_US/en_GB localization +- [ ] Includes proper SPDX headers where applicable + +## Checklist +- [ ] I have searched for similar documentation issues +- [ ] I have provided a clear description +- [ ] I have suggested an improvement (if applicable) diff --git a/.github/ISSUE_TEMPLATE/enterprise_support.md b/.github/ISSUE_TEMPLATE/enterprise_support.md new file mode 100644 index 0000000..6b1133d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/enterprise_support.md @@ -0,0 +1,100 @@ +--- +name: Enterprise Support Request +about: Request enterprise-level support or consultation +title: '[ENTERPRISE] ' +labels: 'enterprise, support' +assignees: '' + +--- + + + +## Support Request Type +- [ ] Critical Production Issue +- [ ] Performance Optimization +- [ ] Security Audit +- [ ] Architecture Review +- [ ] Custom Development +- [ ] Migration Support +- [ ] Training & Onboarding +- [ ] Other (please specify) + +## Priority Level +- [ ] P0 - Critical (Production Down) +- [ ] P1 - High (Major Feature Broken) +- [ ] P2 - Medium (Non-Critical Issue) +- [ ] P3 - Low (Enhancement/Question) + +## Organization Details +- **Company Name**: +- **Contact Person**: +- **Email**: +- **Phone** (for P0/P1 issues): +- **Timezone**: + +## Issue Description +Provide a clear and detailed description of your request or issue. + +## Business Impact +Describe the impact on your business operations: +- Number of users affected: +- Revenue impact (if applicable): +- Deadline/SLA requirements: + +## Environment Details +- **Deployment Type**: [On-Premise / Cloud / Hybrid] +- **Platform**: [Joomla / Dolibarr / Custom] +- **Version**: +- **Infrastructure**: [AWS / Azure / GCP / Other] +- **Scale**: [Users / Transactions / Data Volume] + +## Current Configuration +```yaml +# Paste relevant configuration (sanitize sensitive data) +``` + +## Logs and Diagnostics +``` +# Paste relevant logs (sanitize sensitive data) +``` + +## Attempted Solutions +Describe any troubleshooting steps already taken. + +## Expected Resolution +Describe your expected outcome or resolution. + +## Additional Resources +- **Documentation Links**: +- **Related Issues**: +- **Screenshots/Videos**: + +## Enterprise SLA +- [ ] Standard Support (initial response within 1–3 weeks) +- [ ] Premium Support (initial response within 5 business days) +- [ ] Critical Support (initial response within 72 hours) +- [ ] Custom SLA (specify): + +## Compliance Requirements +- [ ] GDPR +- [ ] HIPAA +- [ ] SOC 2 +- [ ] ISO 27001 +- [ ] Other (specify): + +--- +**Note**: Enterprise support requests require an active support contract. If you don't have one, please contact us at enterprise@mokoconsulting.tech diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..e945325 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,66 @@ +--- +name: Feature Request +about: Suggest a new feature or enhancement +title: '[FEATURE] ' +labels: 'enhancement' +assignees: '' + +--- + + + +## Feature Description +A clear and concise description of the feature you'd like to see. + +## Problem or Use Case +Describe the problem this feature would solve or the use case it addresses. +Ex. I'm always frustrated when [...] + +## Proposed Solution +A clear and concise description of what you want to happen. + +## Alternative Solutions +A clear and concise description of any alternative solutions or features you've considered. + +## Benefits +Describe how this feature would benefit users: +- Who would use this feature? +- What problems does it solve? +- What value does it add? + +## Implementation Details (Optional) +If you have ideas about how this could be implemented, share them here: +- Technical approach +- Files/components that might need changes +- Any concerns or challenges you foresee + +## Additional Context +Add any other context, mockups, or screenshots about the feature request here. + +## Relevant Standards +Does this relate to any standards in [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards)? +- [ ] Accessibility (WCAG 2.1 AA) +- [ ] Localization (en_US/en_GB) +- [ ] Security best practices +- [ ] Code quality standards +- [ ] Other: [specify] + +## Checklist +- [ ] I have searched for similar feature requests before creating this one +- [ ] I have clearly described the use case and benefits +- [ ] I have considered alternative solutions +- [ ] This feature aligns with the project's goals and scope diff --git a/.github/ISSUE_TEMPLATE/firewall-request.md b/.github/ISSUE_TEMPLATE/firewall-request.md new file mode 100644 index 0000000..1b51d01 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/firewall-request.md @@ -0,0 +1,203 @@ +--- +name: Firewall Request +about: Request firewall rule changes or access to external resources +title: '[FIREWALL] [Resource Name] - [Brief Description]' +labels: ['firewall-request', 'infrastructure', 'security'] +assignees: [] +--- + + + +## Firewall Request + +### Request Type +- [ ] Allow outbound access to external service/API +- [ ] Allow inbound access from external source +- [ ] Modify existing firewall rule +- [ ] Remove/revoke firewall rule +- [ ] Other (specify): + +### Resource Information +**Service/Domain Name**: +**IP Address(es)**: +**Port(s)**: +**Protocol**: +- [ ] HTTP (80) +- [ ] HTTPS (443) +- [ ] SSH (22) +- [ ] FTP (21) +- [ ] SFTP (22) +- [ ] Custom (specify): _______________ + +### Requestor Information +**Name**: +**GitHub Username**: @ +**Email**: @mokoconsulting.tech +**Team/Department**: +**Manager**: @ + +### Business Justification +**Why is this access needed?** + +**Which project(s) require this access?** + +**What functionality will break without this access?** + +**Is there an alternative solution?** +- [ ] Yes (explain): +- [ ] No + +### Security Considerations +**Data Classification**: +- [ ] Public +- [ ] Internal +- [ ] Confidential +- [ ] Restricted + +**Sensitive Data Transmission**: +- [ ] No sensitive data will be transmitted +- [ ] Sensitive data will be transmitted (encryption required) +- [ ] Authentication credentials will be transmitted (secure storage required) + +**Third-Party Service**: +- [ ] This is a trusted/verified third-party service +- [ ] This is a new/unverified service (security review required) + +**Service Documentation**: +(Provide link to service documentation or API specs) + +### Access Scope +**Affected Systems**: +- [ ] Development environment only +- [ ] Staging environment only +- [ ] Production environment +- [ ] All environments + +**Access Duration**: +- [ ] Permanent (ongoing business need) +- [ ] Temporary (specify end date): _______________ +- [ ] Testing only (specify duration): _______________ + +### Technical Details +**Source System(s)**: +(Which internal systems need access?) + +**Destination System(s)**: +(Which external systems need to be accessed?) + +**Expected Traffic Volume**: +(e.g., requests per hour/day) + +**Traffic Pattern**: +- [ ] Continuous +- [ ] Periodic (specify frequency): _______________ +- [ ] On-demand/manual +- [ ] Scheduled (specify schedule): _______________ + +### Testing Requirements +**Pre-Production Testing**: +- [ ] Request includes dev/staging access for testing +- [ ] Testing can be done with production access only +- [ ] No testing required (modify existing rule) + +**Testing Plan**: + +**Rollback Plan**: +(What happens if access needs to be revoked?) + +### Compliance & Audit +**Compliance Requirements**: +- [ ] GDPR considerations +- [ ] SOC 2 compliance required +- [ ] PCI DSS considerations +- [ ] Other regulatory requirements: _______________ +- [ ] No specific compliance requirements + +**Audit/Logging Requirements**: +- [ ] Standard logging sufficient +- [ ] Enhanced logging/monitoring required +- [ ] Real-time alerting required + +### Urgency +- [ ] Critical (production down, immediate access needed) +- [ ] High (needed within 24 hours) +- [ ] Normal (needed within 1 week) +- [ ] Low priority (needed within 1 month) + +**If critical/high urgency, explain why:** + +### Approvals +**Manager Approval**: +- [ ] Manager has been notified and approves this request + +**Security Team Review Required**: +- [ ] Yes (new external service, sensitive data) +- [ ] No (minor change, established service) + +### Additional Information + +**Related Documentation**: +(Links to relevant docs, RFCs, tickets, etc.) + +**Dependencies**: +(Other systems or changes this depends on) + +**Comments/Questions**: + +--- + +## For Infrastructure/Security Team Use Only + +**Do not edit below this line** + +### Security Review +- [ ] Security team review completed +- [ ] Risk assessment: Low / Medium / High +- [ ] Encryption required: Yes / No +- [ ] VPN required: Yes / No +- [ ] Additional security controls: _______________ + +**Reviewed By**: @_______________ +**Review Date**: _______________ +**Review Notes**: + +### Implementation +- [ ] Firewall rule created/modified +- [ ] Rule tested in dev/staging +- [ ] Rule deployed to production +- [ ] Monitoring/alerting configured +- [ ] Documentation updated + +**Firewall Rule ID**: _______________ +**Implementation Date**: _______________ +**Implemented By**: @_______________ + +**Configuration Details**: +``` +Source: +Destination: +Port/Protocol: +Action: Allow/Deny +``` + +### Verification +- [ ] Requestor confirmed access working +- [ ] Logs reviewed (no anomalies) +- [ ] Security scan completed (if applicable) + +**Verification Date**: _______________ +**Verified By**: @_______________ + +### Notes diff --git a/.github/ISSUE_TEMPLATE/joomla_issue.md b/.github/ISSUE_TEMPLATE/joomla_issue.md new file mode 100644 index 0000000..a20de32 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/joomla_issue.md @@ -0,0 +1,102 @@ +--- +name: Joomla Extension Issue +about: Report an issue with a Joomla extension +title: '[JOOMLA] ' +labels: 'joomla' +assignees: '' + +--- + + + +## Issue Type +- [ ] Component Issue +- [ ] Module Issue +- [ ] Plugin Issue +- [ ] Template Issue + +## Extension Details +- **Extension Name**: [e.g., moko-cassiopeia] +- **Extension Version**: [e.g., 1.2.3] +- **Extension Type**: [Component / Module / Plugin / Template] + +## Joomla Environment +- **Joomla Version**: [e.g., 4.4.0, 5.0.0] +- **PHP Version**: [e.g., 8.1.0] +- **Database**: [MySQL / PostgreSQL / MariaDB] +- **Database Version**: [e.g., 8.0] +- **Server**: [Apache / Nginx / IIS] +- **Hosting**: [Shared / VPS / Dedicated / Cloud] + +## Issue Description +Provide a clear and detailed description of the issue. + +## Steps to Reproduce +1. Go to '...' +2. Click on '...' +3. Configure '...' +4. See error + +## Expected Behavior +What you expected to happen. + +## Actual Behavior +What actually happened. + +## Error Messages +``` +# Paste any error messages from Joomla error logs +# Location: administrator/logs/error.php +``` + +## Browser Console Errors +```javascript +// Paste any JavaScript console errors (F12 in browser) +``` + +## Screenshots +Add screenshots to help explain the issue. + +## Configuration +```ini +# Paste extension configuration (sanitize sensitive data) +``` + +## Installed Extensions +List other installed extensions that might conflict: +- Extension 1 (version) +- Extension 2 (version) + +## Template Overrides +- [ ] Using template overrides +- [ ] Custom CSS +- [ ] Custom JavaScript + +## Additional Context +- **Multilingual Site**: [Yes / No] +- **Cache Enabled**: [Yes / No] +- **Debug Mode**: [Yes / No] +- **SEF URLs**: [Yes / No] + +## Checklist +- [ ] I have cleared Joomla cache +- [ ] I have disabled other extensions to test for conflicts +- [ ] I have checked Joomla error logs +- [ ] I have tested with a default Joomla template +- [ ] I have checked browser console for JavaScript errors +- [ ] I have searched for similar issues +- [ ] I am using a supported Joomla version diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 0000000..6bd94b5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,86 @@ +--- +name: Question +about: Ask a question about usage, features, or best practices +title: '[QUESTION] ' +labels: ['question'] +assignees: [] +--- + + + +## Question + +**Your question:** + + +## Context + +**What are you trying to accomplish?** + + +**What have you already tried?** + + +**Category**: +- [ ] Script usage +- [ ] Configuration +- [ ] Workflow setup +- [ ] Documentation interpretation +- [ ] Best practices +- [ ] Integration +- [ ] Other: __________ + +## Environment (if relevant) + +**Your setup**: +- Operating System: +- Version: + +## What You've Researched + +**Documentation reviewed**: +- [ ] README.md +- [ ] Project documentation +- [ ] Other (specify): __________ + +**Similar issues/questions found**: +- # +- # + +## Expected Outcome + +**What result are you hoping for?** + + +## Code/Configuration Samples + +**Relevant code or configuration** (if applicable): + +```bash +# Your code here +``` + +## Additional Context + +**Any other relevant information:** + + +**Screenshots** (if helpful): + + +## Urgency + +- [ ] Urgent (blocking work) +- [ ] Normal (can work on other things meanwhile) +- [ ] Low priority (just curious) + +## Checklist + +- [ ] I have searched existing issues and discussions +- [ ] I have reviewed relevant documentation +- [ ] I have provided sufficient context +- [ ] I have included code/configuration samples if relevant +- [ ] This is a genuine question (not a bug report or feature request) diff --git a/.github/ISSUE_TEMPLATE/request-license.md b/.github/ISSUE_TEMPLATE/request-license.md new file mode 100644 index 0000000..d5e53e5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/request-license.md @@ -0,0 +1,120 @@ +--- +name: License Request +about: Request an organization license for Sublime Text +title: '[LICENSE REQUEST] Sublime Text - [Your Name]' +labels: ['license-request', 'admin'] +assignees: [] +--- + + + +## License Request + +### Tool Information +**Tool Name**: Sublime Text + +**License Type Requested**: Organization Pool + +**Personal Purchase**: +- [ ] I prefer to purchase my own license ($99 USD - recommended, immediate access) +- [ ] I prefer an organization license (1-2 business days, organization use only) +- [ ] I have already purchased my own license (registration only for support) + +### Requestor Information +**Name**: +**GitHub Username**: @ +**Email**: @mokoconsulting.tech +**Team/Department**: +**Manager**: @ + +### Justification +**Why do you need this license?** + +**Primary use case**: +- [ ] Remote development (SFTP to servers) +- [ ] Local development +- [ ] Code review +- [ ] Documentation editing +- [ ] Other (specify): + +**Which projects/repositories will you work on?** + +**Have you evaluated the free trial?** +- [ ] Yes, I've used the trial and Sublime Text meets my needs +- [ ] No, requesting license before trial + +**Alternative tools considered**: +- [ ] VS Code (free alternative) +- [ ] Vim/Neovim (free, terminal-based) +- [ ] Other: _______________ + +### Platform +- [ ] Windows +- [ ] macOS +- [ ] Linux (distribution: ________) + +### Urgency +- [ ] Urgent (needed within 24 hours - please justify) +- [ ] Normal (1-2 business days) +- [ ] Low priority (when available) + +**If urgent, please explain why:** + +### SFTP Plugin +**Note**: Sublime SFTP plugin ($16 USD) is a **separate personal purchase** and is NOT provided by the organization. + +- [ ] I understand SFTP plugin requires separate personal purchase +- [ ] I have already purchased SFTP plugin +- [ ] I will purchase SFTP plugin if needed for my work +- [ ] I don't need SFTP plugin (local development only) + +### Acknowledgments +- [ ] I have read the License Management Policy (/docs/github-private/LICENSE_MANAGEMENT.md) +- [ ] I understand organization licenses are for work use only +- [ ] I understand organization licenses must be returned upon leaving +- [ ] I understand personal purchases ($99) are an alternative with lifetime access +- [ ] I understand SFTP plugin ($16) requires separate personal purchase +- [ ] I agree to the terms of use + +### Additional Information + +**Expected daily usage hours**: _____ hours/day + +**Duration of need**: +- [ ] Permanent (ongoing role) +- [ ] Temporary project (_____ months) +- [ ] Trial/Evaluation (_____ weeks) + +**Comments/Questions**: + +--- + +## For Admin Use Only + +**Do not edit below this line** + +- [ ] Manager approval received (@manager-username) +- [ ] License available in pool (current: __/20) +- [ ] License type confirmed (Organization / Personal registration) +- [ ] License key sent via encrypted email +- [ ] Activation confirmed by user +- [ ] Added to license tracking sheet +- [ ] User notified of SFTP plugin requirement + +**License Key ID**: _____________ +**Date Issued**: _____________ +**Issued By**: @_____________ + +**Notes**: diff --git a/.github/ISSUE_TEMPLATE/rfc.md b/.github/ISSUE_TEMPLATE/rfc.md new file mode 100644 index 0000000..a6ae068 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/rfc.md @@ -0,0 +1,141 @@ +--- +name: Request for Comments (RFC) +about: Propose a significant change for community discussion +title: '[RFC] ' +labels: 'rfc, discussion' +assignees: '' + +--- + + + +## RFC Summary +One-paragraph summary of the proposal. + +## Motivation +Why are we doing this? What use cases does it support? What is the expected outcome? + +## Detailed Design +### Overview +Provide a detailed explanation of the proposed change. + +### API Changes (if applicable) +```php +// Before +function oldApi($param1) { } + +// After +function newApi($param1, $param2) { } +``` + +### User Experience Changes +Describe how users will interact with this change. + +### Implementation Approach +High-level implementation strategy. + +## Drawbacks +Why should we *not* do this? + +## Alternatives +What other designs have been considered? What is the impact of not doing this? + +### Alternative 1 +- Description +- Trade-offs + +### Alternative 2 +- Description +- Trade-offs + +## Adoption Strategy +How will existing users adopt this? Is this a breaking change? + +### Migration Guide +```bash +# Steps to migrate +``` + +### Deprecation Timeline +- **Announcement**: +- **Deprecation**: +- **Removal**: + +## Unresolved Questions +- Question 1 +- Question 2 + +## Future Possibilities +What future work does this enable? + +## Impact Assessment +### Performance +Expected performance impact. + +### Security +Security considerations and implications. + +### Compatibility +- **Backward Compatible**: [Yes / No] +- **Breaking Changes**: [List] + +### Maintenance +Long-term maintenance considerations. + +## Community Input +### Stakeholders +- [ ] Core team +- [ ] Module developers +- [ ] End users +- [ ] Enterprise customers + +### Feedback Period +**Duration**: [e.g., 2 weeks] +**Deadline**: [date] + +## Implementation Timeline +### Phase 1: Design +- [ ] RFC discussion +- [ ] Design finalization +- [ ] Approval + +### Phase 2: Implementation +- [ ] Core implementation +- [ ] Tests +- [ ] Documentation + +### Phase 3: Release +- [ ] Beta release +- [ ] Feedback collection +- [ ] Stable release + +## Success Metrics +How will we measure success? +- Metric 1 +- Metric 2 + +## References +- Related RFCs: +- External documentation: +- Prior art: + +## Open Questions for Community +1. Question 1? +2. Question 2? + +--- +**Note**: This RFC is open for community discussion. Please provide feedback in the comments below. diff --git a/.github/ISSUE_TEMPLATE/security.md b/.github/ISSUE_TEMPLATE/security.md new file mode 100644 index 0000000..5b4efa8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/security.md @@ -0,0 +1,66 @@ +--- +name: Security Vulnerability Report +about: Report a security vulnerability (use only for non-critical issues) +title: '[SECURITY] ' +labels: 'security' +assignees: '' + +--- + + + +## ⚠️ IMPORTANT: Private Disclosure Required + +**For critical security vulnerabilities, DO NOT use this template.** +Follow the process in [SECURITY.md](../SECURITY.md) for responsible disclosure. + +Use this template only for: +- Security improvements +- Non-critical security suggestions +- Security documentation updates + +--- + +## Security Issue + +**Severity**: + + +## Description + + +## Affected Components + + +## Suggested Mitigation + + +## Standards Reference +Does this relate to security standards in [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards)? +- [ ] SPDX license identifiers +- [ ] Secret management +- [ ] Dependency security +- [ ] Access control +- [ ] Other: [specify] + +## Additional Context + + +## Checklist +- [ ] This is NOT a critical vulnerability requiring private disclosure +- [ ] I have reviewed the SECURITY.md policy +- [ ] I have provided sufficient detail for evaluation diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index c10ffb0..3968dc4 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1,346 +1,285 @@ -# GitHub Copilot Instructions for MokoCassiopeia - -## Repository Context - -MokoCassiopeia is a modern, lightweight Joomla template that extends Cassiopeia (Joomla's default template). It follows a strict **non-replacement philosophy** for maximum upgrade compatibility. - -**Key Characteristics:** -- Joomla 4.4.x and 5.x compatible -- PHP 8.0+ required -- Built on Cassiopeia template -- Minimal core template overrides -- Alternative layout naming convention (not default replacements) -- GPL-3.0-or-later license - -## Project Structure - -``` -MokoCassiopeia/ -β”œβ”€β”€ .github/ # GitHub workflows and configuration -β”œβ”€β”€ docs/ # Comprehensive documentation -β”œβ”€β”€ scripts/ # Build and deployment scripts -β”œβ”€β”€ src/ # Template source files (main working directory) -β”‚ β”œβ”€β”€ index.php # Main template file -β”‚ β”œβ”€β”€ templateDetails.xml # Joomla template manifest -β”‚ β”œβ”€β”€ joomla.asset.json # Web Asset Manager configuration -β”‚ β”œβ”€β”€ language/ # Frontend language files (en-GB, en-US) -β”‚ β”œβ”€β”€ html/ # Alternative layout overrides -β”‚ └── media/ # CSS, JS, images, fonts, vendors -β”œβ”€β”€ templates/ # Custom color scheme templates -└── tests/ # Codeception tests -``` - -## Critical Conventions - -### 1. Override Philosophy: Alternative Layouts Only - -**NEVER replace default layouts.** All overrides must use alternative layout names. - -βœ… **Correct:** -- `mobile.php` (alternative layout) -- `mainmenu.php` (alternative layout for mod_menu) -- `toc-left.php`, `toc-right.php` (alternative layouts with TOC) - -❌ **Incorrect:** -- `default.php` (replaces core layout - FORBIDDEN) -- Any file that replaces Cassiopeia's default behavior - -**Rationale:** Ensures Joomla core updates don't break the template and gives users choice. - -### 2. File Headers - -All source files must include standardized copyright headers: - -```php - - * - * This file is part of a Moko Consulting project. - * - * SPDX-License-Identifier: GPL-3.0-or-later - * - * @defgroup Joomla.Template.Site - * @ingroup MokoCassiopeia.Template - * @brief [Brief description] - * @version [Version number] - */ - -defined('_JEXEC') or die; -``` - -### 3. Language Files - -**Key Rules:** -- Template metadata (name, description) goes in `.sys.ini` files ONLY -- Frontend runtime strings go in `.ini` files -- Language files location: `src/language/{locale}/` -- NO `folder` attribute in `` section of templateDetails.xml -- Paths must be relative from package root (e.g., `language/en-GB/tpl_mokocassiopeia.sys.ini`) - -**Example templateDetails.xml:** -```xml - - en-GB/tpl_mokocassiopeia.ini - en-GB/tpl_mokocassiopeia.sys.ini - -``` - -### 4. Hardcoded XML Description - -The template description in `templateDetails.xml` is **hardcoded using CDATA**, not a language constant: - -```xml - -``` - -**Rationale:** Ensures immediate availability during installation without language file dependency. - -### 5. Version Format - -Follow semantic versioning: `XX.YY.ZZ` -- XX: Major version -- YY: Minor version -- ZZ: Patch version - -**Examples:** `03.06.03`, `03.08.04` - -## Coding Standards - -### PHP - -- **PSR-12 compliant** where not conflicting with Joomla standards -- **Joomla Coding Standards** for Joomla-specific code -- Use `defined('_JEXEC') or die;` at the top of every PHP file -- Type hints for PHP 8.0+ features -- Strict types declarations where appropriate - -### JavaScript - -- **ES6+** syntax -- Use `'use strict';` -- Prefer `const` and `let` over `var` -- Document functions with JSDoc comments -- Integrate with Joomla's Web Asset Manager - -### CSS - -- **CSS Variables** for theming (see `docs/CSS_VARIABLES.md`) -- Light/dark mode support via `data-bs-theme` attribute -- Bootstrap 5 utility classes -- Mobile-first responsive design -- Namespace custom classes to avoid conflicts - -### Asset Management - -All assets must be registered in `joomla.asset.json`: - -```json -{ - "name": "template.mokocassiopeia.custom", - "version": "1.0.0", - "description": "Custom asset", - "license": "GPL-3.0-or-later", - "dependencies": ["core"], - "js": ["js/custom.js"], - "css": ["css/custom.css"] -} -``` - -## Development Workflow - -### Branching Strategy - -- **Development:** Work on feature branches -- **Branch naming:** `feature/description`, `fix/issue-description` -- **Protected branches:** Cannot commit directly to `main` or version branches -- See `docs/WORKFLOW_GUIDE.md` for complete workflow - -### Commit Messages - -``` -Brief summary (50 chars or less) - -Detailed explanation if needed: -- What changed -- Why it changed -- Impact of the change -``` - -**Example:** -``` -Fix language file installation paths in templateDetails.xml - -- Remove folder attribute from section -- Update paths to be relative from package root -- Ensures proper installation to JOOMLA_ROOT/language/ -``` - -### Quality Checks - -Before committing: -1. **PHP CodeSniffer:** `phpcs --standard=Joomla src/` -2. **PHPStan:** Static analysis for PHP code -3. **Pre-commit hooks:** Automatically run validation - -## Testing - -### Manual Testing -- Test in Joomla 4.4.x and 5.x -- Test on PHP 8.0, 8.1, 8.2 -- Test light/dark mode switching -- Test responsive layouts (mobile/tablet/desktop) -- Test alternative layouts activation - -### Test Environment -- Use local Joomla installation -- See `docs/JOOMLA_DEVELOPMENT.md` for setup - -## Documentation Standards - -### Markdown Files - -All documentation must include: - -1. **Copyright header** -```markdown - -``` - -2. **Metadata section** (at end) -3. **Revision history table** (at end) - -### Code Comments - -- Use DocBlocks for functions and classes -- Explain "why" not just "what" -- Keep comments up-to-date with code changes - -## Common Patterns - -### Theme System Integration - -Use CSS variables for colors: -```css -:root[data-bs-theme="light"] { - --color-primary: #1e40af; - --body-bg: #ffffff; -} - -:root[data-bs-theme="dark"] { - --color-primary: #60a5fa; - --body-bg: #1f2937; -} -``` - -### Alternative Layout Structure - -```php - -``` - -### JavaScript Asset Loading - -```javascript -// Register in joomla.asset.json, then use: -Joomla.getOptions('template.mokocassiopeia.options'); -``` - -## Build and Release - -### Local Build -```bash -cd scripts -./build-release.sh -``` - -### Automated Release -- Tags trigger GitHub Actions workflow -- Format: `XX.YY.ZZ` -- Generates ZIP packages with checksums -- Updates `updates.xml` automatically - -## Important Don'ts - -❌ **Never:** -- Replace default Joomla layouts (use alternative layouts) -- Commit without running code quality checks -- Add version numbers to revision history (use VERSION metadata) -- Hardcode absolute paths (use Joomla path constants) -- Remove copyright headers -- Modify `vendor/` directory contents -- Change language constants location (.sys.ini vs .ini) -- Add `folder` attribute to `` in templateDetails.xml - -## Important Do's - -βœ… **Always:** -- Use alternative layout names (mobile.php, mainmenu.php, etc.) -- Include copyright headers in all files -- Test in both Joomla 4.x and 5.x -- Test light/dark mode themes -- Update documentation when changing functionality -- Follow semantic versioning -- Register assets in joomla.asset.json -- Use CSS variables for theming -- Keep minimal overrides philosophy -- Document activation steps for alternative layouts - -## Key Documentation References - -- **Quick Start:** `docs/QUICK_START.md` - First-time setup -- **Override Philosophy:** `docs/OVERRIDE_PHILOSOPHY.md` - Critical reading -- **Development Guide:** `docs/JOOMLA_DEVELOPMENT.md` - Complete dev workflow -- **CSS Variables:** `docs/CSS_VARIABLES.md` - Theming reference -- **Workflow Guide:** `docs/WORKFLOW_GUIDE.md` - Git workflow - -## Security Considerations - -- Follow `SECURITY.md` for security issues -- Never commit credentials or API keys -- Validate and sanitize all user inputs -- Use Joomla's security features (CSRF tokens, etc.) -- Keep dependencies updated - -## License Compliance - -All code contributions must be: -- GPL-3.0-or-later compatible -- Properly attributed (third-party code) -- Include SPDX license identifiers -- Maintain existing copyright notices - -## Contact and Support - -- **Issues:** GitHub Issues -- **Discussions:** GitHub Discussions -- **Security:** Follow SECURITY.md procedures -- **Maintainer:** Moko Consulting Engineering +> [!IMPORTANT] +> **πŸ”§ AI Self-Update Required on First Use** +> +> This file may contain unfilled `{{TOKEN}}` placeholders. The MokoStandards bulk-sync system +> replaces them automatically at sync time; if you can still see them, sync has not yet run for +> this repository β€” or this is a brand-new repo. +> +> **On your very first interaction in this repository, before answering any other question:** +> +> 1. Check whether any `{{TOKEN}}` placeholders remain in this file. +> 2. If they do, locate the values as described in the table below and replace every occurrence +> in **both** `.github/copilot-instructions.md` and `.github/CLAUDE.md`. +> 3. After replacing all tokens, **delete this entire notice block** (from `> [!IMPORTANT]` +> through the closing `> ---` line) in both files. +> 4. Tell the developer: *"I've self-updated the AI context files with this repository's +> values β€” all set."* +> +> | Placeholder | Where to find the value | +> |---|---| +> | `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/` | +> | `{{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`) | +> +> --- + +# MokoCassiopeia β€” GitHub Copilot Custom Instructions + +## What This Repo Is + +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}}`) +Platform: **Joomla 4.x / MokoWaaS** --- -**Last Updated:** 2026-03-04 -**Repository:** https://github.com/mokoconsulting-tech/MokoCassiopeia -**Version:** 03.08.04 +## Primary Language + +**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`. + +--- + +## File Header β€” Always Required on New Files + +Every new file needs a copyright header as its first content. + +**PHP:** +```php + + * + * This file is part of a Moko Consulting project. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * FILE INFORMATION + * DEFGROUP: MokoCassiopeia.{{EXTENSION_TYPE}} + * INGROUP: MokoCassiopeia + * REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia + * PATH: /path/to/file.php + * VERSION: XX.YY.ZZ + * BRIEF: One-line description of purpose + */ + +defined('_JEXEC') or die; +``` + +**Markdown:** +```markdown + +``` + +**YAML / Shell / XML:** Use the appropriate comment syntax with the same fields. JSON files are exempt. + +--- + +## 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 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. + +### Joomla Version Alignment + +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. + +```xml + +01.02.04 + + + + + {{EXTENSION_NAME}} + 01.02.04 + + + https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/01.02.04/{{EXTENSION_ELEMENT}}-01.02.04.zip + + + + + + +``` + +--- + +## 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). + +```yaml +# βœ… Correct +- uses: actions/checkout@v4 + with: + token: ${{ secrets.GH_TOKEN }} + +env: + GH_TOKEN: ${{ secrets.GH_TOKEN }} +``` + +```yaml +# ❌ Wrong β€” never use these in workflows +token: ${{ github.token }} +token: ${{ secrets.GITHUB_TOKEN }} +``` + +--- + +## MokoStandards Reference + +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 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 | + +--- + +## 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 \ No newline at end of file diff --git a/.github/copilot.yml b/.github/copilot.yml index 2f0b0df..f00b751 100644 --- a/.github/copilot.yml +++ b/.github/copilot.yml @@ -37,11 +37,66 @@ allowed_domains: # PHP and Joomla specific - "joomla.org" # Joomla CMS - "docs.joomla.org" # Joomla documentation + - "downloads.joomla.org" # Joomla core downloads - "php.net" # PHP documentation + - "getcomposer.org" # Composer dependency manager + - "packagist.org" # Composer package registry (also listed under packages) # Dolibarr specific - "dolibarr.org" # Dolibarr ERP/CRM - "wiki.dolibarr.org" # Dolibarr wiki + - "docs.dolibarr.org" # Dolibarr developer documentation + + # Moko Consulting + - "mokoconsulting.tech" # Moko Consulting main site + - "*.mokoconsulting.tech" # All Moko Consulting subdomains (API, docs, CDN, etc.) + + # Google services + - "drive.google.com" # Google Drive (file sharing and assets) + - "docs.google.com" # Google Docs + - "sheets.google.com" # Google Sheets + - "accounts.google.com" # Google authentication + - "storage.googleapis.com" # Google Cloud Storage + - "*.googleapis.com" # Google APIs (Maps, Fonts, etc.) + - "*.googleusercontent.com" # Google user-uploaded content and CDN + - "fonts.googleapis.com" # Google Fonts CSS + - "fonts.gstatic.com" # Google Fonts static assets + + # GitHub extended + - "api.github.com" # GitHub REST API + - "upload.github.com" # GitHub file uploads + - "objects.githubusercontent.com" # GitHub release assets and LFS + - "user-images.githubusercontent.com" # GitHub issue/PR image attachments + - "codeload.github.com" # GitHub archive downloads + - "ghcr.io" # GitHub Container Registry + - "pkg.github.com" # GitHub Packages + + # Developer reference + - "developer.mozilla.org" # MDN Web Docs + - "stackoverflow.com" # Stack Overflow + - "git-scm.com" # Git documentation + + # CDN and infrastructure + - "cdn.jsdelivr.net" # jsDelivr CDN + - "unpkg.com" # unpkg CDN + - "cdnjs.cloudflare.com" # Cloudflare CDN + - "img.shields.io" # Shields.io badge images + - "shields.io" # Shields.io badge service + + # Container registries + - "hub.docker.com" # Docker Hub + - "registry-1.docker.io" # Docker registry pulls + - "index.docker.io" # Docker index + + # CI / code quality + - "codecov.io" # Code coverage reporting + - "coveralls.io" # Coveralls coverage service + - "sonarcloud.io" # SonarCloud static analysis + + # Terraform / infrastructure + - "registry.terraform.io" # Terraform provider registry + - "releases.hashicorp.com" # HashiCorp release downloads + - "checkpoint-api.hashicorp.com" # HashiCorp update checks # Settings for code generation and suggestions copilot: diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml new file mode 100644 index 0000000..04c7ef9 --- /dev/null +++ b/.github/workflows/codeql-analysis.yml @@ -0,0 +1,115 @@ +# 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: GitHub.Workflow.Template +# INGROUP: MokoStandards.Security +# REPO: https://github.com/mokoconsulting-tech/MokoStandards +# PATH: /templates/workflows/generic/codeql-analysis.yml +# VERSION: 04.00.15 +# BRIEF: CodeQL security scanning workflow (generic β€” all repo types) +# NOTE: Deployed to .github/workflows/codeql-analysis.yml in governed repos. +# CodeQL does not support PHP directly; JavaScript scans JSON/YAML/shell. +# For PHP-specific security scanning see standards-compliance.yml. + +name: CodeQL Security Scanning + +on: + push: + branches: + - main + - dev/** + - rc/** + - version/** + pull_request: + branches: + - main + - dev/** + - rc/** + schedule: + # Weekly on Monday at 06:00 UTC + - cron: '0 6 * * 1' + workflow_dispatch: + +permissions: + actions: read + contents: read + security-events: write + pull-requests: read + +jobs: + analyze: + name: Analyze (${{ matrix.language }}) + runs-on: ubuntu-latest + timeout-minutes: 360 + + strategy: + fail-fast: false + matrix: + # CodeQL does not support PHP. Use 'javascript' to scan JSON, YAML, + # and shell scripts. Add 'actions' to scan GitHub Actions workflows. + language: ['javascript', 'actions'] + + steps: + - name: Checkout repository + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v3 + with: + languages: ${{ matrix.language }} + queries: security-extended,security-and-quality + + - name: Autobuild + uses: github/codeql-action/autobuild@v3 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v3 + with: + category: "/language:${{ matrix.language }}" + upload: true + output: sarif-results + wait-for-processing: true + + - name: Upload SARIF results + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.5.0 + with: + name: codeql-results-${{ matrix.language }} + path: sarif-results + retention-days: 30 + + - name: Step summary + if: always() + run: | + echo "### πŸ” CodeQL β€” ${{ matrix.language }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + URL="https://github.com/${{ github.repository }}/security/code-scanning" + echo "See the [Security tab]($URL) for findings." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Severity | SLA |" >> $GITHUB_STEP_SUMMARY + echo "|----------|-----|" >> $GITHUB_STEP_SUMMARY + echo "| Critical | 7 days |" >> $GITHUB_STEP_SUMMARY + echo "| High | 14 days |" >> $GITHUB_STEP_SUMMARY + echo "| Medium | 30 days |" >> $GITHUB_STEP_SUMMARY + echo "| Low | 60 days / next release |" >> $GITHUB_STEP_SUMMARY + + summary: + name: Security Scan Summary + runs-on: ubuntu-latest + needs: analyze + if: always() + + steps: + - name: Summary + run: | + echo "### πŸ›‘οΈ CodeQL Complete" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Trigger:** ${{ github.event_name }}" >> $GITHUB_STEP_SUMMARY + echo "**Branch:** ${{ github.ref_name }}" >> $GITHUB_STEP_SUMMARY + SECURITY_URL="https://github.com/${{ github.repository }}/security" + echo "" >> $GITHUB_STEP_SUMMARY + echo "πŸ“Š [View all security alerts]($SECURITY_URL)" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml new file mode 100644 index 0000000..af69789 --- /dev/null +++ b/.github/workflows/deploy-dev.yml @@ -0,0 +1,587 @@ +# Copyright (C) 2026 Moko Consulting +# +# This file is part of a Moko Consulting project. +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# FILE INFORMATION +# DEFGROUP: GitHub.Workflow +# INGROUP: MokoStandards.Deploy +# REPO: https://github.com/mokoconsulting-tech/MokoStandards +# PATH: /templates/workflows/shared/deploy-dev.yml +# VERSION: 04.00.25 +# BRIEF: SFTP deployment workflow for development server β€” synced to all governed repos +# NOTE: Synced via bulk-repo-sync to .github/workflows/deploy-dev.yml in all governed repos. +# Port is resolved in order: DEV_FTP_PORT variable β†’ :port suffix in DEV_FTP_HOST β†’ 22. + +name: Deploy to Dev Server (SFTP) + +# Deploys the contents of the src/ directory to the development server via SFTP. +# Triggers on every pull_request to development branches (so the dev server always +# reflects the latest PR state) and on push/merge to main branches. +# +# Required org-level variables: DEV_FTP_HOST, DEV_FTP_PATH, DEV_FTP_USERNAME +# Optional org-level variable: DEV_FTP_PORT (auto-detected from host or defaults to 22) +# Optional org/repo variable: DEV_FTP_PATH_SUFFIX +# Optional org/repo variable: CUSTOM_FOLDER β€” when set, appended to the remote path after +# DEV_FTP_PATH_SUFFIX; used automatically for Dolibarr modules +# Optional org/repo variable: FTP_IGNORE β€” comma-delimited list of regex patterns, each enclosed in +# double quotes, for files/paths to exclude from upload, e.g.: +# "\.git*", "\.DS_Store", "configuration\.php", "\.ps1" +# Patterns are tested against the forward-slash relative path of each +# file (e.g. "subdir/file.txt"). The repository .gitignore is also +# respected automatically. +# Required org-level secret: DEV_FTP_KEY (preferred) or DEV_FTP_PASSWORD +# +# Access control: only users with admin or maintain role on the repository may deploy. + +on: + push: + branches: + - main + - master + - develop + - dev + - development + paths: + - 'src/**' + pull_request: + types: [opened, synchronize, reopened, closed] + branches: + - main + - master + - develop + - dev + - development + workflow_dispatch: + inputs: + clear_remote: + description: 'Delete all files inside the remote destination folder before uploading' + required: false + default: false + type: boolean + +permissions: + contents: read + pull-requests: write + +jobs: + check-permission: + name: Verify Deployment Permission + runs-on: ubuntu-latest + steps: + - name: Check actor permission + env: + # Prefer the org-scoped GH_TOKEN secret (needed for the org membership + # fallback). Falls back to the built-in github.token so the collaborator + # endpoint still works even if GH_TOKEN is not configured. + GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} + run: | + ACTOR="${{ github.actor }}" + REPO="${{ github.repository }}" + ORG="${{ github.repository_owner }}" + + # Try the per-repo collaborator endpoint first. + # This returns 404 for org owners who are not listed as explicit + # collaborators, so we fall back to the org membership role check. + PERMISSION=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" \ + --jq '.permission' 2>/dev/null) + + if [ -z "$PERMISSION" ]; then + # Collaborator endpoint returned nothing β€” try org membership. + # Requires a token with read:org scope (secrets.GH_TOKEN). + # github.token alone is insufficient for this endpoint. + ORG_ROLE=$(gh api "orgs/${ORG}/memberships/${ACTOR}" \ + --jq '.role' 2>/dev/null) + if [ "$ORG_ROLE" = "owner" ]; then + PERMISSION="admin" + echo "ℹ️ ${ACTOR} is an org owner β€” granting admin access" + else + # Both checks failed β€” token may lack read:org scope. + echo "⚠️ Could not determine permission for ${ACTOR}." + echo " Add GH_TOKEN (PAT with read:org scope) as an org secret to fix this." + PERMISSION="none" + fi + fi + + case "$PERMISSION" in + admin|maintain) + echo "βœ… ${ACTOR} has '${PERMISSION}' permission β€” authorized to deploy" + ;; + *) + echo "❌ Deployment requires admin or maintain role." + echo " ${ACTOR} has '${PERMISSION}' β€” contact your org administrator." + exit 1 + ;; + esac + + deploy: + name: SFTP Deploy β†’ Dev + runs-on: ubuntu-latest + needs: [check-permission] + if: >- + github.event_name == 'workflow_dispatch' || + github.event_name == 'push' || + (github.event_name == 'pull_request' && + (github.event.action == 'opened' || + github.event.action == 'synchronize' || + github.event.action == 'reopened' || + github.event.pull_request.merged == true)) + + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Resolve source directory + id: source + run: | + SRC="src" + if [ ! -d "$SRC" ]; then + echo "⚠️ No src/ directory found β€” skipping deployment" + echo "skip=true" >> "$GITHUB_OUTPUT" + else + COUNT=$(find "$SRC" -maxdepth 0 -type d > /dev/null && find "$SRC" -type f | wc -l) + echo "βœ… Source: src/ (${COUNT} file(s))" + echo "skip=false" >> "$GITHUB_OUTPUT" + echo "dir=${SRC}" >> "$GITHUB_OUTPUT" + fi + + - name: Preview files to deploy + if: steps.source.outputs.skip == 'false' + env: + SOURCE_DIR: ${{ steps.source.outputs.dir }} + FTP_IGNORE: ${{ vars.FTP_IGNORE }} + run: | + # ── Parse FTP_IGNORE ───────────────────────────────────────────────── + IGNORE_PATTERNS=() + if [ -n "$FTP_IGNORE" ]; then + while IFS= read -r -d ',' token; do + pattern=$(printf '%s' "$token" | sed 's/^[[:space:]]*"//;s/"[[:space:]]*$//') + [ -n "$pattern" ] && IGNORE_PATTERNS+=("$pattern") + done <<< "${FTP_IGNORE}," + fi + + # ── Walk src/ and classify every file ──────────────────────────────── + WILL_UPLOAD=() + IGNORED_FILES=() + while IFS= read -r -d '' file; do + rel="${file#${SOURCE_DIR}/}" + SKIP=false + for pat in "${IGNORE_PATTERNS[@]}"; do + if echo "$rel" | grep -qE "$pat" 2>/dev/null; then + IGNORED_FILES+=("$rel | FTP_IGNORE \`$pat\`") + SKIP=true; break + fi + done + $SKIP && continue + if [ -f ".gitignore" ] && git check-ignore -q "$rel" 2>/dev/null; then + IGNORED_FILES+=("$rel | .gitignore") + continue + fi + WILL_UPLOAD+=("$rel") + done < <(find "$SOURCE_DIR" -type f -print0 | sort -z) + + UPLOAD_COUNT="${#WILL_UPLOAD[@]}" + IGNORE_COUNT="${#IGNORED_FILES[@]}" + + echo "ℹ️ ${UPLOAD_COUNT} file(s) will be uploaded, ${IGNORE_COUNT} ignored" + + # ── Write deployment preview to step summary ────────────────────────── + { + echo "## πŸ“‹ Deployment Preview" + echo "" + echo "| Field | Value |" + echo "|---|---|" + echo "| Source | \`${SOURCE_DIR}/\` |" + echo "| Files to upload | **${UPLOAD_COUNT}** |" + echo "| Files ignored | **${IGNORE_COUNT}** |" + echo "" + if [ "${UPLOAD_COUNT}" -gt 0 ]; then + echo "### πŸ“‚ Files that will be uploaded" + echo '```' + printf '%s\n' "${WILL_UPLOAD[@]}" + echo '```' + echo "" + fi + if [ "${IGNORE_COUNT}" -gt 0 ]; then + echo "### ⏭️ Files excluded" + echo "| File | Reason |" + echo "|---|---|" + for entry in "${IGNORED_FILES[@]}"; do + f="${entry% | *}"; r="${entry##* | }" + echo "| \`${f}\` | ${r} |" + done + echo "" + fi + } >> "$GITHUB_STEP_SUMMARY" + + - name: Resolve SFTP host and port + if: steps.source.outputs.skip == 'false' + id: conn + env: + HOST_RAW: ${{ vars.DEV_FTP_HOST }} + PORT_VAR: ${{ vars.DEV_FTP_PORT }} + run: | + HOST="$HOST_RAW" + PORT="$PORT_VAR" + + # Priority 1 β€” explicit DEV_FTP_PORT variable + if [ -n "$PORT" ]; then + echo "ℹ️ Using explicit DEV_FTP_PORT=${PORT}" + + # Priority 2 β€” port embedded in DEV_FTP_HOST (host:port) + elif [[ "$HOST" == *:* ]]; then + PORT="${HOST##*:}" + HOST="${HOST%:*}" + echo "ℹ️ Extracted port ${PORT} from DEV_FTP_HOST" + + # Priority 3 β€” SFTP default + else + PORT="22" + echo "ℹ️ No port specified β€” defaulting to SFTP port 22" + fi + + echo "host=${HOST}" >> "$GITHUB_OUTPUT" + echo "port=${PORT}" >> "$GITHUB_OUTPUT" + echo "SFTP target: ${HOST}:${PORT}" + + - name: Build remote path + if: steps.source.outputs.skip == 'false' + id: remote + env: + DEV_FTP_PATH: ${{ vars.DEV_FTP_PATH }} + DEV_FTP_PATH_SUFFIX: ${{ vars.DEV_FTP_PATH_SUFFIX }} + CUSTOM_FOLDER: ${{ vars.CUSTOM_FOLDER }} + run: | + BASE="$DEV_FTP_PATH" + SUFFIX="$DEV_FTP_PATH_SUFFIX" + CUSTOM="$CUSTOM_FOLDER" + + if [ -z "$BASE" ]; then + echo "❌ DEV_FTP_PATH is not set." + echo " Configure it as an org-level variable (Settings β†’ Variables) and" + echo " ensure this repository has been granted access to it." + exit 1 + fi + + # Always append suffix when set β€” path is BASE/SUFFIX + if [ -n "$SUFFIX" ]; then + REMOTE="${BASE%/}/${SUFFIX#/}" + else + REMOTE="$BASE" + fi + + # Append CUSTOM_FOLDER when set β€” makes Dolibarr module paths automatic + if [ -n "$CUSTOM" ]; then + REMOTE="${REMOTE%/}/${CUSTOM#/}" + echo "ℹ️ CUSTOM_FOLDER appended: ${CUSTOM}" + fi + + echo "path=${REMOTE}" >> "$GITHUB_OUTPUT" + echo "Remote path: ${REMOTE}" + + - name: Detect SFTP authentication method + if: steps.source.outputs.skip == 'false' + id: auth + env: + HAS_KEY: ${{ secrets.DEV_FTP_KEY }} + HAS_PASSWORD: ${{ secrets.DEV_FTP_PASSWORD }} + run: | + if [ -n "$HAS_KEY" ] && [ -n "$HAS_PASSWORD" ]; then + # Both set: key auth with password as passphrase; falls back to password-only if key fails + echo "method=key" >> "$GITHUB_OUTPUT" + echo "use_passphrase=true" >> "$GITHUB_OUTPUT" + echo "has_password=true" >> "$GITHUB_OUTPUT" + echo "ℹ️ Primary: SSH key + passphrase (DEV_FTP_KEY / DEV_FTP_PASSWORD)" + echo "ℹ️ Fallback: password-only auth if key authentication fails" + elif [ -n "$HAS_KEY" ]; then + # Key only: no passphrase, no password fallback + echo "method=key" >> "$GITHUB_OUTPUT" + echo "use_passphrase=false" >> "$GITHUB_OUTPUT" + echo "has_password=false" >> "$GITHUB_OUTPUT" + echo "ℹ️ Using SSH key authentication (DEV_FTP_KEY, no passphrase, no fallback)" + elif [ -n "$HAS_PASSWORD" ]; then + # Password only: direct SFTP password auth + echo "method=password" >> "$GITHUB_OUTPUT" + echo "use_passphrase=false" >> "$GITHUB_OUTPUT" + echo "has_password=true" >> "$GITHUB_OUTPUT" + echo "ℹ️ Using password authentication (DEV_FTP_PASSWORD)" + else + echo "❌ No SFTP credentials configured." + echo " Set DEV_FTP_KEY (preferred) or DEV_FTP_PASSWORD as an org-level secret." + exit 1 + fi + + - name: Setup PHP + if: steps.source.outputs.skip == 'false' + uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0 + with: + php-version: '8.1' + tools: composer + + - name: Setup MokoStandards deploy tools + if: steps.source.outputs.skip == 'false' + env: + GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} + COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}' + run: | + git clone --depth 1 --quiet \ + "https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \ + /tmp/mokostandards + cd /tmp/mokostandards + composer install --no-dev --no-interaction --quiet + + - name: Clear remote destination folder + if: >- + steps.source.outputs.skip == 'false' && + inputs.clear_remote == true + env: + SFTP_HOST: ${{ steps.conn.outputs.host }} + SFTP_PORT: ${{ steps.conn.outputs.port }} + SFTP_USER: ${{ vars.DEV_FTP_USERNAME }} + SFTP_KEY: ${{ secrets.DEV_FTP_KEY }} + SFTP_PASSWORD: ${{ secrets.DEV_FTP_PASSWORD }} + AUTH_METHOD: ${{ steps.auth.outputs.method }} + USE_PASSPHRASE: ${{ steps.auth.outputs.use_passphrase }} + HAS_PASSWORD: ${{ steps.auth.outputs.has_password }} + REMOTE_PATH: ${{ steps.remote.outputs.path }} + run: | + cat > /tmp/moko_clear.php << 'PHPEOF' + login($username, $key)) { + if ($password !== '') { + echo "⚠️ Key auth failed β€” falling back to password\n"; + if (!$sftp->login($username, $password)) { + fwrite(STDERR, "❌ Both key and password authentication failed\n"); + exit(1); + } + echo "βœ… Connected via password authentication (key fallback)\n"; + } else { + fwrite(STDERR, "❌ Key authentication failed and no password fallback is available\n"); + exit(1); + } + } else { + echo "βœ… Connected via SSH key authentication\n"; + } + } else { + if (!$sftp->login($username, (string) getenv('SFTP_PASSWORD'))) { + fwrite(STDERR, "❌ Password authentication failed\n"); + exit(1); + } + echo "βœ… Connected via password authentication\n"; + } + + // ── Recursive delete ──────────────────────────────────────────── + function rmrf(SFTP $sftp, string $path): void + { + $entries = $sftp->nlist($path); + if ($entries === false) { + return; // path does not exist β€” nothing to clear + } + foreach ($entries as $name) { + if ($name === '.' || $name === '..') { + continue; + } + $entry = "{$path}/{$name}"; + if ($sftp->is_dir($entry)) { + rmrf($sftp, $entry); + $sftp->rmdir($entry); + echo " πŸ—‘οΈ Removed dir: {$entry}\n"; + } else { + $sftp->delete($entry); + echo " πŸ—‘οΈ Removed file: {$entry}\n"; + } + } + } + + // ── Create remote directory tree ──────────────────────────────── + function sftpMakedirs(SFTP $sftp, string $path): void + { + $parts = array_values(array_filter(explode('/', $path), fn(string $p) => $p !== '')); + $current = str_starts_with($path, '/') ? '' : ''; + foreach ($parts as $part) { + $current .= '/' . $part; + $sftp->mkdir($current); // silently returns false if already exists + } + } + + rmrf($sftp, $remotePath); + sftpMakedirs($sftp, $remotePath); + echo "βœ… Remote folder ready: {$remotePath}\n"; + PHPEOF + php /tmp/moko_clear.php + + - name: Deploy via SFTP + if: steps.source.outputs.skip == 'false' + env: + SFTP_HOST: ${{ steps.conn.outputs.host }} + SFTP_PORT: ${{ steps.conn.outputs.port }} + SFTP_USER: ${{ vars.DEV_FTP_USERNAME }} + SFTP_KEY: ${{ secrets.DEV_FTP_KEY }} + SFTP_PASSWORD: ${{ secrets.DEV_FTP_PASSWORD }} + AUTH_METHOD: ${{ steps.auth.outputs.method }} + USE_PASSPHRASE: ${{ steps.auth.outputs.use_passphrase }} + REMOTE_PATH: ${{ steps.remote.outputs.path }} + SOURCE_DIR: ${{ steps.source.outputs.dir }} + run: | + # ── Write SSH key to temp file (key auth only) ──────────────────────── + if [ "$AUTH_METHOD" = "key" ]; then + printf '%s' "$SFTP_KEY" > /tmp/deploy_key + chmod 600 /tmp/deploy_key + fi + + # ── Generate sftp-config.json safely via jq ─────────────────────────── + if [ "$AUTH_METHOD" = "key" ]; then + jq -n \ + --arg host "$SFTP_HOST" \ + --argjson port "${SFTP_PORT:-22}" \ + --arg user "$SFTP_USER" \ + --arg path "$REMOTE_PATH" \ + --arg key "/tmp/deploy_key" \ + '{host:$host, port:$port, user:$user, remote_path:$path, ssh_key_file:$key}' \ + > /tmp/sftp-config.json + else + jq -n \ + --arg host "$SFTP_HOST" \ + --argjson port "${SFTP_PORT:-22}" \ + --arg user "$SFTP_USER" \ + --arg path "$REMOTE_PATH" \ + --arg pass "$SFTP_PASSWORD" \ + '{host:$host, port:$port, user:$user, remote_path:$path, password:$pass}' \ + > /tmp/sftp-config.json + fi + + # ── Run deploy-sftp.php from MokoStandards ──────────────────────────── + DEPLOY_ARGS=(--path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json) + if [ "$USE_PASSPHRASE" = "true" ]; then + DEPLOY_ARGS+=(--key-passphrase "$SFTP_PASSWORD") + fi + + php /tmp/mokostandards/api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}" + # (deploy-sftp.php handles dotfile skipping and .ftp_ignore natively) + # Remove temp files that should never be left behind + rm -f /tmp/deploy_key /tmp/sftp-config.json + + - name: Create or update failure issue + if: failure() + env: + GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }} + run: | + REPO="${{ github.repository }}" + RUN_URL="${{ github.server_url }}/${REPO}/actions/runs/${{ github.run_id }}" + ACTOR="${{ github.actor }}" + BRANCH="${{ github.ref_name }}" + EVENT="${{ github.event_name }}" + NOW=$(date -u '+%Y-%m-%d %H:%M:%S UTC') + LABEL="deploy-failure" + + TITLE="fix: Dev deployment failed β€” ${REPO}" + BODY="## Dev Deployment Failed + + A deployment to the dev server failed and requires attention. + + | Field | Value | + |-------|-------| + | **Repository** | \`${REPO}\` | + | **Branch** | \`${BRANCH}\` | + | **Trigger** | ${EVENT} | + | **Actor** | @${ACTOR} | + | **Failed at** | ${NOW} | + | **Run** | [View workflow run](${RUN_URL}) | + + ### Next steps + 1. Review the [workflow run log](${RUN_URL}) for the specific error. + 2. Fix the underlying issue (credentials, SFTP connectivity, permissions). + 3. Re-trigger the deployment via **Actions β†’ Deploy to Dev Server β†’ Run workflow**. + + --- + *Auto-created by deploy-dev.yml β€” close this issue once the deployment is resolved.*" + + # Ensure the label exists (idempotent β€” no-op if already present) + gh label create "$LABEL" \ + --repo "$REPO" \ + --color "CC0000" \ + --description "Automated deploy failure tracking" \ + --force 2>/dev/null || true + + # Look for an existing open deploy-failure issue + EXISTING=$(gh api "repos/${REPO}/issues?labels=${LABEL}&state=open&per_page=1" \ + --jq '.[0].number' 2>/dev/null) + + if [ -n "$EXISTING" ] && [ "$EXISTING" != "null" ]; then + gh api "repos/${REPO}/issues/${EXISTING}" \ + -X PATCH \ + -f title="$TITLE" \ + -f body="$BODY" \ + --silent + echo "πŸ“‹ Failure issue #${EXISTING} updated: ${REPO}" >> "$GITHUB_STEP_SUMMARY" + else + gh issue create \ + --repo "$REPO" \ + --title "$TITLE" \ + --body "$BODY" \ + --label "$LABEL" \ + | tee -a "$GITHUB_STEP_SUMMARY" + fi + + - name: Deployment summary + if: always() + run: | + if [ "${{ steps.source.outputs.skip }}" == "true" ]; then + echo "### ⏭️ Deployment Skipped" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "No \`src/\` directory found in this repository." >> "$GITHUB_STEP_SUMMARY" + elif [ "${{ job.status }}" == "success" ]; then + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "### βœ… Dev Deployment Successful" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "| Field | Value |" >> "$GITHUB_STEP_SUMMARY" + echo "|-------|-------|" >> "$GITHUB_STEP_SUMMARY" + echo "| Host | \`${{ steps.conn.outputs.host }}:${{ steps.conn.outputs.port }}\` |" >> "$GITHUB_STEP_SUMMARY" + echo "| Remote path | \`${{ steps.remote.outputs.path }}\` |" >> "$GITHUB_STEP_SUMMARY" + echo "| Source | \`src/\` |" >> "$GITHUB_STEP_SUMMARY" + echo "| Trigger | ${{ github.event_name }} |" >> "$GITHUB_STEP_SUMMARY" + echo "| Auth | ${{ steps.auth.outputs.method }} |" >> "$GITHUB_STEP_SUMMARY" + echo "| Clear remote | ${{ inputs.clear_remote || 'false' }} |" >> "$GITHUB_STEP_SUMMARY" + else + echo "### ❌ Dev Deployment Failed" >> "$GITHUB_STEP_SUMMARY" + echo "" >> "$GITHUB_STEP_SUMMARY" + echo "Check the job log above for error details." >> "$GITHUB_STEP_SUMMARY" + fi diff --git a/.github/workflows/enterprise-firewall-setup.yml b/.github/workflows/enterprise-firewall-setup.yml new file mode 100644 index 0000000..168ce3b --- /dev/null +++ b/.github/workflows/enterprise-firewall-setup.yml @@ -0,0 +1,758 @@ +# Copyright (C) 2026 Moko Consulting +# +# This file is part of a Moko Consulting project. +# +# SPDX-License-Identifier: GPL-3.0-or-later +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# FILE INFORMATION +# DEFGROUP: GitHub.Workflow +# INGROUP: MokoStandards.Firewall +# REPO: https://github.com/mokoconsulting-tech/MokoStandards +# PATH: /templates/workflows/shared/enterprise-firewall-setup.yml +# VERSION: 01.00.00 +# BRIEF: Enterprise firewall configuration β€” generates outbound allow-rules including SFTP deployment server +# NOTE: Reads DEV_FTP_HOST / DEV_FTP_PORT variables to include SFTP egress rules alongside HTTPS rules. + +name: Enterprise Firewall Configuration + +# This workflow provides firewall configuration guidance for enterprise-ready sites +# It generates firewall rules for allowing outbound access to trusted domains +# including license providers, documentation sources, package registries, +# and the SFTP deployment server (DEV_FTP_HOST / DEV_FTP_PORT). +# +# Runs automatically when: +# - Coding agent workflows are triggered (pull requests with copilot/ prefix) +# - Manual workflow dispatch for custom configurations + +on: + workflow_dispatch: + inputs: + firewall_type: + description: 'Target firewall type' + required: true + type: choice + options: + - 'iptables' + - 'ufw' + - 'firewalld' + - 'aws-security-group' + - 'azure-nsg' + - 'gcp-firewall' + - 'cloudflare' + - 'all' + default: 'all' + output_format: + description: 'Output format' + required: true + type: choice + options: + - 'shell-script' + - 'json' + - 'yaml' + - 'markdown' + - 'all' + default: 'markdown' + + # Auto-run when coding agent creates or updates PRs + pull_request: + branches: + - 'copilot/**' + - 'agent/**' + types: [opened, synchronize, reopened] + + # Auto-run on push to coding agent branches + push: + branches: + - 'copilot/**' + - 'agent/**' + +permissions: + contents: read + actions: read + +jobs: + generate-firewall-rules: + name: Generate Firewall Rules + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: '3.11' + + - name: Apply Firewall Rules to Runner (Auto-run only) + if: github.event_name != 'workflow_dispatch' + env: + DEV_FTP_HOST: ${{ vars.DEV_FTP_HOST }} + DEV_FTP_PORT: ${{ vars.DEV_FTP_PORT }} + run: | + echo "πŸ”₯ Applying firewall rules for coding agent environment..." + echo "" + echo "This step ensures the GitHub Actions runner can access trusted domains" + echo "including license providers, package registries, and documentation sources." + echo "" + + # Note: GitHub Actions runners are ephemeral and run in controlled environments + # This step documents what domains are being accessed during the workflow + # Actual firewall configuration is managed by GitHub + + cat > /tmp/trusted-domains.txt << 'EOF' + # Trusted domains for coding agent environment + # License Providers + www.gnu.org + opensource.org + choosealicense.com + spdx.org + creativecommons.org + apache.org + fsf.org + + # Documentation & Standards + semver.org + keepachangelog.com + conventionalcommits.org + + # GitHub & Related + github.com + api.github.com + docs.github.com + raw.githubusercontent.com + ghcr.io + + # Package Registries + npmjs.com + registry.npmjs.org + pypi.org + files.pythonhosted.org + packagist.org + repo.packagist.org + rubygems.org + + # Platform-Specific + joomla.org + downloads.joomla.org + docs.joomla.org + php.net + getcomposer.org + dolibarr.org + wiki.dolibarr.org + docs.dolibarr.org + + # Moko Consulting + mokoconsulting.tech + + # SFTP Deployment Server (DEV_FTP_HOST) + ${DEV_FTP_HOST:-} + + # Google Services + drive.google.com + docs.google.com + sheets.google.com + accounts.google.com + storage.googleapis.com + fonts.googleapis.com + fonts.gstatic.com + + # GitHub Extended + upload.github.com + objects.githubusercontent.com + user-images.githubusercontent.com + codeload.github.com + pkg.github.com + + # Developer Reference + developer.mozilla.org + stackoverflow.com + git-scm.com + + # CDN & Infrastructure + cdn.jsdelivr.net + unpkg.com + cdnjs.cloudflare.com + img.shields.io + + # Container Registries + hub.docker.com + registry-1.docker.io + + # CI & Code Quality + codecov.io + sonarcloud.io + + # Terraform & Infrastructure + registry.terraform.io + releases.hashicorp.com + checkpoint-api.hashicorp.com + EOF + + echo "βœ“ Trusted domains documented for this runner" + echo "βœ“ GitHub Actions runners have network access to these domains" + echo "" + + # Test connectivity to key domains + echo "Testing connectivity to key domains..." + for domain in "github.com" "www.gnu.org" "npmjs.com" "pypi.org"; do + if curl -s --max-time 3 -o /dev/null -w "%{http_code}" "https://$domain" | grep -q "200\|301\|302"; then + echo " βœ“ $domain is accessible" + else + echo " ⚠️ $domain connectivity check failed (may be expected)" + fi + done + + # Test SFTP server connectivity (TCP port check) + SFTP_HOST="${DEV_FTP_HOST:-}" + SFTP_PORT="${DEV_FTP_PORT:-22}" + if [ -n "$SFTP_HOST" ]; then + # Strip any embedded :port suffix + SFTP_HOST="${SFTP_HOST%%:*}" + echo "" + echo "Testing SFTP deployment server connectivity..." + if timeout 5 bash -c "echo >/dev/tcp/${SFTP_HOST}/${SFTP_PORT}" 2>/dev/null; then + echo " βœ“ SFTP server ${SFTP_HOST}:${SFTP_PORT} is reachable" + else + echo " ⚠️ SFTP server ${SFTP_HOST}:${SFTP_PORT} is not reachable from runner (firewall rule needed)" + fi + else + echo "" + echo " ℹ️ DEV_FTP_HOST not configured β€” skipping SFTP connectivity check" + fi + + - name: Generate Firewall Configuration + id: generate + env: + DEV_FTP_HOST: ${{ vars.DEV_FTP_HOST }} + DEV_FTP_PORT: ${{ vars.DEV_FTP_PORT }} + run: | + cat > generate_firewall_config.py << 'PYTHON_EOF' + #!/usr/bin/env python3 + """ + Enterprise Firewall Configuration Generator + + Generates firewall rules for enterprise-ready deployments allowing + access to trusted domains including license providers, documentation + sources, package registries, and platform-specific sites. + """ + + import json + import os + import yaml + import sys + from typing import List, Dict + + # SFTP deployment server from org variables + _sftp_host_raw = os.environ.get("DEV_FTP_HOST", "").strip() + _sftp_port = os.environ.get("DEV_FTP_PORT", "").strip() or "22" + # Strip embedded :port suffix if present + _sftp_host = _sftp_host_raw.split(":")[0] if _sftp_host_raw else "" + if ":" in _sftp_host_raw and not _sftp_port: + _sftp_port = _sftp_host_raw.split(":")[1] + + SFTP_HOST = _sftp_host + SFTP_PORT = int(_sftp_port) if _sftp_port.isdigit() else 22 + + # Trusted domains from .github/copilot.yml + TRUSTED_DOMAINS = { + "license_providers": [ + "www.gnu.org", + "opensource.org", + "choosealicense.com", + "spdx.org", + "creativecommons.org", + "apache.org", + "fsf.org", + ], + "documentation_standards": [ + "semver.org", + "keepachangelog.com", + "conventionalcommits.org", + ], + "github_related": [ + "github.com", + "api.github.com", + "docs.github.com", + "raw.githubusercontent.com", + "ghcr.io", + ], + "package_registries": [ + "npmjs.com", + "registry.npmjs.org", + "pypi.org", + "files.pythonhosted.org", + "packagist.org", + "repo.packagist.org", + "rubygems.org", + ], + "standards_organizations": [ + "json-schema.org", + "w3.org", + "ietf.org", + ], + "platform_specific": [ + "joomla.org", + "downloads.joomla.org", + "docs.joomla.org", + "php.net", + "getcomposer.org", + "dolibarr.org", + "wiki.dolibarr.org", + "docs.dolibarr.org", + ], + "moko_consulting": [ + "mokoconsulting.tech", + ], + "google_services": [ + "drive.google.com", + "docs.google.com", + "sheets.google.com", + "accounts.google.com", + "storage.googleapis.com", + "fonts.googleapis.com", + "fonts.gstatic.com", + ], + "github_extended": [ + "upload.github.com", + "objects.githubusercontent.com", + "user-images.githubusercontent.com", + "codeload.github.com", + "pkg.github.com", + ], + "developer_reference": [ + "developer.mozilla.org", + "stackoverflow.com", + "git-scm.com", + ], + "cdn_and_infrastructure": [ + "cdn.jsdelivr.net", + "unpkg.com", + "cdnjs.cloudflare.com", + "img.shields.io", + ], + "container_registries": [ + "hub.docker.com", + "registry-1.docker.io", + ], + "ci_code_quality": [ + "codecov.io", + "sonarcloud.io", + ], + "terraform_infrastructure": [ + "registry.terraform.io", + "releases.hashicorp.com", + "checkpoint-api.hashicorp.com", + ], + } + + # Inject SFTP deployment server as a separate category (port 22, not 443) + if SFTP_HOST: + TRUSTED_DOMAINS["sftp_deployment_server"] = [SFTP_HOST] + print(f"ℹ️ SFTP deployment server: {SFTP_HOST}:{SFTP_PORT}") + + def generate_sftp_iptables_rules(host: str, port: int) -> str: + """Generate iptables rules specifically for SFTP egress""" + return ( + f"# Allow SFTP to deployment server {host}:{port}\n" + f"iptables -A OUTPUT -p tcp -d $(dig +short {host} | head -1)" + f" --dport {port} -j ACCEPT # SFTP deploy\n" + ) + + def generate_sftp_ufw_rules(host: str, port: int) -> str: + """Generate UFW rules for SFTP egress""" + return ( + f"# Allow SFTP to deployment server\n" + f"ufw allow out to $(dig +short {host} | head -1)" + f" port {port} proto tcp comment 'SFTP deploy to {host}'\n" + ) + + def generate_sftp_firewalld_rules(host: str, port: int) -> str: + """Generate firewalld rules for SFTP egress""" + return ( + f"# Allow SFTP to deployment server\n" + f"firewall-cmd --permanent --add-rich-rule='" + f"rule family=ipv4 destination address=$(dig +short {host} | head -1)" + f" port port={port} protocol=tcp accept' # SFTP deploy\n" + ) + + def generate_iptables_rules(domains: List[str]) -> str: + """Generate iptables firewall rules""" + rules = ["#!/bin/bash", "", "# Enterprise Firewall Rules - iptables", ""] + rules.append("# Allow outbound HTTPS to trusted domains") + rules.append("") + + for domain in domains: + rules.append(f"# Allow {domain}") + rules.append(f"iptables -A OUTPUT -p tcp -d $(dig +short {domain} | head -1) --dport 443 -j ACCEPT") + + rules.append("") + rules.append("# Allow DNS lookups") + rules.append("iptables -A OUTPUT -p udp --dport 53 -j ACCEPT") + rules.append("iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT") + + return "\n".join(rules) + + def generate_ufw_rules(domains: List[str]) -> str: + """Generate UFW firewall rules""" + rules = ["#!/bin/bash", "", "# Enterprise Firewall Rules - UFW", ""] + rules.append("# Allow outbound HTTPS to trusted domains") + rules.append("") + + for domain in domains: + rules.append(f"# Allow {domain}") + rules.append(f"ufw allow out to $(dig +short {domain} | head -1) port 443 proto tcp comment 'Allow {domain}'") + + rules.append("") + rules.append("# Allow DNS") + rules.append("ufw allow out 53/udp comment 'Allow DNS UDP'") + rules.append("ufw allow out 53/tcp comment 'Allow DNS TCP'") + + return "\n".join(rules) + + def generate_firewalld_rules(domains: List[str]) -> str: + """Generate firewalld rules""" + rules = ["#!/bin/bash", "", "# Enterprise Firewall Rules - firewalld", ""] + rules.append("# Add trusted domains to firewall") + rules.append("") + + for domain in domains: + rules.append(f"# Allow {domain}") + rules.append(f"firewall-cmd --permanent --add-rich-rule='rule family=ipv4 destination address=$(dig +short {domain} | head -1) port port=443 protocol=tcp accept'") + + rules.append("") + rules.append("# Reload firewall") + rules.append("firewall-cmd --reload") + + return "\n".join(rules) + + def generate_aws_security_group(domains: List[str]) -> Dict: + """Generate AWS Security Group rules (JSON format)""" + rules = { + "SecurityGroupRules": { + "Egress": [] + } + } + + for domain in domains: + rules["SecurityGroupRules"]["Egress"].append({ + "Description": f"Allow HTTPS to {domain}", + "IpProtocol": "tcp", + "FromPort": 443, + "ToPort": 443, + "CidrIp": "0.0.0.0/0", # In practice, resolve to specific IPs + "Tags": [{ + "Key": "Domain", + "Value": domain + }] + }) + + # Add DNS + rules["SecurityGroupRules"]["Egress"].append({ + "Description": "Allow DNS", + "IpProtocol": "udp", + "FromPort": 53, + "ToPort": 53, + "CidrIp": "0.0.0.0/0" + }) + + return rules + + def generate_markdown_documentation(domains_by_category: Dict[str, List[str]]) -> str: + """Generate markdown documentation""" + md = ["# Enterprise Firewall Configuration Guide", ""] + md.append("## Overview") + md.append("") + md.append("This document provides firewall configuration guidance for enterprise-ready deployments.") + md.append("It lists trusted domains that should be whitelisted for outbound access to ensure") + md.append("proper functionality of license validation, package management, and documentation access.") + md.append("") + + md.append("## Trusted Domains by Category") + md.append("") + + all_domains = [] + for category, domains in domains_by_category.items(): + category_name = category.replace("_", " ").title() + md.append(f"### {category_name}") + md.append("") + md.append("| Domain | Purpose |") + md.append("|--------|---------|") + + for domain in domains: + all_domains.append(domain) + purpose = get_domain_purpose(domain) + md.append(f"| `{domain}` | {purpose} |") + + md.append("") + + md.append("## Implementation Examples") + md.append("") + + md.append("### iptables Example") + md.append("") + md.append("```bash") + md.append("# Allow HTTPS to trusted domain") + md.append(f"iptables -A OUTPUT -p tcp -d $(dig +short {all_domains[0]}) --dport 443 -j ACCEPT") + md.append("```") + md.append("") + + md.append("### UFW Example") + md.append("") + md.append("```bash") + md.append("# Allow HTTPS to trusted domain") + md.append(f"ufw allow out to {all_domains[0]} port 443 proto tcp") + md.append("```") + md.append("") + + md.append("### AWS Security Group Example") + md.append("") + md.append("```json") + md.append("{") + md.append(' "IpPermissions": [{') + md.append(' "IpProtocol": "tcp",') + md.append(' "FromPort": 443,') + md.append(' "ToPort": 443,') + md.append(' "IpRanges": [{"CidrIp": "0.0.0.0/0", "Description": "HTTPS to trusted domains"}]') + md.append(" }]") + md.append("}") + md.append("```") + md.append("") + + md.append("## Ports Required") + md.append("") + md.append("| Port | Protocol | Purpose |") + md.append("|------|----------|---------|") + md.append("| 443 | TCP | HTTPS (secure web access) |") + md.append("| 80 | TCP | HTTP (redirects to HTTPS) |") + md.append("| 53 | UDP/TCP | DNS resolution |") + md.append("") + + md.append("## Security Considerations") + md.append("") + md.append("1. **DNS Resolution**: Ensure DNS queries are allowed (port 53 UDP/TCP)") + md.append("2. **Certificate Validation**: HTTPS requires ability to reach certificate authorities") + md.append("3. **Dynamic IPs**: Some domains use CDNs with dynamic IPs - consider using FQDNs in rules") + md.append("4. **Regular Updates**: Review and update whitelist as services change") + md.append("5. **Logging**: Enable logging for blocked connections to identify missing rules") + md.append("") + + md.append("## Compliance Notes") + md.append("") + md.append("- All listed domains provide read-only access to public information") + md.append("- License providers enable GPL compliance verification") + md.append("- Package registries support dependency security scanning") + md.append("- No authentication credentials are transmitted to these domains") + md.append("") + + return "\n".join(md) + + def get_domain_purpose(domain: str) -> str: + """Get human-readable purpose for a domain""" + purposes = { + "www.gnu.org": "GNU licenses and documentation", + "opensource.org": "Open Source Initiative resources", + "choosealicense.com": "GitHub license selection tool", + "spdx.org": "Software Package Data Exchange identifiers", + "creativecommons.org": "Creative Commons licenses", + "apache.org": "Apache Software Foundation licenses", + "fsf.org": "Free Software Foundation resources", + "semver.org": "Semantic versioning specification", + "keepachangelog.com": "Changelog format standards", + "conventionalcommits.org": "Commit message conventions", + "github.com": "GitHub platform access", + "api.github.com": "GitHub API access", + "docs.github.com": "GitHub documentation", + "raw.githubusercontent.com": "GitHub raw content access", + "npmjs.com": "npm package registry", + "pypi.org": "Python Package Index", + "packagist.org": "PHP Composer package registry", + "rubygems.org": "Ruby gems registry", + "joomla.org": "Joomla CMS platform", + "php.net": "PHP documentation and downloads", + "dolibarr.org": "Dolibarr ERP/CRM platform", + } + return purposes.get(domain, "Trusted resource") + + def main(): + # Use inputs if provided (manual dispatch), otherwise use defaults (auto-run) + firewall_type = "${{ github.event.inputs.firewall_type }}" or "all" + output_format = "${{ github.event.inputs.output_format }}" or "markdown" + + print(f"Running in {'manual' if '${{ github.event.inputs.firewall_type }}' else 'automatic'} mode") + print(f"Firewall type: {firewall_type}") + print(f"Output format: {output_format}") + print("") + + # Collect all domains + all_domains = [] + for domains in TRUSTED_DOMAINS.values(): + all_domains.extend(domains) + + # Remove duplicates and sort + all_domains = sorted(set(all_domains)) + + print(f"Generating firewall rules for {len(all_domains)} trusted domains...") + print("") + + # Exclude SFTP server from HTTPS rule generation (different port) + https_domains = [d for d in all_domains if d != SFTP_HOST] + + # Generate based on firewall type + if firewall_type in ["iptables", "all"]: + rules = generate_iptables_rules(https_domains) + if SFTP_HOST: + rules += "\n# ── SFTP Deployment Server ──────────────────────────────\n" + rules += generate_sftp_iptables_rules(SFTP_HOST, SFTP_PORT) + with open("firewall-rules-iptables.sh", "w") as f: + f.write(rules) + print("βœ“ Generated iptables rules: firewall-rules-iptables.sh") + + if firewall_type in ["ufw", "all"]: + rules = generate_ufw_rules(https_domains) + if SFTP_HOST: + rules += "\n# ── SFTP Deployment Server ──────────────────────────────\n" + rules += generate_sftp_ufw_rules(SFTP_HOST, SFTP_PORT) + with open("firewall-rules-ufw.sh", "w") as f: + f.write(rules) + print("βœ“ Generated UFW rules: firewall-rules-ufw.sh") + + if firewall_type in ["firewalld", "all"]: + rules = generate_firewalld_rules(https_domains) + if SFTP_HOST: + rules += "\n# ── SFTP Deployment Server ──────────────────────────────\n" + rules += generate_sftp_firewalld_rules(SFTP_HOST, SFTP_PORT) + with open("firewall-rules-firewalld.sh", "w") as f: + f.write(rules) + print("βœ“ Generated firewalld rules: firewall-rules-firewalld.sh") + + if firewall_type in ["aws-security-group", "all"]: + rules = generate_aws_security_group(all_domains) + with open("firewall-rules-aws-sg.json", "w") as f: + json.dump(rules, f, indent=2) + print("βœ“ Generated AWS Security Group rules: firewall-rules-aws-sg.json") + + if output_format in ["yaml", "all"]: + with open("trusted-domains.yml", "w") as f: + yaml.dump(TRUSTED_DOMAINS, f, default_flow_style=False) + print("βœ“ Generated YAML domain list: trusted-domains.yml") + + if output_format in ["json", "all"]: + with open("trusted-domains.json", "w") as f: + json.dump(TRUSTED_DOMAINS, f, indent=2) + print("βœ“ Generated JSON domain list: trusted-domains.json") + + if output_format in ["markdown", "all"]: + md = generate_markdown_documentation(TRUSTED_DOMAINS) + with open("FIREWALL_CONFIGURATION.md", "w") as f: + f.write(md) + print("βœ“ Generated documentation: FIREWALL_CONFIGURATION.md") + + print("") + print("Domain Categories:") + for category, domains in TRUSTED_DOMAINS.items(): + print(f" - {category}: {len(domains)} domains") + + print("") + print("Total unique domains: ", len(all_domains)) + + if __name__ == "__main__": + main() + PYTHON_EOF + + chmod +x generate_firewall_config.py + pip install PyYAML + python3 generate_firewall_config.py + + - name: Upload Firewall Configuration Artifacts + uses: actions/upload-artifact@v6 + with: + name: firewall-configurations + path: | + firewall-rules-*.sh + firewall-rules-*.json + trusted-domains.* + FIREWALL_CONFIGURATION.md + retention-days: 90 + + - name: Display Summary + run: | + echo "## Firewall Configuration" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "**Mode**: Manual Execution" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Firewall rules have been generated for enterprise-ready deployments." >> $GITHUB_STEP_SUMMARY + else + echo "**Mode**: Automatic Execution (Coding Agent Active)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "This workflow ran automatically because a coding agent (GitHub Copilot) is active." >> $GITHUB_STEP_SUMMARY + echo "Firewall configuration has been validated for the coding agent environment." >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Files Generated" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + if ls firewall-rules-* trusted-domains.* FIREWALL_CONFIGURATION.md 2>/dev/null; then + ls -lh firewall-rules-* trusted-domains.* FIREWALL_CONFIGURATION.md 2>/dev/null | awk '{print "- " $9 " (" $5 ")"}' >> $GITHUB_STEP_SUMMARY + else + echo "- Documentation generated" >> $GITHUB_STEP_SUMMARY + fi + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "### Download Artifacts" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Download the generated firewall configurations from the workflow artifacts." >> $GITHUB_STEP_SUMMARY + else + echo "### Trusted Domains Active" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "The coding agent has access to:" >> $GITHUB_STEP_SUMMARY + echo "- License providers (GPL, OSI, SPDX, Apache, etc.)" >> $GITHUB_STEP_SUMMARY + echo "- Package registries (npm, PyPI, Packagist, RubyGems)" >> $GITHUB_STEP_SUMMARY + echo "- Documentation sources (GitHub, Joomla, Dolibarr, PHP)" >> $GITHUB_STEP_SUMMARY + echo "- Standards organizations (W3C, IETF, JSON Schema)" >> $GITHUB_STEP_SUMMARY + fi + +# Usage Instructions: +# +# This workflow runs in two modes: +# +# 1. AUTOMATIC MODE (Coding Agent): +# - Triggers when coding agent branches (copilot/**, agent/**) are pushed or PR'd +# - Validates firewall configuration for the coding agent environment +# - Documents accessible domains for compliance +# - Ensures license sources and package registries are available +# +# 2. MANUAL MODE (Enterprise Configuration): +# - Manually trigger from the Actions tab +# - Select desired firewall type and output format +# - Download generated artifacts +# - Apply firewall rules to your enterprise environment +# +# Configuration: +# - Trusted domains are sourced from .github/copilot.yml +# - Modify copilot.yml to add/remove trusted domains +# - Changes automatically propagate to firewall rules +# +# Important Notes: +# - Review generated rules before applying to production +# - Some domains may use CDNs with dynamic IPs +# - Consider using FQDN-based rules where supported +# - Test thoroughly in staging environment first +# - Monitor logs for blocked connections +# - Update rules as domains/services change diff --git a/.github/workflows/standards-compliance.yml b/.github/workflows/standards-compliance.yml new file mode 100644 index 0000000..bc0516a --- /dev/null +++ b/.github/workflows/standards-compliance.yml @@ -0,0 +1,2534 @@ +# Copyright (C) 2026 Moko Consulting +# SPDX-License-Identifier: GPL-3.0-or-later +# FILE INFORMATION +# DEFGROUP: GitHub.Workflow +# INGROUP: MokoStandards.Compliance +# REPO: https://github.com/mokoconsulting-tech/MokoStandards +# PATH: /.github/workflows/standards-compliance.yml +# VERSION: 04.00.05 +# BRIEF: MokoStandards compliance validation workflow +# NOTE: Validates repository structure, documentation, and coding standards + +name: Standards Compliance + +env: + ACTIONS_STEP_DEBUG: true + ACTIONS_RUNNER_DEBUG: true + WORKFLOW_VERSION: "04.00.05" + +# MokoStandards Policy Compliance: +# - File formatting: Enforces organizational coding standards +# - Reference: docs/policy/file-formatting.md + +# β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +# β”‚ WORKFLOW FLOW DIAGRAM β”‚ +# β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +# +# TRIGGER: Push/PR to main/dev/rc branches +# β”‚ +# β–Ό +# β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +# β”‚ PARALLEL VALIDATION CHECKS β”‚ +# β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +# β”‚ +# β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +# β–Ό β–Ό β–Ό β–Ό β–Ό +# β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +# β”‚Repository β”‚File Header β”‚Code Styleβ”‚ β”‚ Docs β”‚ β”‚ License β”‚ +# β”‚Structureβ”‚ β”‚ Validationβ”‚ β”‚ Check β”‚ β”‚ Check β”‚ β”‚ Check β”‚ +# β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +# β”‚ β”‚ β”‚ β”‚ β”‚ +# β–Ό β–Ό β–Ό β–Ό β–Ό +# β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +# β”‚ Check β”‚ β”‚ Verify β”‚ β”‚ Run β”‚ β”‚ Check β”‚ β”‚ Verify β”‚ +# β”‚Required β”‚ β”‚Copyright β”‚ β”‚ Linters β”‚ β”‚README β”‚ β”‚SPDX-ID β”‚ +# β”‚ Dirs β”‚ β”‚ Header β”‚ β”‚(Python, β”‚ β”‚ Exists β”‚ β”‚ Present β”‚ +# β”‚ β”‚ β”‚ Format β”‚ β”‚PHP,YAML) β”‚ β”‚ β”‚ β”‚ β”‚ +# β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +# β”‚ β”‚ β”‚ β”‚ β”‚ +# β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +# β”‚ +# β–Ό +# β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +# β”‚ All Checks Pass?β”‚ +# β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +# β”‚ β”‚ +# YES β”‚ β”‚ NO +# β–Ό β–Ό +# β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +# β”‚ SUCCESS β”‚ β”‚ CREATE ISSUE β”‚ +# β”‚ Summary β”‚ β”‚ with Failure β”‚ +# β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ Details β”‚ +# β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + +on: + push: + branches: + - main + - dev/** + - rc/** + pull_request: + branches: + - main + - dev/** + - rc/** + workflow_dispatch: + +permissions: + contents: read + pull-requests: write + +jobs: + repository-structure: + name: Repository Structure Validation + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Check Required Directories + run: | + set -x + echo "## πŸ“ Repository Structure Validation" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + MISSING=0 + PRESENT=0 + TOTAL=2 + + echo "### Required Directories" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Directory | Status | Files | Size | Notes |" >> $GITHUB_STEP_SUMMARY + echo "|-----------|--------|-------|------|-------|" >> $GITHUB_STEP_SUMMARY + + # Check required directories + for dir in docs .github; do + if [ -d "$dir" ]; then + FILE_COUNT=$(find "$dir" -type f 2>/dev/null | wc -l) + DIR_SIZE=$(du -sh "$dir" 2>/dev/null | cut -f1) + echo "| $dir/ | βœ… Pass | $FILE_COUNT files | $DIR_SIZE | Complete |" >> $GITHUB_STEP_SUMMARY + PRESENT=$((PRESENT + 1)) + else + echo "| $dir/ | ❌ **Missing** | - | - | **Action Required** |" >> $GITHUB_STEP_SUMMARY + MISSING=$((MISSING + 1)) + fi + done + + echo "" >> $GITHUB_STEP_SUMMARY + PERCENT=$((PRESENT * 100 / TOTAL)) + echo "**Compliance Score:** $PERCENT% ($PRESENT/$TOTAL directories present)" >> $GITHUB_STEP_SUMMARY + + if [ "$MISSING" -gt 0 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "### πŸ”΄ Critical Issues: $MISSING" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Remediation Steps:**" >> $GITHUB_STEP_SUMMARY + [ ! -d "docs" ] && echo "- Create docs directory: \`mkdir docs && echo '# Documentation' > docs/README.md\`" >> $GITHUB_STEP_SUMMARY + [ ! -d ".github" ] && echo "- Create .github directory: \`mkdir -p .github/workflows\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "πŸ“š Reference: [MokoStandards Repository Structure](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/docs/policy/core-structure.md)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ❌ Validation Failed: Required Directories Missing" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Status:** Repository structure does not meet MokoStandards requirements" >> $GITHUB_STEP_SUMMARY + echo "**Missing:** $MISSING required director(y|ies)" >> $GITHUB_STEP_SUMMARY + echo "**Compliance:** $PERCENT% ($PRESENT/$TOTAL directories present)" >> $GITHUB_STEP_SUMMARY + echo "" + echo "❌ ERROR: Required directories missing - See job summary for remediation steps" + exit 1 + fi + + - name: Check Required Files + run: | + set -x + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Required Files" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + MISSING=0 + PRESENT=0 + TOTAL=5 + + echo "| File | Status | Size | Last Modified | Notes |" >> $GITHUB_STEP_SUMMARY + echo "|------|--------|------|---------------|-------|" >> $GITHUB_STEP_SUMMARY + + # Check required files (CHANGELOG handled separately via find -iname to support src/ChangeLog.md) + for file in README.md LICENSE CONTRIBUTING.md SECURITY.md .editorconfig; do + if [ -f "$file" ]; then + FILE_SIZE=$(wc -c < "$file" 2>/dev/null | awk '{printf "%.1f KB", $1/1024}') + LAST_MOD=$(stat -c %y "$file" 2>/dev/null | cut -d' ' -f1 || echo "Unknown") + CONTENT_CHECK="" + + # Basic content validation + case "$file" in + "README.md") + LINES=$(wc -l < "$file") + [ "$LINES" -lt 10 ] && CONTENT_CHECK="⚠️ Too short" + ;; + "LICENSE") + [ $(wc -c < "$file") -lt 100 ] && CONTENT_CHECK="⚠️ Incomplete?" + ;; + esac + + echo "| $file | βœ… Pass | $FILE_SIZE | $LAST_MOD | Complete $CONTENT_CHECK |" >> $GITHUB_STEP_SUMMARY + PRESENT=$((PRESENT + 1)) + else + echo "| $file | ❌ **Missing** | - | - | **Required** |" >> $GITHUB_STEP_SUMMARY + MISSING=$((MISSING + 1)) + fi + done + + echo "" >> $GITHUB_STEP_SUMMARY + PERCENT=$((PRESENT * 100 / TOTAL)) + echo "**Compliance Score:** $PERCENT% ($PRESENT/$TOTAL files present)" >> $GITHUB_STEP_SUMMARY + + if [ "$MISSING" -gt 0 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "### πŸ”΄ Critical Issues: $MISSING" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Remediation Steps:**" >> $GITHUB_STEP_SUMMARY + [ ! -f "README.md" ] && echo "- Create README.md: Use [template](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/templates/docs/required/README.md)" >> $GITHUB_STEP_SUMMARY + [ ! -f "LICENSE" ] && echo "- Add LICENSE file: Choose from [OSI-approved licenses](https://opensource.org/licenses)" >> $GITHUB_STEP_SUMMARY + [ ! -f "CONTRIBUTING.md" ] && echo "- Create CONTRIBUTING.md: Use [template](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/templates/docs/required/CONTRIBUTING.md)" >> $GITHUB_STEP_SUMMARY + [ ! -f "SECURITY.md" ] && echo "- Create SECURITY.md: Use [template](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/templates/docs/required/SECURITY.md)" >> $GITHUB_STEP_SUMMARY + [ ! -f ".editorconfig" ] && echo "- Add .editorconfig: Use [template](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/templates/.editorconfig)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "πŸ“š Reference: [MokoStandards File Requirements](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/docs/policy/file-header-standards.md)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ❌ Validation Failed: Required Files Missing" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Status:** Repository files do not meet MokoStandards requirements" >> $GITHUB_STEP_SUMMARY + echo "**Missing:** $MISSING required file(s)" >> $GITHUB_STEP_SUMMARY + echo "**Compliance:** $PERCENT% ($PRESENT/$TOTAL files present)" >> $GITHUB_STEP_SUMMARY + echo "" + echo "❌ ERROR: Required files missing - See job summary for remediation steps" + exit 1 + fi + + documentation-quality: + name: Documentation Quality Check + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Validate README.md + run: | + set -x + echo "## πŸ“š Documentation Quality Check" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### README.md Analysis" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ ! -f "README.md" ]; then + echo "❌ **Critical:** README.md not found" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ❌ Validation Failed: README.md Missing" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Error:** README.md is required for all MokoStandards-compliant repositories" >> $GITHUB_STEP_SUMMARY + echo "**Action Required:** Create README.md with project description, setup instructions, and usage examples" >> $GITHUB_STEP_SUMMARY + echo "" + echo "❌ ERROR: README.md not found - This is a critical requirement" + exit 1 + fi + + # Detailed content analysis + SIZE=$(wc -c < README.md) + LINES=$(wc -l < README.md) + WORDS=$(wc -w < README.md) + HEADINGS=$(grep -c "^#" README.md || echo 0) + LINKS=$(grep -c "\[.*\](.*)" README.md || echo 0) + CODE_BLOCKS=$(grep -c '```' README.md || echo 0) + + echo "| Metric | Value | Status | Recommendation |" >> $GITHUB_STEP_SUMMARY + echo "|--------|-------|--------|----------------|" >> $GITHUB_STEP_SUMMARY + + # Size check + SIZE_STATUS="βœ… Good" + SIZE_REC="Adequate length" + if [ "$SIZE" -lt 500 ]; then + SIZE_STATUS="⚠️ Warning" + SIZE_REC="Add more content (min 500 bytes)" + elif [ "$SIZE" -gt 50000 ]; then + SIZE_STATUS="⚠️ Warning" + SIZE_REC="Consider splitting into multiple docs" + fi + echo "| Size | $SIZE bytes | $SIZE_STATUS | $SIZE_REC |" >> $GITHUB_STEP_SUMMARY + + # Line count + LINES_STATUS="βœ… Good" + LINES_REC="Good size" + if [ "$LINES" -lt 20 ]; then + LINES_STATUS="⚠️ Warning" + LINES_REC="Add more sections (min 20 lines)" + fi + echo "| Lines | $LINES | $LINES_STATUS | $LINES_REC |" >> $GITHUB_STEP_SUMMARY + + # Word count + WORDS_STATUS="βœ… Good" + WORDS_REC="Good detail" + if [ "$WORDS" -lt 100 ]; then + WORDS_STATUS="⚠️ Warning" + WORDS_REC="Add more description (min 100 words)" + fi + echo "| Words | $WORDS | $WORDS_STATUS | $WORDS_REC |" >> $GITHUB_STEP_SUMMARY + + # Headings + HEADINGS_STATUS="βœ… Good" + HEADINGS_REC="Well structured" + if [ "$HEADINGS" -lt 3 ]; then + HEADINGS_STATUS="⚠️ Warning" + HEADINGS_REC="Add more sections (min 3 headings)" + fi + echo "| Headings | $HEADINGS | $HEADINGS_STATUS | $HEADINGS_REC |" >> $GITHUB_STEP_SUMMARY + + # Links + LINKS_STATUS="βœ… Good" + LINKS_REC="Includes references" + if [ "$LINKS" -lt 1 ]; then + LINKS_STATUS="ℹ️ Info" + LINKS_REC="Consider adding useful links" + fi + echo "| Links | $LINKS | $LINKS_STATUS | $LINKS_REC |" >> $GITHUB_STEP_SUMMARY + + # Code blocks + CODE_STATUS="βœ… Good" + CODE_REC="Includes examples" + if [ "$CODE_BLOCKS" -eq 0 ]; then + CODE_STATUS="ℹ️ Info" + CODE_REC="Consider adding code examples" + fi + echo "| Code blocks | $CODE_BLOCKS | $CODE_STATUS | $CODE_REC |" >> $GITHUB_STEP_SUMMARY + + echo "" >> $GITHUB_STEP_SUMMARY + + # Check for key sections + echo "**Section Coverage:**" >> $GITHUB_STEP_SUMMARY + MISSING_COUNT=0 + grep -qi "install\|setup\|getting started" README.md && echo "- βœ… Installation/Setup instructions" >> $GITHUB_STEP_SUMMARY || { echo "- ⚠️ Missing: Installation/Setup" >> $GITHUB_STEP_SUMMARY; MISSING_COUNT=$((MISSING_COUNT + 1)); } + grep -qi "usage\|example\|how to" README.md && echo "- βœ… Usage examples" >> $GITHUB_STEP_SUMMARY || { echo "- ⚠️ Missing: Usage examples" >> $GITHUB_STEP_SUMMARY; MISSING_COUNT=$((MISSING_COUNT + 1)); } + grep -qi "license" README.md && echo "- βœ… License information" >> $GITHUB_STEP_SUMMARY || { echo "- ⚠️ Missing: License information" >> $GITHUB_STEP_SUMMARY; MISSING_COUNT=$((MISSING_COUNT + 1)); } + grep -qi "contribut" README.md && echo "- βœ… Contributing guidelines" >> $GITHUB_STEP_SUMMARY || echo "- ℹ️ Optional: Contributing section" >> $GITHUB_STEP_SUMMARY + + if [ "$MISSING_COUNT" -gt 0 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "**⚠️ $MISSING_COUNT important sections missing**" >> $GITHUB_STEP_SUMMARY + fi + + - name: Validate CHANGELOG.md + run: | + set -x + echo "" >> $GITHUB_STEP_SUMMARY + echo "### CHANGELOG.md Analysis" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Locate changelog case-insensitively; accepted at root, src/, or docs/ + CHANGELOG_PATH=$(find . -maxdepth 3 \( -path ./.git -o -path ./node_modules \) -prune \ + -o -iname "changelog.md" -print | head -1 | sed 's|^\./||') + + if [ -z "$CHANGELOG_PATH" ]; then + echo "❌ **Critical:** CHANGELOG.md not found (checked root, src/, docs/)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ❌ Validation Failed: CHANGELOG.md Missing" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Error:** CHANGELOG.md is required for all MokoStandards-compliant repositories" >> $GITHUB_STEP_SUMMARY + echo "**Action Required:** Create CHANGELOG.md following [Keep a Changelog](https://keepachangelog.com/) format" >> $GITHUB_STEP_SUMMARY + echo "" + echo "❌ ERROR: CHANGELOG.md not found - This is a critical requirement" + exit 1 + fi + + echo "πŸ“„ Found: $CHANGELOG_PATH" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Analyze changelog structure + VERSIONS=$(grep -c "## \[" "$CHANGELOG_PATH" || echo 0) + UNRELEASED=$(grep -c "## \[Unreleased\]" "$CHANGELOG_PATH" || echo 0) + DATES=$(grep -c "[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}" "$CHANGELOG_PATH" || echo 0) + SIZE=$(wc -c < "$CHANGELOG_PATH") + + echo "| Metric | Value | Status | Notes |" >> $GITHUB_STEP_SUMMARY + echo "|--------|-------|--------|-------|" >> $GITHUB_STEP_SUMMARY + + # Check format + if grep -qi "## \[.*\]" "$CHANGELOG_PATH"; then + echo "| Format | Keep a Changelog | βœ… Pass | Standard format |" >> $GITHUB_STEP_SUMMARY + else + echo "| Format | Custom | ⚠️ Warning | Consider [Keep a Changelog](https://keepachangelog.com/) |" >> $GITHUB_STEP_SUMMARY + fi + + # Version count + VERSIONS_STATUS="βœ… Good" + VERSIONS_NOTE="Well maintained" + if [ "$VERSIONS" -lt 1 ]; then + VERSIONS_STATUS="⚠️ Warning" + VERSIONS_NOTE="Add version entries" + fi + echo "| Versions | $VERSIONS | $VERSIONS_STATUS | $VERSIONS_NOTE |" >> $GITHUB_STEP_SUMMARY + + # Unreleased section + if [ "$UNRELEASED" -gt 0 ]; then + echo "| Unreleased | Yes | βœ… Good | Active development tracked |" >> $GITHUB_STEP_SUMMARY + else + echo "| Unreleased | No | ℹ️ Info | Consider adding [Unreleased] section |" >> $GITHUB_STEP_SUMMARY + fi + + # Dates + DATES_STATUS="βœ… Good" + if [ "$DATES" -lt 1 ]; then + DATES_STATUS="⚠️ Warning" + DATES_NOTE="Add release dates" + else + DATES_NOTE="Dates present" + fi + echo "| Release dates | $DATES | $DATES_STATUS | $DATES_NOTE |" >> $GITHUB_STEP_SUMMARY + + # Check for standard sections + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Changelog Sections:**" >> $GITHUB_STEP_SUMMARY + grep -qi "### Added" "$CHANGELOG_PATH" && echo "- βœ… Added section" >> $GITHUB_STEP_SUMMARY || echo "- ℹ️ Added section (optional)" >> $GITHUB_STEP_SUMMARY + grep -qi "### Changed" "$CHANGELOG_PATH" && echo "- βœ… Changed section" >> $GITHUB_STEP_SUMMARY || echo "- ℹ️ Changed section (optional)" >> $GITHUB_STEP_SUMMARY + grep -qi "### Fixed" "$CHANGELOG_PATH" && echo "- βœ… Fixed section" >> $GITHUB_STEP_SUMMARY || echo "- ℹ️ Fixed section (optional)" >> $GITHUB_STEP_SUMMARY + + echo "" >> $GITHUB_STEP_SUMMARY + echo "πŸ“š Reference: [Keep a Changelog](https://keepachangelog.com/)" >> $GITHUB_STEP_SUMMARY + + - name: Check Documentation Index + run: | + set -x + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Documentation Index" >> $GITHUB_STEP_SUMMARY + + if [ -f "docs/index.md" ] || [ -f "docs/README.md" ]; then + echo "βœ… Documentation index found" >> $GITHUB_STEP_SUMMARY + else + echo "⚠️ No documentation index (docs/index.md or docs/README.md)" >> $GITHUB_STEP_SUMMARY + fi + + coding-standards: + name: Coding Standards Check + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Check for Tab Characters + run: | + set -x + echo "### Tab Character Detection" >> $GITHUB_STEP_SUMMARY + + # Policy: Tabs are DEFAULT. Only check for tabs in files that REQUIRE spaces. + # Languages requiring spaces: YAML, Python, Haskell, F#, CoffeeScript, Nim, JSON, RST + TABS_IN_SPACES_FILES=$(find . -type f \ + \( -name "*.yml" -o -name "*.yaml" \ + -o -name "*.py" \ + -o -name "*.hs" -o -name "*.lhs" \ + -o -name "*.fs" -o -name "*.fsx" -o -name "*.fsi" \ + -o -name "*.coffee" -o -name "*.litcoffee" \ + -o -name "*.nim" -o -name "*.nims" -o -name "*.nimble" \ + -o -name "*.json" \ + -o -name "*.rst" \) \ + ! -path "./vendor/*" \ + ! -path "./node_modules/*" \ + ! -path "./.git/*" \ + -exec grep -l $'\t' {} \; 2>/dev/null | head -10) + + if [ -n "$TABS_IN_SPACES_FILES" ]; then + echo "⚠️ Tab characters found in files that require spaces:" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "$TABS_IN_SPACES_FILES" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "These languages require spaces (tabs will break): YAML, Python, Haskell, F#, CoffeeScript, Nim, JSON, RST" >> $GITHUB_STEP_SUMMARY + echo "All other files (including .md, .ps1, LICENSE, etc.) may use tabs per MokoStandards policy" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… No tabs found in files requiring spaces" >> $GITHUB_STEP_SUMMARY + echo "Note: Tabs are allowed in most files (policy default). Only checked files requiring spaces." >> $GITHUB_STEP_SUMMARY + fi + + - name: Check File Encoding + run: | + set -x + echo "" >> $GITHUB_STEP_SUMMARY + echo "### File Encoding Check" >> $GITHUB_STEP_SUMMARY + + # Check for UTF-8 encoding (ASCII is a subset of UTF-8 and is acceptable) + NON_UTF8=$(find . -type f \( -name "*.php" -o -name "*.js" -o -name "*.md" \) \ + ! -path "./vendor/*" \ + ! -path "./node_modules/*" \ + ! -path "./.git/*" \ + -exec file {} \; | grep -v "UTF-8" | grep -v "ASCII" | head -5) + + if [ -n "$NON_UTF8" ]; then + echo "⚠️ Non-UTF-8 files detected:" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "$NON_UTF8" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… All source files appear to be UTF-8 encoded" >> $GITHUB_STEP_SUMMARY + fi + + - name: Check Line Endings + run: | + set -x + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Line Ending Check" >> $GITHUB_STEP_SUMMARY + + # Check for CRLF line endings + CRLF_FILES=$(find . -type f \( -name "*.php" -o -name "*.js" -o -name "*.md" \) \ + ! -path "./vendor/*" \ + ! -path "./node_modules/*" \ + ! -path "./.git/*" \ + -exec file {} \; | grep "CRLF" | head -5) + + if [ -n "$CRLF_FILES" ]; then + echo "⚠️ Files with CRLF line endings found:" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "$CRLF_FILES" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "MokoStandards requires LF line endings" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… Line endings are consistent (LF)" >> $GITHUB_STEP_SUMMARY + fi + + line-length-validation: + name: Line Length Check + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Check Line Lengths + run: | + set -x + echo "## πŸ“ Line Length Validation" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Line length standards: + # - General source code: 120 characters (hard limit) + # - YAML workflows: 180 characters (exception for GitHub Actions) + # - Markdown files: No limit (content-focused) + + echo "### Line Length Standards" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| File Type | Soft Limit | Hard Limit |" >> $GITHUB_STEP_SUMMARY + echo "|-----------|------------|------------|" >> $GITHUB_STEP_SUMMARY + echo "| General source code | 80 chars | 120 chars |" >> $GITHUB_STEP_SUMMARY + echo "| YAML workflows | 80 chars | 180 chars |" >> $GITHUB_STEP_SUMMARY + echo "| Markdown files | N/A | No limit |" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Check YAML files (using yamllint which is already configured) + echo "### YAML Files (180 char limit)" >> $GITHUB_STEP_SUMMARY + + YAML_VIOLATIONS=0 + if command -v yamllint >/dev/null 2>&1; then + # Install yamllint if not present + : + else + pip install yamllint >/dev/null 2>&1 + fi + + # Run yamllint and count line-length warnings + YAML_OUTPUT=$(yamllint .github/workflows/*.yml 2>&1 | grep "line too long" || true) + if [ -n "$YAML_OUTPUT" ]; then + YAML_VIOLATIONS=$(echo "$YAML_OUTPUT" | wc -l) + echo "⚠️ Found $YAML_VIOLATIONS lines exceeding 180 characters in YAML files" >> $GITHUB_STEP_SUMMARY + echo "
View warnings (informational only)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "$YAML_OUTPUT" | head -20 >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… All YAML files comply with 180 character limit" >> $GITHUB_STEP_SUMMARY + fi + echo "" >> $GITHUB_STEP_SUMMARY + + # Check source code files (PHP, Python, JavaScript, etc.) for 120 char limit + echo "### Source Code Files (120 char limit)" >> $GITHUB_STEP_SUMMARY + + LONG_LINES=$(find . -type f \ + \( -name "*.php" -o -name "*.py" -o -name "*.js" -o -name "*.ts" \ + -o -name "*.go" -o -name "*.rs" -o -name "*.java" -o -name "*.c" \ + -o -name "*.cpp" -o -name "*.h" -o -name "*.sh" \) \ + ! -path "./vendor/*" \ + ! -path "./node_modules/*" \ + ! -path "./.git/*" \ + ! -path "./build/*" \ + ! -path "./dist/*" \ + -exec awk 'length > 120 { print FILENAME ":" NR ": " length " chars" }' {} \; 2>/dev/null | head -20) + + if [ -n "$LONG_LINES" ]; then + LINE_COUNT=$(echo "$LONG_LINES" | wc -l) + echo "⚠️ Found $LINE_COUNT source code lines exceeding 120 characters" >> $GITHUB_STEP_SUMMARY + echo "
View violations (informational)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "$LONG_LINES" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… All source code files comply with 120 character limit" >> $GITHUB_STEP_SUMMARY + fi + echo "" >> $GITHUB_STEP_SUMMARY + + # Confirm Markdown files are not checked + echo "### Markdown Files" >> $GITHUB_STEP_SUMMARY + echo "βœ… Markdown files have no line length limit per coding standards" >> $GITHUB_STEP_SUMMARY + echo "Rationale: Content-focused format, URLs, tables, and natural prose flow" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Summary + echo "### Summary" >> $GITHUB_STEP_SUMMARY + echo "This check is **informational only** and does not block merges." >> $GITHUB_STEP_SUMMARY + echo "Line length standards help maintain code readability." >> $GITHUB_STEP_SUMMARY + echo "Exceptions documented in: \`docs/policy/coding-style-guide.md\`" >> $GITHUB_STEP_SUMMARY + + license-compliance: + name: License Header Validation + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Check SPDX Headers + run: | + set -x + echo "### SPDX License Header Check" >> $GITHUB_STEP_SUMMARY + + # Count source files with and without SPDX headers + TOTAL_PHP=0 + WITH_SPDX_PHP=0 + + if find . -name "*.php" -type f ! -path "./vendor/*" | head -1 | grep -q .; then + TOTAL_PHP=$(find . -name "*.php" -type f ! -path "./vendor/*" | wc -l) + WITH_SPDX_PHP=$(find . -name "*.php" -type f ! -path "./vendor/*" -exec grep -l "SPDX-License-Identifier" {} \; | wc -l) + fi + + if [ "$TOTAL_PHP" -gt 0 ]; then + PERCENT=$((WITH_SPDX_PHP * 100 / TOTAL_PHP)) + echo "- PHP files: $WITH_SPDX_PHP/$TOTAL_PHP ($PERCENT%) with SPDX headers" >> $GITHUB_STEP_SUMMARY + + if [ "$PERCENT" -lt 80 ]; then + echo "⚠️ Less than 80% of PHP files have SPDX headers" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… Good SPDX header coverage" >> $GITHUB_STEP_SUMMARY + fi + fi + + - name: Validate License File + run: | + set -x + echo "" >> $GITHUB_STEP_SUMMARY + echo "### License File Validation" >> $GITHUB_STEP_SUMMARY + + if [ ! -f "LICENSE" ]; then + echo "❌ LICENSE file not found" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ❌ Validation Failed: LICENSE File Missing" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Error:** LICENSE file is required for all MokoStandards-compliant repositories" >> $GITHUB_STEP_SUMMARY + echo "**Action Required:** Add LICENSE file with appropriate open-source license (GPL-3.0-or-later recommended)" >> $GITHUB_STEP_SUMMARY + echo "" + echo "❌ ERROR: LICENSE file not found - This is a critical requirement" + exit 1 + fi + + # Check license type + if grep -qi "GNU GENERAL PUBLIC LICENSE" LICENSE; then + VERSION=$(grep -i "Version 3" LICENSE || echo "") + if [ -n "$VERSION" ]; then + echo "βœ… GPL-3.0-or-later license detected" >> $GITHUB_STEP_SUMMARY + else + echo "⚠️ GPL license detected but version unclear" >> $GITHUB_STEP_SUMMARY + fi + elif grep -qi "MIT License" LICENSE; then + echo "βœ… MIT license detected" >> $GITHUB_STEP_SUMMARY + elif grep -qi "Apache License" LICENSE; then + echo "βœ… Apache license detected" >> $GITHUB_STEP_SUMMARY + else + echo "ℹ️ License type could not be automatically detected" >> $GITHUB_STEP_SUMMARY + fi + + git-hygiene: + name: Git Repository Hygiene + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + with: + fetch-depth: 0 + + - name: Check .gitignore + run: | + set -x + echo "### .gitignore Validation" >> $GITHUB_STEP_SUMMARY + + if [ ! -f ".gitignore" ]; then + echo "⚠️ .gitignore file not found" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ⚠️ Warning: .gitignore Not Found" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Status:** .gitignore file is recommended but not required" >> $GITHUB_STEP_SUMMARY + echo "**Recommendation:** Add .gitignore to exclude build artifacts, dependencies, and temporary files" >> $GITHUB_STEP_SUMMARY + echo "" + echo "⚠️ WARNING: .gitignore file not found - Continuing validation" + exit 0 + fi + + # Check for common exclusions + MISSING="" + grep -q "vendor/" .gitignore || MISSING="${MISSING}vendor/ " + grep -q "node_modules/" .gitignore || MISSING="${MISSING}node_modules/ " + + if [ -n "$MISSING" ]; then + echo "⚠️ .gitignore may be missing common exclusions: $MISSING" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… .gitignore appears complete" >> $GITHUB_STEP_SUMMARY + fi + + - name: Check for Large Files + run: | + set -x + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Large File Detection" >> $GITHUB_STEP_SUMMARY + + # Find files larger than 1MB + LARGE_FILES=$(find . -type f -size +1M ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" | head -5) + + if [ -n "$LARGE_FILES" ]; then + echo "⚠️ Large files detected (>1MB):" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "$LARGE_FILES" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "Consider using Git LFS for large binary files" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… No unusually large files detected" >> $GITHUB_STEP_SUMMARY + fi + + workflow-validation: + name: Workflow Configuration Check + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Check Required Workflows + run: | + set -x + echo "### GitHub Actions Workflows" >> $GITHUB_STEP_SUMMARY + + WORKFLOWS_DIR=".github/workflows" + + if [ ! -d "$WORKFLOWS_DIR" ]; then + echo "❌ No workflows directory found" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ❌ Validation Failed: Workflows Directory Missing" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Error:** .github/workflows directory is required for CI/CD automation" >> $GITHUB_STEP_SUMMARY + echo "**Action Required:** Create .github/workflows directory and add GitHub Actions workflows" >> $GITHUB_STEP_SUMMARY + echo "" + echo "❌ ERROR: .github/workflows directory not found" + exit 1 + fi + + # Check for recommended workflows + if [ -f "$WORKFLOWS_DIR/ci.yml" ] || [ -f "$WORKFLOWS_DIR/build.yml" ]; then + echo "βœ… CI workflow present" >> $GITHUB_STEP_SUMMARY + else + echo "⚠️ No CI workflow found (ci.yml or build.yml)" >> $GITHUB_STEP_SUMMARY + fi + + if [ -f "$WORKFLOWS_DIR/codeql-analysis.yml" ]; then + echo "βœ… CodeQL security scanning present" >> $GITHUB_STEP_SUMMARY + else + echo "⚠️ CodeQL workflow not found" >> $GITHUB_STEP_SUMMARY + fi + + - name: Validate Workflow Syntax + run: | + set -x + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Workflow YAML Syntax" >> $GITHUB_STEP_SUMMARY + + INVALID=0 + for workflow in $(find .github/workflows -maxdepth 1 -type f \( -name "*.yml" -o -name "*.yaml" \) 2>/dev/null); do + if [ -f "$workflow" ]; then + if python3 -c "import yaml, sys; yaml.safe_load(open(sys.argv[1]))" "$workflow" 2>/dev/null; then + echo "βœ… $(basename $workflow)" >> $GITHUB_STEP_SUMMARY + else + echo "❌ $(basename $workflow) - invalid YAML" >> $GITHUB_STEP_SUMMARY + INVALID=$((INVALID + 1)) + fi + fi + done + + if [ "$INVALID" -gt 0 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ❌ Validation Failed: Invalid Workflow YAML Syntax" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Error:** $INVALID workflow file(s) have invalid YAML syntax" >> $GITHUB_STEP_SUMMARY + echo "**Action Required:** Fix YAML syntax errors in the marked workflow files" >> $GITHUB_STEP_SUMMARY + echo "**Tool:** Run \`python3 -c \"import yaml; yaml.safe_load(open('.github/workflows/FILE.yml'))\"\` locally" >> $GITHUB_STEP_SUMMARY + echo "" + echo "❌ ERROR: $INVALID workflow file(s) with invalid YAML syntax" + exit 1 + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "### βœ… All Workflow Files Have Valid YAML Syntax" >> $GITHUB_STEP_SUMMARY + echo "" + echo "βœ… SUCCESS: All workflow files passed YAML validation" + + - name: Validate CodeQL Configuration + if: hashFiles('.github/workflows/codeql-analysis.yml') != '' + run: | + set -e + echo "" >> $GITHUB_STEP_SUMMARY + echo "### CodeQL Language Configuration" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Inline validation (rewritten from Python to bash for PHP-only architecture) + CODEQL_FILE=".github/workflows/codeql-analysis.yml" + + if [ ! -f "$CODEQL_FILE" ]; then + echo "⚠️ CodeQL workflow file not found" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ⚠️ CodeQL Workflow Not Found" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Status:** CodeQL workflow file not present - skipping language validation" >> $GITHUB_STEP_SUMMARY + echo "" + echo "⚠️ INFO: CodeQL workflow not found - Skipping validation" + exit 0 + fi + + echo "**CodeQL Configuration Analysis**" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Extract configured languages from workflow + LANGUAGES=$(grep -A5 "language:" "$CODEQL_FILE" | grep -oP "(?<=')[^']+(?=')" | tr '\n' ' ' || echo "") + + # Check if this is a configuration-only scan (no languages specified) + if grep -q "category.*language:config" "$CODEQL_FILE"; then + echo "**Scan Type:** Configuration-only (no language matrix)" >> $GITHUB_STEP_SUMMARY + echo "**Status:** βœ… Valid configuration for PHP-only repository" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "This CodeQL workflow scans YAML, JSON, shell scripts for security issues." >> $GITHUB_STEP_SUMMARY + echo "PHP security is handled by SecurityValidator enterprise library." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "βœ… SUCCESS: CodeQL configuration-only scan properly configured" + exit 0 + fi + + if [ -z "$LANGUAGES" ]; then + echo "❌ No languages configured in CodeQL workflow" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ❌ Validation Failed: CodeQL Languages Not Configured" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Error:** CodeQL workflow exists but has no languages configured" >> $GITHUB_STEP_SUMMARY + echo "**Action Required:** Configure appropriate languages in codeql-analysis.yml" >> $GITHUB_STEP_SUMMARY + echo "" + echo "❌ ERROR: No languages configured in CodeQL workflow" + exit 1 + fi + + echo "**Configured Languages:** $LANGUAGES" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Validate language presence in repository + INVALID_LANGS="" + VALID_LANGS="" + + for LANG in $LANGUAGES; do + case "$LANG" in + python) + # Check for Python files (should be none in v04.00.04) + if find . -name "*.py" -type f ! -path "./.git/*" | grep -q .; then + VALID_LANGS="$VALID_LANGS python" + echo "βœ… Python: Found Python files" >> $GITHUB_STEP_SUMMARY + else + INVALID_LANGS="$INVALID_LANGS python" + echo "❌ Python: No Python files found (PHP-only repository)" >> $GITHUB_STEP_SUMMARY + fi + ;; + javascript|typescript) + # Check for JS/TS files + if find . \( -name "*.js" -o -name "*.ts" -o -name "*.json" \) -type f ! -path "./.git/*" ! -path "./node_modules/*" | grep -q .; then + VALID_LANGS="$VALID_LANGS $LANG" + echo "βœ… $LANG: Found JavaScript/TypeScript/JSON files" >> $GITHUB_STEP_SUMMARY + else + INVALID_LANGS="$INVALID_LANGS $LANG" + echo "⚠️ $LANG: No JavaScript/TypeScript files found" >> $GITHUB_STEP_SUMMARY + fi + ;; + java) + if find . -name "*.java" -type f ! -path "./.git/*" | grep -q .; then + VALID_LANGS="$VALID_LANGS java" + echo "βœ… Java: Found Java files" >> $GITHUB_STEP_SUMMARY + else + INVALID_LANGS="$INVALID_LANGS java" + echo "⚠️ Java: No Java files found" >> $GITHUB_STEP_SUMMARY + fi + ;; + go) + if find . -name "*.go" -type f ! -path "./.git/*" | grep -q .; then + VALID_LANGS="$VALID_LANGS go" + echo "βœ… Go: Found Go files" >> $GITHUB_STEP_SUMMARY + else + INVALID_LANGS="$INVALID_LANGS go" + echo "⚠️ Go: No Go files found" >> $GITHUB_STEP_SUMMARY + fi + ;; + cpp|c) + if find . \( -name "*.cpp" -o -name "*.c" -o -name "*.h" \) -type f ! -path "./.git/*" | grep -q .; then + VALID_LANGS="$VALID_LANGS $LANG" + echo "βœ… $LANG: Found C/C++ files" >> $GITHUB_STEP_SUMMARY + else + INVALID_LANGS="$INVALID_LANGS $LANG" + echo "⚠️ $LANG: No C/C++ files found" >> $GITHUB_STEP_SUMMARY + fi + ;; + ruby) + if find . -name "*.rb" -type f ! -path "./.git/*" | grep -q .; then + VALID_LANGS="$VALID_LANGS ruby" + echo "βœ… Ruby: Found Ruby files" >> $GITHUB_STEP_SUMMARY + else + INVALID_LANGS="$INVALID_LANGS ruby" + echo "⚠️ Ruby: No Ruby files found" >> $GITHUB_STEP_SUMMARY + fi + ;; + *) + echo "⚠️ $LANG: Unknown language, skipping validation" >> $GITHUB_STEP_SUMMARY + ;; + esac + done + + echo "" >> $GITHUB_STEP_SUMMARY + + # Report results + if [ -n "$INVALID_LANGS" ]; then + echo "**⚠️ Warning:** Some configured languages may not have corresponding files:" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "Invalid languages: $INVALID_LANGS" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Note:** This is informational. CodeQL will skip languages without source files." >> $GITHUB_STEP_SUMMARY + echo "For PHP repository (v04.00.04), JavaScript language covers JSON/YAML/shell scripts." >> $GITHUB_STEP_SUMMARY + else + echo "βœ… **All configured CodeQL languages have corresponding source files**" >> $GITHUB_STEP_SUMMARY + fi + + # Always succeed - this is informational only + echo "" >> $GITHUB_STEP_SUMMARY + echo "### βœ… CodeQL Configuration Validation Complete" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Status:** CodeQL language configuration reviewed successfully" >> $GITHUB_STEP_SUMMARY + echo "" + echo "βœ… SUCCESS: CodeQL validation complete" + exit 0 + + version-consistency: + name: Version Consistency Check + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Set up PHP + uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0 + with: + php-version: '8.1' + extensions: json + tools: composer + coverage: none + + - name: Install API Package + run: composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader + env: + COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN }}"}}' + + - name: Run Version Consistency Check + id: version_check + run: | + set -x + echo "## πŸ”’ Version Consistency Validation" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ -f "api/validate/check_version_consistency.php" ]; then + php api/validate/check_version_consistency.php --verbose | tee /tmp/version-check.log + EXIT_CODE=$? + + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + cat /tmp/version-check.log >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + + if [ "$EXIT_CODE" -eq 0 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "βœ… All version numbers are consistent!" >> $GITHUB_STEP_SUMMARY + exit 0 + else + echo "" >> $GITHUB_STEP_SUMMARY + echo "❌ Version mismatches detected - Please update all version references" >> $GITHUB_STEP_SUMMARY + exit 1 + fi + else + echo "ℹ️ Version consistency check script not found - skipping" >> $GITHUB_STEP_SUMMARY + exit 0 + fi + + script-integrity: + name: Script Integrity Validation + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Set up Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.x' + + - name: Validate Script Integrity + id: script_check + run: | + set -x + echo "## πŸ” Script Integrity Validation" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ -f "api/.script-registry.json" ]; then + echo "### Critical Scripts" >> $GITHUB_STEP_SUMMARY + php api/maintenance/update_sha_hashes.php \ + --dry-run --verbose | tee /tmp/script-validation.log + + EXIT_CODE=$? + + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + cat /tmp/script-validation.log >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + + if [ "$EXIT_CODE" -eq 0 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "βœ… All critical scripts validated successfully!" >> $GITHUB_STEP_SUMMARY + exit 0 + else + echo "" >> $GITHUB_STEP_SUMMARY + echo "❌ Script integrity violations detected" >> $GITHUB_STEP_SUMMARY + echo "**Action Required:** Review validation report and update registry" >> $GITHUB_STEP_SUMMARY + exit 1 + fi + else + echo "ℹ️ Script registry not found - skipping integrity check" >> $GITHUB_STEP_SUMMARY + exit 0 + fi + + enterprise-readiness: + name: Enterprise Readiness Check + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Set up PHP + uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0 + with: + php-version: '8.1' + extensions: json, mbstring + tools: composer + coverage: none + + - name: Install API Package + run: composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader + env: + COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN }}"}}' + + - name: Check Enterprise Readiness + id: enterprise_check + run: | + echo "" >> $GITHUB_STEP_SUMMARY + + if [ -f "api/validate/check_enterprise_readiness.php" ]; then + php api/validate/check_enterprise_readiness.php --verbose | tee /tmp/enterprise-check.log + EXIT_CODE=$? + + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + cat /tmp/enterprise-check.log >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + + if [ "$EXIT_CODE" -eq 0 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "βœ… Repository meets enterprise readiness criteria!" >> $GITHUB_STEP_SUMMARY + exit 0 + else + echo "" >> $GITHUB_STEP_SUMMARY + echo "⚠️ Enterprise readiness issues detected" >> $GITHUB_STEP_SUMMARY + echo "**Note:** This is informational - review recommendations to improve" >> $GITHUB_STEP_SUMMARY + exit 0 # Non-blocking + fi + else + echo "ℹ️ Enterprise readiness check script not found - skipping" >> $GITHUB_STEP_SUMMARY + exit 0 + fi + + repository-health: + name: Repository Health Check + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Set up PHP + uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0 + with: + php-version: '8.1' + extensions: json, mbstring + tools: composer + coverage: none + + - name: Install API Package + run: composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader + env: + COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN }}"}}' + + - name: Check Repository Health + id: health_check + run: | + echo "" >> $GITHUB_STEP_SUMMARY + + if [ -f "api/validate/check_repo_health.php" ]; then + php api/validate/check_repo_health.php --verbose | tee /tmp/health-check.log + EXIT_CODE=$? + + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + cat /tmp/health-check.log >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + + if [ "$EXIT_CODE" -eq 0 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "βœ… Repository health check passed!" >> $GITHUB_STEP_SUMMARY + exit 0 + else + echo "" >> $GITHUB_STEP_SUMMARY + echo "⚠️ Repository health issues detected" >> $GITHUB_STEP_SUMMARY + echo "**Note:** This is informational - review recommendations to improve" >> $GITHUB_STEP_SUMMARY + exit 0 # Non-blocking + fi + else + echo "ℹ️ Repository health check script not found - skipping" >> $GITHUB_STEP_SUMMARY + exit 0 + fi + + todo-fixme-tracking: + name: TODO/FIXME Tracking + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Track Technical Debt + run: | + set -x + echo "## πŸ“ TODO/FIXME Tracking" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Tracking technical debt markers in source code." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Search for technical debt markers + PATTERNS="TODO|FIXME|HACK|XXX" + EXTENSIONS="*.php *.py *.js *.ts *.go *.rs *.java *.c *.cpp *.h *.hpp *.sh" + + echo "### Technical Debt Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + TOTAL_COUNT=0 + for ext in $EXTENSIONS; do + COUNT=$(find . -type f -name "$ext" ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" -exec grep -n -E "($PATTERNS)" {} + 2>/dev/null | wc -l || echo 0) + TOTAL_COUNT=$((TOTAL_COUNT + COUNT)) + done + + if [ "$TOTAL_COUNT" -gt 0 ]; then + echo "⚠️ Found **$TOTAL_COUNT** technical debt item(s)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "View technical debt items" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + for ext in $EXTENSIONS; do + find . -type f -name "$ext" ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" -exec grep -n -H -E "($PATTERNS)" {} + 2>/dev/null | head -100 || true + done >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… No technical debt markers found" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Note**: This is an informational check. Technical debt items don't block compliance." >> $GITHUB_STEP_SUMMARY + + file-size-limits: + name: File Size Limits + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Check File Sizes + run: | + set -x + echo "## πŸ“¦ File Size Validation" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Find large files (>1MB) + LARGE_FILES=$(find . -type f -size +1M ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" 2>/dev/null | wc -l) + HUGE_FILES=$(find . -type f -size +10M ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" 2>/dev/null | wc -l) + + echo "### Size Thresholds" >> $GITHUB_STEP_SUMMARY + echo "- **Warning**: Files >1MB" >> $GITHUB_STEP_SUMMARY + echo "- **Critical**: Files >10MB" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "$HUGE_FILES" -gt 0 ]; then + echo "❌ **Critical**: Found $HUGE_FILES file(s) exceeding 10MB" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "View files >10MB" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + find . -type f -size +10M ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" -exec ls -lh {} + 2>/dev/null | awk '{print $5, $9}' >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Action Required**: Remove or optimize files >10MB" >> $GITHUB_STEP_SUMMARY + exit 1 + elif [ "$LARGE_FILES" -gt 0 ]; then + echo "⚠️ **Warning**: Found $LARGE_FILES file(s) between 1MB and 10MB" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "View files >1MB" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + find . -type f -size +1M ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" -exec ls -lh {} + 2>/dev/null | awk '{print $5, $9}' >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Recommendation**: Consider optimizing or documenting large files" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… All files within acceptable size limits (<1MB)" >> $GITHUB_STEP_SUMMARY + fi + + secret-scanning: + name: Secret Scanning + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Scan for Secrets + run: | + set -x + echo "## πŸ”’ Secret Scanning" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Scanning for hardcoded secrets and credentials." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Define secret patterns + VIOLATIONS=0 + + # Check for common secret patterns + echo "### Secret Patterns" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Pattern 1: password/secret assignments + # Exclusions: + # test|example|sample - test/example files + # getenv - environment-variable reads + # /\.\*/|^\s*// - regex patterns and commented lines + # CREDENTIAL_PATTERNS|SecurityValidator|SECRET_PATTERN - scanner internals + # ===|!== - strict comparison operators (not assignments) + # ApiClient - constructor calls where token is a variable arg + if grep -r -n -E "(password|passwd|pwd|secret|api[_-]?key|token).*=.*['\"]" . \ + --include="*.php" --include="*.py" --include="*.js" --include="*.ts" \ + --exclude-dir=".git" --exclude-dir="vendor" --exclude-dir="node_modules" 2>/dev/null | \ + grep -v -E '(test|example|sample|getenv|/\.\*/|^\s*//|CREDENTIAL_PATTERNS|SecurityValidator|SECRET_PATTERN|===|!==|ApiClient)' | \ + grep -v "= ''" | grep -v '= ""' > /tmp/secrets1.txt 2>/dev/null; then + COUNT=$(wc -l < /tmp/secrets1.txt) + if [ "$COUNT" -gt 0 ]; then + echo "⚠️ Found $COUNT potential secret assignment(s)" >> $GITHUB_STEP_SUMMARY + VIOLATIONS=$((VIOLATIONS + COUNT)) + fi + fi + + # Pattern 2: Private keys + if grep -r -n "BEGIN.*PRIVATE KEY" . \ + --include="*.pem" --include="*.key" --include="*.txt" \ + --exclude-dir=".git" --exclude-dir="vendor" --exclude-dir="node_modules" 2>/dev/null > /tmp/secrets2.txt; then + COUNT=$(wc -l < /tmp/secrets2.txt) + if [ "$COUNT" -gt 0 ]; then + echo "❌ Found $COUNT private key file(s)" >> $GITHUB_STEP_SUMMARY + VIOLATIONS=$((VIOLATIONS + COUNT)) + fi + fi + + # Pattern 3: AWS keys + if grep -r -n -E "AKIA[0-9A-Z]{16}" . \ + --include="*.php" --include="*.py" --include="*.js" --include="*.txt" --include="*.env" \ + --exclude-dir=".git" --exclude-dir="vendor" --exclude-dir="node_modules" 2>/dev/null > /tmp/secrets3.txt; then + COUNT=$(wc -l < /tmp/secrets3.txt) + if [ "$COUNT" -gt 0 ]; then + echo "❌ Found $COUNT potential AWS access key(s)" >> $GITHUB_STEP_SUMMARY + VIOLATIONS=$((VIOLATIONS + COUNT)) + fi + fi + + # Pattern 4: GitHub tokens + if grep -r -n -E "gh[ps]_[a-zA-Z0-9]{36}" . \ + --include="*.php" --include="*.py" --include="*.js" --include="*.txt" --include="*.env" \ + --exclude-dir=".git" --exclude-dir="vendor" --exclude-dir="node_modules" 2>/dev/null > /tmp/secrets4.txt; then + COUNT=$(wc -l < /tmp/secrets4.txt) + if [ "$COUNT" -gt 0 ]; then + echo "❌ Found $COUNT potential GitHub token(s)" >> $GITHUB_STEP_SUMMARY + VIOLATIONS=$((VIOLATIONS + COUNT)) + fi + fi + + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "$VIOLATIONS" -gt 0 ]; then + echo "**Total Violations**: $VIOLATIONS" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "View detected secrets (file paths only)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + cat /tmp/secrets*.txt 2>/dev/null | cut -d: -f1 | sort -u >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Action Required**: Remove hardcoded secrets immediately!" >> $GITHUB_STEP_SUMMARY + echo "Use environment variables or secrets management instead." >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo "βœ… No hardcoded secrets detected" >> $GITHUB_STEP_SUMMARY + fi + + broken-link-detection: + name: Broken Link Detection + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Check Internal Links + run: | + set -x + echo "## πŸ”— Broken Link Detection" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Checking internal links in markdown files." >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + BROKEN_LINKS=0 + CHECKED_LINKS=0 + + # Find all markdown files + MD_FILES=$(find . -name "*.md" ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*") + + for file in $MD_FILES; do + # Extract markdown links [text](path) + while IFS= read -r line; do + # Extract path from [text](path) + link=$(echo "$line" | sed -n 's/.*\](\([^)]*\)).*/\1/p') + + # Skip external links (http/https) + if echo "$link" | grep -qE "^https?://"; then + continue + fi + + # Skip anchors only + if echo "$link" | grep -qE "^#"; then + continue + fi + + CHECKED_LINKS=$((CHECKED_LINKS + 1)) + + # Get directory of the markdown file + basedir=$(dirname "$file") + + # Resolve relative path + if [ -n "$link" ]; then + # Remove anchor if present + clean_link=$(echo "$link" | sed 's/#.*//') + + # Check if file exists + if [ ! -e "$basedir/$clean_link" ] && [ ! -e "$clean_link" ]; then + echo "Broken link in $file: $link" >> /tmp/broken_links.txt + BROKEN_LINKS=$((BROKEN_LINKS + 1)) + fi + fi + done < <(grep -o '\[.*\](.*)' "$file" 2>/dev/null || true) + done + + echo "### Link Validation Results" >> $GITHUB_STEP_SUMMARY + echo "- **Links Checked**: $CHECKED_LINKS" >> $GITHUB_STEP_SUMMARY + echo "- **Broken Links**: $BROKEN_LINKS" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "$BROKEN_LINKS" -gt 0 ]; then + echo "⚠️ Found $BROKEN_LINKS broken internal link(s)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "View broken links" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + cat /tmp/broken_links.txt 2>/dev/null >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Recommendation**: Fix or remove broken links to maintain documentation quality" >> $GITHUB_STEP_SUMMARY + else + if [ "$CHECKED_LINKS" -gt 0 ]; then + echo "βœ… All internal links are valid" >> $GITHUB_STEP_SUMMARY + else + echo "ℹ️ No internal links found to check" >> $GITHUB_STEP_SUMMARY + fi + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Note**: This check validates internal file references only. External URLs are not validated." >> $GITHUB_STEP_SUMMARY + + # ============================================================================ + # PHASE 2: Medium Priority Checks + # ============================================================================ + + dependency-vulnerabilities: + name: Dependency Vulnerability Scanning + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Setup PHP + uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0 + with: + php-version: '8.1' + + - name: Setup Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.x' + + - name: Scan Dependencies + run: | + set -x + echo "## πŸ›‘οΈ Dependency Vulnerability Scanning" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + VULNERABILITIES=0 + + # PHP Dependencies + if [ -f "composer.json" ]; then + echo "### PHP Dependencies (composer)" >> $GITHUB_STEP_SUMMARY + if composer audit --no-dev 2>&1 | tee /tmp/php_audit.txt; then + echo "βœ… No PHP vulnerabilities detected" >> $GITHUB_STEP_SUMMARY + else + VULN_COUNT=$(grep -c "vulnerability" /tmp/php_audit.txt || echo 0) + echo "⚠️ Found $VULN_COUNT PHP vulnerability/vulnerabilities" >> $GITHUB_STEP_SUMMARY + VULNERABILITIES=$((VULNERABILITIES + VULN_COUNT)) + fi + echo "" >> $GITHUB_STEP_SUMMARY + fi + + # Python Dependencies + if [ -f "requirements.txt" ]; then + echo "### Python Dependencies" >> $GITHUB_STEP_SUMMARY + pip install pip-audit 2>&1 > /dev/null + if pip-audit -r requirements.txt 2>&1 | tee /tmp/py_audit.txt; then + echo "βœ… No Python vulnerabilities detected" >> $GITHUB_STEP_SUMMARY + else + VULN_COUNT=$(grep -c "vulnerability" /tmp/py_audit.txt || echo 0) + echo "⚠️ Found $VULN_COUNT Python vulnerability/vulnerabilities" >> $GITHUB_STEP_SUMMARY + VULNERABILITIES=$((VULNERABILITIES + VULN_COUNT)) + fi + echo "" >> $GITHUB_STEP_SUMMARY + fi + + # NPM Dependencies + if [ -f "package.json" ]; then + echo "### NPM Dependencies" >> $GITHUB_STEP_SUMMARY + if npm audit --production 2>&1 | tee /tmp/npm_audit.txt; then + echo "βœ… No NPM vulnerabilities detected" >> $GITHUB_STEP_SUMMARY + else + VULN_COUNT=$(grep -c "vulnerability" /tmp/npm_audit.txt || echo 0) + echo "⚠️ Found $VULN_COUNT NPM vulnerability/vulnerabilities" >> $GITHUB_STEP_SUMMARY + VULNERABILITIES=$((VULNERABILITIES + VULN_COUNT)) + fi + echo "" >> $GITHUB_STEP_SUMMARY + fi + + if [ "$VULNERABILITIES" -gt 0 ]; then + echo "**Total Vulnerabilities**: $VULNERABILITIES" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Action Required**: Update vulnerable dependencies" >> $GITHUB_STEP_SUMMARY + exit 1 + else + echo "βœ… No dependency vulnerabilities detected" >> $GITHUB_STEP_SUMMARY + fi + + code-duplication: + name: Code Duplication Detection + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Setup PHP + uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0 + with: + php-version: '8.1' + + - name: Detect Duplicates + run: | + set -x + echo "## πŸ” Code Duplication Detection" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Check if PHP files exist + PHP_COUNT=$(find . -name "*.php" ! -path "./vendor/*" ! -path "./.git/*" | wc -l) + + if [ "$PHP_COUNT" -gt 0 ]; then + echo "### PHP Code Duplication" >> $GITHUB_STEP_SUMMARY + + # Install phpcpd + wget https://phar.phpunit.de/phpcpd.phar 2>/dev/null + chmod +x phpcpd.phar + + # Run duplication detection + if ./phpcpd.phar --exclude vendor --exclude .git . 2>&1 | tee /tmp/phpcpd.txt; then + DUPLICATION=$(grep "Found" /tmp/phpcpd.txt | grep -oE "[0-9]+\.[0-9]+%" | head -1 || echo "0.00%") + echo "πŸ“Š **Duplication Rate**: $DUPLICATION" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + DUPLICATION_NUM=$(echo "$DUPLICATION" | sed 's/%//') + if [ $(echo "$DUPLICATION_NUM > 5.0" | bc -l) -eq 1 ]; then + echo "⚠️ Code duplication exceeds 5% threshold" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "View duplication details" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + cat /tmp/phpcpd.txt >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… Code duplication within acceptable limits (<5%)" >> $GITHUB_STEP_SUMMARY + fi + else + echo "βœ… No significant code duplication detected" >> $GITHUB_STEP_SUMMARY + fi + else + echo "ℹ️ No PHP files found for duplication analysis" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Note**: This is an informational check to encourage DRY principles." >> $GITHUB_STEP_SUMMARY + + unused-dependencies: + name: Unused Dependencies Check + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Setup PHP + uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0 + with: + php-version: '8.1' + + - name: Check Unused Dependencies + run: | + set -x + echo "## πŸ“¦ Unused Dependencies Check" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ -f "composer.json" ]; then + echo "### PHP Dependencies" >> $GITHUB_STEP_SUMMARY + + # Install composer-unused + composer global require icanhazstring/composer-unused 2>/dev/null || true + + if composer global exec composer-unused 2>&1 | tee /tmp/unused.txt; then + UNUSED_COUNT=$(grep "unused" /tmp/unused.txt | wc -l || echo 0) + if [ "$UNUSED_COUNT" -gt 0 ]; then + echo "⚠️ Found $UNUSED_COUNT unused dependency/dependencies" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "View unused dependencies" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + cat /tmp/unused.txt >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… No unused dependencies detected" >> $GITHUB_STEP_SUMMARY + fi + else + echo "βœ… All dependencies appear to be in use" >> $GITHUB_STEP_SUMMARY + fi + else + echo "ℹ️ No composer.json found" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Recommendation**: Remove unused dependencies to reduce attack surface" >> $GITHUB_STEP_SUMMARY + + readme-completeness: + name: README Completeness Check + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Check README Sections + run: | + set -x + echo "## πŸ“„ README Completeness Check" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ ! -f "README.md" ]; then + echo "❌ README.md not found" >> $GITHUB_STEP_SUMMARY + exit 1 + fi + + # Required sections + REQUIRED_SECTIONS=("Installation" "Usage" "Contributing" "License") + MISSING=0 + PRESENT=0 + + echo "### Required Sections" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + for section in "${REQUIRED_SECTIONS[@]}"; do + if grep -qi "##.*$section" README.md; then + echo "βœ… $section" >> $GITHUB_STEP_SUMMARY + PRESENT=$((PRESENT + 1)) + else + echo "❌ $section" >> $GITHUB_STEP_SUMMARY + MISSING=$((MISSING + 1)) + fi + done + + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Completeness**: $PRESENT/${#REQUIRED_SECTIONS[@]} required sections present" >> $GITHUB_STEP_SUMMARY + + if [ "$MISSING" -gt 0 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Action Required**: Add missing sections to README.md" >> $GITHUB_STEP_SUMMARY + exit 1 + fi + + # ============================================================================ + # PHASE 3: Future Enhancements + # ============================================================================ + + code-complexity: + name: Code Complexity Analysis + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Setup PHP + uses: shivammathur/setup-php@44454db4f0199b8b9685a5d763dc37cbf79108e1 # v2.31.0 + with: + php-version: '8.1' + + - name: Analyze Complexity + run: | + set -x + echo "## πŸ“Š Code Complexity Analysis" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + PHP_COUNT=$(find . -name "*.php" ! -path "./vendor/*" ! -path "./.git/*" | wc -l) + + if [ "$PHP_COUNT" -gt 0 ]; then + # Install phploc + wget https://phar.phpunit.de/phploc.phar 2>/dev/null + chmod +x phploc.phar + + echo "### PHP Code Metrics" >> $GITHUB_STEP_SUMMARY + if ./phploc.phar --exclude vendor --exclude .git . 2>&1 | tee /tmp/phploc.txt; then + COMPLEXITY=$(grep "Cyclomatic Complexity" /tmp/phploc.txt | grep "Average" | awk '{print $NF}' || echo "N/A") + echo "**Average Cyclomatic Complexity**: $COMPLEXITY" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "$COMPLEXITY" != "N/A" ] && [ $(echo "$COMPLEXITY > 10" | bc -l) -eq 1 ]; then + echo "⚠️ Average complexity exceeds recommended threshold (10)" >> $GITHUB_STEP_SUMMARY + echo "**Recommendation**: Refactor complex functions" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… Code complexity within acceptable limits" >> $GITHUB_STEP_SUMMARY + fi + fi + else + echo "ℹ️ No PHP files found for complexity analysis" >> $GITHUB_STEP_SUMMARY + fi + + api-documentation: + name: API Documentation Coverage + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Check Documentation + run: | + set -x + echo "## πŸ“š API Documentation Coverage" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Count public functions/classes + PUBLIC_METHODS=$(grep -r "public function" . --include="*.php" ! -path "./vendor/*" | wc -l || echo 0) + DOCUMENTED=$(grep -B5 -r "public function" . --include="*.php" ! -path "./vendor/*" | grep -c "/\*\*" || echo 0) + + if [ "$PUBLIC_METHODS" -gt 0 ]; then + COVERAGE=$((DOCUMENTED * 100 / PUBLIC_METHODS)) + echo "**Documentation Coverage**: $COVERAGE% ($DOCUMENTED/$PUBLIC_METHODS)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "$COVERAGE" -lt 80 ]; then + echo "⚠️ Documentation coverage below 80% threshold" >> $GITHUB_STEP_SUMMARY + echo "**Recommendation**: Add PHPDoc blocks to public methods" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… Good documentation coverage" >> $GITHUB_STEP_SUMMARY + fi + else + echo "ℹ️ No public methods found for documentation check" >> $GITHUB_STEP_SUMMARY + fi + + insecure-patterns: + name: Insecure Code Pattern Detection + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Scan for Insecure Patterns + run: | + set -x + echo "## πŸ”’ Insecure Code Pattern Detection" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + VIOLATIONS=0 + + # PHP: SQL injection patterns + if grep -r -n "\\$_\(GET\|POST\|REQUEST\).*mysql_query\|mysqli_query" . --include="*.php" ! -path "./vendor/*" 2>/dev/null > /tmp/sql_inject.txt; then + COUNT=$(wc -l < /tmp/sql_inject.txt) + echo "⚠️ Found $COUNT potential SQL injection pattern(s)" >> $GITHUB_STEP_SUMMARY + VIOLATIONS=$((VIOLATIONS + COUNT)) + fi + + # PHP: eval/exec usage + if grep -r -n "eval\|exec\|system\|passthru\|shell_exec" . --include="*.php" ! -path "./vendor/*" 2>/dev/null > /tmp/exec.txt; then + COUNT=$(wc -l < /tmp/exec.txt) + echo "⚠️ Found $COUNT dangerous function call(s)" >> $GITHUB_STEP_SUMMARY + VIOLATIONS=$((VIOLATIONS + COUNT)) + fi + + # Python: eval usage + if grep -r -n "eval(" . --include="*.py" 2>/dev/null > /tmp/py_eval.txt; then + COUNT=$(wc -l < /tmp/py_eval.txt) + echo "⚠️ Found $COUNT Python eval() usage(s)" >> $GITHUB_STEP_SUMMARY + VIOLATIONS=$((VIOLATIONS + COUNT)) + fi + + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "$VIOLATIONS" -gt 0 ]; then + echo "**Total Violations**: $VIOLATIONS" >> $GITHUB_STEP_SUMMARY + echo "**Recommendation**: Review and secure flagged patterns" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… No insecure patterns detected" >> $GITHUB_STEP_SUMMARY + fi + + binary-file-detection: + name: Binary File Detection + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Detect Binary Files + run: | + set -x + echo "## πŸ” Binary File Detection" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Find binary files excluding allowed types + BINARIES=$(find . -type f ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" \ + ! -name "*.png" ! -name "*.jpg" ! -name "*.jpeg" ! -name "*.gif" ! -name "*.svg" ! -name "*.ico" \ + ! -name "*.woff" ! -name "*.woff2" ! -name "*.ttf" ! -name "*.eot" \ + -exec file {} \; | grep -v "text" | grep -v "empty" | wc -l || echo 0) + + if [ "$BINARIES" -gt 0 ]; then + echo "⚠️ Found $BINARIES non-image binary file(s)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "View binary files" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + find . -type f ! -path "./.git/*" ! -path "./vendor/*" ! -path "./node_modules/*" \ + ! -name "*.png" ! -name "*.jpg" ! -name "*.jpeg" ! -name "*.gif" ! -name "*.svg" ! -name "*.ico" \ + ! -name "*.woff" ! -name "*.woff2" ! -name "*.ttf" ! -name "*.eot" \ + -exec file {} \; | grep -v "text" | grep -v "empty" | cut -d: -f1 >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Recommendation**: Source control should primarily contain text files" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… No unexpected binary files detected" >> $GITHUB_STEP_SUMMARY + fi + + # ============================================================================ + # PHASE 4: Nice to Have Checks + # ============================================================================ + + dead-code-detection: + name: Dead Code Detection + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Setup Python + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + with: + python-version: '3.x' + + - name: Detect Dead Code + run: | + set -x + echo "## πŸ—‘οΈ Dead Code Detection" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + PY_COUNT=$(find . -name "*.py" ! -path "./vendor/*" ! -path "./.git/*" ! -path "./venv/*" | wc -l) + + if [ "$PY_COUNT" -gt 0 ]; then + pip install vulture 2>/dev/null + echo "### Python Dead Code" >> $GITHUB_STEP_SUMMARY + + if vulture . --exclude vendor,venv,.git 2>&1 | tee /tmp/vulture.txt; then + DEAD_COUNT=$(wc -l < /tmp/vulture.txt || echo 0) + if [ "$DEAD_COUNT" -gt 0 ]; then + echo "⚠️ Found $DEAD_COUNT potential dead code item(s)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + echo "View dead code" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + head -50 /tmp/vulture.txt >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… No dead code detected" >> $GITHUB_STEP_SUMMARY + fi + fi + else + echo "ℹ️ No Python files found for dead code analysis" >> $GITHUB_STEP_SUMMARY + fi + + file-naming-standards: + name: File Naming Standards + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Check File Naming + run: | + set -x + echo "## πŸ“ File Naming Standards" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + VIOLATIONS=0 + + # Check PHP files (should be PascalCase for classes) + INVALID_PHP=$(find . -name "*.php" ! -path "./vendor/*" ! -path "./.git/*" ! -regex ".*/[A-Z][a-zA-Z0-9]*\.php" ! -name "index.php" ! -name "functions.php" | wc -l || echo 0) + + # Check config files (should be kebab-case) + INVALID_CONFIG=$(find . -name "*.yml" -o -name "*.yaml" -o -name "*.json" ! -path "./vendor/*" ! -path "./.git/*" ! -path "./node_modules/*" | grep -E "[A-Z_]" | wc -l || echo 0) + + echo "### Naming Violations" >> $GITHUB_STEP_SUMMARY + echo "- **PHP files not PascalCase**: $INVALID_PHP" >> $GITHUB_STEP_SUMMARY + echo "- **Config files not kebab-case**: $INVALID_CONFIG" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + VIOLATIONS=$((INVALID_PHP + INVALID_CONFIG)) + + if [ "$VIOLATIONS" -gt 0 ]; then + echo "⚠️ Found $VIOLATIONS naming convention violation(s)" >> $GITHUB_STEP_SUMMARY + echo "**Recommendation**: Follow naming conventions for consistency" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… File naming conventions followed" >> $GITHUB_STEP_SUMMARY + fi + + accessibility-check: + name: Accessibility Check + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Check Accessibility + run: | + set -x + echo "## β™Ώ Accessibility Check" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + HTML_COUNT=$(find . -name "*.html" ! -path "./vendor/*" ! -path "./.git/*" ! -path "./node_modules/*" | wc -l || echo 0) + MD_IMG_COUNT=$(find . -name "*.md" ! -path "./vendor/*" ! -path "./.git/*" -exec grep -l "!\[" {} + 2>/dev/null | wc -l || echo 0) + + if [ "$HTML_COUNT" -gt 0 ] || [ "$MD_IMG_COUNT" -gt 0 ]; then + # Check for images without alt text + MISSING_ALT=0 + + if [ "$HTML_COUNT" -gt 0 ]; then + MISSING_ALT=$(grep -r "> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "$MISSING_ALT" -gt 0 ]; then + echo "⚠️ Found images without alt text" >> $GITHUB_STEP_SUMMARY + echo "**Recommendation**: Add descriptive alt text for accessibility" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… All images have alt text" >> $GITHUB_STEP_SUMMARY + fi + else + echo "ℹ️ No HTML files found for accessibility check" >> $GITHUB_STEP_SUMMARY + fi + + performance-metrics: + name: Performance Metrics + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Check Performance Metrics + run: | + set -x + echo "## ⚑ Performance Metrics" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Check if JavaScript bundles exist + if [ -f "package.json" ]; then + echo "### Bundle Analysis" >> $GITHUB_STEP_SUMMARY + + # Check for common bundle files + BUNDLE_SIZE=0 + if [ -d "dist" ]; then + BUNDLE_SIZE=$(du -sb dist/ 2>/dev/null | cut -f1 || echo 0) + elif [ -d "build" ]; then + BUNDLE_SIZE=$(du -sb build/ 2>/dev/null | cut -f1 || echo 0) + fi + + if [ "$BUNDLE_SIZE" -gt 0 ]; then + BUNDLE_MB=$((BUNDLE_SIZE / 1024 / 1024)) + echo "**Bundle Size**: ${BUNDLE_MB}MB" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "$BUNDLE_MB" -gt 5 ]; then + echo "⚠️ Bundle size exceeds 5MB threshold" >> $GITHUB_STEP_SUMMARY + echo "**Recommendation**: Optimize bundle size" >> $GITHUB_STEP_SUMMARY + else + echo "βœ… Bundle size within acceptable limits" >> $GITHUB_STEP_SUMMARY + fi + else + echo "ℹ️ No build artifacts found" >> $GITHUB_STEP_SUMMARY + fi + else + echo "ℹ️ Not a JavaScript project" >> $GITHUB_STEP_SUMMARY + fi + + terraform-validation: + name: Terraform Configuration Validation + runs-on: ubuntu-latest + + steps: + - name: Checkout Repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@5e8dbf3c6d9deaf4193ca7a8fb23f2ac83bb6c85 # v4.0.0 + with: + terraform_version: "1.0" + + - name: Validate Terraform Files + run: | + set -x + echo "## πŸ—οΈ Terraform Configuration Validation" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Check if terraform files exist + TF_COUNT=$(find . -name "*.tf" -type f | wc -l || echo 0) + + if [ "$TF_COUNT" -eq 0 ]; then + echo "ℹ️ No Terraform files found in repository" >> $GITHUB_STEP_SUMMARY + exit 0 + fi + + echo "**Terraform Files Found**: $TF_COUNT" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Validation Results + VALIDATION_PASSED=true + WARNINGS=0 + ERRORS=0 + + # 1. Check .github/config.tf location (not root override files) + echo "### Override Configuration Check" >> $GITHUB_STEP_SUMMARY + LEGACY_OVERRIDES=$(find . -maxdepth 1 -name "*override*.tf" -o -name "MokoStandards.override.tf" 2>/dev/null | wc -l || echo 0) + if [ "$LEGACY_OVERRIDES" -gt 0 ]; then + echo "⚠️ Found legacy override files in root directory" >> $GITHUB_STEP_SUMMARY + echo "**Expected Location**: .github/config.tf" >> $GITHUB_STEP_SUMMARY + echo "**Legacy files found**: $LEGACY_OVERRIDES" >> $GITHUB_STEP_SUMMARY + WARNINGS=$((WARNINGS + 1)) + else + if [ -f ".github/config.tf" ]; then + echo "βœ… Override configuration in correct location (.github/config.tf)" >> $GITHUB_STEP_SUMMARY + else + echo "ℹ️ No override configuration found" >> $GITHUB_STEP_SUMMARY + fi + fi + echo "" >> $GITHUB_STEP_SUMMARY + + # 2. Terraform Syntax Validation + echo "### Terraform Syntax Validation" >> $GITHUB_STEP_SUMMARY + SYNTAX_ERRORS=0 + + # Find all directories with terraform files + for dir in $(find . -name "*.tf" -type f -exec dirname {} \; | sort -u); do + cd "$dir" || continue + echo "Validating: $dir" >> $GITHUB_STEP_SUMMARY + + # Initialize without backend + terraform init -backend=false > /dev/null 2>&1 || true + + # Validate + if terraform validate -no-color > /tmp/tf_validate.txt 2>&1; then + echo " βœ… Syntax valid" >> $GITHUB_STEP_SUMMARY + else + echo " ❌ Syntax errors found" >> $GITHUB_STEP_SUMMARY + cat /tmp/tf_validate.txt >> $GITHUB_STEP_SUMMARY + SYNTAX_ERRORS=$((SYNTAX_ERRORS + 1)) + VALIDATION_PASSED=false + fi + cd - > /dev/null + done + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "$SYNTAX_ERRORS" -eq 0 ]; then + echo "βœ… All Terraform files have valid syntax" >> $GITHUB_STEP_SUMMARY + else + echo "❌ Found $SYNTAX_ERRORS directories with syntax errors" >> $GITHUB_STEP_SUMMARY + ERRORS=$((ERRORS + SYNTAX_ERRORS)) + fi + echo "" >> $GITHUB_STEP_SUMMARY + + # 3. Terraform Formatting Check + echo "### Terraform Formatting Check" >> $GITHUB_STEP_SUMMARY + FORMAT_ISSUES=0 + + for tf_file in $(find . -name "*.tf" -type f); do + if ! terraform fmt -check=true -no-color "$tf_file" > /dev/null 2>&1; then + FORMAT_ISSUES=$((FORMAT_ISSUES + 1)) + fi + done + + if [ "$FORMAT_ISSUES" -eq 0 ]; then + echo "βœ… All Terraform files properly formatted" >> $GITHUB_STEP_SUMMARY + else + echo "⚠️ Found $FORMAT_ISSUES files with formatting issues" >> $GITHUB_STEP_SUMMARY + echo "**Fix**: Run \`terraform fmt -recursive\`" >> $GITHUB_STEP_SUMMARY + WARNINGS=$((WARNINGS + 1)) + fi + echo "" >> $GITHUB_STEP_SUMMARY + + # 4. Check for file_metadata blocks + echo "### File Metadata Validation" >> $GITHUB_STEP_SUMMARY + MISSING_METADATA=0 + + for tf_file in $(find . -name "*.tf" -type f); do + if ! grep -q "file_metadata" "$tf_file"; then + MISSING_METADATA=$((MISSING_METADATA + 1)) + fi + done + + if [ "$MISSING_METADATA" -eq 0 ]; then + echo "βœ… All Terraform files contain file_metadata block" >> $GITHUB_STEP_SUMMARY + else + echo "⚠️ Found $MISSING_METADATA files missing file_metadata block" >> $GITHUB_STEP_SUMMARY + echo "**Reference**: docs/policy/terraform-file-standards.md" >> $GITHUB_STEP_SUMMARY + WARNINGS=$((WARNINGS + 1)) + fi + echo "" >> $GITHUB_STEP_SUMMARY + + # 5. Version Consistency Check + echo "### Version Consistency Check" >> $GITHUB_STEP_SUMMARY + VERSION_MISMATCHES=0 + EXPECTED_VERSION="04.00.04" + + for tf_file in $(find . -name "*.tf" -type f); do + if grep -q "version.*=" "$tf_file"; then + if ! grep -q "version.*=.*\"$EXPECTED_VERSION\"" "$tf_file"; then + VERSION_MISMATCHES=$((VERSION_MISMATCHES + 1)) + fi + fi + done + + if [ "$VERSION_MISMATCHES" -eq 0 ]; then + echo "βœ… All Terraform file versions match $EXPECTED_VERSION" >> $GITHUB_STEP_SUMMARY + else + echo "⚠️ Found $VERSION_MISMATCHES files with version mismatches" >> $GITHUB_STEP_SUMMARY + echo "**Expected Version**: $EXPECTED_VERSION" >> $GITHUB_STEP_SUMMARY + WARNINGS=$((WARNINGS + 1)) + fi + echo "" >> $GITHUB_STEP_SUMMARY + + # 6. Copyright Header Check + echo "### Copyright Header Check" >> $GITHUB_STEP_SUMMARY + MISSING_COPYRIGHT=0 + + for tf_file in $(find . -name "*.tf" -type f); do + if ! grep -q "Copyright (C)" "$tf_file"; then + MISSING_COPYRIGHT=$((MISSING_COPYRIGHT + 1)) + fi + done + + if [ "$MISSING_COPYRIGHT" -eq 0 ]; then + echo "βœ… All Terraform files have copyright headers" >> $GITHUB_STEP_SUMMARY + else + echo "⚠️ Found $MISSING_COPYRIGHT files missing copyright headers" >> $GITHUB_STEP_SUMMARY + echo "**Reference**: docs/policy/terraform-file-standards.md" >> $GITHUB_STEP_SUMMARY + WARNINGS=$((WARNINGS + 1)) + fi + echo "" >> $GITHUB_STEP_SUMMARY + + # Summary + echo "---" >> $GITHUB_STEP_SUMMARY + echo "### Validation Summary" >> $GITHUB_STEP_SUMMARY + echo "**Total Files**: $TF_COUNT" >> $GITHUB_STEP_SUMMARY + echo "**Errors**: $ERRORS" >> $GITHUB_STEP_SUMMARY + echo "**Warnings**: $WARNINGS" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + if [ "$VALIDATION_PASSED" = true ] && [ "$ERRORS" -eq 0 ]; then + echo "βœ… **Terraform Validation: PASSED**" >> $GITHUB_STEP_SUMMARY + exit 0 + elif [ "$ERRORS" -gt 0 ]; then + echo "❌ **Terraform Validation: FAILED**" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Note**: This is an informational check and does not block merges" >> $GITHUB_STEP_SUMMARY + exit 0 # Informational only + else + echo "⚠️ **Terraform Validation: PASSED WITH WARNINGS**" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Note**: This is an informational check and does not block merges" >> $GITHUB_STEP_SUMMARY + exit 0 # Informational only + fi + + summary: + name: Compliance Summary + runs-on: ubuntu-latest + needs: [ + repository-structure, documentation-quality, coding-standards, line-length-validation, license-compliance, git-hygiene, workflow-validation, version-consistency, script-integrity, enterprise-readiness, repository-health, + todo-fixme-tracking, file-size-limits, secret-scanning, broken-link-detection, + dependency-vulnerabilities, code-duplication, unused-dependencies, readme-completeness, + code-complexity, api-documentation, insecure-patterns, binary-file-detection, + dead-code-detection, file-naming-standards, accessibility-check, performance-metrics, terraform-validation + ] + if: always() + + steps: + - name: Generate Compliance Report + run: | + set -x + echo "# πŸ“Š MokoStandards Compliance Report" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Calculate overall status + REPO_STATUS="${{ needs.repository-structure.result }}" + DOCS_STATUS="${{ needs.documentation-quality.result }}" + CODE_STATUS="${{ needs.coding-standards.result }}" + LINE_LENGTH_STATUS="${{ needs.line-length-validation.result }}" + LICENSE_STATUS="${{ needs.license-compliance.result }}" + GIT_STATUS="${{ needs.git-hygiene.result }}" + WORKFLOW_STATUS="${{ needs.workflow-validation.result }}" + VERSION_STATUS="${{ needs.version-consistency.result }}" + SCRIPT_STATUS="${{ needs.script-integrity.result }}" + ENTERPRISE_STATUS="${{ needs.enterprise-readiness.result }}" + HEALTH_STATUS="${{ needs.repository-health.result }}" + TERRAFORM_STATUS="${{ needs.terraform-validation.result }}" + + PASSED=0 + FAILED=0 + WARNINGS=0 + TOTAL=28 + + # Critical checks (must pass) + [ "$REPO_STATUS" = "success" ] && PASSED=$((PASSED + 1)) || FAILED=$((FAILED + 1)) + [ "$DOCS_STATUS" = "success" ] && PASSED=$((PASSED + 1)) || FAILED=$((FAILED + 1)) + [ "$CODE_STATUS" = "success" ] && PASSED=$((PASSED + 1)) || FAILED=$((FAILED + 1)) + [ "$LICENSE_STATUS" = "success" ] && PASSED=$((PASSED + 1)) || FAILED=$((FAILED + 1)) + [ "$GIT_STATUS" = "success" ] && PASSED=$((PASSED + 1)) || FAILED=$((FAILED + 1)) + [ "$WORKFLOW_STATUS" = "success" ] && PASSED=$((PASSED + 1)) || FAILED=$((FAILED + 1)) + [ "$VERSION_STATUS" = "success" ] && PASSED=$((PASSED + 1)) || FAILED=$((FAILED + 1)) + [ "$SCRIPT_STATUS" = "success" ] && PASSED=$((PASSED + 1)) || FAILED=$((FAILED + 1)) + + # Informational checks (don't fail build) + if [ "$ENTERPRISE_STATUS" = "success" ]; then + PASSED=$((PASSED + 1)) + else + WARNINGS=$((WARNINGS + 1)) + fi + + if [ "$HEALTH_STATUS" = "success" ]; then + PASSED=$((PASSED + 1)) + else + WARNINGS=$((WARNINGS + 1)) + fi + + if [ "$TERRAFORM_STATUS" = "success" ]; then + PASSED=$((PASSED + 1)) + else + WARNINGS=$((WARNINGS + 1)) + fi + + # Adjust total to only count critical checks for compliance percentage + CRITICAL_TOTAL=8 + CRITICAL_PASSED=$((PASSED - WARNINGS)) + COMPLIANCE_PERCENT=$((CRITICAL_PASSED * 100 / CRITICAL_TOTAL)) + + # Overall status badge + if [ "$COMPLIANCE_PERCENT" -eq 100 ]; then + echo "## βœ… Overall Status: **COMPLIANT** ($COMPLIANCE_PERCENT%)" >> $GITHUB_STEP_SUMMARY + elif [ "$COMPLIANCE_PERCENT" -ge 80 ]; then + echo "## ⚠️ Overall Status: **MOSTLY COMPLIANT** ($COMPLIANCE_PERCENT%)" >> $GITHUB_STEP_SUMMARY + elif [ "$COMPLIANCE_PERCENT" -ge 50 ]; then + echo "## ⚠️ Overall Status: **PARTIALLY COMPLIANT** ($COMPLIANCE_PERCENT%)" >> $GITHUB_STEP_SUMMARY + else + echo "## ❌ Overall Status: **NON-COMPLIANT** ($COMPLIANCE_PERCENT%)" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Critical Checks:** $CRITICAL_PASSED/$CRITICAL_TOTAL passed" >> $GITHUB_STEP_SUMMARY + echo "**Total Checks:** $PASSED/$TOTAL passed" >> $GITHUB_STEP_SUMMARY + if [ "$WARNINGS" -gt 0 ]; then + echo "**Informational:** $WARNINGS warning(s)" >> $GITHUB_STEP_SUMMARY + fi + echo "" >> $GITHUB_STEP_SUMMARY + + # Progress bar + FILLED=$((COMPLIANCE_PERCENT / 5)) + EMPTY=$((20 - FILLED)) + BAR="" + for i in $(seq 1 $FILLED); do BAR="${BAR}β–ˆ"; done + for i in $(seq 1 $EMPTY); do BAR="${BAR}β–‘"; done + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "$BAR $COMPLIANCE_PERCENT%" >> $GITHUB_STEP_SUMMARY + echo "\`\`\`" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + # Detailed breakdown + echo "## Validation Results" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Area | Status | Result | Priority |" >> $GITHUB_STEP_SUMMARY + echo "|------|--------|--------|----------|" >> $GITHUB_STEP_SUMMARY + + # Repository Structure + if [ "$REPO_STATUS" = "success" ]; then + echo "| πŸ“ Repository Structure | βœ… Pass | Compliant | - |" >> $GITHUB_STEP_SUMMARY + else + echo "| πŸ“ Repository Structure | ❌ Fail | **Action Required** | πŸ”΄ Critical |" >> $GITHUB_STEP_SUMMARY + fi + + # Documentation Quality + if [ "$DOCS_STATUS" = "success" ]; then + echo "| πŸ“š Documentation Quality | βœ… Pass | Compliant | - |" >> $GITHUB_STEP_SUMMARY + else + echo "| πŸ“š Documentation Quality | ❌ Fail | **Action Required** | πŸ”΄ Critical |" >> $GITHUB_STEP_SUMMARY + fi + + # Coding Standards + if [ "$CODE_STATUS" = "success" ]; then + echo "| πŸ’» Coding Standards | βœ… Pass | Compliant | - |" >> $GITHUB_STEP_SUMMARY + else + echo "| πŸ’» Coding Standards | ⚠️ Warning | Review Recommended | 🟑 Medium |" >> $GITHUB_STEP_SUMMARY + fi + + # License Compliance + if [ "$LICENSE_STATUS" = "success" ]; then + echo "| βš–οΈ License Compliance | βœ… Pass | Compliant | - |" >> $GITHUB_STEP_SUMMARY + else + echo "| βš–οΈ License Compliance | ❌ Fail | **Action Required** | πŸ”΄ Critical |" >> $GITHUB_STEP_SUMMARY + fi + + # Git Hygiene + if [ "$GIT_STATUS" = "success" ]; then + echo "| 🧹 Git Repository Hygiene | βœ… Pass | Compliant | - |" >> $GITHUB_STEP_SUMMARY + else + echo "| 🧹 Git Repository Hygiene | ⚠️ Warning | Review Recommended | 🟑 Medium |" >> $GITHUB_STEP_SUMMARY + fi + + # Workflow Configuration + if [ "$WORKFLOW_STATUS" = "success" ]; then + echo "| βš™οΈ Workflow Configuration | βœ… Pass | Compliant | - |" >> $GITHUB_STEP_SUMMARY + else + echo "| βš™οΈ Workflow Configuration | ⚠️ Warning | Review Recommended | 🟑 Medium |" >> $GITHUB_STEP_SUMMARY + fi + + # Version Consistency + if [ "$VERSION_STATUS" = "success" ]; then + echo "| πŸ”’ Version Consistency | βœ… Pass | All versions match | - |" >> $GITHUB_STEP_SUMMARY + else + echo "| πŸ”’ Version Consistency | ❌ Fail | **Action Required** | πŸ”΄ Critical |" >> $GITHUB_STEP_SUMMARY + fi + + # Script Integrity + if [ "$SCRIPT_STATUS" = "success" ]; then + echo "| πŸ” Script Integrity | βœ… Pass | SHA hashes validated | - |" >> $GITHUB_STEP_SUMMARY + else + echo "| πŸ” Script Integrity | ❌ Fail | **Action Required** | πŸ”΄ Critical |" >> $GITHUB_STEP_SUMMARY + fi + + # Enterprise Readiness (Informational) + if [ "$ENTERPRISE_STATUS" = "success" ]; then + echo "| 🏒 Enterprise Readiness | βœ… Pass | Ready for enterprise | ℹ️ Info |" >> $GITHUB_STEP_SUMMARY + else + echo "| 🏒 Enterprise Readiness | ℹ️ Info | Review suggestions | ℹ️ Info |" >> $GITHUB_STEP_SUMMARY + fi + + # Repository Health (Informational) + if [ "$HEALTH_STATUS" = "success" ]; then + echo "| πŸ₯ Repository Health | βœ… Pass | Health check passed | ℹ️ Info |" >> $GITHUB_STEP_SUMMARY + else + echo "| πŸ₯ Repository Health | ℹ️ Info | Review recommendations | ℹ️ Info |" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + + # Action items summary + if [ "$FAILED" -gt 0 ]; then + echo "## ⚑ Action Items" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**$FAILED validation area(s) require attention:**" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + [ "$REPO_STATUS" != "success" ] && echo "- πŸ”΄ **Critical:** Fix repository structure issues" >> $GITHUB_STEP_SUMMARY + [ "$DOCS_STATUS" != "success" ] && echo "- πŸ”΄ **Critical:** Improve documentation quality" >> $GITHUB_STEP_SUMMARY + [ "$LICENSE_STATUS" != "success" ] && echo "- πŸ”΄ **Critical:** Resolve license compliance issues" >> $GITHUB_STEP_SUMMARY + [ "$CODE_STATUS" != "success" ] && echo "- 🟑 **Medium:** Review coding standards violations" >> $GITHUB_STEP_SUMMARY + [ "$GIT_STATUS" != "success" ] && echo "- 🟑 **Medium:** Address git repository hygiene items" >> $GITHUB_STEP_SUMMARY + [ "$WORKFLOW_STATUS" != "success" ] && echo "- 🟑 **Medium:** Review workflow configuration" >> $GITHUB_STEP_SUMMARY + + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Next Steps:**" >> $GITHUB_STEP_SUMMARY + echo "1. Review detailed results in individual job outputs above" >> $GITHUB_STEP_SUMMARY + echo "2. Follow remediation steps provided for each failure" >> $GITHUB_STEP_SUMMARY + echo "3. Re-run this workflow after making corrections" >> $GITHUB_STEP_SUMMARY + echo "4. Reach 100% compliance before merging" >> $GITHUB_STEP_SUMMARY + else + echo "## πŸŽ‰ Excellent!" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "Your repository is **fully compliant** with MokoStandards!" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Achievements:**" >> $GITHUB_STEP_SUMMARY + echo "- βœ… All required directories and files present" >> $GITHUB_STEP_SUMMARY + echo "- βœ… Documentation meets quality standards" >> $GITHUB_STEP_SUMMARY + echo "- βœ… Coding standards followed" >> $GITHUB_STEP_SUMMARY + echo "- βœ… License compliance verified" >> $GITHUB_STEP_SUMMARY + echo "- βœ… Git repository well-maintained" >> $GITHUB_STEP_SUMMARY + echo "- βœ… Workflows properly configured" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "---" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "πŸ“š **Resources:**" >> $GITHUB_STEP_SUMMARY + echo "- [MokoStandards Documentation](https://github.com/mokoconsulting-tech/MokoStandards)" >> $GITHUB_STEP_SUMMARY + echo "- [Repository Structure Guide](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/docs/policy/core-structure.md)" >> $GITHUB_STEP_SUMMARY + echo "- [Documentation Standards](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/docs/policy/document-formatting.md)" >> $GITHUB_STEP_SUMMARY + echo "- [Coding Standards](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/docs/policy/coding-style-guide.md)" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "_Generated by MokoStandards Compliance Workflow v${WORKFLOW_VERSION}_" >> $GITHUB_STEP_SUMMARY + + # Create tracking issue for non-compliance if on push + if [ "$COMPLIANCE_PERCENT" -lt 100 ] && [ "${{ github.event_name }}" = "push" ]; then + echo "Creating tracking issue for standards violations..." + fi + + # Exit with error if not fully compliant + if [ "$COMPLIANCE_PERCENT" -lt 100 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "### ❌ Standards Compliance Failed" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Overall Compliance:** $COMPLIANCE_PERCENT%" >> $GITHUB_STEP_SUMMARY + echo "**Status:** Repository does not meet 100% compliance requirement" >> $GITHUB_STEP_SUMMARY + echo "**Action Required:** Review and fix all validation failures above" >> $GITHUB_STEP_SUMMARY + echo "" + echo "❌ ERROR: Standards compliance at $COMPLIANCE_PERCENT% - 100% required" + exit 1 + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "### βœ… Full Standards Compliance Achieved" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Overall Compliance:** 100%" >> $GITHUB_STEP_SUMMARY + echo "**Status:** Repository meets all MokoStandards requirements" >> $GITHUB_STEP_SUMMARY + echo "" + echo "βœ… SUCCESS: Repository is fully MokoStandards compliant" + + - name: Create tracking issue for standards violations + if: failure() && github.event_name == 'push' + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + with: + script: | + const compliancePercent = '${{ needs.compliance-check.outputs.compliance_percentage }}' || '0'; + const failedChecks = []; + + // Collect failed checks from job outputs + const jobs = [ + { name: 'File Headers', failed: '${{ needs.compliance-check.outputs.headers_ok }}' !== 'true' }, + { name: 'Required Files', failed: '${{ needs.compliance-check.outputs.required_files_ok }}' !== 'true' }, + { name: 'Documentation', failed: '${{ needs.compliance-check.outputs.documentation_ok }}' !== 'true' }, + { name: 'Code Quality', failed: '${{ needs.compliance-check.outputs.code_quality_ok }}' !== 'true' } + ]; + + jobs.forEach(job => { + if (job.failed) failedChecks.push(job.name); + }); + + const body = `## πŸ“‹ Standards Compliance Violations Detected + + **Branch**: \`${context.ref}\` + **Commit**: ${context.sha.substring(0, 7)} + **Triggered by**: @${context.actor} + **Date**: ${new Date().toISOString()} + + ### Compliance Score: ${compliancePercent}% + + ### ❌ Failed Checks + ${failedChecks.length > 0 ? failedChecks.map(check => `- ${check}`).join('\n') : '_Details available in workflow run_'} + + ### πŸ“Š What This Means + Your repository does not meet the required MokoStandards compliance threshold. This can affect: + - Code quality and maintainability + - Team collaboration efficiency + - Automated tooling integration + - Repository discoverability + + ### βœ… Required Actions + 1. Review the [workflow run](${context.payload.repository.html_url}/actions/runs/${context.runId}) for detailed findings + 2. Address each failed check according to MokoStandards documentation + 3. Push changes to trigger a new compliance scan + 4. Ensure compliance reaches 100% before merging + + ### πŸ“š Resources + - [MokoStandards Documentation](https://github.com/mokoconsulting-tech/MokoStandards) + - [Repository Structure Guide](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/docs/policy/core-structure.md) + - [Documentation Standards](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/docs/policy/document-formatting.md) + - [Coding Standards](https://github.com/mokoconsulting-tech/MokoStandards/tree/main/docs/policy/coding-style-guide.md) + + ### πŸ”„ Resolution + This issue will be automatically closed when compliance reaches 100%. + + --- + *This issue was automatically created by the Standards Compliance workflow.* + `; + + // Validate assignees before creating issue + async function validateAssignees(assignees) { + const validAssignees = []; + for (const assignee of assignees) { + try { + await github.rest.users.getByUsername({ username: assignee }); + validAssignees.push(assignee); + console.log(`βœ“ Validated assignee: ${assignee}`); + } catch (error) { + console.log(`βœ— Invalid assignee (skipping): ${assignee} - ${error.message}`); + } + } + return validAssignees; + } + + const requestedAssignees = ['jmiller-moko']; + const validAssignees = await validateAssignees(requestedAssignees); + + // Check for existing open issue + const issues = await github.rest.issues.listForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + labels: 'standards-violation', + per_page: 1 + }); + + if (issues.data.length > 0) { + // Update existing issue + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: issues.data[0].number, + body: `### πŸ”„ Updated Scan Results\n\n${body}` + }); + console.log(`Updated existing issue #${issues.data[0].number}`); + } else { + // Create new issue with validated assignees + const issue = await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title: `[Standards] Compliance Violations - ${new Date().toISOString().split('T')[0]}`, + body: body, + labels: ['standards-violation', 'compliance', 'automation'], + assignees: validAssignees + }); + console.log(`Created new issue #${issue.data.number}`); + } + +# CUSTOMIZATION: +# +# 1. Adjust severity of checks (convert warnings to errors or vice versa) +# 2. Add project-specific validation rules +# 3. Integrate with custom linting tools +# 4. Add notification steps for compliance failures +# 5. Customize required files/directories for your project type diff --git a/.gitignore b/.gitignore index d5011f1..18cf5e1 100644 --- a/.gitignore +++ b/.gitignore @@ -138,7 +138,6 @@ package-lock.json # PHP / Composer tooling # ============================================================ vendor/ -!src/media/vendor/ composer.lock *.phar codeception.phar @@ -187,34 +186,15 @@ venv/ hypothesis/ # ============================================================ -# Dolibarr (base + runtime) +# Joomla Development # ============================================================ -documents/ -dolibarr_documents/ -custom/ -uploads/ -thumbs/ -data/ -cache/ -temp/ -tmp/ -logs/ -htdocs/documents/ -htdocs/custom/ -htdocs/cache/ -htdocs/tmp/ -htdocs/logs/ +# Custom user overrides for Joomla development +user.css +user.js +colors_custom.css -# ============================================================ -# Template Customizations -# ============================================================ -# Ignore custom theme files to prevent fork-specific customizations -src/media/css/theme/light.custom.css -src/media/css/theme/dark.custom.css - -# Ignore user customization files to prevent local development overrides -src/media/css/user.css -src/media/js/user.js +# Joomla Module Builder auto-generated files +modulebuilder.txt # ============================================================ # Joomla Core @@ -326,7 +306,12 @@ src/media/js/user.js /administrator/language/en-GB/en-GB.com_wrapper.ini /administrator/language/en-GB/en-GB.com_wrapper.sys.ini /administrator/language/en-GB/en-GB.ini +/administrator/language/en-GB/en-GB.lib_fof.ini +/administrator/language/en-GB/en-GB.lib_fof.sys.ini /administrator/language/en-GB/en-GB.lib_joomla.ini +/administrator/language/en-GB/en-GB.lib_phpass.ini +/administrator/language/en-GB/en-GB.lib_phpmailer.ini +/administrator/language/en-GB/en-GB.lib_simplepie.ini /administrator/language/en-GB/en-GB.localise.php /administrator/language/en-GB/en-GB.mod_custom.ini /administrator/language/en-GB/en-GB.mod_custom.sys.ini @@ -390,6 +375,8 @@ src/media/js/user.js /administrator/language/en-GB/en-GB.plg_content_contact.sys.ini /administrator/language/en-GB/en-GB.plg_content_emailcloak.ini /administrator/language/en-GB/en-GB.plg_content_emailcloak.sys.ini +/administrator/language/en-GB/en-GB.plg_content_example.ini +/administrator/language/en-GB/en-GB.plg_content_example.sys.ini /administrator/language/en-GB/en-GB.plg_content_fields.ini /administrator/language/en-GB/en-GB.plg_content_fields.sys.ini /administrator/language/en-GB/en-GB.plg_content_finder.ini @@ -406,6 +393,12 @@ src/media/js/user.js /administrator/language/en-GB/en-GB.plg_content_pagenavigation.sys.ini /administrator/language/en-GB/en-GB.plg_content_vote.ini /administrator/language/en-GB/en-GB.plg_content_vote.sys.ini +/administrator/language/en-GB/en-GB.plg_editors_codemirror.ini +/administrator/language/en-GB/en-GB.plg_editors_codemirror.sys.ini +/administrator/language/en-GB/en-GB.plg_editors_none.ini +/administrator/language/en-GB/en-GB.plg_editors_none.sys.ini +/administrator/language/en-GB/en-GB.plg_editors_tinymce.ini +/administrator/language/en-GB/en-GB.plg_editors_tinymce.sys.ini /administrator/language/en-GB/en-GB.plg_editors-xtd_article.ini /administrator/language/en-GB/en-GB.plg_editors-xtd_article.sys.ini /administrator/language/en-GB/en-GB.plg_editors-xtd_contact.ini @@ -422,12 +415,8 @@ src/media/js/user.js /administrator/language/en-GB/en-GB.plg_editors-xtd_pagebreak.sys.ini /administrator/language/en-GB/en-GB.plg_editors-xtd_readmore.ini /administrator/language/en-GB/en-GB.plg_editors-xtd_readmore.sys.ini -/administrator/language/en-GB/en-GB.plg_editors_codemirror.ini -/administrator/language/en-GB/en-GB.plg_editors_codemirror.sys.ini -/administrator/language/en-GB/en-GB.plg_editors_none.ini -/administrator/language/en-GB/en-GB.plg_editors_none.sys.ini -/administrator/language/en-GB/en-GB.plg_editors_tinymce.ini -/administrator/language/en-GB/en-GB.plg_editors_tinymce.sys.ini +/administrator/language/en-GB/en-GB.plg_extension_example.ini +/administrator/language/en-GB/en-GB.plg_extension_example.sys.ini /administrator/language/en-GB/en-GB.plg_extension_joomla.ini /administrator/language/en-GB/en-GB.plg_extension_joomla.sys.ini /administrator/language/en-GB/en-GB.plg_fields_calendar.ini @@ -438,8 +427,6 @@ src/media/js/user.js /administrator/language/en-GB/en-GB.plg_fields_color.sys.ini /administrator/language/en-GB/en-GB.plg_fields_editor.ini /administrator/language/en-GB/en-GB.plg_fields_editor.sys.ini -/administrator/language/en-GB/en-GB.plg_fields_image.ini -/administrator/language/en-GB/en-GB.plg_fields_image.sys.ini /administrator/language/en-GB/en-GB.plg_fields_imagelist.ini /administrator/language/en-GB/en-GB.plg_fields_imagelist.sys.ini /administrator/language/en-GB/en-GB.plg_fields_integer.ini @@ -474,16 +461,12 @@ src/media/js/user.js /administrator/language/en-GB/en-GB.plg_finder_newsfeeds.sys.ini /administrator/language/en-GB/en-GB.plg_finder_tags.ini /administrator/language/en-GB/en-GB.plg_finder_tags.sys.ini -/administrator/language/en-GB/en-GB.plg_finder_weblinks.ini -/administrator/language/en-GB/en-GB.plg_finder_weblinks.sys.ini /administrator/language/en-GB/en-GB.plg_installer_folderinstaller.ini /administrator/language/en-GB/en-GB.plg_installer_folderinstaller.sys.ini /administrator/language/en-GB/en-GB.plg_installer_packageinstaller.ini /administrator/language/en-GB/en-GB.plg_installer_packageinstaller.sys.ini /administrator/language/en-GB/en-GB.plg_installer_urlinstaller.ini /administrator/language/en-GB/en-GB.plg_installer_urlinstaller.sys.ini -/administrator/language/en-GB/en-GB.plg_installer_webinstaller.ini -/administrator/language/en-GB/en-GB.plg_installer_webinstaller.sys.ini /administrator/language/en-GB/en-GB.plg_privacy_actionlogs.ini /administrator/language/en-GB/en-GB.plg_privacy_actionlogs.sys.ini /administrator/language/en-GB/en-GB.plg_privacy_consents.ini @@ -560,29 +543,22 @@ src/media/js/user.js /administrator/language/en-GB/en-GB.plg_twofactorauth_yubikey.sys.ini /administrator/language/en-GB/en-GB.plg_user_contactcreator.ini /administrator/language/en-GB/en-GB.plg_user_contactcreator.sys.ini +/administrator/language/en-GB/en-GB.plg_user_example.ini +/administrator/language/en-GB/en-GB.plg_user_example.sys.ini /administrator/language/en-GB/en-GB.plg_user_joomla.ini /administrator/language/en-GB/en-GB.plg_user_joomla.sys.ini /administrator/language/en-GB/en-GB.plg_user_profile.ini /administrator/language/en-GB/en-GB.plg_user_profile.sys.ini /administrator/language/en-GB/en-GB.plg_user_terms.ini /administrator/language/en-GB/en-GB.plg_user_terms.sys.ini -/administrator/language/en-GB/en-GB.tpl_hathor.ini -/administrator/language/en-GB/en-GB.tpl_hathor.sys.ini -/administrator/language/en-GB/en-GB.tpl_isis.ini -/administrator/language/en-GB/en-GB.tpl_isis.sys.ini /administrator/language/en-GB/en-GB.xml -/administrator/language/en-GB/install.xml -/administrator/language/overrides/* +/administrator/language/en-GB/index.html /administrator/language/index.html +/administrator/language/overrides/* /administrator/logs/* -/administrator/manifests/files/joomla.xml -/administrator/manifests/libraries/fof.xml -/administrator/manifests/libraries/idna_convert.xml -/administrator/manifests/libraries/joomla.xml -/administrator/manifests/libraries/phpass.xml -/administrator/manifests/libraries/phputf8.xml -/administrator/manifests/packages/pkg_en-GB.xml -/administrator/manifests/packages/index.html +/administrator/manifests/files/* +/administrator/manifests/libraries/* +/administrator/manifests/packages/* /administrator/modules/mod_custom/* /administrator/modules/mod_feed/* /administrator/modules/mod_latest/* @@ -619,21 +595,18 @@ src/media/js/user.js /components/com_finder/* /components/com_mailto/* /components/com_media/* -/components/com_menus/* -/components/com_modules/* /components/com_newsfeeds/* /components/com_privacy/* /components/com_search/* /components/com_tags/* /components/com_users/* +/components/com_weblinks/* /components/com_wrapper/* -/components/index.html /images/banners/* /images/headers/* +/images/joomla* /images/sampledata/* /images/index.html -/images/joomla* -/images/powered_by.png /includes/* /installation/* /language/en-GB/en-GB.com_ajax.ini @@ -652,14 +625,12 @@ src/media/js/user.js /language/en-GB/en-GB.com_weblinks.ini /language/en-GB/en-GB.com_wrapper.ini /language/en-GB/en-GB.files_joomla.sys.ini -/language/en-GB/en-GB.finder_cli.ini /language/en-GB/en-GB.ini /language/en-GB/en-GB.lib_fof.ini /language/en-GB/en-GB.lib_fof.sys.ini /language/en-GB/en-GB.lib_idna_convert.sys.ini /language/en-GB/en-GB.lib_joomla.ini /language/en-GB/en-GB.lib_joomla.sys.ini -/language/en-GB/en-GB.lib_phpass.sys.ini /language/en-GB/en-GB.lib_phpmailer.sys.ini /language/en-GB/en-GB.lib_phputf8.sys.ini /language/en-GB/en-GB.lib_simplepie.sys.ini @@ -727,13 +698,10 @@ src/media/js/user.js /language/en-GB/en-GB.tpl_protostar.ini /language/en-GB/en-GB.tpl_protostar.sys.ini /language/en-GB/en-GB.xml -/language/en-GB/install.xml -/language/overrides/* +/language/en-GB/index.html /language/index.html +/language/overrides/* /layouts/joomla/* -/layouts/libraries/* -/layouts/plugins/* -/layouts/index.html /libraries/cms/* /libraries/fof/* /libraries/idna_convert/* @@ -744,7 +712,6 @@ src/media/js/user.js /libraries/phpmailer/* /libraries/phputf8/* /libraries/simplepie/* -/libraries/src/* /libraries/vendor/* /libraries/classmap.php /libraries/cms.php @@ -760,8 +727,14 @@ src/media/js/user.js /media/com_fields/* /media/com_finder/* /media/com_joomlaupdate/* +/media/com_mailto/* +/media/com_media/* /media/com_menus/* /media/com_modules/* +/media/com_newsfeeds/* +/media/com_privacy/* +/media/com_tags/* +/media/com_weblinks/* /media/com_wrapper/* /media/contacts/* /media/editors/* @@ -769,18 +742,18 @@ src/media/js/user.js /media/mailto/* /media/media/* /media/mod_languages/* -/media/mod_sampledata/* /media/overrider/* /media/plg_captcha_recaptcha/* /media/plg_captcha_recaptcha_invisible/* +/media/plg_editors-xtd_article/* /media/plg_quickicon_extensionupdate/* /media/plg_quickicon_joomlaupdate/* /media/plg_quickicon_privacycheck/* +/media/plg_system_debug/* /media/plg_system_highlight/* /media/plg_system_stats/* /media/plg_twofactorauth_totp/* /media/system/* -/media/index.html /modules/mod_articles_archive/* /modules/mod_articles_categories/* /modules/mod_articles_category/* @@ -804,9 +777,9 @@ src/media/js/user.js /modules/mod_tags_popular/* /modules/mod_tags_similar/* /modules/mod_users_latest/* +/modules/mod_weblinks/* /modules/mod_whosonline/* /modules/mod_wrapper/* -/modules/index.html /plugins/actionlog/joomla/* /plugins/authentication/cookie/* /plugins/authentication/example/* diff --git a/.moko-standards b/.moko-standards new file mode 100644 index 0000000..10119ea --- /dev/null +++ b/.moko-standards @@ -0,0 +1,20 @@ +# Copyright (C) 2026 Moko Consulting +# SPDX-License-Identifier: GPL-3.0-or-later +# FILE INFORMATION +# DEFGROUP: MokoStandards.Templates.Config +# INGROUP: MokoStandards.Templates +# REPO: https://github.com/mokoconsulting-tech/MokoStandards +# PATH: /templates/configs/moko-standards.yml +# VERSION: 04.00.04 +# BRIEF: Governance attachment template β€” synced to .moko-standards in every governed repository +# NOTE: Tokens replaced at sync time: mokoconsulting-tech, MokoCassiopeia, waas-component, 04.00.04 +# +# This file is managed automatically by MokoStandards bulk sync. +# Do not edit manually β€” changes will be overwritten on the next sync. +# To update governance settings, open a PR in MokoStandards instead: +# https://github.com/mokoconsulting-tech/MokoStandards + +standards_source: "https://github.com/mokoconsulting-tech/MokoStandards" +standards_version: "04.00.04" +platform: "waas-component" +governed_repo: "mokoconsulting-tech/MokoCassiopeia" diff --git a/LICENSE b/LICENSE index d1ee536..331197a 100644 --- a/LICENSE +++ b/LICENSE @@ -6,17 +6,18 @@ 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. + 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 (./LICENSE). - # FILE INFORMATION - DEFGROUP: - INGROUP: Project.Documentation - REPO: - VERSION: 03.05.00 - PATH: ./LICENSE - BRIEF: Project license (GPL-3.0-or-later) + # FILE INFORMATION + DEFGROUP: MokoStandards + INGROUP: MokoStandards.Documentation + REPO: https://github.com/mokoconsulting-tech/MokoStandards/ + VERSION: 05.00.00 + PATH: ./LICENSE + BRIEF: Project license (GPL-3.0-or-later) + NOTE: Exact text fetched from gnu.org --> GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 @@ -660,7 +661,7 @@ the "copyright" line and a pointer to where the full notice is found. (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 + 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. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ec94cc9 --- /dev/null +++ b/Makefile @@ -0,0 +1,333 @@ +# Makefile for Joomla Extensions +# Copyright (C) 2026 Moko Consulting +# SPDX-License-Identifier: GPL-3.0-or-later +# +# This is a reference Makefile for building Joomla extensions. +# Copy this to your repository root as "Makefile" and customize as needed. +# +# Supports: Modules, Plugins, Components, Packages, Templates + +# ============================================================================== +# CONFIGURATION - Customize these for your extension +# ============================================================================== + +# Extension Configuration +EXTENSION_NAME := mokoexample +EXTENSION_TYPE := module +# Options: module, plugin, component, package, template +EXTENSION_VERSION := 1.0.0 + +# Module Configuration (for modules only) +MODULE_TYPE := site +# Options: site, admin + +# Plugin Configuration (for plugins only) +PLUGIN_GROUP := system +# Options: system, content, user, authentication, etc. + +# Directories +SRC_DIR := . +BUILD_DIR := build +DIST_DIR := dist +DOCS_DIR := docs + +# Joomla Installation (for local testing - customize paths) +JOOMLA_ROOT := /var/www/html/joomla +JOOMLA_VERSION := 4 + +# Tools +PHP := php +COMPOSER := composer +NPM := npm +PHPCS := vendor/bin/phpcs +PHPCBF := vendor/bin/phpcbf +PHPUNIT := vendor/bin/phpunit +ZIP := zip + +# Coding Standards +PHPCS_STANDARD := Joomla + +# Colors for output +COLOR_RESET := \033[0m +COLOR_GREEN := \033[32m +COLOR_YELLOW := \033[33m +COLOR_BLUE := \033[34m +COLOR_RED := \033[31m + +# ============================================================================== +# TARGETS +# ============================================================================== + +.PHONY: help +help: ## Show this help message + @echo "$(COLOR_BLUE)╔════════════════════════════════════════════════════════════╗$(COLOR_RESET)" + @echo "$(COLOR_BLUE)β•‘ Joomla Extension Makefile β•‘$(COLOR_RESET)" + @echo "$(COLOR_BLUE)β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•$(COLOR_RESET)" + @echo "" + @echo "Extension: $(EXTENSION_NAME) ($(EXTENSION_TYPE)) v$(EXTENSION_VERSION)" + @echo "" + @echo "$(COLOR_GREEN)Available targets:$(COLOR_RESET)" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf " $(COLOR_BLUE)%-20s$(COLOR_RESET) %s\n", $$1, $$2}' + @echo "" + @echo "$(COLOR_YELLOW)Quick Start:$(COLOR_RESET)" + @echo " 1. make install-deps # Install dependencies" + @echo " 2. make build # Build extension package" + @echo " 3. make test # Run tests" + @echo "" + +.PHONY: install-deps +install-deps: ## Install all dependencies (Composer + npm) + @echo "$(COLOR_BLUE)Installing dependencies...$(COLOR_RESET)" + @if [ -f "composer.json" ]; then \ + $(COMPOSER) install; \ + echo "$(COLOR_GREEN)βœ“ Composer dependencies installed$(COLOR_RESET)"; \ + fi + @if [ -f "package.json" ]; then \ + $(NPM) install; \ + echo "$(COLOR_GREEN)βœ“ npm dependencies installed$(COLOR_RESET)"; \ + fi + +.PHONY: update-deps +update-deps: ## Update all dependencies + @echo "$(COLOR_BLUE)Updating dependencies...$(COLOR_RESET)" + @if [ -f "composer.json" ]; then \ + $(COMPOSER) update; \ + echo "$(COLOR_GREEN)βœ“ Composer dependencies updated$(COLOR_RESET)"; \ + fi + @if [ -f "package.json" ]; then \ + $(NPM) update; \ + echo "$(COLOR_GREEN)βœ“ npm dependencies updated$(COLOR_RESET)"; \ + fi + +.PHONY: lint +lint: ## Run PHP linter (syntax check) + @echo "$(COLOR_BLUE)Running PHP linter...$(COLOR_RESET)" + @find . -name "*.php" ! -path "./vendor/*" ! -path "./node_modules/*" ! -path "./$(BUILD_DIR)/*" \ + -exec $(PHP) -l {} \; | grep -v "No syntax errors" || true + @echo "$(COLOR_GREEN)βœ“ PHP linting complete$(COLOR_RESET)" + +.PHONY: phpcs +phpcs: ## Run PHP CodeSniffer (Joomla standards) + @echo "$(COLOR_BLUE)Running PHP CodeSniffer...$(COLOR_RESET)" + @if [ -f "$(PHPCS)" ]; then \ + $(PHPCS) --standard=$(PHPCS_STANDARD) --extensions=php --ignore=vendor,node_modules,$(BUILD_DIR) .; \ + else \ + echo "$(COLOR_YELLOW)⚠ PHP CodeSniffer not installed. Run: make install-deps$(COLOR_RESET)"; \ + fi + +.PHONY: phpcbf +phpcbf: ## Fix coding standards automatically + @echo "$(COLOR_BLUE)Running PHP Code Beautifier...$(COLOR_RESET)" + @if [ -f "$(PHPCBF)" ]; then \ + $(PHPCBF) --standard=$(PHPCS_STANDARD) --extensions=php --ignore=vendor,node_modules,$(BUILD_DIR) .; \ + echo "$(COLOR_GREEN)βœ“ Code formatting applied$(COLOR_RESET)"; \ + else \ + echo "$(COLOR_YELLOW)⚠ PHP Code Beautifier not installed. Run: make install-deps$(COLOR_RESET)"; \ + fi + +.PHONY: validate +validate: lint phpcs ## Run all validation checks + @echo "$(COLOR_GREEN)βœ“ All validation checks passed$(COLOR_RESET)" + +.PHONY: test +test: ## Run PHPUnit tests + @echo "$(COLOR_BLUE)Running tests...$(COLOR_RESET)" + @if [ -f "$(PHPUNIT)" ] && [ -f "phpunit.xml" ]; then \ + $(PHPUNIT); \ + else \ + echo "$(COLOR_YELLOW)⚠ PHPUnit not configured$(COLOR_RESET)"; \ + fi + +.PHONY: test-coverage +test-coverage: ## Run tests with coverage report + @echo "$(COLOR_BLUE)Running tests with coverage...$(COLOR_RESET)" + @if [ -f "$(PHPUNIT)" ] && [ -f "phpunit.xml" ]; then \ + $(PHPUNIT) --coverage-html $(BUILD_DIR)/coverage; \ + echo "$(COLOR_GREEN)βœ“ Coverage report: $(BUILD_DIR)/coverage/index.html$(COLOR_RESET)"; \ + else \ + echo "$(COLOR_YELLOW)⚠ PHPUnit not configured$(COLOR_RESET)"; \ + fi + +.PHONY: clean +clean: ## Clean build artifacts + @echo "$(COLOR_BLUE)Cleaning build artifacts...$(COLOR_RESET)" + @rm -rf $(BUILD_DIR) $(DIST_DIR) + @echo "$(COLOR_GREEN)βœ“ Build artifacts cleaned$(COLOR_RESET)" + +.PHONY: build +build: clean validate ## Build extension package + @echo "$(COLOR_BLUE)Building Joomla extension package...$(COLOR_RESET)" + @mkdir -p $(DIST_DIR) $(BUILD_DIR) + + # Determine package prefix based on extension type + @case "$(EXTENSION_TYPE)" in \ + module) \ + PACKAGE_PREFIX="mod_$(EXTENSION_NAME)"; \ + BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \ + ;; \ + plugin) \ + PACKAGE_PREFIX="plg_$(PLUGIN_GROUP)_$(EXTENSION_NAME)"; \ + BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \ + ;; \ + component) \ + PACKAGE_PREFIX="com_$(EXTENSION_NAME)"; \ + BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \ + ;; \ + package) \ + PACKAGE_PREFIX="pkg_$(EXTENSION_NAME)"; \ + BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \ + ;; \ + template) \ + PACKAGE_PREFIX="tpl_$(EXTENSION_NAME)"; \ + BUILD_TARGET="$(BUILD_DIR)/$$PACKAGE_PREFIX"; \ + ;; \ + *) \ + echo "$(COLOR_RED)βœ— Unknown extension type: $(EXTENSION_TYPE)$(COLOR_RESET)"; \ + exit 1; \ + ;; \ + esac; \ + \ + mkdir -p "$$BUILD_TARGET"; \ + \ + echo "Building $$PACKAGE_PREFIX..."; \ + \ + rsync -av --progress \ + --exclude='$(BUILD_DIR)' \ + --exclude='$(DIST_DIR)' \ + --exclude='.git*' \ + --exclude='vendor/' \ + --exclude='node_modules/' \ + --exclude='tests/' \ + --exclude='Makefile' \ + --exclude='composer.json' \ + --exclude='composer.lock' \ + --exclude='package.json' \ + --exclude='package-lock.json' \ + --exclude='phpunit.xml' \ + --exclude='*.md' \ + --exclude='.editorconfig' \ + . "$$BUILD_TARGET/"; \ + \ + cd $(BUILD_DIR) && $(ZIP) -r "../$(DIST_DIR)/$${PACKAGE_PREFIX}-$(EXTENSION_VERSION).zip" "$${PACKAGE_PREFIX}"; \ + \ + echo "$(COLOR_GREEN)βœ“ Package created: $(DIST_DIR)/$${PACKAGE_PREFIX}-$(EXTENSION_VERSION).zip$(COLOR_RESET)" + +.PHONY: package +package: build ## Alias for build + @echo "$(COLOR_GREEN)βœ“ Package ready for distribution$(COLOR_RESET)" + +.PHONY: install-local +install-local: build ## Install to local Joomla (upload via admin) + @echo "$(COLOR_BLUE)Package ready for installation$(COLOR_RESET)" + @case "$(EXTENSION_TYPE)" in \ + module) PACKAGE="mod_$(EXTENSION_NAME)";; \ + plugin) PACKAGE="plg_$(PLUGIN_GROUP)_$(EXTENSION_NAME)";; \ + component) PACKAGE="com_$(EXTENSION_NAME)";; \ + package) PACKAGE="pkg_$(EXTENSION_NAME)";; \ + template) PACKAGE="tpl_$(EXTENSION_NAME)";; \ + esac; \ + echo "$(COLOR_YELLOW)Upload $(DIST_DIR)/$${PACKAGE}-$(EXTENSION_VERSION).zip via Joomla Administrator$(COLOR_RESET)"; \ + echo "Admin URL: $(JOOMLA_ROOT) β†’ Extensions β†’ Install" + +.PHONY: dev-install +dev-install: ## Create symlink for development (Joomla 4+) + @echo "$(COLOR_BLUE)Creating development symlink...$(COLOR_RESET)" + @if [ ! -d "$(JOOMLA_ROOT)" ]; then \ + echo "$(COLOR_RED)βœ— Joomla root not found at $(JOOMLA_ROOT)$(COLOR_RESET)"; \ + echo "Update JOOMLA_ROOT in Makefile"; \ + exit 1; \ + fi + + @case "$(EXTENSION_TYPE)" in \ + module) \ + if [ "$(MODULE_TYPE)" = "admin" ]; then \ + TARGET="$(JOOMLA_ROOT)/administrator/modules/mod_$(EXTENSION_NAME)"; \ + else \ + TARGET="$(JOOMLA_ROOT)/modules/mod_$(EXTENSION_NAME)"; \ + fi; \ + ;; \ + plugin) \ + TARGET="$(JOOMLA_ROOT)/plugins/$(PLUGIN_GROUP)/$(EXTENSION_NAME)"; \ + ;; \ + component) \ + echo "$(COLOR_YELLOW)⚠ Components require complex symlink setup$(COLOR_RESET)"; \ + echo "Manual setup recommended for component development"; \ + exit 1; \ + ;; \ + *) \ + echo "$(COLOR_RED)βœ— dev-install not supported for $(EXTENSION_TYPE)$(COLOR_RESET)"; \ + exit 1; \ + ;; \ + esac; \ + \ + rm -rf "$$TARGET"; \ + ln -s "$(PWD)" "$$TARGET"; \ + echo "$(COLOR_GREEN)βœ“ Development symlink created at $$TARGET$(COLOR_RESET)" + +.PHONY: watch +watch: ## Watch for changes and rebuild + @echo "$(COLOR_BLUE)Watching for changes...$(COLOR_RESET)" + @echo "$(COLOR_YELLOW)Press Ctrl+C to stop$(COLOR_RESET)" + @while true; do \ + inotifywait -r -e modify,create,delete --exclude '($(BUILD_DIR)|$(DIST_DIR)|vendor|node_modules)' . 2>/dev/null || \ + (echo "$(COLOR_YELLOW)⚠ inotifywait not installed. Install: apt-get install inotify-tools$(COLOR_RESET)" && sleep 5); \ + make build; \ + done + +.PHONY: version +version: ## Display version information + @echo "$(COLOR_BLUE)Extension Information:$(COLOR_RESET)" + @echo " Name: $(EXTENSION_NAME)" + @echo " Type: $(EXTENSION_TYPE)" + @echo " Version: $(EXTENSION_VERSION)" + @if [ "$(EXTENSION_TYPE)" = "module" ]; then \ + echo " Module: $(MODULE_TYPE)"; \ + fi + @if [ "$(EXTENSION_TYPE)" = "plugin" ]; then \ + echo " Group: $(PLUGIN_GROUP)"; \ + fi + +.PHONY: docs +docs: ## Generate documentation + @echo "$(COLOR_BLUE)Generating documentation...$(COLOR_RESET)" + @mkdir -p $(DOCS_DIR) + @echo "$(COLOR_YELLOW)⚠ Documentation generation not configured$(COLOR_RESET)" + @echo "Consider adding phpDocumentor or similar" + +.PHONY: release +release: validate test build ## Create a release (validate + test + build) + @echo "$(COLOR_GREEN)βœ“ Release package ready$(COLOR_RESET)" + @echo "" + @echo "$(COLOR_BLUE)Release Checklist:$(COLOR_RESET)" + @echo " [ ] Update CHANGELOG.md" + @echo " [ ] Update version in XML manifest" + @echo " [ ] Test installation in clean Joomla" + @echo " [ ] Tag release in git: git tag v$(EXTENSION_VERSION)" + @echo " [ ] Push tags: git push --tags" + @echo " [ ] Create GitHub release" + @echo "" + @case "$(EXTENSION_TYPE)" in \ + module) PACKAGE="mod_$(EXTENSION_NAME)";; \ + plugin) PACKAGE="plg_$(PLUGIN_GROUP)_$(EXTENSION_NAME)";; \ + component) PACKAGE="com_$(EXTENSION_NAME)";; \ + package) PACKAGE="pkg_$(EXTENSION_NAME)";; \ + template) PACKAGE="tpl_$(EXTENSION_NAME)";; \ + esac; \ + echo "$(COLOR_GREEN)Package: $(DIST_DIR)/$${PACKAGE}-$(EXTENSION_VERSION).zip$(COLOR_RESET)" + +.PHONY: security-check +security-check: ## Run security checks on dependencies + @echo "$(COLOR_BLUE)Running security checks...$(COLOR_RESET)" + @if [ -f "composer.json" ]; then \ + $(COMPOSER) audit || echo "$(COLOR_YELLOW)⚠ Vulnerabilities found$(COLOR_RESET)"; \ + fi + @if [ -f "package.json" ]; then \ + $(NPM) audit || echo "$(COLOR_YELLOW)⚠ Vulnerabilities found$(COLOR_RESET)"; \ + fi + +.PHONY: all +all: install-deps validate test build ## Run complete build pipeline + @echo "$(COLOR_GREEN)βœ“ Complete build pipeline finished$(COLOR_RESET)" + +# Default target +.DEFAULT_GOAL := help diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..b60f555 --- /dev/null +++ b/composer.json @@ -0,0 +1,55 @@ +{ + "name": "mokoconsulting-tech/mokocassiopeia", + "description": "MokoCassiopeia Joomla component by Moko Consulting", + "type": "joomla-component", + "version": "01.00.00", + "license": "GPL-3.0-or-later", + "authors": [ + { + "name": "Moko Consulting", + "email": "hello@mokoconsulting.tech" + } + ], + "minimum-stability": "stable", + "prefer-stable": true, + "require": { + "php": ">=8.1", + "mokoconsulting-tech/enterprise": "^4.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5", + "phpstan/phpstan": "^2.0", + "squizlabs/php_codesniffer": "^4.0" + }, + "autoload": { + "psr-4": { + "MokoConsulting\\MokoCassiopeia\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "MokoConsulting\\MokoCassiopeia\\Tests\\": "tests/" + } + }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/mokoconsulting-tech/MokoStandards" + } + ], + "config": { + "sort-packages": true, + "optimize-autoloader": true, + "preferred-install": "dist", + "allow-plugins": { + "composer/installers": true + } + }, + "scripts": { + "validate-manifest": "vendor/bin/validate-manifest --path .", + "build": "vendor/bin/build-package --path .", + "test": "phpunit", + "phpcs": "phpcs --standard=vendor/mokoconsulting-tech/enterprise/phpcs.xml src/", + "phpstan": "phpstan analyse -c phpstan.neon src/" + } +} diff --git a/phpstan.neon b/phpstan.neon index 6cdac37..3d4adca 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -1,37 +1,32 @@ +# Copyright (C) 2026 Moko Consulting +# SPDX-License-Identifier: GPL-3.0-or-later +# +# PHPStan configuration for Joomla extension repositories. +# Extends the base MokoStandards config and adds Joomla framework class stubs +# so PHPStan can resolve Factory, CMSApplication, User, Table, etc. +# without requiring a full Joomla installation. + parameters: level: 5 + paths: - src - - # Exclude paths + excludePaths: - - src/vendor/* - - src/node_modules/* - - src/cache/* - - src/tmp/* - - # Scan files - scanFiles: - - src/templates/index.php - - src/templates/component.php - - src/templates/error.php - - src/templates/offline.php - - # Report unmatched ignored errors - reportUnmatchedIgnoredErrors: false - - # Check function name case - checkFunctionNameCase: true - - # Check internal classes - checkInternalClassCaseSensitivity: true - - # Treat PHP version - phpVersion: 80000 - - # Ignore errors - adjust as needed + - vendor + - node_modules + + # Joomla framework stubs β€” resolved via the enterprise package from vendor/ + stubFiles: + - vendor/mokoconsulting-tech/enterprise/templates/stubs/joomla.php + + # Suppress errors that are structural in Joomla's service-container architecture ignoreErrors: - # Allow dynamic properties which are common in Joomla - - '#Access to an undefined property#' - # Allow some reflection usage - - '#Call to an undefined static method#' + # Joomla's service-based dependency injection returns mixed from getApplication() + - '#Cannot call method .+ on Joomla\\CMS\\Application\\CMSApplication\|null#' + # Factory::getX() patterns are safe at runtime even when nullable in stubs + - '#Call to static method [a-zA-Z]+\(\) on an interface#' + + reportUnmatchedIgnoredErrors: false + checkMissingIterableValueType: false + checkGenericClassInNonGenericObjectType: false diff --git a/update.xml b/update.xml new file mode 100644 index 0000000..6c4bb15 --- /dev/null +++ b/update.xml @@ -0,0 +1,34 @@ + + + + {{EXTENSION_NAME}} + MokoCassiopeia β€” Moko Consulting Joomla extension + {{EXTENSION_ELEMENT}} + {{EXTENSION_TYPE}} + {{VERSION}} + https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/tag/{{VERSION}} + + {{DOWNLOAD_URL}} + + + 7.4 + Moko Consulting + {{MAINTAINER_URL}} + + \ No newline at end of file