chore: Sync MokoStandards v04.04 #110
1
.github/.mokostandards
vendored
Normal file
1
.github/.mokostandards
vendored
Normal file
@@ -0,0 +1 @@
|
||||
platform: waas-component
|
||||
376
.github/CLAUDE.md
vendored
376
.github/CLAUDE.md
vendored
@@ -1,3 +1,22 @@
|
||||
<!--
|
||||
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
|
||||
This file is part of a Moko Consulting project.
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# FILE INFORMATION
|
||||
DEFGROUP: MokoStandards.Templates.GitHub
|
||||
INGROUP: MokoStandards.Templates
|
||||
REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
PATH: /templates/github/copilot-instructions.joomla.md.template
|
||||
VERSION: XX.YY.ZZ
|
||||
BRIEF: GitHub Copilot custom instructions template for Joomla/MokoWaaS governed repositories
|
||||
NOTE: Synced to .github/copilot-instructions.md in all Joomla/WaaS repos via bulk sync.
|
||||
Tokens replaced at sync time: MokoCassiopeia, https://github.com/mokoconsulting-tech/MokoCassiopeia, {{EXTENSION_NAME}},
|
||||
{{EXTENSION_TYPE}}, {{EXTENSION_ELEMENT}}
|
||||
-->
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **🔧 AI Self-Update Required on First Use**
|
||||
>
|
||||
@@ -19,135 +38,34 @@
|
||||
> |---|---|
|
||||
> | `MokoCassiopeia` | The GitHub repository name (visible in the URL, `README.md` heading, or `git remote -v`) |
|
||||
> | `https://github.com/mokoconsulting-tech/MokoCassiopeia` | Full GitHub URL, e.g. `https://github.com/mokoconsulting-tech/<repo-name>` |
|
||||
> | `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 `<name>` element in `manifest.xml` at the repository root |
|
||||
> | `{{EXTENSION_TYPE}}` | The `type` attribute of the `<extension>` tag in `manifest.xml` (`component`, `module`, `plugin`, or `template`) |
|
||||
> | `{{EXTENSION_ELEMENT}}` | The `<element>` tag in `manifest.xml`, or the filename prefix (e.g. `com_myextension`, `mod_mymodule`) |
|
||||
>
|
||||
> ---
|
||||
|
||||
# What This Repo Is
|
||||
# MokoCassiopeia — GitHub Copilot Custom Instructions
|
||||
|
||||
**MokoCassiopeia** is a Moko Consulting **MokoWaaS** (Joomla) extension repository.
|
||||
## What This Repo Is
|
||||
|
||||
A modern enhancement layer for Joomla’s Cassiopeia template. Moko-Cassiopeia adds Font Awesome 7, Bootstrap 5 helpers, an automatic Table of Contents (TOC) utility, and optional expansions including Google Tag Manager and Google Analytics (GA4) hooks.
|
||||
This is a **Moko Consulting MokoWaaS** (Joomla) repository governed by [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards). All coding standards, workflows, and policies are defined there and enforced here via bulk sync.
|
||||
|
||||
Repository URL: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
||||
Extension name: **{{EXTENSION_NAME}}**
|
||||
Extension type: **{{EXTENSION_TYPE}}** (`{{EXTENSION_ELEMENT}}`)
|
||||
Repository URL: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
||||
|
||||
This repository is governed by [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards) — the single source of truth for coding standards, file-header policies, GitHub Actions workflows, and Terraform configuration templates across all Moko Consulting repositories.
|
||||
Platform: **Joomla 4.x / MokoWaaS**
|
||||
|
||||
---
|
||||
|
||||
# Repo Structure
|
||||
## Primary Language
|
||||
|
||||
```
|
||||
MokoCassiopeia/
|
||||
├── manifest.xml # Joomla installer manifest (root — required)
|
||||
├── update.xml # Update server manifest (root — required)
|
||||
├── site/ # Frontend (site) code
|
||||
│ ├── controller.php
|
||||
│ ├── controllers/
|
||||
│ ├── models/
|
||||
│ └── views/
|
||||
├── admin/ # Backend (admin) code
|
||||
│ ├── controller.php
|
||||
│ ├── controllers/
|
||||
│ ├── models/
|
||||
│ ├── views/
|
||||
│ └── sql/
|
||||
├── language/ # Language INI files
|
||||
├── media/ # CSS, JS, images
|
||||
├── docs/ # Technical documentation
|
||||
├── tests/ # Test suite
|
||||
├── .github/
|
||||
│ ├── workflows/ # CI/CD workflows (synced from MokoStandards)
|
||||
│ ├── copilot-instructions.md
|
||||
│ └── CLAUDE.md # This file
|
||||
├── README.md # Version source of truth
|
||||
├── CHANGELOG.md
|
||||
├── CONTRIBUTING.md
|
||||
└── LICENSE # GPL-3.0-or-later
|
||||
```
|
||||
**PHP** (≥ 7.4) is the primary language for this Joomla extension. JavaScript may be used for frontend enhancements. YAML uses 2-space indentation. All other text files use tabs per `.editorconfig`.
|
||||
|
||||
---
|
||||
|
||||
# Primary Language
|
||||
## File Header — Always Required on New Files
|
||||
|
||||
**PHP** (≥ 7.4) is the primary language for this Joomla extension. YAML uses 2-space indentation. All other text files use tabs per `.editorconfig`.
|
||||
|
||||
---
|
||||
|
||||
# Version Management
|
||||
|
||||
**`README.md` is the single source of truth for the repository version.**
|
||||
|
||||
- **Bump the patch version on every PR** — increment `XX.YY.ZZ` (e.g. `01.02.03` → `01.02.04`) in `README.md` before opening the PR; the `sync-version-on-merge` workflow propagates it to all `FILE INFORMATION` headers automatically on merge.
|
||||
- Version format is zero-padded semver: `XX.YY.ZZ` (e.g. `01.02.03`).
|
||||
- Never hardcode a version number in body text — use the badge or FILE INFORMATION header only.
|
||||
|
||||
### Joomla Version Alignment
|
||||
|
||||
Three files must **always have the same version**:
|
||||
|
||||
| File | Where the version lives |
|
||||
|------|------------------------|
|
||||
| `README.md` | `FILE INFORMATION` block + badge |
|
||||
| `manifest.xml` | `<version>` tag |
|
||||
| `update.xml` | `<version>` in the most recent `<update>` 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
|
||||
<!-- In manifest.xml -->
|
||||
<updateservers>
|
||||
<server type="extension" priority="1" name="{{EXTENSION_NAME}}">
|
||||
https://github.com/mokoconsulting-tech/MokoCassiopeia/raw/main/update.xml
|
||||
</server>
|
||||
</updateservers>
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
- Every release prepends a new `<update>` block at the top — older entries are preserved.
|
||||
- `<version>` in `update.xml` must exactly match `<version>` in `manifest.xml` and `README.md`.
|
||||
- `<downloadurl>` must be a publicly accessible GitHub Releases asset URL.
|
||||
- `<targetplatform version="4\.[0-9]+">` — backslash is literal (Joomla regex syntax).
|
||||
|
||||
Example `update.xml` entry for a new release:
|
||||
```xml
|
||||
<updates>
|
||||
<update>
|
||||
<name>{{EXTENSION_NAME}}</name>
|
||||
<description>MokoCassiopeia</description>
|
||||
<element>{{EXTENSION_ELEMENT}}</element>
|
||||
<type>{{EXTENSION_TYPE}}</type>
|
||||
<version>01.02.04</version>
|
||||
<infourl title="Release Information">https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/tag/01.02.04</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">
|
||||
https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/01.02.04/{{EXTENSION_ELEMENT}}-01.02.04.zip
|
||||
</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="4\.[0-9]+" />
|
||||
<php_minimum>7.4</php_minimum>
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
</update>
|
||||
</updates>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# File Header Requirements
|
||||
|
||||
Every new file **must** have a copyright header as its first content. JSON files, binary files, generated files, and third-party files are exempt.
|
||||
Every new file needs a copyright header as its first content.
|
||||
|
||||
**PHP:**
|
||||
```php
|
||||
@@ -162,47 +80,141 @@ Every new file **must** have a copyright header as its first content. JSON files
|
||||
* DEFGROUP: MokoCassiopeia.{{EXTENSION_TYPE}}
|
||||
* INGROUP: MokoCassiopeia
|
||||
* REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
||||
* PATH: /site/controllers/item.php
|
||||
* PATH: /path/to/file.php
|
||||
* VERSION: XX.YY.ZZ
|
||||
* BRIEF: One-line description of file purpose
|
||||
* BRIEF: One-line description of purpose
|
||||
*/
|
||||
|
||||
defined('_JEXEC') or die;
|
||||
```
|
||||
|
||||
**Markdown / YAML / Shell / XML:** Use the appropriate comment syntax with the same fields.
|
||||
**Markdown:**
|
||||
```markdown
|
||||
<!--
|
||||
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
|
||||
This file is part of a Moko Consulting project.
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# FILE INFORMATION
|
||||
DEFGROUP: MokoCassiopeia.Documentation
|
||||
INGROUP: MokoCassiopeia
|
||||
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
||||
PATH: /docs/file.md
|
||||
VERSION: XX.YY.ZZ
|
||||
BRIEF: One-line description
|
||||
-->
|
||||
```
|
||||
|
||||
**YAML / Shell / XML:** Use the appropriate comment syntax with the same fields. JSON files are exempt.
|
||||
|
||||
---
|
||||
|
||||
# Coding Standards
|
||||
## Version Management
|
||||
|
||||
## Naming Conventions
|
||||
**`README.md` is the single source of truth for the repository version.**
|
||||
|
||||
| Context | Convention | Example |
|
||||
|---------|-----------|---------|
|
||||
| PHP class | `PascalCase` | `ItemModel` |
|
||||
| PHP method / function | `camelCase` | `getItems()` |
|
||||
| PHP variable | `$snake_case` | `$item_id` |
|
||||
| PHP constant | `UPPER_SNAKE_CASE` | `MAX_ITEMS` |
|
||||
| PHP class file | `PascalCase.php` | `ItemModel.php` |
|
||||
| YAML workflow | `kebab-case.yml` | `ci-joomla.yml` |
|
||||
| Markdown doc | `kebab-case.md` | `installation-guide.md` |
|
||||
- **Bump the patch version on every PR** — increment `XX.YY.ZZ` (e.g. `01.02.03` → `01.02.04`) in `README.md` before opening the PR; the `sync-version-on-merge` workflow propagates it automatically to all badges and `FILE INFORMATION` headers on merge to `main`.
|
||||
- The `VERSION: XX.YY.ZZ` field in `README.md` governs all other version references.
|
||||
- Version format is zero-padded semver: `XX.YY.ZZ` (e.g. `01.02.03`).
|
||||
- Never hardcode a specific version in document body text — use the badge or FILE INFORMATION header only.
|
||||
|
||||
## Commit Messages
|
||||
### Joomla Version Alignment
|
||||
|
||||
Format: `<type>(<scope>): <subject>` — imperative, lower-case subject, no trailing period.
|
||||
The version in `README.md` **must always match** the `<version>` tag in `manifest.xml` and the latest entry in `updates.xml`. The `make release` command / release workflow updates all three automatically.
|
||||
|
||||
Valid types: `feat` · `fix` · `docs` · `chore` · `ci` · `refactor` · `style` · `test` · `perf` · `revert` · `build`
|
||||
```xml
|
||||
<!-- In manifest.xml — must match README.md version -->
|
||||
<version>01.02.04</version>
|
||||
|
||||
## Branch Naming
|
||||
|
||||
Format: `<prefix>/<MAJOR.MINOR.PATCH>[/description]`
|
||||
|
||||
Approved prefixes: `dev/` · `rc/` · `version/` · `patch/` · `copilot/` · `dependabot/`
|
||||
<!-- In updates.xml — prepend a new <update> block for every release.
|
||||
Note: the backslash in version="4\.[0-9]+" is a literal backslash character
|
||||
in the XML attribute value. Joomla's update server treats the value as a
|
||||
regular expression, so \. matches a literal dot. -->
|
||||
<updates>
|
||||
<update>
|
||||
<name>{{EXTENSION_NAME}}</name>
|
||||
<version>01.02.04</version>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">
|
||||
https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/01.02.04/{{EXTENSION_ELEMENT}}-01.02.04.zip
|
||||
</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="4\.[0-9]+" />
|
||||
</update>
|
||||
<!-- … older entries preserved below … -->
|
||||
</updates>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# GitHub Actions — Token Usage
|
||||
## Joomla Extension Structure
|
||||
|
||||
```
|
||||
MokoCassiopeia/
|
||||
├── manifest.xml # Joomla installer manifest (root — required)
|
||||
├── updates.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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## updates.xml — Required in Repo Root
|
||||
|
||||
`updates.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
|
||||
<updateservers>
|
||||
<server type="extension" priority="1" name="{{EXTENSION_NAME}}">
|
||||
https://github.com/mokoconsulting-tech/MokoCassiopeia/raw/main/updates.xml
|
||||
</server>
|
||||
</updateservers>
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
- Every release must prepend a new `<update>` block at the top of `updates.xml` — old entries must be preserved below.
|
||||
- The `<version>` in `updates.xml` must exactly match `<version>` in `manifest.xml` and the version in `README.md`.
|
||||
- The `<downloadurl>` must be a publicly accessible direct download link (GitHub Releases asset URL).
|
||||
- `<targetplatform name="joomla" version="4\.[0-9]+">` — 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/`).
|
||||
- `<version>` tag must be kept in sync with `README.md` version and `updates.xml`.
|
||||
- Must include `<updateservers>` block pointing to this repo's `updates.xml`.
|
||||
- Must include `<files folder="site">` and `<administration>` sections.
|
||||
- Joomla 4.x requires `<namespace path="src">Moko\{{EXTENSION_NAME}}</namespace>` for namespaced extensions.
|
||||
|
||||
---
|
||||
|
||||
## GitHub Actions — Token Usage
|
||||
|
||||
Every workflow must use **`secrets.GH_TOKEN`** (the org-level Personal Access Token).
|
||||
|
||||
@@ -217,58 +229,76 @@ env:
|
||||
```
|
||||
|
||||
```yaml
|
||||
# ❌ Wrong — never use these
|
||||
# ❌ Wrong — never use these in workflows
|
||||
token: ${{ github.token }}
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
# Keeping Documentation Current
|
||||
## MokoStandards Reference
|
||||
|
||||
| Change type | Documentation to update |
|
||||
|-------------|------------------------|
|
||||
| New or renamed PHP class/method | PHPDoc block; `docs/api/` entry |
|
||||
| New or changed `manifest.xml` | Sync version to `update.xml` and `README.md` |
|
||||
| New release | Prepend `<update>` to `update.xml`; update `CHANGELOG.md`; bump `README.md` |
|
||||
| New or changed workflow | `docs/workflows/<workflow-name>.md` |
|
||||
| Any modified file | Update the `VERSION` field in that file's `FILE INFORMATION` block |
|
||||
| **Every PR** | **Bump the patch version** — increment `XX.YY.ZZ` in `README.md`; `sync-version-on-merge` propagates it |
|
||||
|
||||
---
|
||||
|
||||
# What NOT to Do
|
||||
|
||||
- **Never commit directly to `main`** — all changes go through a PR.
|
||||
- **Never hardcode version numbers** in body text — update `README.md` and let automation propagate.
|
||||
- **Never let `manifest.xml`, `update.xml`, and `README.md` versions diverge.**
|
||||
- **Never skip the FILE INFORMATION block** on a new source file.
|
||||
- **Never use bare `catch (\Throwable $e) {}`** — always log or re-throw.
|
||||
- **Never mix tabs and spaces** within a file — follow `.editorconfig`.
|
||||
- **Never use `github.token` or `secrets.GITHUB_TOKEN` in workflows** — always use `secrets.GH_TOKEN`.
|
||||
- **Never remove `defined('_JEXEC') or die;`** from web-accessible PHP files.
|
||||
|
||||
---
|
||||
|
||||
# PR Checklist
|
||||
|
||||
Before opening a PR, verify:
|
||||
|
||||
- [ ] Patch version bumped in `README.md` (e.g. `01.02.03` → `01.02.04`)
|
||||
- [ ] If this is a release: `manifest.xml` version updated; `update.xml` updated with new entry
|
||||
- [ ] FILE INFORMATION headers updated in modified files
|
||||
- [ ] CHANGELOG.md updated
|
||||
- [ ] Tests pass
|
||||
|
||||
---
|
||||
|
||||
# Key Policy Documents (MokoStandards)
|
||||
This repository is governed by [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards). Authoritative policies:
|
||||
|
||||
| Document | Purpose |
|
||||
|----------|---------|
|
||||
| [file-header-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/file-header-standards.md) | Copyright-header rules for every file type |
|
||||
| [coding-style-guide.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/coding-style-guide.md) | Naming and formatting conventions |
|
||||
| [branching-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/branching-strategy.md) | Branch naming, hierarchy, and release workflow |
|
||||
| [merge-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/merge-strategy.md) | Squash-merge policy and PR conventions |
|
||||
| [merge-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/merge-strategy.md) | Squash-merge policy and PR title/body conventions |
|
||||
| [changelog-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/changelog-standards.md) | How and when to update CHANGELOG.md |
|
||||
| [joomla-development-guide.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/guide/waas/joomla-development-guide.md) | MokoWaaS Joomla extension development guide |
|
||||
|
||||
---
|
||||
|
||||
## 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: `<type>(<scope>): <subject>` — imperative, lower-case subject, no trailing period.
|
||||
|
||||
Valid types: `feat` · `fix` · `docs` · `chore` · `ci` · `refactor` · `style` · `test` · `perf` · `revert` · `build`
|
||||
|
||||
---
|
||||
|
||||
## Branch Naming
|
||||
|
||||
Format: `<prefix>/<MAJOR.MINOR.PATCH>[/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 `updates.xml` version; bump README.md version |
|
||||
| New release | Prepend `<update>` block to `updates.xml`; update CHANGELOG.md; bump README.md version |
|
||||
| New or changed workflow | `docs/workflows/<workflow-name>.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, `updates.xml` version, and `README.md` version go out of sync
|
||||
|
||||
24
.github/CODEOWNERS
vendored
24
.github/CODEOWNERS
vendored
@@ -8,8 +8,26 @@
|
||||
# Combined with branch protection (require PR reviews), this prevents
|
||||
# unauthorized modifications to workflows, configs, and governance files.
|
||||
|
||||
# ── Workflows (synced from MokoStandards — must not be manually edited) ──
|
||||
/.github/workflows/ @jmiller-moko
|
||||
# ── Synced workflows (managed by MokoStandards — do not edit manually) ────
|
||||
/.github/workflows/deploy-dev.yml @jmiller-moko
|
||||
/.github/workflows/deploy-demo.yml @jmiller-moko
|
||||
/.github/workflows/deploy-rs.yml @jmiller-moko
|
||||
/.github/workflows/auto-release.yml @jmiller-moko
|
||||
/.github/workflows/auto-dev-issue.yml @jmiller-moko
|
||||
/.github/workflows/auto-assign.yml @jmiller-moko
|
||||
/.github/workflows/sync-version-on-merge.yml @jmiller-moko
|
||||
/.github/workflows/enterprise-firewall-setup.yml @jmiller-moko
|
||||
/.github/workflows/repository-cleanup.yml @jmiller-moko
|
||||
/.github/workflows/standards-compliance.yml @jmiller-moko
|
||||
/.github/workflows/codeql-analysis.yml @jmiller-moko
|
||||
/.github/workflows/repo_health.yml @jmiller-moko
|
||||
/.github/workflows/ci-joomla.yml @jmiller-moko
|
||||
/.github/workflows/update-server.yml @jmiller-moko
|
||||
/.github/workflows/deploy-manual.yml @jmiller-moko
|
||||
/.github/workflows/ci-dolibarr.yml @jmiller-moko
|
||||
/.github/workflows/publish-to-mokodolimods.yml @jmiller-moko
|
||||
/.github/workflows/changelog-validation.yml @jmiller-moko
|
||||
# Custom workflows in .github/workflows/ not listed above are repo-owned.
|
||||
|
||||
# ── GitHub configuration ─────────────────────────────────────────────────
|
||||
/.github/ISSUE_TEMPLATE/ @jmiller-moko
|
||||
@@ -23,7 +41,7 @@
|
||||
/composer.json @jmiller-moko
|
||||
/phpstan.neon @jmiller-moko
|
||||
/Makefile @jmiller-moko
|
||||
/.ftp_ignore @jmiller-moko
|
||||
/.ftpignore @jmiller-moko
|
||||
/.gitignore @jmiller-moko
|
||||
/.gitattributes @jmiller-moko
|
||||
/.editorconfig @jmiller-moko
|
||||
|
||||
45
.github/copilot-instructions.md
vendored
45
.github/copilot-instructions.md
vendored
@@ -1,3 +1,22 @@
|
||||
<!--
|
||||
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
|
||||
This file is part of a Moko Consulting project.
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# FILE INFORMATION
|
||||
DEFGROUP: MokoStandards.Templates.GitHub
|
||||
INGROUP: MokoStandards.Templates
|
||||
REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
PATH: /templates/github/copilot-instructions.joomla.md.template
|
||||
VERSION: XX.YY.ZZ
|
||||
BRIEF: GitHub Copilot custom instructions template for Joomla/MokoWaaS governed repositories
|
||||
NOTE: Synced to .github/copilot-instructions.md in all Joomla/WaaS repos via bulk sync.
|
||||
Tokens replaced at sync time: MokoCassiopeia, https://github.com/mokoconsulting-tech/MokoCassiopeia, {{EXTENSION_NAME}},
|
||||
{{EXTENSION_TYPE}}, {{EXTENSION_ELEMENT}}
|
||||
-->
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **🔧 AI Self-Update Required on First Use**
|
||||
>
|
||||
@@ -103,13 +122,13 @@ BRIEF: One-line description
|
||||
|
||||
### Joomla Version Alignment
|
||||
|
||||
The version in `README.md` **must always match** the `<version>` tag in `manifest.xml` and the latest entry in `update.xml`. The `make release` command / release workflow updates all three automatically.
|
||||
The version in `README.md` **must always match** the `<version>` tag in `manifest.xml` and the latest entry in `updates.xml`. The `make release` command / release workflow updates all three automatically.
|
||||
|
||||
```xml
|
||||
<!-- In manifest.xml — must match README.md version -->
|
||||
<version>01.02.04</version>
|
||||
|
||||
<!-- In update.xml — prepend a new <update> block for every release.
|
||||
<!-- In updates.xml — prepend a new <update> block for every release.
|
||||
Note: the backslash in version="4\.[0-9]+" is a literal backslash character
|
||||
in the XML attribute value. Joomla's update server treats the value as a
|
||||
regular expression, so \. matches a literal dot. -->
|
||||
@@ -135,7 +154,7 @@ The version in `README.md` **must always match** the `<version>` tag in `manifes
|
||||
```
|
||||
MokoCassiopeia/
|
||||
├── manifest.xml # Joomla installer manifest (root — required)
|
||||
├── update.xml # Update server manifest (root — required, see below)
|
||||
├── updates.xml # Update server manifest (root — required, see below)
|
||||
├── site/ # Frontend (site) code
|
||||
│ ├── controller.php
|
||||
│ ├── controllers/
|
||||
@@ -164,22 +183,22 @@ MokoCassiopeia/
|
||||
|
||||
---
|
||||
|
||||
## update.xml — Required in Repo Root
|
||||
## updates.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.
|
||||
`updates.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
|
||||
<updateservers>
|
||||
<server type="extension" priority="1" name="{{EXTENSION_NAME}}">
|
||||
https://github.com/mokoconsulting-tech/MokoCassiopeia/raw/main/update.xml
|
||||
https://github.com/mokoconsulting-tech/MokoCassiopeia/raw/main/updates.xml
|
||||
</server>
|
||||
</updateservers>
|
||||
```
|
||||
|
||||
**Rules:**
|
||||
- Every release must prepend a new `<update>` block at the top of `update.xml` — old entries must be preserved below.
|
||||
- The `<version>` in `update.xml` must exactly match `<version>` in `manifest.xml` and the version in `README.md`.
|
||||
- Every release must prepend a new `<update>` block at the top of `updates.xml` — old entries must be preserved below.
|
||||
- The `<version>` in `updates.xml` must exactly match `<version>` in `manifest.xml` and the version in `README.md`.
|
||||
- The `<downloadurl>` must be a publicly accessible direct download link (GitHub Releases asset URL).
|
||||
- `<targetplatform name="joomla" version="4\.[0-9]+">` — 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.
|
||||
|
||||
@@ -188,8 +207,8 @@ The `manifest.xml` must reference it via:
|
||||
## manifest.xml Rules
|
||||
|
||||
- Lives at the repo root as `manifest.xml` (not inside `site/` or `admin/`).
|
||||
- `<version>` tag must be kept in sync with `README.md` version and `update.xml`.
|
||||
- Must include `<updateservers>` block pointing to this repo's `update.xml`.
|
||||
- `<version>` tag must be kept in sync with `README.md` version and `updates.xml`.
|
||||
- Must include `<updateservers>` block pointing to this repo's `updates.xml`.
|
||||
- Must include `<files folder="site">` and `<administration>` sections.
|
||||
- Joomla 4.x requires `<namespace path="src">Moko\{{EXTENSION_NAME}}</namespace>` for namespaced extensions.
|
||||
|
||||
@@ -267,8 +286,8 @@ Approved prefixes: `dev/` · `rc/` · `version/` · `patch/` · `copilot/` · `d
|
||||
| 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 `<update>` block to `update.xml`; update CHANGELOG.md; bump README.md version |
|
||||
| New or changed manifest.xml | Update `updates.xml` version; bump README.md version |
|
||||
| New release | Prepend `<update>` block to `updates.xml`; update CHANGELOG.md; bump README.md version |
|
||||
| New or changed workflow | `docs/workflows/<workflow-name>.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 |
|
||||
@@ -282,4 +301,4 @@ Approved prefixes: `dev/` · `rc/` · `version/` · `patch/` · `copilot/` · `d
|
||||
- Never add `defined('_JEXEC') or die;` to CLI scripts or model tests — only to web-accessible PHP files
|
||||
- Never hardcode version numbers in body text — update `README.md` and let automation propagate
|
||||
- Never use `github.token` or `secrets.GITHUB_TOKEN` in workflows — always use `secrets.GH_TOKEN`
|
||||
- Never let `manifest.xml` version, `update.xml` version, and `README.md` version go out of sync
|
||||
- Never let `manifest.xml` version, `updates.xml` version, and `README.md` version go out of sync
|
||||
|
||||
2
.github/dependabot.yml
vendored
2
.github/dependabot.yml
vendored
@@ -5,7 +5,7 @@
|
||||
# INGROUP: MokoStandards.Security
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /.github/dependabot.yml
|
||||
# VERSION: 01.00.00
|
||||
# VERSION: 03.09.03
|
||||
# BRIEF: Dependabot configuration for automated dependency updates and security patches
|
||||
# NOTE: Monitors GitHub Actions for vulnerabilities and keeps ecosystem secure
|
||||
|
||||
|
||||
76
.github/workflows/auto-assign.yml
vendored
Normal file
76
.github/workflows/auto-assign.yml
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: GitHub.Workflow
|
||||
# INGROUP: MokoStandards.Workflows.Shared
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /.github/workflows/auto-assign.yml
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: Auto-assign jmiller-moko to unassigned issues and PRs every 15 minutes
|
||||
|
||||
name: Auto-Assign Issues & PRs
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened]
|
||||
pull_request_target:
|
||||
types: [opened]
|
||||
schedule:
|
||||
- cron: '0 */12 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
auto-assign:
|
||||
name: Assign unassigned issues and PRs
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Assign unassigned issues
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
run: |
|
||||
REPO="${{ github.repository }}"
|
||||
ASSIGNEE="jmiller-moko"
|
||||
|
||||
echo "## 🏷️ Auto-Assign Report" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
ASSIGNED_ISSUES=0
|
||||
ASSIGNED_PRS=0
|
||||
|
||||
# Assign unassigned open issues
|
||||
ISSUES=$(gh api "repos/$REPO/issues?state=open&per_page=100&assignee=none" --jq '.[].number' 2>/dev/null || true)
|
||||
for NUM in $ISSUES; do
|
||||
# Skip PRs (the issues endpoint returns PRs too)
|
||||
IS_PR=$(gh api "repos/$REPO/issues/$NUM" --jq '.pull_request // empty' 2>/dev/null || true)
|
||||
if [ -z "$IS_PR" ]; then
|
||||
gh api "repos/$REPO/issues/$NUM/assignees" -X POST -f "assignees[]=$ASSIGNEE" --silent 2>/dev/null && {
|
||||
ASSIGNED_ISSUES=$((ASSIGNED_ISSUES + 1))
|
||||
echo " Assigned issue #$NUM"
|
||||
} || true
|
||||
fi
|
||||
done
|
||||
|
||||
# Assign unassigned open PRs
|
||||
PRS=$(gh api "repos/$REPO/pulls?state=open&per_page=100" --jq '.[] | select(.assignees | length == 0) | .number' 2>/dev/null || true)
|
||||
for NUM in $PRS; do
|
||||
gh api "repos/$REPO/issues/$NUM/assignees" -X POST -f "assignees[]=$ASSIGNEE" --silent 2>/dev/null && {
|
||||
ASSIGNED_PRS=$((ASSIGNED_PRS + 1))
|
||||
echo " Assigned PR #$NUM"
|
||||
} || true
|
||||
done
|
||||
|
||||
echo "| Type | Assigned |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|------|----------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Issues | $ASSIGNED_ISSUES |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Pull Requests | $ASSIGNED_PRS |" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
if [ "$ASSIGNED_ISSUES" -eq 0 ] && [ "$ASSIGNED_PRS" -eq 0 ]; then
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "✅ All issues and PRs already have assignees" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
179
.github/workflows/auto-dev-issue.yml
vendored
179
.github/workflows/auto-dev-issue.yml
vendored
@@ -9,14 +9,22 @@
|
||||
# INGROUP: MokoStandards.Automation
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /templates/workflows/shared/auto-dev-issue.yml.template
|
||||
# VERSION: 04.04.01
|
||||
# BRIEF: Auto-create tracking issue when a dev/** or rc/** branch is pushed
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: Auto-create tracking issue with sub-issues for dev/rc branch workflow
|
||||
# NOTE: Synced via bulk-repo-sync to .github/workflows/auto-dev-issue.yml in all governed repos.
|
||||
|
||||
name: Auto Dev Branch Issue
|
||||
name: Dev/RC Branch Issue
|
||||
|
||||
on:
|
||||
# Auto-create on RC branch creation
|
||||
create:
|
||||
# Manual trigger for dev branches
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch:
|
||||
description: 'Branch name (e.g., dev/my-feature or dev/04.06)'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
@@ -30,15 +38,23 @@ jobs:
|
||||
name: Create version tracking issue
|
||||
runs-on: ubuntu-latest
|
||||
if: >-
|
||||
github.event.ref_type == 'branch' &&
|
||||
(startsWith(github.event.ref, 'dev/') || startsWith(github.event.ref, 'rc/'))
|
||||
(github.event_name == 'workflow_dispatch') ||
|
||||
(github.event.ref_type == 'branch' &&
|
||||
(startsWith(github.event.ref, 'rc/') ||
|
||||
startsWith(github.event.ref, 'alpha/') ||
|
||||
startsWith(github.event.ref, 'beta/')))
|
||||
|
||||
steps:
|
||||
- name: Create tracking issue
|
||||
- name: Create tracking issue and sub-issues
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
run: |
|
||||
# For manual dispatch, use input; for auto, use event ref
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
BRANCH="${{ inputs.branch }}"
|
||||
else
|
||||
BRANCH="${{ github.event.ref }}"
|
||||
fi
|
||||
REPO="${{ github.repository }}"
|
||||
ACTOR="${{ github.actor }}"
|
||||
NOW=$(date -u '+%Y-%m-%d %H:%M UTC')
|
||||
@@ -49,6 +65,16 @@ jobs:
|
||||
BRANCH_TYPE="Release Candidate"
|
||||
LABEL_TYPE="type: release"
|
||||
TITLE_PREFIX="rc"
|
||||
elif [[ "$BRANCH" == beta/* ]]; then
|
||||
VERSION="${BRANCH#beta/}"
|
||||
BRANCH_TYPE="Beta"
|
||||
LABEL_TYPE="type: release"
|
||||
TITLE_PREFIX="beta"
|
||||
elif [[ "$BRANCH" == alpha/* ]]; then
|
||||
VERSION="${BRANCH#alpha/}"
|
||||
BRANCH_TYPE="Alpha"
|
||||
LABEL_TYPE="type: release"
|
||||
TITLE_PREFIX="alpha"
|
||||
else
|
||||
VERSION="${BRANCH#dev/}"
|
||||
BRANCH_TYPE="Development"
|
||||
@@ -58,45 +84,124 @@ jobs:
|
||||
|
||||
TITLE="${TITLE_PREFIX}(${VERSION}): ${BRANCH_TYPE} tracking for ${BRANCH}"
|
||||
|
||||
BODY="## ${BRANCH_TYPE} Branch Created
|
||||
|
||||
| Field | Value |
|
||||
|-------|-------|
|
||||
| **Branch** | \`${BRANCH}\` |
|
||||
| **Version** | \`${VERSION}\` |
|
||||
| **Type** | ${BRANCH_TYPE} |
|
||||
| **Created by** | @${ACTOR} |
|
||||
| **Created at** | ${NOW} |
|
||||
| **Repository** | \`${REPO}\` |
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] Feature development complete
|
||||
- [ ] Tests passing
|
||||
- [ ] README.md version bumped to \`${VERSION}\`
|
||||
- [ ] CHANGELOG.md updated
|
||||
- [ ] PR created targeting \`main\`
|
||||
- [ ] Code reviewed and approved
|
||||
- [ ] Merged to \`main\`
|
||||
|
||||
---
|
||||
*Auto-created by [auto-dev-issue.yml](.github/workflows/auto-dev-issue.yml) on branch creation.*"
|
||||
|
||||
# Dedent heredoc
|
||||
BODY=$(echo "$BODY" | sed 's/^ //')
|
||||
|
||||
# Check for existing issue with same title prefix
|
||||
EXISTING=$(gh api "repos/${REPO}/issues?state=open&per_page=5" \
|
||||
EXISTING=$(gh api "repos/${REPO}/issues?state=open&per_page=10" \
|
||||
--jq ".[] | select(.title | startswith(\"${TITLE_PREFIX}(${VERSION})\")) | .number" 2>/dev/null | head -1)
|
||||
|
||||
if [ -n "$EXISTING" ]; then
|
||||
echo "ℹ️ Issue #${EXISTING} already exists for ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# ── Define sub-issues for the workflow ─────────────────────────
|
||||
if [[ "$BRANCH" == rc/* ]]; then
|
||||
SUB_ISSUES=(
|
||||
"RC Testing|Verify all features work on rc branch|type: test,release-candidate"
|
||||
"Regression Testing|Run full regression suite before merge|type: test,release-candidate"
|
||||
"Version Bump|Bump version in README.md and all headers|type: version,release-candidate"
|
||||
"Changelog Update|Update CHANGELOG.md with release notes|documentation,release-candidate"
|
||||
"Merge to Version Branch|Create PR to version/XX|type: release,needs-review"
|
||||
)
|
||||
elif [[ "$BRANCH" == alpha/* ]] || [[ "$BRANCH" == beta/* ]]; then
|
||||
SUB_ISSUES=(
|
||||
"Testing|Verify features on ${BRANCH_TYPE} branch|type: test,status: in-progress"
|
||||
"Bug Fixes|Fix issues found during ${BRANCH_TYPE} testing|type: bug,status: pending"
|
||||
"Promote to Next Stage|Create PR to promote to next release stage|type: release,needs-review"
|
||||
)
|
||||
else
|
||||
ISSUE_URL=$(gh issue create \
|
||||
SUB_ISSUES=(
|
||||
"Development|Implement feature/fix on dev branch|type: feature,status: in-progress"
|
||||
"Unit Testing|Write and pass unit tests|type: test,status: pending"
|
||||
"Code Review|Request and complete code review|needs-review,status: pending"
|
||||
"Version Bump|Bump version in README.md and all headers|type: version,status: pending"
|
||||
"Changelog Update|Update CHANGELOG.md with release notes|documentation,status: pending"
|
||||
"Create RC Branch|Promote dev to rc branch for final testing|type: release,status: pending"
|
||||
"Merge to Main|Create PR from rc/dev to main|type: release,needs-review,status: pending"
|
||||
)
|
||||
fi
|
||||
|
||||
# ── Create sub-issues first ───────────────────────────────────────
|
||||
SUB_LIST=""
|
||||
SUB_NUMBERS=""
|
||||
for SUB in "${SUB_ISSUES[@]}"; do
|
||||
IFS='|' read -r SUB_TITLE SUB_DESC SUB_LABELS <<< "$SUB"
|
||||
SUB_FULL_TITLE="${TITLE_PREFIX}(${VERSION}): ${SUB_TITLE}"
|
||||
|
||||
SUB_BODY=$(printf '### %s\n\n%s\n\n| Field | Value |\n|-------|-------|\n| **Parent Branch** | `%s` |\n| **Version** | `%s` |\n\n---\n*Sub-issue of the %s tracking issue for `%s`.*' \
|
||||
"$SUB_TITLE" "$SUB_DESC" "$BRANCH" "$VERSION" "$BRANCH_TYPE" "$BRANCH")
|
||||
|
||||
SUB_URL=$(gh issue create \
|
||||
--repo "$REPO" \
|
||||
--title "$SUB_FULL_TITLE" \
|
||||
--body "$SUB_BODY" \
|
||||
--label "${SUB_LABELS}" \
|
||||
--assignee "jmiller-moko" 2>&1)
|
||||
|
||||
SUB_NUM=$(echo "$SUB_URL" | grep -oE '[0-9]+$')
|
||||
if [ -n "$SUB_NUM" ]; then
|
||||
SUB_LIST="${SUB_LIST}\n- [ ] ${SUB_TITLE} (#${SUB_NUM})"
|
||||
SUB_NUMBERS="${SUB_NUMBERS} #${SUB_NUM}"
|
||||
fi
|
||||
sleep 0.3
|
||||
done
|
||||
|
||||
# ── Create parent tracking issue ──────────────────────────────────
|
||||
PARENT_BODY=$(printf '## %s Branch Created\n\n| Field | Value |\n|-------|-------|\n| **Branch** | `%s` |\n| **Version** | `%s` |\n| **Type** | %s |\n| **Created by** | @%s |\n| **Created at** | %s |\n| **Repository** | `%s` |\n\n## Workflow Sub-Issues\n\n%b\n\n---\n*Auto-created by [auto-dev-issue.yml](.github/workflows/auto-dev-issue.yml) on branch creation.*' \
|
||||
"$BRANCH_TYPE" "$BRANCH" "$VERSION" "$BRANCH_TYPE" "$ACTOR" "$NOW" "$REPO" "$SUB_LIST")
|
||||
|
||||
PARENT_URL=$(gh issue create \
|
||||
--repo "$REPO" \
|
||||
--title "$TITLE" \
|
||||
--body "$BODY" \
|
||||
--body "$PARENT_BODY" \
|
||||
--label "${LABEL_TYPE},version" \
|
||||
--assignee "jmiller-moko" 2>&1)
|
||||
echo "✅ Created tracking issue: ${ISSUE_URL}" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
PARENT_NUM=$(echo "$PARENT_URL" | grep -oE '[0-9]+$')
|
||||
|
||||
# ── Link sub-issues back to parent ────────────────────────────────
|
||||
if [ -n "$PARENT_NUM" ]; then
|
||||
for SUB in "${SUB_ISSUES[@]}"; do
|
||||
IFS='|' read -r SUB_TITLE _ _ <<< "$SUB"
|
||||
SUB_FULL_TITLE="${TITLE_PREFIX}(${VERSION}): ${SUB_TITLE}"
|
||||
SUB_NUM=$(gh api "repos/${REPO}/issues?state=open&per_page=20" \
|
||||
--jq ".[] | select(.title == \"${SUB_FULL_TITLE}\") | .number" 2>/dev/null | head -1)
|
||||
if [ -n "$SUB_NUM" ]; then
|
||||
gh api "repos/${REPO}/issues/${SUB_NUM}" -X PATCH \
|
||||
-f body="$(gh api "repos/${REPO}/issues/${SUB_NUM}" --jq '.body' 2>/dev/null)
|
||||
|
||||
> **Parent Issue:** #${PARENT_NUM}" --silent 2>/dev/null || true
|
||||
fi
|
||||
sleep 0.2
|
||||
done
|
||||
fi
|
||||
|
||||
# ── Create or update prerelease for alpha/beta/rc ────────────────
|
||||
if [[ "$BRANCH" == rc/* ]] || [[ "$BRANCH" == alpha/* ]] || [[ "$BRANCH" == beta/* ]]; then
|
||||
case "$BRANCH_TYPE" in
|
||||
Alpha) RELEASE_TAG="alpha" ;;
|
||||
Beta) RELEASE_TAG="beta" ;;
|
||||
"Release Candidate") RELEASE_TAG="release-candidate" ;;
|
||||
esac
|
||||
|
||||
EXISTING=$(gh release view "$RELEASE_TAG" --json tagName -q .tagName 2>/dev/null || true)
|
||||
if [ -z "$EXISTING" ]; then
|
||||
gh release create "$RELEASE_TAG" \
|
||||
--title "${RELEASE_TAG} (${VERSION})" \
|
||||
--notes "## ${BRANCH_TYPE} ${VERSION}\n\nBranch: \`${BRANCH}\`\nTracking issue: ${PARENT_URL}" \
|
||||
--prerelease \
|
||||
--target main 2>/dev/null || true
|
||||
echo "${BRANCH_TYPE} release created: ${RELEASE_TAG}" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
gh release edit "$RELEASE_TAG" \
|
||||
--title "${RELEASE_TAG} (${VERSION})" --prerelease 2>/dev/null || true
|
||||
echo "${BRANCH_TYPE} release updated: ${RELEASE_TAG}" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Summary ───────────────────────────────────────────────────────
|
||||
echo "## Dev Workflow Issues Created" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Item | Issue |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| **Parent** | ${PARENT_URL} |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| **Sub-issues** |${SUB_NUMBERS} |" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
409
.github/workflows/auto-release.yml
vendored
409
.github/workflows/auto-release.yml
vendored
@@ -6,29 +6,31 @@
|
||||
# DEFGROUP: GitHub.Workflow
|
||||
# INGROUP: MokoStandards.Release
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /templates/workflows/shared/auto-release.yml.template
|
||||
# VERSION: 04.04.01
|
||||
# BRIEF: Unified build & release pipeline — version branch, platform version, badges, tag, release
|
||||
# PATH: /templates/workflows/joomla/auto-release.yml.template
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: Joomla build & release — ZIP package, updates.xml, SHA-256 checksum
|
||||
#
|
||||
# ╔════════════════════════════════════════════════════════════════════════╗
|
||||
# ║ BUILD & RELEASE PIPELINE ║
|
||||
# ╠════════════════════════════════════════════════════════════════════════╣
|
||||
# ║ ║
|
||||
# ║ Triggers on push to main (skips bot commits + [skip ci]): ║
|
||||
# ║ ║
|
||||
# ║ Every push: ║
|
||||
# ║ 1. Read version from README.md ║
|
||||
# ║ 3. Set platform version (Dolibarr $this->version, Joomla <version>)║
|
||||
# ║ 4. Update [VERSION: XX.YY.ZZ] badges in markdown files ║
|
||||
# ║ 5. Write update.txt / update.xml ║
|
||||
# ║ 6. Create git tag vXX.YY.ZZ ║
|
||||
# ║ 7a. Patch: update existing GitHub Release for this minor ║
|
||||
# ║ ║
|
||||
# ║ Minor releases only (patch == 00): ║
|
||||
# ║ 2. Create/update version/XX.YY branch (patches update in-place) ║
|
||||
# ║ 7b. Create new GitHub Release ║
|
||||
# ║ ║
|
||||
# ╚════════════════════════════════════════════════════════════════════════╝
|
||||
# +========================================================================+
|
||||
# | BUILD & RELEASE PIPELINE (JOOMLA) |
|
||||
# +========================================================================+
|
||||
# | |
|
||||
# | Triggers on push to main (skips bot commits + [skip ci]): |
|
||||
# | |
|
||||
# | Every push: |
|
||||
# | 1. Read version from README.md |
|
||||
# | 3. Set platform version (Joomla <version>) |
|
||||
# | 4. Update [VERSION: XX.YY.ZZ] badges in markdown files |
|
||||
# | 5. Write updates.xml (Joomla update server XML) |
|
||||
# | 6. Create git tag vXX.YY.ZZ |
|
||||
# | 7a. Patch: update existing GitHub Release for this minor |
|
||||
# | 8. Build ZIP, upload asset, write SHA-256 to updates.xml |
|
||||
# | |
|
||||
# | Every version change: archives main -> version/XX.YY branch |
|
||||
# | Patch 00 = development (no release). First release = patch 01. |
|
||||
# | First release only (patch == 01): |
|
||||
# | 7b. Create new GitHub Release |
|
||||
# | |
|
||||
# +========================================================================+
|
||||
|
||||
name: Build & Release
|
||||
|
||||
@@ -37,6 +39,9 @@ on:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'htdocs/**'
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
@@ -64,19 +69,19 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
git clone --depth 1 --branch version/04.04 --quiet \
|
||||
git clone --depth 1 --branch version/04 --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
|
||||
|
||||
# ── STEP 1: Read version ───────────────────────────────────────────
|
||||
# -- STEP 1: Read version -----------------------------------------------
|
||||
- name: "Step 1: Read version from README.md"
|
||||
id: version
|
||||
run: |
|
||||
VERSION=$(php /tmp/mokostandards/api/cli/version_read.php --path . 2>/dev/null)
|
||||
if [ -z "$VERSION" ]; then
|
||||
echo "⏭️ No VERSION in README.md — skipping release"
|
||||
echo "No VERSION in README.md — skipping release"
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
@@ -84,24 +89,34 @@ jobs:
|
||||
MINOR=$(echo "$VERSION" | awk -F. '{printf "%s.%s", $1, $2}')
|
||||
PATCH=$(echo "$VERSION" | awk -F. '{print $3}')
|
||||
|
||||
MAJOR=$(echo "$VERSION" | awk -F. '{print $1}')
|
||||
MINOR_NUM=$(echo "$VERSION" | awk -F. '{print $2}')
|
||||
|
||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||
echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "branch=version/${MINOR}" >> "$GITHUB_OUTPUT"
|
||||
echo "branch=version/${MAJOR}" >> "$GITHUB_OUTPUT"
|
||||
echo "minor=$MINOR" >> "$GITHUB_OUTPUT"
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
echo "major=$MAJOR" >> "$GITHUB_OUTPUT"
|
||||
echo "release_tag=v${MAJOR}" >> "$GITHUB_OUTPUT"
|
||||
if [ "$PATCH" = "00" ]; then
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
echo "is_minor=false" >> "$GITHUB_OUTPUT"
|
||||
echo "Version: $VERSION (patch 00 = development — skipping release)"
|
||||
else
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
if [ "$PATCH" = "01" ]; then
|
||||
echo "is_minor=true" >> "$GITHUB_OUTPUT"
|
||||
echo "✅ Version: $VERSION (minor release — full pipeline)"
|
||||
echo "Version: $VERSION (first release — full pipeline)"
|
||||
else
|
||||
echo "is_minor=false" >> "$GITHUB_OUTPUT"
|
||||
echo "✅ Version: $VERSION (patch — platform version + badges only)"
|
||||
echo "Version: $VERSION (patch — platform version + badges only)"
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Check if already released
|
||||
if: steps.version.outputs.skip != 'true'
|
||||
id: check
|
||||
run: |
|
||||
TAG="${{ steps.version.outputs.tag }}"
|
||||
TAG="${{ steps.version.outputs.release_tag }}"
|
||||
BRANCH="${{ steps.version.outputs.branch }}"
|
||||
|
||||
TAG_EXISTS=false
|
||||
@@ -119,102 +134,109 @@ jobs:
|
||||
echo "already_released=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
# ── SANITY CHECKS ────────────────────────────────────────────────────
|
||||
- name: "Sanity: Platform-specific validation"
|
||||
# -- SANITY CHECKS -------------------------------------------------------
|
||||
- name: "Sanity: Pre-release validation"
|
||||
if: >-
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
steps.check.outputs.already_released != 'true'
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null)
|
||||
ERRORS=0
|
||||
|
||||
echo "## 🔍 Pre-Release Sanity Checks" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Platform: \`${PLATFORM}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo "## Pre-Release Sanity Checks (Joomla)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# -- Version drift check (must pass before release) --------
|
||||
README_VER=$(grep -oP 'VERSION:\s*\K[\d.]+' README.md 2>/dev/null | head -1)
|
||||
if [ "$README_VER" != "$VERSION" ]; then
|
||||
echo "- Version drift: README says \`${README_VER}\` but releasing \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS+1))
|
||||
else
|
||||
echo "- Version consistent: \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# Check CHANGELOG version matches
|
||||
CL_VER=$(grep -oP 'VERSION:\s*\K[\d.]+' CHANGELOG.md 2>/dev/null | head -1)
|
||||
if [ -n "$CL_VER" ] && [ "$CL_VER" != "$VERSION" ]; then
|
||||
echo "- CHANGELOG drift: \`${CL_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS+1))
|
||||
fi
|
||||
|
||||
# Check composer.json version if present
|
||||
if [ -f "composer.json" ]; then
|
||||
COMP_VER=$(grep -oP '"version"\s*:\s*"\K[^"]+' composer.json 2>/dev/null | head -1)
|
||||
if [ -n "$COMP_VER" ] && [ "$COMP_VER" != "$VERSION" ]; then
|
||||
echo "- composer.json drift: \`${COMP_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS+1))
|
||||
fi
|
||||
fi
|
||||
|
||||
# Common checks
|
||||
if [ ! -f "LICENSE" ]; then
|
||||
echo "❌ Missing LICENSE file" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Missing LICENSE file" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS+1))
|
||||
else
|
||||
echo "✅ LICENSE" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- LICENSE present" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [ ! -d "src" ]; then
|
||||
echo "⚠️ No src/ directory" >> $GITHUB_STEP_SUMMARY
|
||||
if [ ! -d "src" ] && [ ! -d "htdocs" ]; then
|
||||
echo "- Warning: No src/ or htdocs/ directory" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "✅ src/ directory" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Source directory present" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# Dolibarr-specific checks
|
||||
if [ "$PLATFORM" = "crm-module" ]; then
|
||||
MOD_FILE=$(find src htdocs -path "*/core/modules/mod*.class.php" -print -quit 2>/dev/null)
|
||||
if [ -z "$MOD_FILE" ]; then
|
||||
echo "❌ No module descriptor (src/core/modules/mod*.class.php)" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS+1))
|
||||
else
|
||||
echo "✅ Module descriptor: \`${MOD_FILE}\`" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Check module number
|
||||
NUMERO=$(grep -oP '\$this->numero\s*=\s*\K\d+' "$MOD_FILE" 2>/dev/null || echo "0")
|
||||
if [ "$NUMERO" = "0" ] || [ -z "$NUMERO" ]; then
|
||||
echo "❌ Module number (\$this->numero) is 0 or not set" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS+1))
|
||||
else
|
||||
echo "✅ Module number: ${NUMERO}" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# Check url_last_version exists
|
||||
if grep -q 'url_last_version' "$MOD_FILE" 2>/dev/null; then
|
||||
echo "✅ url_last_version is set" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "⚠️ url_last_version not set — update checks won't work" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Joomla-specific checks
|
||||
if [ "$PLATFORM" = "waas-component" ]; then
|
||||
# -- Joomla: manifest version drift --------
|
||||
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||
if [ -z "$MANIFEST" ]; then
|
||||
echo "❌ No Joomla XML manifest found" >> $GITHUB_STEP_SUMMARY
|
||||
if [ -n "$MANIFEST" ]; then
|
||||
XML_VER=$(grep -oP '<version>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1)
|
||||
if [ -n "$XML_VER" ] && [ "$XML_VER" != "$VERSION" ]; then
|
||||
echo "- Manifest drift: \`${XML_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS+1))
|
||||
else
|
||||
echo "✅ Manifest: \`${MANIFEST}\`" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Check extension type
|
||||
TYPE=$(grep -oP '<extension[^>]+type="\K[^"]+' "$MANIFEST" 2>/dev/null)
|
||||
echo "✅ Extension type: ${TYPE:-unknown}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "- Manifest version: \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
fi
|
||||
|
||||
# -- Joomla: XML manifest existence --------
|
||||
if [ -z "$MANIFEST" ]; then
|
||||
echo "- No Joomla XML manifest found" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS+1))
|
||||
else
|
||||
echo "- Manifest: \`${MANIFEST}\`" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# -- Joomla: extension type check --------
|
||||
TYPE=$(grep -oP '<extension[^>]+type="\K[^"]+' "$MANIFEST" 2>/dev/null)
|
||||
echo "- Extension type: ${TYPE:-unknown}" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
if [ "$ERRORS" -gt 0 ]; then
|
||||
echo "**❌ ${ERRORS} error(s) — release may be incomplete**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**${ERRORS} error(s) — release may be incomplete**" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "**✅ All sanity checks passed**" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**All sanity checks passed**" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# ── STEP 2: Create or update version/XX.YY branch ──────────────────
|
||||
- name: "Step 2: Version branch"
|
||||
if: >-
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
steps.check.outputs.already_released != 'true'
|
||||
# -- STEP 2: Create or update version/XX.YY archive branch ---------------
|
||||
# Always runs — every version change on main archives to version/XX.YY
|
||||
- name: "Step 2: Version archive branch"
|
||||
if: steps.check.outputs.already_released != 'true'
|
||||
run: |
|
||||
BRANCH="${{ steps.version.outputs.branch }}"
|
||||
IS_MINOR="${{ steps.version.outputs.is_minor }}"
|
||||
if [ "$IS_MINOR" = "true" ]; then
|
||||
PATCH="${{ steps.version.outputs.version }}"
|
||||
PATCH_NUM=$(echo "$PATCH" | awk -F. '{print $3}')
|
||||
|
||||
# Check if branch exists
|
||||
if git ls-remote --heads origin "$BRANCH" | grep -q "$BRANCH"; then
|
||||
git push origin HEAD:"$BRANCH" --force
|
||||
echo "Updated archive branch: ${BRANCH} (patch ${PATCH_NUM})" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
git checkout -b "$BRANCH" 2>/dev/null || git checkout "$BRANCH"
|
||||
git push origin "$BRANCH" --force
|
||||
echo "🌿 Created branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
git push origin HEAD:"$BRANCH" --force
|
||||
echo "📝 Updated branch: ${BRANCH} (patch)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Created archive branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# ── STEP 3: Set platform version ───────────────────────────────────
|
||||
# -- STEP 3: Set platform version ----------------------------------------
|
||||
- name: "Step 3: Set platform version"
|
||||
if: >-
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
@@ -224,7 +246,7 @@ jobs:
|
||||
php /tmp/mokostandards/api/cli/version_set_platform.php \
|
||||
--path . --version "$VERSION" --branch main
|
||||
|
||||
# ── STEP 4: Update version badges ──────────────────────────────────
|
||||
# -- STEP 4: Update version badges ----------------------------------------
|
||||
- name: "Step 4: Update version badges"
|
||||
if: >-
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
@@ -237,27 +259,22 @@ jobs:
|
||||
fi
|
||||
done
|
||||
|
||||
# ── STEP 5: Write update files (Dolibarr: update.txt / Joomla: update.xml)
|
||||
- name: "Step 5: Write update files"
|
||||
# -- STEP 5: Write updates.xml (Joomla update server) ---------------------
|
||||
- name: "Step 5: Write updates.xml"
|
||||
if: >-
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
steps.check.outputs.already_released != 'true'
|
||||
run: |
|
||||
PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null)
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
REPO="${{ github.repository }}"
|
||||
|
||||
if [ "$PLATFORM" = "crm-module" ]; then
|
||||
printf '%s' "$VERSION" > update.txt
|
||||
echo "📦 update.txt: ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [ "$PLATFORM" = "waas-component" ]; then
|
||||
# ── Parse extension metadata from XML manifest ──────────────
|
||||
# -- Parse extension metadata from XML manifest ----------------
|
||||
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||
if [ -z "$MANIFEST" ]; then
|
||||
echo "⚠️ No Joomla XML manifest found — skipping update.xml" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "Warning: No Joomla XML manifest found — skipping updates.xml" >> $GITHUB_STEP_SUMMARY
|
||||
exit 0
|
||||
fi
|
||||
|
||||
EXT_NAME=$(grep -oP '<name>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "${{ github.event.repository.name }}")
|
||||
EXT_TYPE=$(grep -oP '<extension[^>]+type="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "component")
|
||||
EXT_ELEMENT=$(grep -oP '<element>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "")
|
||||
@@ -285,7 +302,7 @@ jobs:
|
||||
FOLDER_TAG="<folder>${EXT_FOLDER}</folder>"
|
||||
fi
|
||||
|
||||
# Build targetplatform (fallback to Joomla 5+6 if not in manifest)
|
||||
# Build targetplatform (fallback to Joomla 5 if not in manifest)
|
||||
if [ -z "$TARGET_PLATFORM" ]; then
|
||||
TARGET_PLATFORM=$(printf '<targetplatform name="joomla" version="5.*" %s>' "/")
|
||||
fi
|
||||
@@ -299,10 +316,8 @@ jobs:
|
||||
DOWNLOAD_URL="https://github.com/${REPO}/releases/download/v${VERSION}/${EXT_ELEMENT}-${VERSION}.zip"
|
||||
INFO_URL="https://github.com/${REPO}/releases/tag/v${VERSION}"
|
||||
|
||||
# ── Write update.xml (stable release) ───────────────────────
|
||||
# -- Build stable entry to temp file ─────────────────────────
|
||||
{
|
||||
printf '%s\n' '<?xml version="1.0" encoding="utf-8"?>'
|
||||
printf '%s\n' '<updates>'
|
||||
printf '%s\n' ' <update>'
|
||||
printf '%s\n' " <name>${EXT_NAME}</name>"
|
||||
printf '%s\n' " <description>${EXT_NAME} update</description>"
|
||||
@@ -323,21 +338,40 @@ jobs:
|
||||
printf '%s\n' ' <maintainer>Moko Consulting</maintainer>'
|
||||
printf '%s\n' ' <maintainerurl>https://mokoconsulting.tech</maintainerurl>'
|
||||
printf '%s\n' ' </update>'
|
||||
} > /tmp/stable_entry.xml
|
||||
|
||||
# -- Write updates.xml preserving dev/rc entries ──────────────
|
||||
RC_ENTRY=""
|
||||
DEV_ENTRY=""
|
||||
if [ -f "updates.xml" ]; then
|
||||
printf 'import re\n' > /tmp/extract.py
|
||||
printf 'with open("updates.xml") as f: c = f.read()\n' >> /tmp/extract.py
|
||||
printf 'import sys; tag = sys.argv[1]\n' >> /tmp/extract.py
|
||||
printf 'm = re.search(r"( <update>.*?<tag>" + re.escape(tag) + r"</tag>.*?</update>)", c, re.DOTALL)\n' >> /tmp/extract.py
|
||||
printf 'if m: print(m.group(1))\n' >> /tmp/extract.py
|
||||
RC_ENTRY=$(python3 /tmp/extract.py rc 2>/dev/null || true)
|
||||
DEV_ENTRY=$(python3 /tmp/extract.py development 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
{
|
||||
printf '%s\n' '<?xml version="1.0" encoding="utf-8"?>'
|
||||
printf '%s\n' '<updates>'
|
||||
cat /tmp/stable_entry.xml
|
||||
[ -n "$RC_ENTRY" ] && echo "$RC_ENTRY"
|
||||
[ -n "$DEV_ENTRY" ] && echo "$DEV_ENTRY"
|
||||
printf '%s\n' '</updates>'
|
||||
} > update.xml
|
||||
} > updates.xml
|
||||
|
||||
echo "📦 update.xml: ${VERSION} (stable) — ${EXT_TYPE}/${EXT_ELEMENT}" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
fi
|
||||
echo "updates.xml: ${VERSION} (stable + rc/dev preserved)" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# ── Commit all changes ─────────────────────────────────────────────
|
||||
# -- Commit all changes ---------------------------------------------------
|
||||
- name: Commit release changes
|
||||
if: >-
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
steps.check.outputs.already_released != 'true'
|
||||
run: |
|
||||
if git diff --quiet && git diff --cached --quiet; then
|
||||
echo "ℹ️ No changes to commit"
|
||||
echo "No changes to commit"
|
||||
exit 0
|
||||
fi
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
@@ -348,18 +382,25 @@ jobs:
|
||||
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
|
||||
git push
|
||||
|
||||
# ── STEP 6: Create tag ─────────────────────────────────────────────
|
||||
# -- STEP 6: Create tag ---------------------------------------------------
|
||||
- name: "Step 6: Create git tag"
|
||||
if: >-
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
steps.check.outputs.tag_exists != 'true'
|
||||
steps.check.outputs.tag_exists != 'true' &&
|
||||
steps.version.outputs.is_minor == 'true'
|
||||
run: |
|
||||
TAG="${{ steps.version.outputs.tag }}"
|
||||
git tag "$TAG"
|
||||
git push origin "$TAG"
|
||||
echo "🏷️ Tag: ${TAG}" >> $GITHUB_STEP_SUMMARY
|
||||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||
# Only create the major release tag if it doesn't exist yet
|
||||
if ! git rev-parse "$RELEASE_TAG" >/dev/null 2>&1; then
|
||||
git tag "$RELEASE_TAG"
|
||||
git push origin "$RELEASE_TAG"
|
||||
echo "Tag created: ${RELEASE_TAG}" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "Tag ${RELEASE_TAG} already exists" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
echo "Tag: ${TAG}" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# ── STEP 7: Create or update GitHub Release ──────────────────────────
|
||||
# -- STEP 7: Create or update GitHub Release ------------------------------
|
||||
- name: "Step 7: GitHub Release"
|
||||
if: >-
|
||||
steps.version.outputs.skip != 'true' &&
|
||||
@@ -368,67 +409,129 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
TAG="${{ steps.version.outputs.tag }}"
|
||||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||
BRANCH="${{ steps.version.outputs.branch }}"
|
||||
IS_MINOR="${{ steps.version.outputs.is_minor }}"
|
||||
|
||||
# Derive the minor version base (XX.YY.00)
|
||||
MINOR_BASE=$(echo "$VERSION" | sed 's/\.[0-9]*$/.00/')
|
||||
MINOR_TAG="v${MINOR_BASE}"
|
||||
MAJOR="${{ steps.version.outputs.major }}"
|
||||
|
||||
NOTES=$(php /tmp/mokostandards/api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null)
|
||||
[ -z "$NOTES" ] && NOTES="Release ${VERSION}"
|
||||
echo "$NOTES" > /tmp/release_notes.md
|
||||
|
||||
if [ "$IS_MINOR" = "true" ]; then
|
||||
# Minor release: create new GitHub Release
|
||||
gh release create "$TAG" \
|
||||
--title "${VERSION}" \
|
||||
# Check if the major release already exists
|
||||
EXISTING=$(gh release view "$RELEASE_TAG" --json tagName -q .tagName 2>/dev/null || true)
|
||||
|
||||
if [ -z "$EXISTING" ]; then
|
||||
# First release for this major
|
||||
gh release create "$RELEASE_TAG" \
|
||||
--title "v${MAJOR} (latest: ${VERSION})" \
|
||||
--notes-file /tmp/release_notes.md \
|
||||
--target "$BRANCH"
|
||||
echo "🚀 Release created: ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Release created: ${RELEASE_TAG} (${VERSION})" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
# Patch release: update the existing minor release with new tag
|
||||
# Find the latest release for this minor version
|
||||
EXISTING=$(gh release view "$MINOR_TAG" --json tagName -q .tagName 2>/dev/null || true)
|
||||
if [ -n "$EXISTING" ]; then
|
||||
# Update existing release body with patch info
|
||||
CURRENT_NOTES=$(gh release view "$MINOR_TAG" --json body -q .body 2>/dev/null || true)
|
||||
# Append version notes to existing major release
|
||||
CURRENT_NOTES=$(gh release view "$RELEASE_TAG" --json body -q .body 2>/dev/null || true)
|
||||
{
|
||||
echo "$CURRENT_NOTES"
|
||||
echo ""
|
||||
echo "---"
|
||||
echo "### Patch ${VERSION}"
|
||||
echo "### ${VERSION}"
|
||||
echo ""
|
||||
cat /tmp/release_notes.md
|
||||
} > /tmp/updated_notes.md
|
||||
|
||||
gh release edit "$MINOR_TAG" \
|
||||
--title "${MINOR_BASE} (latest: ${VERSION})" \
|
||||
gh release edit "$RELEASE_TAG" \
|
||||
--title "v${MAJOR} (latest: ${VERSION})" \
|
||||
--notes-file /tmp/updated_notes.md
|
||||
echo "📝 Release updated: ${MINOR_BASE} → patch ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
# No existing minor release found — create one for this patch
|
||||
gh release create "$TAG" \
|
||||
--title "${VERSION}" \
|
||||
--notes-file /tmp/release_notes.md
|
||||
echo "🚀 Release created: ${VERSION} (no minor release found)" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
echo "Release updated: ${RELEASE_TAG} -> ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# ── Summary ────────────────────────────────────────────────────────
|
||||
# -- STEP 8: Build Joomla install ZIP + SHA-256 checksum ------------------
|
||||
# Every patch builds an install-ready ZIP and uploads it to the minor release.
|
||||
# Result: one Release per minor version with a ZIP for each patch.
|
||||
- name: "Step 8: Build Joomla package and update checksum"
|
||||
if: >-
|
||||
steps.version.outputs.skip != 'true'
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||
REPO="${{ github.repository }}"
|
||||
|
||||
# All ZIPs upload to the major release tag (vXX)
|
||||
gh release view "$RELEASE_TAG" --json tagName > /dev/null 2>&1 || {
|
||||
echo "No release ${RELEASE_TAG} found — skipping ZIP upload"
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Find extension element name from manifest
|
||||
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1 || true)
|
||||
[ -z "$MANIFEST" ] && exit 0
|
||||
|
||||
EXT_ELEMENT=$(grep -oP '<element>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || basename "$MANIFEST" .xml)
|
||||
PACKAGE_NAME="${EXT_ELEMENT}-${VERSION}.zip"
|
||||
|
||||
# -- Build install-ready ZIP from src/ ----------------------------
|
||||
SOURCE_DIR="src"
|
||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||
[ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ — skipping package"; exit 0; }
|
||||
|
||||
cd "$SOURCE_DIR"
|
||||
zip -r "/tmp/${PACKAGE_NAME}" . -x '.ftpignore'
|
||||
cd ..
|
||||
|
||||
FILESIZE=$(stat -c%s "/tmp/${PACKAGE_NAME}" 2>/dev/null || stat -f%z "/tmp/${PACKAGE_NAME}" 2>/dev/null || echo "unknown")
|
||||
|
||||
# -- Calculate SHA-256 -------------------------------------------
|
||||
SHA256=$(sha256sum "/tmp/${PACKAGE_NAME}" | cut -d' ' -f1)
|
||||
|
||||
# -- Upload ZIP to the minor release tag -------------------------
|
||||
gh release upload "$RELEASE_TAG" "/tmp/${PACKAGE_NAME}" --clobber 2>/dev/null || {
|
||||
echo "Could not upload with --clobber, retrying..."
|
||||
gh release upload "$RELEASE_TAG" "/tmp/${PACKAGE_NAME}" 2>/dev/null || true
|
||||
}
|
||||
|
||||
# -- Update updates.xml with SHA-256 for latest patch -------------
|
||||
if [ -f "updates.xml" ]; then
|
||||
if grep -q '<sha256>' updates.xml; then
|
||||
sed -i "s|<sha256>.*</sha256>|<sha256>sha256:${SHA256}</sha256>|" updates.xml
|
||||
else
|
||||
sed -i "s|</downloads>|</downloads>\n <sha256>sha256:${SHA256}</sha256>|" updates.xml
|
||||
fi
|
||||
|
||||
# Also update the download URL to point to this patch's ZIP
|
||||
DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}"
|
||||
sed -i "s|<downloadurl[^>]*>[^<]*</downloadurl>|<downloadurl type=\"full\" format=\"zip\">${DOWNLOAD_URL}</downloadurl>|" updates.xml
|
||||
|
||||
git add updates.xml
|
||||
git commit -m "chore(release): SHA-256 + download URL for ${VERSION} [skip ci]" \
|
||||
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>" || true
|
||||
git push || true
|
||||
fi
|
||||
|
||||
echo "### Joomla Package" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Package | \`${PACKAGE_NAME}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Size | ${FILESIZE} bytes |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| SHA-256 | \`${SHA256}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Release | \`${RELEASE_TAG}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Download | [${PACKAGE_NAME}](https://github.com/${REPO}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}) |" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# -- Summary --------------------------------------------------------------
|
||||
- name: Pipeline Summary
|
||||
if: always()
|
||||
run: |
|
||||
VERSION="${{ steps.version.outputs.version }}"
|
||||
if [ "${{ steps.version.outputs.skip }}" = "true" ]; then
|
||||
echo "## ⏭️ Release Skipped" >> $GITHUB_STEP_SUMMARY
|
||||
echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY
|
||||
echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY
|
||||
elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then
|
||||
echo "## ℹ️ Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
||||
echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "## ✅ Build & Release Complete" >> $GITHUB_STEP_SUMMARY
|
||||
echo "## Build & Release Complete (Joomla)" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|------|--------|" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
7
.github/workflows/auto-update-sha.yml
vendored
7
.github/workflows/auto-update-sha.yml
vendored
@@ -5,7 +5,7 @@
|
||||
# INGROUP: MokoCassiopeia.Automation
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
||||
# PATH: /.github/workflows/auto-update-sha.yml
|
||||
# VERSION: 01.00.00
|
||||
# VERSION: 03.09.03
|
||||
# BRIEF: Automatically update SHA-256 hash in updates.xml after release
|
||||
# NOTE: Ensures updates.xml stays synchronized with release packages
|
||||
|
||||
@@ -119,10 +119,7 @@ jobs:
|
||||
git config --local user.name "github-actions[bot]"
|
||||
|
||||
git add updates.xml
|
||||
git commit -m "chore: Update SHA-256 hash for release ${TAG}
|
||||
|
||||
Auto-generated by auto-update-sha workflow
|
||||
SHA-256: ${{ steps.sha.outputs.sha256 }}"
|
||||
git commit -m "chore: Update SHA-256 hash for release ${TAG} - SHA: ${{ steps.sha.outputs.sha256 }}"
|
||||
|
||||
git push origin main
|
||||
|
||||
|
||||
101
.github/workflows/changelog-validation.yml
vendored
Normal file
101
.github/workflows/changelog-validation.yml
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# This file is part of a Moko Consulting project.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: GitHub.Workflow.Template
|
||||
# INGROUP: MokoStandards.CI
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /templates/workflows/shared/changelog-validation.yml.template
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: Validates CHANGELOG.md format and version consistency
|
||||
# NOTE: Deployed to .github/workflows/changelog-validation.yml in governed repos.
|
||||
|
||||
name: Changelog Validation
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
validate-changelog:
|
||||
name: Validate CHANGELOG.md
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Check CHANGELOG.md exists
|
||||
run: |
|
||||
echo "### Changelog Validation" >> $GITHUB_STEP_SUMMARY
|
||||
if [ ! -f "CHANGELOG.md" ]; then
|
||||
echo "CHANGELOG.md not found in repository root." >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
fi
|
||||
echo "CHANGELOG.md exists." >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Check VERSION header matches README.md
|
||||
run: |
|
||||
# Extract version from README.md FILE INFORMATION block
|
||||
README_VERSION=$(grep -oP '^\s*VERSION:\s*\K[0-9]{2}\.[0-9]{2}\.[0-9]{2}' README.md | head -1)
|
||||
if [ -z "$README_VERSION" ]; then
|
||||
echo "No VERSION found in README.md FILE INFORMATION block." >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check that CHANGELOG.md has a matching version header
|
||||
CHANGELOG_VERSION=$(grep -oP '^\#\#\s*\[\K[0-9]{2}\.[0-9]{2}\.[0-9]{2}' CHANGELOG.md | head -1)
|
||||
if [ -z "$CHANGELOG_VERSION" ]; then
|
||||
echo "No version header found in CHANGELOG.md (expected \`## [XX.YY.ZZ] - YYYY-MM-DD\`)." >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$CHANGELOG_VERSION" != "$README_VERSION" ]; then
|
||||
echo "CHANGELOG latest version \`${CHANGELOG_VERSION}\` does not match README VERSION \`${README_VERSION}\`." >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "CHANGELOG version \`${CHANGELOG_VERSION}\` matches README VERSION." >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Validate conventional changelog format
|
||||
run: |
|
||||
ERRORS=0
|
||||
|
||||
# Check that version entries follow ## [XX.YY.ZZ] - YYYY-MM-DD format
|
||||
while IFS= read -r LINE; do
|
||||
if ! echo "$LINE" | grep -qP '^\#\#\s*\[[0-9]{2}\.[0-9]{2}\.[0-9]{2}\]\s*-\s*[0-9]{4}-[0-9]{2}-[0-9]{2}'; then
|
||||
echo "Malformed version header: \`${LINE}\`" >> $GITHUB_STEP_SUMMARY
|
||||
echo " Expected format: \`## [XX.YY.ZZ] - YYYY-MM-DD\`" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
done < <(grep -P '^\#\#\s*\[' CHANGELOG.md)
|
||||
|
||||
ENTRY_COUNT=$(grep -cP '^\#\#\s*\[' CHANGELOG.md || echo "0")
|
||||
if [ "$ENTRY_COUNT" -eq 0 ]; then
|
||||
echo "No version entries found in CHANGELOG.md." >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo "Found ${ENTRY_COUNT} version entr(ies) in CHANGELOG.md." >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
if [ "${ERRORS}" -gt 0 ]; then
|
||||
echo "**${ERRORS} format issue(s) found.**" >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
else
|
||||
echo "**Changelog format validation passed.**" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
391
.github/workflows/ci-joomla.yml
vendored
Normal file
391
.github/workflows/ci-joomla.yml
vendored
Normal file
@@ -0,0 +1,391 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# This file is part of a Moko Consulting project.
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: GitHub.Workflow.Template
|
||||
# INGROUP: MokoStandards.CI
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /templates/workflows/joomla/ci-joomla.yml.template
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: CI workflow for Joomla extensions — lint, validate, test
|
||||
# NOTE: Deployed to .github/workflows/ci-joomla.yml in governed Joomla extension repos.
|
||||
|
||||
name: Joomla Extension CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- dev/**
|
||||
- rc/**
|
||||
- version/**
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
- dev/**
|
||||
- rc/**
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
lint-and-validate:
|
||||
name: Lint & Validate
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2.31.0
|
||||
with:
|
||||
php-version: '8.2'
|
||||
extensions: mbstring, xml, zip, gd, curl, json, simplexml
|
||||
tools: composer:v2
|
||||
coverage: none
|
||||
|
||||
- name: Clone MokoStandards
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
run: |
|
||||
git clone --depth 1 --branch version/04 --quiet \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||
/tmp/mokostandards
|
||||
|
||||
- name: Install dependencies
|
||||
env:
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
if [ -f "composer.json" ]; then
|
||||
composer install \
|
||||
--no-interaction \
|
||||
--prefer-dist \
|
||||
--optimize-autoloader
|
||||
else
|
||||
echo "No composer.json found — skipping dependency install"
|
||||
fi
|
||||
|
||||
- name: PHP syntax check
|
||||
run: |
|
||||
ERRORS=0
|
||||
for DIR in src/ htdocs/; do
|
||||
if [ -d "$DIR" ]; then
|
||||
FOUND=1
|
||||
while IFS= read -r -d '' FILE; do
|
||||
OUTPUT=$(php -l "$FILE" 2>&1)
|
||||
if echo "$OUTPUT" | grep -q "Parse error"; then
|
||||
echo "::error file=${FILE}::${OUTPUT}"
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
done < <(find "$DIR" -name "*.php" -print0)
|
||||
fi
|
||||
done
|
||||
echo "### PHP Syntax Check" >> $GITHUB_STEP_SUMMARY
|
||||
if [ "${ERRORS}" -gt 0 ]; then
|
||||
echo "**${ERRORS} syntax error(s) found.**" >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
else
|
||||
echo "All PHP files passed syntax check." >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
- name: XML manifest validation
|
||||
run: |
|
||||
echo "### XML Manifest Validation" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=0
|
||||
|
||||
# Find the extension manifest (XML with <extension tag)
|
||||
MANIFEST=""
|
||||
for XML_FILE in $(find . -maxdepth 2 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do
|
||||
if grep -q "<extension" "$XML_FILE" 2>/dev/null; then
|
||||
MANIFEST="$XML_FILE"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$MANIFEST" ]; then
|
||||
echo "No Joomla extension manifest found (XML file with \`<extension\` tag)." >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo "Manifest found: \`${MANIFEST}\`" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Validate well-formed XML
|
||||
php -r "
|
||||
\$xml = @simplexml_load_file('$MANIFEST');
|
||||
if (\$xml === false) {
|
||||
echo 'INVALID';
|
||||
exit(1);
|
||||
}
|
||||
echo 'VALID';
|
||||
" > /tmp/xml_result 2>&1
|
||||
XML_RESULT=$(cat /tmp/xml_result)
|
||||
if [ "$XML_RESULT" != "VALID" ]; then
|
||||
echo "Manifest is not well-formed XML." >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo "Manifest is well-formed XML." >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# Check required tags: name, version, author, namespace (Joomla 5+)
|
||||
for TAG in name version author namespace; do
|
||||
if ! grep -q "<${TAG}>" "$MANIFEST" 2>/dev/null; then
|
||||
echo "Missing required tag: \`<${TAG}>\`" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo "Found required tag: \`<${TAG}>\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "${ERRORS}" -gt 0 ]; then
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**${ERRORS} manifest issue(s) found.**" >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
else
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Manifest validation passed.**" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
- name: Check language files referenced in manifest
|
||||
run: |
|
||||
echo "### Language File Check" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=0
|
||||
|
||||
MANIFEST=""
|
||||
for XML_FILE in $(find . -maxdepth 2 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do
|
||||
if grep -q "<extension" "$XML_FILE" 2>/dev/null; then
|
||||
MANIFEST="$XML_FILE"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -n "$MANIFEST" ]; then
|
||||
# Extract language file references from manifest
|
||||
LANG_FILES=$(grep -oP 'language\s+tag="[^"]*"[^>]*>\K[^<]+' "$MANIFEST" 2>/dev/null || true)
|
||||
if [ -z "$LANG_FILES" ]; then
|
||||
echo "No language file references found in manifest — skipping." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
while IFS= read -r LANG_FILE; do
|
||||
LANG_FILE=$(echo "$LANG_FILE" | xargs)
|
||||
if [ -z "$LANG_FILE" ]; then
|
||||
continue
|
||||
fi
|
||||
# Check in common locations
|
||||
FOUND=0
|
||||
for BASE in "." "src" "htdocs"; do
|
||||
if [ -f "${BASE}/${LANG_FILE}" ]; then
|
||||
FOUND=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [ "$FOUND" -eq 0 ]; then
|
||||
echo "Missing language file: \`${LANG_FILE}\`" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo "Language file present: \`${LANG_FILE}\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
done <<< "$LANG_FILES"
|
||||
fi
|
||||
else
|
||||
echo "No manifest found — skipping language check." >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
if [ "${ERRORS}" -gt 0 ]; then
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**${ERRORS} missing language file(s).**" >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
else
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**Language file check passed.**" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
- name: Check index.html files in directories
|
||||
run: |
|
||||
echo "### Index.html Check" >> $GITHUB_STEP_SUMMARY
|
||||
MISSING=0
|
||||
CHECKED=0
|
||||
|
||||
for DIR in src/ htdocs/; do
|
||||
if [ -d "$DIR" ]; then
|
||||
while IFS= read -r -d '' SUBDIR; do
|
||||
CHECKED=$((CHECKED + 1))
|
||||
if [ ! -f "${SUBDIR}/index.html" ]; then
|
||||
echo "Missing index.html in: \`${SUBDIR}\`" >> $GITHUB_STEP_SUMMARY
|
||||
MISSING=$((MISSING + 1))
|
||||
fi
|
||||
done < <(find "$DIR" -type d -print0)
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${CHECKED}" -eq 0 ]; then
|
||||
echo "No src/ or htdocs/ directories found — skipping." >> $GITHUB_STEP_SUMMARY
|
||||
elif [ "${MISSING}" -gt 0 ]; then
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "**${MISSING} director(ies) missing index.html out of ${CHECKED} checked.**" >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
else
|
||||
echo "All ${CHECKED} directories contain index.html." >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
release-readiness:
|
||||
name: Release Readiness Check
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'pull_request' && github.base_ref == 'main'
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Validate release readiness
|
||||
run: |
|
||||
echo "## Release Readiness" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=0
|
||||
|
||||
# Extract version from README.md
|
||||
README_VERSION=$(grep -oP '^\s*VERSION:\s*\K[0-9]{2}\.[0-9]{2}\.[0-9]{2}' README.md | head -1)
|
||||
if [ -z "$README_VERSION" ]; then
|
||||
echo "No VERSION found in README.md FILE INFORMATION block." >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo "README version: \`${README_VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# Find the extension manifest
|
||||
MANIFEST=""
|
||||
for XML_FILE in $(find . -maxdepth 2 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do
|
||||
if grep -q "<extension" "$XML_FILE" 2>/dev/null; then
|
||||
MANIFEST="$XML_FILE"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$MANIFEST" ]; then
|
||||
echo "No Joomla extension manifest found." >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo "Manifest: \`${MANIFEST}\`" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Check <version> matches README VERSION
|
||||
MANIFEST_VERSION=$(grep -oP '<version>\K[^<]+' "$MANIFEST" | head -1)
|
||||
if [ -z "$MANIFEST_VERSION" ]; then
|
||||
echo "No \`<version>\` tag in manifest." >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
elif [ -n "$README_VERSION" ] && [ "$MANIFEST_VERSION" != "$README_VERSION" ]; then
|
||||
echo "Manifest version \`${MANIFEST_VERSION}\` does not match README \`${README_VERSION}\`." >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo "Manifest version: \`${MANIFEST_VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# Check extension type, element, client attributes
|
||||
EXT_TYPE=$(grep -oP '<extension[^>]*\btype="\K[^"]+' "$MANIFEST" | head -1)
|
||||
if [ -z "$EXT_TYPE" ]; then
|
||||
echo "Missing \`type\` attribute on \`<extension>\` tag." >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
else
|
||||
echo "Extension type: \`${EXT_TYPE}\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
# Element check (component/module/plugin name)
|
||||
HAS_ELEMENT=$(grep -cP '<(element|name)>' "$MANIFEST" 2>/dev/null || echo "0")
|
||||
if [ "$HAS_ELEMENT" -eq 0 ]; then
|
||||
echo "Missing \`<element>\` or \`<name>\` in manifest." >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
|
||||
# Client attribute for site/admin modules and plugins
|
||||
if echo "$EXT_TYPE" | grep -qP "^(module|plugin)$"; then
|
||||
HAS_CLIENT=$(grep -cP '<extension[^>]*\bclient=' "$MANIFEST" 2>/dev/null || echo "0")
|
||||
if [ "$HAS_CLIENT" -eq 0 ]; then
|
||||
echo "Missing \`client\` attribute for ${EXT_TYPE} extension." >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check updates.xml exists
|
||||
if [ -f "updates.xml" ] || [ -f "updates.xml" ]; then
|
||||
echo "Update XML present." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "No updates.xml found." >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
|
||||
# Check CHANGELOG.md exists
|
||||
if [ -f "CHANGELOG.md" ]; then
|
||||
echo "CHANGELOG.md present." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "No CHANGELOG.md found." >> $GITHUB_STEP_SUMMARY
|
||||
ERRORS=$((ERRORS + 1))
|
||||
fi
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
if [ $ERRORS -gt 0 ]; then
|
||||
echo "**${ERRORS} issue(s) must be resolved before release.**" >> $GITHUB_STEP_SUMMARY
|
||||
exit 1
|
||||
else
|
||||
echo "**Extension is ready for release.**" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
test:
|
||||
name: Tests (PHP ${{ matrix.php }})
|
||||
runs-on: ubuntu-latest
|
||||
needs: lint-and-validate
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
php: ['8.2', '8.3']
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Setup PHP ${{ matrix.php }}
|
||||
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2.31.0
|
||||
with:
|
||||
php-version: ${{ matrix.php }}
|
||||
extensions: mbstring, xml, zip, gd, curl, json, simplexml
|
||||
tools: composer:v2
|
||||
coverage: none
|
||||
|
||||
- name: Install dependencies
|
||||
env:
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
if [ -f "composer.json" ]; then
|
||||
composer install \
|
||||
--no-interaction \
|
||||
--prefer-dist \
|
||||
--optimize-autoloader
|
||||
else
|
||||
echo "No composer.json found — skipping dependency install"
|
||||
fi
|
||||
|
||||
- name: Run tests
|
||||
run: |
|
||||
echo "### Test Results (PHP ${{ matrix.php }})" >> $GITHUB_STEP_SUMMARY
|
||||
if [ -f "phpunit.xml" ] || [ -f "phpunit.xml.dist" ]; then
|
||||
vendor/bin/phpunit --testdox 2>&1 | tee /tmp/test-output.log
|
||||
EXIT=${PIPESTATUS[0]}
|
||||
if [ $EXIT -eq 0 ]; then
|
||||
echo "All tests passed." >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "Test failures detected — see log." >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
cat /tmp/test-output.log >> $GITHUB_STEP_SUMMARY
|
||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
exit $EXIT
|
||||
else
|
||||
echo "No phpunit.xml found — skipping tests." >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
2
.github/workflows/codeql-analysis.yml
vendored
2
.github/workflows/codeql-analysis.yml
vendored
@@ -9,7 +9,7 @@
|
||||
# INGROUP: MokoStandards.Security
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /templates/workflows/generic/codeql-analysis.yml.template
|
||||
# VERSION: 04.04.01
|
||||
# VERSION: 03.09.03
|
||||
# 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.
|
||||
|
||||
37
.github/workflows/deploy-demo.yml
vendored
37
.github/workflows/deploy-demo.yml
vendored
@@ -22,7 +22,7 @@
|
||||
# INGROUP: MokoStandards.Deploy
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /templates/workflows/shared/deploy-demo.yml.template
|
||||
# VERSION: 04.04.01
|
||||
# VERSION: 04.05.00
|
||||
# BRIEF: SFTP deployment workflow for demo server — synced to all governed repos
|
||||
# NOTE: Synced via bulk-repo-sync to .github/workflows/deploy-demo.yml in all governed repos.
|
||||
# Port is resolved in order: DEMO_FTP_PORT variable → :port suffix in DEMO_FTP_HOST → 22.
|
||||
@@ -36,10 +36,9 @@ name: Deploy to Demo Server (SFTP)
|
||||
# Optional org-level variable: DEMO_FTP_PORT (auto-detected from host or defaults to 22)
|
||||
# Optional org/repo variable: DEMO_FTP_SUFFIX — when set, appended to DEMO_FTP_PATH to form the
|
||||
# full remote destination: DEMO_FTP_PATH/DEMO_FTP_SUFFIX
|
||||
# Ignore rules: Place a .ftp_ignore file in the repository root. Each non-empty,
|
||||
# non-comment line is a regex pattern tested against the relative path
|
||||
# of each file (e.g. "subdir/file.txt"). The .gitignore is also
|
||||
# respected automatically.
|
||||
# Ignore rules: Place a .ftpignore file in the repository root. Each non-empty,
|
||||
# non-comment line is a glob pattern tested against the relative path
|
||||
# of each file (e.g. "subdir/file.txt"). The .gitignore is NOT used.
|
||||
# Required org-level secret: DEMO_FTP_KEY (preferred) or DEMO_FTP_PASSWORD
|
||||
#
|
||||
# Access control: only users with admin or maintain role on the repository may deploy.
|
||||
@@ -195,8 +194,8 @@ jobs:
|
||||
env:
|
||||
SOURCE_DIR: ${{ steps.source.outputs.dir }}
|
||||
run: |
|
||||
# ── Convert a gitignore-style glob line to an ERE pattern ──────────────
|
||||
ftp_ignore_to_regex() {
|
||||
# ── Convert a ftpignore-style glob line to an ERE pattern ──────────────
|
||||
ftpignore_to_regex() {
|
||||
local line="$1"
|
||||
local anchored=false
|
||||
# Strip inline comments and whitespace
|
||||
@@ -226,15 +225,15 @@ jobs:
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Read .ftp_ignore (gitignore-style globs) ─────────────────────────
|
||||
# ── Read .ftpignore (ftpignore-style globs) ─────────────────────────
|
||||
IGNORE_PATTERNS=()
|
||||
IGNORE_SOURCES=()
|
||||
if [ -f ".ftp_ignore" ]; then
|
||||
if [ -f ".ftpignore" ]; then
|
||||
while IFS= read -r line; do
|
||||
[[ "$line" =~ ^[[:space:]]*$ || "$line" =~ ^[[:space:]]*# ]] && continue
|
||||
regex=$(ftp_ignore_to_regex "$line")
|
||||
regex=$(ftpignore_to_regex "$line")
|
||||
[ -n "$regex" ] && IGNORE_PATTERNS+=("$regex") && IGNORE_SOURCES+=("$line")
|
||||
done < ".ftp_ignore"
|
||||
done < ".ftpignore"
|
||||
fi
|
||||
|
||||
# ── Walk src/ and classify every file ────────────────────────────────
|
||||
@@ -245,17 +244,11 @@ jobs:
|
||||
SKIP=false
|
||||
for i in "${!IGNORE_PATTERNS[@]}"; do
|
||||
if echo "$rel" | grep -qE "${IGNORE_PATTERNS[$i]}" 2>/dev/null; then
|
||||
IGNORED_FILES+=("$rel | .ftp_ignore \`${IGNORE_SOURCES[$i]}\`")
|
||||
IGNORED_FILES+=("$rel | .ftpignore \`${IGNORE_SOURCES[$i]}\`")
|
||||
SKIP=true; break
|
||||
fi
|
||||
done
|
||||
$SKIP && continue
|
||||
if [ -f ".gitignore" ]; then
|
||||
git check-ignore -q "$rel" 2>/dev/null && {
|
||||
IGNORED_FILES+=("$rel | .gitignore")
|
||||
continue
|
||||
} || true
|
||||
fi
|
||||
WILL_UPLOAD+=("$rel")
|
||||
done < <(find "$SOURCE_DIR" -type f -print0 | sort -z)
|
||||
|
||||
@@ -426,7 +419,7 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
git clone --depth 1 --branch version/04.04 --quiet \
|
||||
git clone --depth 1 --branch version/04.05 --quiet \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||
/tmp/mokostandards
|
||||
cd /tmp/mokostandards
|
||||
@@ -637,8 +630,12 @@ jobs:
|
||||
DEPLOY_ARGS+=(--key-passphrase "$SFTP_PASSWORD")
|
||||
fi
|
||||
|
||||
PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null || true)
|
||||
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards/api/deploy/deploy-joomla.php" ]; then
|
||||
php /tmp/mokostandards/api/deploy/deploy-joomla.php "${DEPLOY_ARGS[@]}"
|
||||
else
|
||||
php /tmp/mokostandards/api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
|
||||
# (deploy-sftp.php handles dotfile skipping and .ftp_ignore natively)
|
||||
fi
|
||||
# Remove temp files that should never be left behind
|
||||
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
||||
|
||||
|
||||
109
.github/workflows/deploy-dev.yml
vendored
109
.github/workflows/deploy-dev.yml
vendored
@@ -22,7 +22,7 @@
|
||||
# INGROUP: MokoStandards.Deploy
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /templates/workflows/shared/deploy-dev.yml.template
|
||||
# VERSION: 04.04.01
|
||||
# VERSION: 04.05.00
|
||||
# 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.
|
||||
@@ -37,10 +37,9 @@ name: Deploy to Dev Server (SFTP)
|
||||
# Optional org-level variable: DEV_FTP_PORT (auto-detected from host or defaults to 22)
|
||||
# Optional org/repo variable: DEV_FTP_SUFFIX — when set, appended to DEV_FTP_PATH to form the
|
||||
# full remote destination: DEV_FTP_PATH/DEV_FTP_SUFFIX
|
||||
# Ignore rules: Place a .ftp_ignore file in the repository root. Each non-empty,
|
||||
# non-comment line is a regex pattern tested against the relative path
|
||||
# of each file (e.g. "subdir/file.txt"). The .gitignore is also
|
||||
# respected automatically.
|
||||
# Ignore rules: Place a .ftpignore file in the repository root. Each non-empty,
|
||||
# non-comment line is a glob pattern tested against the relative path
|
||||
# of each file (e.g. "subdir/file.txt"). The .gitignore is NOT used.
|
||||
# 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.
|
||||
@@ -200,8 +199,8 @@ jobs:
|
||||
env:
|
||||
SOURCE_DIR: ${{ steps.source.outputs.dir }}
|
||||
run: |
|
||||
# ── Convert a gitignore-style glob line to an ERE pattern ──────────────
|
||||
ftp_ignore_to_regex() {
|
||||
# ── Convert a ftpignore-style glob line to an ERE pattern ──────────────
|
||||
ftpignore_to_regex() {
|
||||
local line="$1"
|
||||
local anchored=false
|
||||
# Strip inline comments and whitespace
|
||||
@@ -231,15 +230,15 @@ jobs:
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Read .ftp_ignore (gitignore-style globs) ─────────────────────────
|
||||
# ── Read .ftpignore (ftpignore-style globs) ─────────────────────────
|
||||
IGNORE_PATTERNS=()
|
||||
IGNORE_SOURCES=()
|
||||
if [ -f ".ftp_ignore" ]; then
|
||||
if [ -f ".ftpignore" ]; then
|
||||
while IFS= read -r line; do
|
||||
[[ "$line" =~ ^[[:space:]]*$ || "$line" =~ ^[[:space:]]*# ]] && continue
|
||||
regex=$(ftp_ignore_to_regex "$line")
|
||||
regex=$(ftpignore_to_regex "$line")
|
||||
[ -n "$regex" ] && IGNORE_PATTERNS+=("$regex") && IGNORE_SOURCES+=("$line")
|
||||
done < ".ftp_ignore"
|
||||
done < ".ftpignore"
|
||||
fi
|
||||
|
||||
# ── Walk src/ and classify every file ────────────────────────────────
|
||||
@@ -250,17 +249,11 @@ jobs:
|
||||
SKIP=false
|
||||
for i in "${!IGNORE_PATTERNS[@]}"; do
|
||||
if echo "$rel" | grep -qE "${IGNORE_PATTERNS[$i]}" 2>/dev/null; then
|
||||
IGNORED_FILES+=("$rel | .ftp_ignore \`${IGNORE_SOURCES[$i]}\`")
|
||||
IGNORED_FILES+=("$rel | .ftpignore \`${IGNORE_SOURCES[$i]}\`")
|
||||
SKIP=true; break
|
||||
fi
|
||||
done
|
||||
$SKIP && continue
|
||||
if [ -f ".gitignore" ]; then
|
||||
git check-ignore -q "$rel" 2>/dev/null && {
|
||||
IGNORED_FILES+=("$rel | .gitignore")
|
||||
continue
|
||||
} || true
|
||||
fi
|
||||
WILL_UPLOAD+=("$rel")
|
||||
done < <(find "$SOURCE_DIR" -type f -print0 | sort -z)
|
||||
|
||||
@@ -431,7 +424,7 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
git clone --depth 1 --branch version/04.04 --quiet \
|
||||
git clone --depth 1 --branch version/04.05 --quiet \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||
/tmp/mokostandards
|
||||
cd /tmp/mokostandards
|
||||
@@ -582,6 +575,10 @@ jobs:
|
||||
> /tmp/sftp-config.json
|
||||
fi
|
||||
|
||||
# Dev deploys skip minified files — use unminified sources for debugging
|
||||
echo "*.min.js" >> .ftpignore
|
||||
echo "*.min.css" >> .ftpignore
|
||||
|
||||
# ── Run deploy-sftp.php from MokoStandards ────────────────────────────
|
||||
DEPLOY_ARGS=(--path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json)
|
||||
if [ "$USE_PASSPHRASE" = "true" ]; then
|
||||
@@ -662,74 +659,20 @@ jobs:
|
||||
fi
|
||||
fi
|
||||
|
||||
# Use Joomla-aware deploy for waas-component (routes files to correct Joomla dirs)
|
||||
# Use standard SFTP deploy for everything else
|
||||
PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null || true)
|
||||
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards/api/deploy/deploy-joomla.php" ]; then
|
||||
php /tmp/mokostandards/api/deploy/deploy-joomla.php "${DEPLOY_ARGS[@]}"
|
||||
else
|
||||
php /tmp/mokostandards/api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
|
||||
# (deploy-sftp.php handles dotfile skipping and .ftp_ignore natively)
|
||||
fi
|
||||
# (both scripts handle dotfile skipping and .ftpignore 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=all&per_page=1&sort=created&direction=desc" \
|
||||
--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" \
|
||||
-f state="open" \
|
||||
--silent
|
||||
echo "📋 Failure issue #${EXISTING} updated/reopened: ${REPO}" >> "$GITHUB_STEP_SUMMARY"
|
||||
else
|
||||
gh issue create \
|
||||
--repo "$REPO" \
|
||||
--title "$TITLE" \
|
||||
--body "$BODY" \
|
||||
--label "$LABEL" \
|
||||
--assignee "jmiller-moko" \
|
||||
| tee -a "$GITHUB_STEP_SUMMARY"
|
||||
fi
|
||||
# Dev deploys fail silently — no issue creation.
|
||||
# Demo and RS deploys create failure issues (production-facing).
|
||||
|
||||
- name: Deployment summary
|
||||
if: always()
|
||||
|
||||
132
.github/workflows/deploy-manual.yml
vendored
Normal file
132
.github/workflows/deploy-manual.yml
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: GitHub.Workflow
|
||||
# INGROUP: MokoStandards.Deploy
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /templates/workflows/joomla/deploy-manual.yml.template
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: Manual SFTP deploy to dev server for Joomla repos
|
||||
# NOTE: Joomla repos use update.xml for distribution. This is for manual
|
||||
# dev server testing only — triggered via workflow_dispatch.
|
||||
|
||||
name: Deploy to Dev (Manual)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
clear_remote:
|
||||
description: 'Delete all remote files before uploading'
|
||||
required: false
|
||||
default: 'false'
|
||||
type: boolean
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
name: SFTP Deploy to Dev
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@accd6127cb78bee3e8082180cb391013d204ef9f # v2.31.0
|
||||
with:
|
||||
php-version: '8.2'
|
||||
extensions: json, ssh2
|
||||
tools: composer
|
||||
coverage: none
|
||||
|
||||
- name: Setup MokoStandards tools
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
git clone --depth 1 --branch version/04 --quiet \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||
/tmp/mokostandards 2>/dev/null || true
|
||||
if [ -d "/tmp/mokostandards" ] && [ -f "/tmp/mokostandards/composer.json" ]; then
|
||||
cd /tmp/mokostandards && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
||||
fi
|
||||
|
||||
- name: Check FTP configuration
|
||||
id: check
|
||||
env:
|
||||
HOST: ${{ vars.DEV_FTP_HOST }}
|
||||
PATH_VAR: ${{ vars.DEV_FTP_PATH }}
|
||||
SUFFIX: ${{ vars.DEV_FTP_SUFFIX }}
|
||||
PORT: ${{ vars.DEV_FTP_PORT }}
|
||||
run: |
|
||||
if [ -z "$HOST" ] || [ -z "$PATH_VAR" ]; then
|
||||
echo "DEV_FTP_HOST or DEV_FTP_PATH not configured — cannot deploy"
|
||||
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||
exit 0
|
||||
fi
|
||||
echo "skip=false" >> "$GITHUB_OUTPUT"
|
||||
echo "host=$HOST" >> "$GITHUB_OUTPUT"
|
||||
|
||||
REMOTE="${PATH_VAR%/}"
|
||||
[ -n "$SUFFIX" ] && REMOTE="${REMOTE}/${SUFFIX#/}"
|
||||
echo "remote=$REMOTE" >> "$GITHUB_OUTPUT"
|
||||
|
||||
[ -z "$PORT" ] && PORT="22"
|
||||
echo "port=$PORT" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Deploy via SFTP
|
||||
if: steps.check.outputs.skip != 'true'
|
||||
env:
|
||||
SFTP_KEY: ${{ secrets.DEV_FTP_KEY }}
|
||||
SFTP_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
|
||||
SFTP_USER: ${{ vars.DEV_FTP_USERNAME }}
|
||||
run: |
|
||||
SOURCE_DIR="src"
|
||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||
[ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ — nothing to deploy"; exit 0; }
|
||||
|
||||
printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \
|
||||
"${{ steps.check.outputs.host }}" "${{ steps.check.outputs.port }}" "$SFTP_USER" "${{ steps.check.outputs.remote }}" \
|
||||
> /tmp/sftp-config.json
|
||||
|
||||
if [ -n "$SFTP_KEY" ]; then
|
||||
echo "$SFTP_KEY" > /tmp/deploy_key
|
||||
chmod 600 /tmp/deploy_key
|
||||
printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json
|
||||
else
|
||||
printf ',"password":"%s"}' "$SFTP_PASS" >> /tmp/sftp-config.json
|
||||
fi
|
||||
|
||||
DEPLOY_ARGS=(--path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json)
|
||||
[ "${{ inputs.clear_remote }}" = "true" ] && DEPLOY_ARGS+=(--clear-remote)
|
||||
|
||||
PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null || true)
|
||||
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards/api/deploy/deploy-joomla.php" ]; then
|
||||
php /tmp/mokostandards/api/deploy/deploy-joomla.php "${DEPLOY_ARGS[@]}"
|
||||
else
|
||||
php /tmp/mokostandards/api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
|
||||
fi
|
||||
|
||||
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
||||
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
if [ "${{ steps.check.outputs.skip }}" = "true" ]; then
|
||||
echo "### Deploy Skipped — FTP not configured" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
echo "### Manual Dev Deploy Complete" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Host | \`${{ steps.check.outputs.host }}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Remote | \`${{ steps.check.outputs.remote }}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Clear | ${{ inputs.clear_remote }} |" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
37
.github/workflows/deploy-rs.yml
vendored
37
.github/workflows/deploy-rs.yml
vendored
@@ -22,7 +22,7 @@
|
||||
# INGROUP: MokoStandards.Deploy
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /templates/workflows/shared/deploy-rs.yml.template
|
||||
# VERSION: 04.04.01
|
||||
# VERSION: 04.05.00
|
||||
# BRIEF: SFTP deployment workflow for release staging server — synced to all governed repos
|
||||
# NOTE: Synced via bulk-repo-sync to .github/workflows/deploy-rs.yml in all governed repos.
|
||||
# Port is resolved in order: RS_FTP_PORT variable → :port suffix in RS_FTP_HOST → 22.
|
||||
@@ -36,10 +36,9 @@ name: Deploy to RS Server (SFTP)
|
||||
# Optional org-level variable: RS_FTP_PORT (auto-detected from host or defaults to 22)
|
||||
# Optional org/repo variable: RS_FTP_SUFFIX — when set, appended to RS_FTP_PATH to form the
|
||||
# full remote destination: RS_FTP_PATH/RS_FTP_SUFFIX
|
||||
# Ignore rules: Place a .ftp_ignore file in the repository root. Each non-empty,
|
||||
# non-comment line is a regex pattern tested against the relative path
|
||||
# of each file (e.g. "subdir/file.txt"). The .gitignore is also
|
||||
# respected automatically.
|
||||
# Ignore rules: Place a .ftpignore file in the repository root. Each non-empty,
|
||||
# non-comment line is a glob pattern tested against the relative path
|
||||
# of each file (e.g. "subdir/file.txt"). The .gitignore is NOT used.
|
||||
# Required org-level secret: RS_FTP_KEY (preferred) or RS_FTP_PASSWORD
|
||||
#
|
||||
# Access control: only users with admin or maintain role on the repository may deploy.
|
||||
@@ -195,8 +194,8 @@ jobs:
|
||||
env:
|
||||
SOURCE_DIR: ${{ steps.source.outputs.dir }}
|
||||
run: |
|
||||
# ── Convert a gitignore-style glob line to an ERE pattern ──────────────
|
||||
ftp_ignore_to_regex() {
|
||||
# ── Convert a ftpignore-style glob line to an ERE pattern ──────────────
|
||||
ftpignore_to_regex() {
|
||||
local line="$1"
|
||||
local anchored=false
|
||||
# Strip inline comments and whitespace
|
||||
@@ -226,15 +225,15 @@ jobs:
|
||||
fi
|
||||
}
|
||||
|
||||
# ── Read .ftp_ignore (gitignore-style globs) ─────────────────────────
|
||||
# ── Read .ftpignore (ftpignore-style globs) ─────────────────────────
|
||||
IGNORE_PATTERNS=()
|
||||
IGNORE_SOURCES=()
|
||||
if [ -f ".ftp_ignore" ]; then
|
||||
if [ -f ".ftpignore" ]; then
|
||||
while IFS= read -r line; do
|
||||
[[ "$line" =~ ^[[:space:]]*$ || "$line" =~ ^[[:space:]]*# ]] && continue
|
||||
regex=$(ftp_ignore_to_regex "$line")
|
||||
regex=$(ftpignore_to_regex "$line")
|
||||
[ -n "$regex" ] && IGNORE_PATTERNS+=("$regex") && IGNORE_SOURCES+=("$line")
|
||||
done < ".ftp_ignore"
|
||||
done < ".ftpignore"
|
||||
fi
|
||||
|
||||
# ── Walk src/ and classify every file ────────────────────────────────
|
||||
@@ -245,17 +244,11 @@ jobs:
|
||||
SKIP=false
|
||||
for i in "${!IGNORE_PATTERNS[@]}"; do
|
||||
if echo "$rel" | grep -qE "${IGNORE_PATTERNS[$i]}" 2>/dev/null; then
|
||||
IGNORED_FILES+=("$rel | .ftp_ignore \`${IGNORE_SOURCES[$i]}\`")
|
||||
IGNORED_FILES+=("$rel | .ftpignore \`${IGNORE_SOURCES[$i]}\`")
|
||||
SKIP=true; break
|
||||
fi
|
||||
done
|
||||
$SKIP && continue
|
||||
if [ -f ".gitignore" ]; then
|
||||
git check-ignore -q "$rel" 2>/dev/null && {
|
||||
IGNORED_FILES+=("$rel | .gitignore")
|
||||
continue
|
||||
} || true
|
||||
fi
|
||||
WILL_UPLOAD+=("$rel")
|
||||
done < <(find "$SOURCE_DIR" -type f -print0 | sort -z)
|
||||
|
||||
@@ -407,7 +400,7 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
git clone --depth 1 --branch version/04.04 --quiet \
|
||||
git clone --depth 1 --branch version/04.05 --quiet \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||
/tmp/mokostandards
|
||||
cd /tmp/mokostandards
|
||||
@@ -564,8 +557,12 @@ jobs:
|
||||
DEPLOY_ARGS+=(--key-passphrase "$SFTP_PASSWORD")
|
||||
fi
|
||||
|
||||
PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null || true)
|
||||
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards/api/deploy/deploy-joomla.php" ]; then
|
||||
php /tmp/mokostandards/api/deploy/deploy-joomla.php "${DEPLOY_ARGS[@]}"
|
||||
else
|
||||
php /tmp/mokostandards/api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
|
||||
# (deploy-sftp.php handles dotfile skipping and .ftp_ignore natively)
|
||||
fi
|
||||
# Remove temp files that should never be left behind
|
||||
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
# INGROUP: MokoStandards.Firewall
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /templates/workflows/shared/enterprise-firewall-setup.yml.template
|
||||
# VERSION: 04.04.01
|
||||
# VERSION: 04.06.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.
|
||||
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -22,7 +22,7 @@
|
||||
# INGROUP: MokoCassiopeia.Release
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
||||
# PATH: /.github/workflows/release.yml
|
||||
# VERSION: 01.00.00
|
||||
# VERSION: 03.09.03
|
||||
# BRIEF: Automated release workflow for MokoCassiopeia Joomla template
|
||||
# NOTE: Creates release packages and publishes to GitHub Releases
|
||||
|
||||
|
||||
101
.github/workflows/repo_health.yml
vendored
101
.github/workflows/repo_health.yml
vendored
@@ -10,7 +10,7 @@
|
||||
# INGROUP: MokoStandards.Validation
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /.github/workflows/repo_health.yml
|
||||
# VERSION: 04.01.00
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: Enforces repository guardrails by validating release configuration, scripts governance, tooling availability, and core repository health artifacts.
|
||||
# NOTE: Field is user-managed.
|
||||
# ============================================================================
|
||||
@@ -29,7 +29,7 @@ on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
profile:
|
||||
description: Which configuration profile to validate. release checks SFTP variables used by release pipeline. scripts checks baseline script prerequisites. repo runs repository health only. al[...]
|
||||
description: 'Validation profile: all, release, scripts, or repo'
|
||||
required: true
|
||||
default: all
|
||||
type: choice
|
||||
@@ -39,19 +39,7 @@ on:
|
||||
- scripts
|
||||
- repo
|
||||
pull_request:
|
||||
paths:
|
||||
- .github/workflows/**
|
||||
- scripts/**
|
||||
- docs/**
|
||||
- dev/**
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- .github/workflows/**
|
||||
- scripts/**
|
||||
- docs/**
|
||||
- dev/**
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
@@ -68,7 +56,7 @@ env:
|
||||
|
||||
# Repo health policy
|
||||
# Files are listed as-is; directories must end with a trailing slash.
|
||||
REPO_REQUIRED_ARTIFACTS: README.md,LICENSE,CHANGELOG.md,CONTRIBUTING.md,CODE_OF_CONDUCT.md,.github/workflows/,src/
|
||||
REPO_REQUIRED_ARTIFACTS: README.md,LICENSE,CHANGELOG.md,CONTRIBUTING.md,CODE_OF_CONDUCT.md,.github/workflows/
|
||||
REPO_OPTIONAL_FILES: SECURITY.md,GOVERNANCE.md,.editorconfig,.gitattributes,.gitignore,README.md,docs/
|
||||
REPO_DISALLOWED_DIRS:
|
||||
REPO_DISALLOWED_FILES: TODO.md,todo.md
|
||||
@@ -82,6 +70,7 @@ env:
|
||||
WORKFLOWS_DIR: .github/workflows
|
||||
SHELLCHECK_PATTERN: '*.sh'
|
||||
SPDX_FILE_GLOBS: '*.sh,*.php,*.js,*.ts,*.css,*.xml,*.yml,*.yaml'
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
access_check:
|
||||
@@ -173,7 +162,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -268,7 +257,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -382,7 +371,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -412,6 +401,15 @@ jobs:
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Source directory: src/ or htdocs/ (either is valid)
|
||||
if [ -d "src" ]; then
|
||||
SOURCE_DIR="src"
|
||||
elif [ -d "htdocs" ]; then
|
||||
SOURCE_DIR="htdocs"
|
||||
else
|
||||
missing_required+=("src/ or htdocs/ (source directory required)")
|
||||
fi
|
||||
|
||||
IFS=',' read -r -a required_artifacts <<< "${REPO_REQUIRED_ARTIFACTS}"
|
||||
IFS=',' read -r -a optional_files <<< "${REPO_OPTIONAL_FILES}"
|
||||
IFS=',' read -r -a disallowed_dirs <<< "${REPO_DISALLOWED_DIRS}"
|
||||
@@ -561,6 +559,73 @@ jobs:
|
||||
} >> "${GITHUB_STEP_SUMMARY}"
|
||||
fi
|
||||
|
||||
# ── Joomla-specific checks ───────────────────────────────────────
|
||||
joomla_findings=()
|
||||
|
||||
# XML manifest: find any XML file containing <extension
|
||||
MANIFEST="$(find . -maxdepth 2 -name '*.xml' -exec grep -l '<extension' {} \; 2>/dev/null | head -1 || true)"
|
||||
if [ -z "${MANIFEST}" ]; then
|
||||
joomla_findings+=("Joomla XML manifest not found (no *.xml with <extension> tag)")
|
||||
else
|
||||
# Check <version> tag exists
|
||||
if ! grep -qP '<version>' "${MANIFEST}"; then
|
||||
joomla_findings+=("XML manifest: <version> tag missing")
|
||||
fi
|
||||
# Check extension type attribute
|
||||
if ! grep -qP 'type="(component|module|plugin|library|package|template|language)"' "${MANIFEST}"; then
|
||||
joomla_findings+=("XML manifest: type attribute missing or invalid")
|
||||
fi
|
||||
# Check <name> tag
|
||||
if ! grep -qP '<name>' "${MANIFEST}"; then
|
||||
joomla_findings+=("XML manifest: <name> tag missing")
|
||||
fi
|
||||
# Check <author> tag
|
||||
if ! grep -qP '<author>' "${MANIFEST}"; then
|
||||
joomla_findings+=("XML manifest: <author> tag missing")
|
||||
fi
|
||||
# Check <namespace> for Joomla 5+
|
||||
if ! grep -qP '<namespace' "${MANIFEST}"; then
|
||||
joomla_findings+=("XML manifest: <namespace> missing (required for Joomla 5+)")
|
||||
fi
|
||||
fi
|
||||
|
||||
# Language files: check for at least one .ini file
|
||||
INI_COUNT="$(find . -name '*.ini' -type f 2>/dev/null | wc -l)"
|
||||
if [ "${INI_COUNT}" -eq 0 ]; then
|
||||
joomla_findings+=("No .ini language files found")
|
||||
fi
|
||||
|
||||
# updates.xml must exist in root (Joomla update server)
|
||||
if [ ! -f 'updates.xml' ]; then
|
||||
joomla_findings+=("updates.xml missing in root (required for Joomla update server)")
|
||||
fi
|
||||
|
||||
# index.html files for directory listing protection
|
||||
INDEX_DIRS=("${SOURCE_DIR}" "${SOURCE_DIR}/admin" "${SOURCE_DIR}/site")
|
||||
for dir in "${INDEX_DIRS[@]}"; do
|
||||
if [ -d "${dir}" ] && [ ! -f "${dir}/index.html" ]; then
|
||||
joomla_findings+=("${dir}/index.html missing (directory listing protection)")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "${#joomla_findings[@]}" -gt 0 ]; then
|
||||
{
|
||||
printf '%s\n' '### Joomla extension checks'
|
||||
printf '%s\n' '| Check | Status |'
|
||||
printf '%s\n' '|---|---|'
|
||||
for f in "${joomla_findings[@]}"; do
|
||||
printf '%s\n' "| ${f} | Warning |"
|
||||
done
|
||||
printf '\n'
|
||||
} >> "${GITHUB_STEP_SUMMARY}"
|
||||
else
|
||||
{
|
||||
printf '%s\n' '### Joomla extension checks'
|
||||
printf '%s\n' 'All Joomla-specific checks passed.'
|
||||
printf '\n'
|
||||
} >> "${GITHUB_STEP_SUMMARY}"
|
||||
fi
|
||||
|
||||
extended_enabled="${EXTENDED_CHECKS:-true}"
|
||||
extended_findings=()
|
||||
|
||||
|
||||
4
.github/workflows/repository-cleanup.yml
vendored
4
.github/workflows/repository-cleanup.yml
vendored
@@ -9,7 +9,7 @@
|
||||
# INGROUP: MokoStandards.Maintenance
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /templates/workflows/shared/repository-cleanup.yml.template
|
||||
# VERSION: 04.04.01
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: Recurring repository maintenance — labels, branches, workflows, logs, doc indexes
|
||||
# NOTE: Synced via bulk-repo-sync to .github/workflows/repository-cleanup.yml in all governed repos.
|
||||
# Runs on the 1st and 15th of each month at 6:00 AM UTC, and on manual dispatch.
|
||||
@@ -266,7 +266,7 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
run: |
|
||||
REPO="${{ github.repository }}"
|
||||
CURRENT="chore/sync-mokostandards-v04.04"
|
||||
CURRENT="chore/sync-mokostandards-v04.05"
|
||||
echo "## 🌿 Branch Cleanup" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
|
||||
91
.github/workflows/standards-compliance.yml
vendored
91
.github/workflows/standards-compliance.yml
vendored
@@ -5,7 +5,7 @@
|
||||
# INGROUP: MokoStandards.Compliance
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /.github/workflows/standards-compliance.yml
|
||||
# VERSION: 04.04.01
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: MokoStandards compliance validation workflow
|
||||
# NOTE: Validates repository structure, documentation, and coding standards
|
||||
|
||||
@@ -163,7 +163,11 @@ jobs:
|
||||
--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|getString|getArgument|config\[|/\.\*/|^\s*//|^\s*\*|CREDENTIAL_PATTERNS|SecurityValidator|SECRET_PATTERN|===|!==|ApiClient|str_contains|gen_wrappers)' | \
|
||||
grep -v "= ''" | grep -v '= ""' | grep -v '\$this->config' > /tmp/secrets1.txt 2>/dev/null || true
|
||||
grep -v "= ''" | grep -v '= ""' | grep -v '\$this->config' | \
|
||||
grep -v 'type="password"' | grep -v 'type="text"' | grep -v 'name="password"' | grep -v 'name="secretkey"' | \
|
||||
grep -v '<input ' | grep -v '<label ' | grep -v 'for="' | \
|
||||
grep -v 'index\.php?option=' | grep -v 'Route::_' | grep -v 'lostpassword' | \
|
||||
grep -v 'resetpassword' | grep -v 'JRoute' | grep -v 'href=' > /tmp/secrets1.txt 2>/dev/null || true
|
||||
scan_pattern "Secret assignments" "⚠️" /tmp/secrets1.txt
|
||||
|
||||
# Pattern 2: Private keys
|
||||
@@ -500,10 +504,18 @@ jobs:
|
||||
tools: composer
|
||||
coverage: none
|
||||
|
||||
- name: Install API Package
|
||||
run: composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
|
||||
- name: Setup MokoStandards tools
|
||||
env:
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN }}"}}'
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
git clone --depth 1 --branch version/04 --quiet \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||
/tmp/mokostandards 2>/dev/null || true
|
||||
if [ -d "/tmp/mokostandards" ] && [ -f "/tmp/mokostandards/composer.json" ]; then
|
||||
cd /tmp/mokostandards
|
||||
composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
||||
fi
|
||||
|
||||
- name: Run Version Consistency Check
|
||||
id: version_check
|
||||
@@ -512,18 +524,15 @@ jobs:
|
||||
echo "## 🔢 Version Consistency Validation" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
# Use PHP enterprise library for version consistency check
|
||||
if [ -f "vendor/bin/moko" ]; then
|
||||
php vendor/bin/moko check:version -- --path . --verbose 2>&1 | tee /tmp/version-check.log
|
||||
EXIT_CODE=${PIPESTATUS[0]}
|
||||
elif [ -f "/tmp/mokostandards/api/validate/check_version_consistency.php" ]; then
|
||||
# Use MokoStandards tools (no Composer needed on the governed repo)
|
||||
if [ -f "/tmp/mokostandards/api/validate/check_version_consistency.php" ]; then
|
||||
php /tmp/mokostandards/api/validate/check_version_consistency.php --path . --verbose 2>&1 | tee /tmp/version-check.log
|
||||
EXIT_CODE=${PIPESTATUS[0]}
|
||||
elif [ -f "api/validate/check_version_consistency.php" ]; then
|
||||
php api/validate/check_version_consistency.php --path . --verbose 2>&1 | tee /tmp/version-check.log
|
||||
EXIT_CODE=${PIPESTATUS[0]}
|
||||
else
|
||||
echo "⏭️ Install mokoconsulting-tech/enterprise via Composer for version checks" >> $GITHUB_STEP_SUMMARY
|
||||
echo "⏭️ MokoStandards tools not available — skipping version check" >> $GITHUB_STEP_SUMMARY
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -1960,17 +1969,39 @@ jobs:
|
||||
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 }}"}}'
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
if [ -f "composer.json" ]; then
|
||||
composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
|
||||
else
|
||||
echo "No composer.json — pulling MokoStandards tools"
|
||||
if [ ! -d "/tmp/mokostandards" ]; then
|
||||
git clone --depth 1 --branch version/04 --quiet \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||
/tmp/mokostandards 2>/dev/null || true
|
||||
if [ -f "/tmp/mokostandards/composer.json" ]; then
|
||||
cd /tmp/mokostandards && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
||||
cd -
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Check Enterprise Readiness
|
||||
id: enterprise_check
|
||||
run: |
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
SCRIPT=""
|
||||
if [ -f "api/validate/check_enterprise_readiness.php" ]; then
|
||||
php api/validate/check_enterprise_readiness.php --verbose | tee /tmp/enterprise-check.log
|
||||
SCRIPT="api/validate/check_enterprise_readiness.php"
|
||||
elif [ -f "/tmp/mokostandards/api/validate/check_enterprise_readiness.php" ]; then
|
||||
SCRIPT="/tmp/mokostandards/api/validate/check_enterprise_readiness.php"
|
||||
fi
|
||||
|
||||
if [ -n "$SCRIPT" ]; then
|
||||
php "$SCRIPT" --verbose | tee /tmp/enterprise-check.log
|
||||
EXIT_CODE=$?
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -2010,17 +2041,39 @@ jobs:
|
||||
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 }}"}}'
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
if [ -f "composer.json" ]; then
|
||||
composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
|
||||
else
|
||||
echo "No composer.json — pulling MokoStandards tools"
|
||||
if [ ! -d "/tmp/mokostandards" ]; then
|
||||
git clone --depth 1 --branch version/04 --quiet \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||
/tmp/mokostandards 2>/dev/null || true
|
||||
if [ -f "/tmp/mokostandards/composer.json" ]; then
|
||||
cd /tmp/mokostandards && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
||||
cd -
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Check Repository Health
|
||||
id: health_check
|
||||
run: |
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
SCRIPT=""
|
||||
if [ -f "api/validate/check_repo_health.php" ]; then
|
||||
php api/validate/check_repo_health.php --verbose | tee /tmp/health-check.log
|
||||
SCRIPT="api/validate/check_repo_health.php"
|
||||
elif [ -f "/tmp/mokostandards/api/validate/check_repo_health.php" ]; then
|
||||
SCRIPT="/tmp/mokostandards/api/validate/check_repo_health.php"
|
||||
fi
|
||||
|
||||
if [ -n "$SCRIPT" ]; then
|
||||
php "$SCRIPT" --verbose | tee /tmp/health-check.log
|
||||
EXIT_CODE=$?
|
||||
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -2481,8 +2534,8 @@ jobs:
|
||||
echo ""
|
||||
echo "✅ SUCCESS: Repository is fully MokoStandards compliant"
|
||||
|
||||
- name: Create tracking issue for standards violations
|
||||
if: failure() && github.event_name == 'push'
|
||||
- name: Create or reopen tracking issue for standards violations
|
||||
if: failure()
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
run: |
|
||||
|
||||
4
.github/workflows/sync-version-on-merge.yml
vendored
4
.github/workflows/sync-version-on-merge.yml
vendored
@@ -9,7 +9,7 @@
|
||||
# INGROUP: MokoStandards.Automation
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /templates/workflows/shared/sync-version-on-merge.yml.template
|
||||
# VERSION: 04.04.01
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: Auto-bump patch version on every push to main and propagate to all file headers
|
||||
# NOTE: Synced via bulk-repo-sync to .github/workflows/sync-version-on-merge.yml in all governed repos.
|
||||
# README.md is the single source of truth for the repository version.
|
||||
@@ -58,7 +58,7 @@ jobs:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
git clone --depth 1 --branch version/04.04 --quiet \
|
||||
git clone --depth 1 --branch version/04 --quiet \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||
/tmp/mokostandards
|
||||
cd /tmp/mokostandards
|
||||
|
||||
321
.github/workflows/update-server.yml
vendored
Normal file
321
.github/workflows/update-server.yml
vendored
Normal file
@@ -0,0 +1,321 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: GitHub.Workflow
|
||||
# INGROUP: MokoStandards.Joomla
|
||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||
# PATH: /templates/workflows/joomla/update-server.yml.template
|
||||
# VERSION: 04.06.00
|
||||
# BRIEF: Update Joomla update server XML feed with stable/rc/dev entries
|
||||
#
|
||||
# Writes updates.xml with multiple <update> entries:
|
||||
# - <tag>stable</tag> on push to main (from auto-release)
|
||||
# - <tag>rc</tag> on push to rc/**
|
||||
# - <tag>development</tag> on push to dev/**
|
||||
#
|
||||
# Joomla filters by user's "Minimum Stability" setting.
|
||||
|
||||
name: Update Joomla Update Server XML Feed
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'dev/**'
|
||||
- 'alpha/**'
|
||||
- 'beta/**'
|
||||
- 'rc/**'
|
||||
paths:
|
||||
- 'src/**'
|
||||
- 'htdocs/**'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
stability:
|
||||
description: 'Stability tag'
|
||||
required: true
|
||||
default: 'development'
|
||||
type: choice
|
||||
options:
|
||||
- development
|
||||
- alpha
|
||||
- beta
|
||||
- rc
|
||||
- stable
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update-xml:
|
||||
name: Update updates.xml
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||
with:
|
||||
token: ${{ secrets.GH_TOKEN || github.token }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Setup MokoStandards tools
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
git clone --depth 1 --branch version/04 --quiet \
|
||||
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||
/tmp/mokostandards 2>/dev/null || true
|
||||
if [ -d "/tmp/mokostandards" ] && [ -f "/tmp/mokostandards/composer.json" ]; then
|
||||
cd /tmp/mokostandards && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
||||
fi
|
||||
|
||||
- name: Generate updates.xml entry
|
||||
run: |
|
||||
BRANCH="${{ github.ref_name }}"
|
||||
REPO="${{ github.repository }}"
|
||||
VERSION=$(php /tmp/mokostandards/api/cli/version_read.php --path . 2>/dev/null || echo "0.0.0")
|
||||
|
||||
# Auto-bump patch on alpha/beta/rc branches (not dev — dev bumps manually)
|
||||
if [[ "$BRANCH" != dev/* ]]; then
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
BUMPED=$(php /tmp/mokostandards/api/cli/version_bump.php --path . 2>/dev/null || true)
|
||||
if [ -n "$BUMPED" ]; then
|
||||
VERSION=$(php /tmp/mokostandards/api/cli/version_read.php --path . 2>/dev/null || echo "$VERSION")
|
||||
git add -A
|
||||
git commit -m "chore(version): auto-bump patch ${VERSION} [skip ci]" \
|
||||
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>" 2>/dev/null || true
|
||||
git push 2>/dev/null || true
|
||||
fi
|
||||
fi
|
||||
|
||||
# Determine stability from branch or input
|
||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||
STABILITY="${{ inputs.stability }}"
|
||||
elif [[ "$BRANCH" == rc/* ]]; then
|
||||
STABILITY="rc"
|
||||
elif [[ "$BRANCH" == beta/* ]]; then
|
||||
STABILITY="beta"
|
||||
elif [[ "$BRANCH" == alpha/* ]]; then
|
||||
STABILITY="alpha"
|
||||
elif [[ "$BRANCH" == dev/* ]]; then
|
||||
STABILITY="development"
|
||||
else
|
||||
STABILITY="stable"
|
||||
fi
|
||||
|
||||
# Parse manifest
|
||||
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||
if [ -z "$MANIFEST" ]; then
|
||||
echo "No Joomla manifest found — skipping"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
EXT_NAME=$(grep -oP '<name>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "${{ github.event.repository.name }}")
|
||||
EXT_TYPE=$(grep -oP '<extension[^>]+type="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "component")
|
||||
EXT_ELEMENT=$(grep -oP '<element>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || basename "$MANIFEST" .xml)
|
||||
EXT_CLIENT=$(grep -oP '<extension[^>]+client="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "")
|
||||
EXT_FOLDER=$(grep -oP '<extension[^>]+group="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "")
|
||||
TARGET_PLATFORM=$(grep -oP '<targetplatform[^/]*/>' "$MANIFEST" 2>/dev/null | head -1 || echo "")
|
||||
PHP_MINIMUM=$(grep -oP '<php_minimum>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "")
|
||||
|
||||
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(basename "$MANIFEST" .xml)
|
||||
[ -z "$TARGET_PLATFORM" ] && TARGET_PLATFORM=$(printf '<targetplatform name="joomla" version="5.*" %s>' "/")
|
||||
|
||||
CLIENT_TAG=""
|
||||
[ -n "$EXT_CLIENT" ] && CLIENT_TAG="<client>${EXT_CLIENT}</client>"
|
||||
[ -z "$CLIENT_TAG" ] && ([ "$EXT_TYPE" = "module" ] || [ "$EXT_TYPE" = "plugin" ]) && CLIENT_TAG="<client>site</client>"
|
||||
|
||||
FOLDER_TAG=""
|
||||
[ -n "$EXT_FOLDER" ] && [ "$EXT_TYPE" = "plugin" ] && FOLDER_TAG="<folder>${EXT_FOLDER}</folder>"
|
||||
|
||||
PHP_TAG=""
|
||||
[ -n "$PHP_MINIMUM" ] && PHP_TAG="<php_minimum>${PHP_MINIMUM}</php_minimum>"
|
||||
|
||||
# Version suffix for non-stable
|
||||
DISPLAY_VERSION="$VERSION"
|
||||
case "$STABILITY" in
|
||||
development) DISPLAY_VERSION="${VERSION}-dev" ;;
|
||||
alpha) DISPLAY_VERSION="${VERSION}-alpha" ;;
|
||||
beta) DISPLAY_VERSION="${VERSION}-beta" ;;
|
||||
rc) DISPLAY_VERSION="${VERSION}-rc" ;;
|
||||
esac
|
||||
|
||||
MAJOR=$(echo "$VERSION" | awk -F. '{print $1}')
|
||||
|
||||
# Each stability level has its own release tag
|
||||
case "$STABILITY" in
|
||||
development) RELEASE_TAG="development" ;;
|
||||
alpha) RELEASE_TAG="alpha" ;;
|
||||
beta) RELEASE_TAG="beta" ;;
|
||||
rc) RELEASE_TAG="release-candidate" ;;
|
||||
*) RELEASE_TAG="v${MAJOR}" ;;
|
||||
esac
|
||||
|
||||
PACKAGE_NAME="${EXT_ELEMENT}-${DISPLAY_VERSION}.zip"
|
||||
DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}"
|
||||
INFO_URL="https://github.com/${REPO}"
|
||||
|
||||
# ── Build install-ready ZIP ─────────────────────────────────
|
||||
SOURCE_DIR="src"
|
||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||
if [ -d "$SOURCE_DIR" ]; then
|
||||
cd "$SOURCE_DIR"
|
||||
zip -r "/tmp/${PACKAGE_NAME}" . -x '.ftpignore'
|
||||
cd ..
|
||||
|
||||
SHA256=$(sha256sum "/tmp/${PACKAGE_NAME}" | cut -d' ' -f1)
|
||||
|
||||
# Ensure draft release exists for this major
|
||||
gh release view "$RELEASE_TAG" --json tagName > /dev/null 2>&1 || \
|
||||
gh release create "$RELEASE_TAG" --title "${RELEASE_TAG} (${DISPLAY_VERSION})" --notes "${STABILITY} release" --prerelease --target main 2>/dev/null || true
|
||||
|
||||
# Upload ZIP to the major release
|
||||
gh release upload "$RELEASE_TAG" "/tmp/${PACKAGE_NAME}" --clobber 2>/dev/null || true
|
||||
|
||||
echo "Package: ${PACKAGE_NAME} (SHA: ${SHA256})" >> $GITHUB_STEP_SUMMARY
|
||||
else
|
||||
SHA256=""
|
||||
fi
|
||||
|
||||
# ── Build the new entry ───────────────────────────────────────
|
||||
NEW_ENTRY=""
|
||||
NEW_ENTRY="${NEW_ENTRY} <update>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <name>${EXT_NAME}</name>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <description>${EXT_NAME} (${STABILITY})</description>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <element>${EXT_ELEMENT}</element>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <type>${EXT_TYPE}</type>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <version>${DISPLAY_VERSION}</version>\n"
|
||||
[ -n "$CLIENT_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${CLIENT_TAG}\n"
|
||||
[ -n "$FOLDER_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${FOLDER_TAG}\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <tags>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <tag>${STABILITY}</tag>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} </tags>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <infourl title=\"${EXT_NAME}\">${INFO_URL}</infourl>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <downloads>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <downloadurl type=\"full\" format=\"zip\">${DOWNLOAD_URL}</downloadurl>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} </downloads>\n"
|
||||
[ -n "$SHA256" ] && NEW_ENTRY="${NEW_ENTRY} <sha256>sha256:${SHA256}</sha256>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} ${TARGET_PLATFORM}\n"
|
||||
[ -n "$PHP_TAG" ] && NEW_ENTRY="${NEW_ENTRY} ${PHP_TAG}\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <maintainer>Moko Consulting</maintainer>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} <maintainerurl>https://mokoconsulting.tech</maintainerurl>\n"
|
||||
NEW_ENTRY="${NEW_ENTRY} </update>"
|
||||
|
||||
# ── Write new entry to temp file ───────────────────────────────
|
||||
printf '%b' "$NEW_ENTRY" > /tmp/new_entry.xml
|
||||
|
||||
# ── Merge into updates.xml ─────────────────────────────────────
|
||||
if [ ! -f "updates.xml" ]; then
|
||||
printf '%s\n' '<?xml version="1.0" encoding="utf-8"?>' > updates.xml
|
||||
printf '%s\n' '<updates>' >> updates.xml
|
||||
cat /tmp/new_entry.xml >> updates.xml
|
||||
printf '\n%s\n' '</updates>' >> updates.xml
|
||||
else
|
||||
# Remove existing entry for this stability, insert new one
|
||||
printf 'import re\nstability = "%s"\n' "${STABILITY}" > /tmp/merge_xml.py
|
||||
printf 'with open("updates.xml") as f: content = f.read()\n' >> /tmp/merge_xml.py
|
||||
printf 'with open("/tmp/new_entry.xml") as f: new_entry = f.read()\n' >> /tmp/merge_xml.py
|
||||
printf 'pattern = r" <update>.*?<tag>" + re.escape(stability) + r"</tag>.*?</update>\\n?"\n' >> /tmp/merge_xml.py
|
||||
printf 'content = re.sub(pattern, "", content, flags=re.DOTALL)\n' >> /tmp/merge_xml.py
|
||||
printf 'content = content.replace("</updates>", new_entry + "\\n</updates>")\n' >> /tmp/merge_xml.py
|
||||
printf 'content = re.sub(r"\\n{3,}", "\\n\\n", content)\n' >> /tmp/merge_xml.py
|
||||
printf 'with open("updates.xml", "w") as f: f.write(content)\n' >> /tmp/merge_xml.py
|
||||
python3 /tmp/merge_xml.py 2>/dev/null || {
|
||||
# Fallback: rebuild keeping other stability entries
|
||||
{
|
||||
printf '%s\n' '<?xml version="1.0" encoding="utf-8"?>'
|
||||
printf '%s\n' '<updates>'
|
||||
for TAG in stable rc development; do
|
||||
[ "$TAG" = "${STABILITY}" ] && continue
|
||||
if grep -q "<tag>${TAG}</tag>" updates.xml 2>/dev/null; then
|
||||
sed -n "/<update>/,/<\/update>/{ /<tag>${TAG}<\/tag>/p; }" updates.xml
|
||||
fi
|
||||
done
|
||||
cat /tmp/new_entry.xml
|
||||
printf '\n%s\n' '</updates>'
|
||||
} > /tmp/updates_new.xml
|
||||
mv /tmp/updates_new.xml updates.xml
|
||||
}
|
||||
fi
|
||||
|
||||
# Commit
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git add updates.xml
|
||||
git diff --cached --quiet || {
|
||||
git commit -m "chore: update updates.xml (${STABILITY}: ${DISPLAY_VERSION}) [skip ci]" \
|
||||
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
|
||||
git push
|
||||
}
|
||||
|
||||
- name: SFTP deploy to dev server
|
||||
if: contains(github.ref, 'dev/')
|
||||
env:
|
||||
DEV_HOST: ${{ vars.DEV_FTP_HOST }}
|
||||
DEV_PATH: ${{ vars.DEV_FTP_PATH }}
|
||||
DEV_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }}
|
||||
DEV_USER: ${{ vars.DEV_FTP_USERNAME }}
|
||||
DEV_PORT: ${{ vars.DEV_FTP_PORT }}
|
||||
DEV_KEY: ${{ secrets.DEV_FTP_KEY }}
|
||||
DEV_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
|
||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||
run: |
|
||||
# ── Permission check: admin or maintain role required ──────
|
||||
ACTOR="${{ github.actor }}"
|
||||
REPO="${{ github.repository }}"
|
||||
PERMISSION=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" \
|
||||
--jq '.permission' 2>/dev/null || \
|
||||
gh api "repos/${REPO}/collaborators/${ACTOR}" \
|
||||
--jq '.role' 2>/dev/null || echo "read")
|
||||
case "$PERMISSION" in
|
||||
admin|maintain|write) ;;
|
||||
*)
|
||||
echo "Deploy denied: ${ACTOR} has '${PERMISSION}' — requires admin, maintain, or write"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
[ -z "$DEV_HOST" ] || [ -z "$DEV_PATH" ] && { echo "DEV FTP not configured — skipping SFTP"; exit 0; }
|
||||
|
||||
SOURCE_DIR="src"
|
||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||
[ ! -d "$SOURCE_DIR" ] && exit 0
|
||||
|
||||
PORT="${DEV_PORT:-22}"
|
||||
REMOTE="${DEV_PATH%/}"
|
||||
[ -n "$DEV_SUFFIX" ] && REMOTE="${REMOTE}/${DEV_SUFFIX#/}"
|
||||
|
||||
printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \
|
||||
"$DEV_HOST" "$PORT" "$DEV_USER" "$REMOTE" > /tmp/sftp-config.json
|
||||
if [ -n "$DEV_KEY" ]; then
|
||||
echo "$DEV_KEY" > /tmp/deploy_key && chmod 600 /tmp/deploy_key
|
||||
printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json
|
||||
else
|
||||
printf ',"password":"%s"}' "$DEV_PASS" >> /tmp/sftp-config.json
|
||||
fi
|
||||
|
||||
PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null || true)
|
||||
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards/api/deploy/deploy-joomla.php" ]; then
|
||||
php /tmp/mokostandards/api/deploy/deploy-joomla.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
|
||||
elif [ -f "/tmp/mokostandards/api/deploy/deploy-sftp.php" ]; then
|
||||
php /tmp/mokostandards/api/deploy/deploy-sftp.php --path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json
|
||||
fi
|
||||
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
||||
echo "SFTP deploy to dev complete" >> $GITHUB_STEP_SUMMARY
|
||||
|
||||
- name: Summary
|
||||
if: always()
|
||||
run: |
|
||||
echo "## Joomla Update Server" >> $GITHUB_STEP_SUMMARY
|
||||
echo "" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Stability | \`${STABILITY}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Version | \`${DISPLAY_VERSION}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Element | \`${EXT_ELEMENT}\` |" >> $GITHUB_STEP_SUMMARY
|
||||
echo "| Download | [ZIP](${DOWNLOAD_URL}) |" >> $GITHUB_STEP_SUMMARY
|
||||
747
.gitignore
vendored
747
.gitignore
vendored
@@ -1,3 +1,8 @@
|
||||
# ============================================================
|
||||
# Local task tracking (not version controlled)
|
||||
# ============================================================
|
||||
TODO.md
|
||||
|
||||
# ============================================================
|
||||
# Environment and secrets
|
||||
# ============================================================
|
||||
@@ -23,10 +28,7 @@ secrets/
|
||||
*.log
|
||||
*.pid
|
||||
*.seed
|
||||
*.sql
|
||||
*.sql.gz
|
||||
*.sqlite
|
||||
*.sqlite3
|
||||
|
||||
|
||||
# ============================================================
|
||||
# OS / Editor / IDE cruft
|
||||
@@ -43,6 +45,7 @@ System Volume Information/
|
||||
Icon?
|
||||
.idea/
|
||||
.settings/
|
||||
.claude/
|
||||
.vscode/*
|
||||
!.vscode/tasks.json
|
||||
!.vscode/settings.json.example
|
||||
@@ -70,6 +73,17 @@ todo*
|
||||
# SFTP / sync tools
|
||||
# ============================================================
|
||||
sftp-config*.json
|
||||
sftp-config.json.template
|
||||
sftp-settings.json
|
||||
|
||||
# ============================================================
|
||||
# Sublime SFTP / FTP sync
|
||||
# ============================================================
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
*.sublime-settings
|
||||
.libsass.json
|
||||
*.ffs*
|
||||
|
||||
# ============================================================
|
||||
# Replit / cloud IDE
|
||||
@@ -138,6 +152,7 @@ package-lock.json
|
||||
# PHP / Composer tooling
|
||||
# ============================================================
|
||||
vendor/
|
||||
!src/media/vendor/
|
||||
composer.lock
|
||||
*.phar
|
||||
codeception.phar
|
||||
@@ -185,727 +200,3 @@ venv/
|
||||
*.coverage
|
||||
hypothesis/
|
||||
|
||||
# ============================================================
|
||||
# Joomla Development
|
||||
# ============================================================
|
||||
# Custom user overrides for Joomla development
|
||||
user.css
|
||||
user.js
|
||||
colors_custom.css
|
||||
|
||||
# Joomla Module Builder auto-generated files
|
||||
modulebuilder.txt
|
||||
|
||||
# ============================================================
|
||||
# Joomla Core
|
||||
# ============================================================
|
||||
/.htaccess
|
||||
/administrator/cache/*
|
||||
/administrator/components/com_actionlogs/*
|
||||
/administrator/components/com_admin/*
|
||||
/administrator/components/com_ajax/*
|
||||
/administrator/components/com_associations/*
|
||||
/administrator/components/com_banners/*
|
||||
/administrator/components/com_cache/*
|
||||
/administrator/components/com_categories/*
|
||||
/administrator/components/com_checkin/*
|
||||
/administrator/components/com_config/*
|
||||
/administrator/components/com_contact/*
|
||||
/administrator/components/com_content/*
|
||||
/administrator/components/com_contenthistory/*
|
||||
/administrator/components/com_cpanel/*
|
||||
/administrator/components/com_fields/*
|
||||
/administrator/components/com_finder/*
|
||||
/administrator/components/com_installer/*
|
||||
/administrator/components/com_joomlaupdate/*
|
||||
/administrator/components/com_languages/*
|
||||
/administrator/components/com_login/*
|
||||
/administrator/components/com_media/*
|
||||
/administrator/components/com_menus/*
|
||||
/administrator/components/com_messages/*
|
||||
/administrator/components/com_modules/*
|
||||
/administrator/components/com_newsfeeds/*
|
||||
/administrator/components/com_plugins/*
|
||||
/administrator/components/com_postinstall/*
|
||||
/administrator/components/com_privacy/*
|
||||
/administrator/components/com_redirect/*
|
||||
/administrator/components/com_search/*
|
||||
/administrator/components/com_tags/*
|
||||
/administrator/components/com_templates/*
|
||||
/administrator/components/com_users/*
|
||||
/administrator/help/*
|
||||
/administrator/includes/*
|
||||
/administrator/index.php
|
||||
/administrator/language/en-GB/en-GB.com_actionlogs.ini
|
||||
/administrator/language/en-GB/en-GB.com_actionlogs.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_admin.ini
|
||||
/administrator/language/en-GB/en-GB.com_admin.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_ajax.ini
|
||||
/administrator/language/en-GB/en-GB.com_ajax.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_associations.ini
|
||||
/administrator/language/en-GB/en-GB.com_associations.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_banners.ini
|
||||
/administrator/language/en-GB/en-GB.com_banners.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_cache.ini
|
||||
/administrator/language/en-GB/en-GB.com_cache.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_categories.ini
|
||||
/administrator/language/en-GB/en-GB.com_categories.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_checkin.ini
|
||||
/administrator/language/en-GB/en-GB.com_checkin.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_config.ini
|
||||
/administrator/language/en-GB/en-GB.com_config.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_contact.ini
|
||||
/administrator/language/en-GB/en-GB.com_contact.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_content.ini
|
||||
/administrator/language/en-GB/en-GB.com_content.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_contenthistory.ini
|
||||
/administrator/language/en-GB/en-GB.com_contenthistory.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_cpanel.ini
|
||||
/administrator/language/en-GB/en-GB.com_cpanel.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_fields.ini
|
||||
/administrator/language/en-GB/en-GB.com_fields.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_finder.ini
|
||||
/administrator/language/en-GB/en-GB.com_finder.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_installer.ini
|
||||
/administrator/language/en-GB/en-GB.com_installer.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_joomlaupdate.ini
|
||||
/administrator/language/en-GB/en-GB.com_joomlaupdate.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_languages.ini
|
||||
/administrator/language/en-GB/en-GB.com_languages.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_login.ini
|
||||
/administrator/language/en-GB/en-GB.com_login.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_mailto.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_media.ini
|
||||
/administrator/language/en-GB/en-GB.com_media.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_menus.ini
|
||||
/administrator/language/en-GB/en-GB.com_menus.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_messages.ini
|
||||
/administrator/language/en-GB/en-GB.com_messages.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_modules.ini
|
||||
/administrator/language/en-GB/en-GB.com_modules.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_newsfeeds.ini
|
||||
/administrator/language/en-GB/en-GB.com_newsfeeds.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_plugins.ini
|
||||
/administrator/language/en-GB/en-GB.com_plugins.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_postinstall.ini
|
||||
/administrator/language/en-GB/en-GB.com_postinstall.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_privacy.ini
|
||||
/administrator/language/en-GB/en-GB.com_privacy.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_redirect.ini
|
||||
/administrator/language/en-GB/en-GB.com_redirect.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_search.ini
|
||||
/administrator/language/en-GB/en-GB.com_search.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_tags.ini
|
||||
/administrator/language/en-GB/en-GB.com_tags.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_templates.ini
|
||||
/administrator/language/en-GB/en-GB.com_templates.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_users.ini
|
||||
/administrator/language/en-GB/en-GB.com_users.sys.ini
|
||||
/administrator/language/en-GB/en-GB.com_weblinks.ini
|
||||
/administrator/language/en-GB/en-GB.com_weblinks.sys.ini
|
||||
/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
|
||||
/administrator/language/en-GB/en-GB.mod_feed.ini
|
||||
/administrator/language/en-GB/en-GB.mod_feed.sys.ini
|
||||
/administrator/language/en-GB/en-GB.mod_latest.ini
|
||||
/administrator/language/en-GB/en-GB.mod_latest.sys.ini
|
||||
/administrator/language/en-GB/en-GB.mod_latestactions.ini
|
||||
/administrator/language/en-GB/en-GB.mod_latestactions.sys.ini
|
||||
/administrator/language/en-GB/en-GB.mod_logged.ini
|
||||
/administrator/language/en-GB/en-GB.mod_logged.sys.ini
|
||||
/administrator/language/en-GB/en-GB.mod_login.ini
|
||||
/administrator/language/en-GB/en-GB.mod_login.sys.ini
|
||||
/administrator/language/en-GB/en-GB.mod_menu.ini
|
||||
/administrator/language/en-GB/en-GB.mod_menu.sys.ini
|
||||
/administrator/language/en-GB/en-GB.mod_multilangstatus.ini
|
||||
/administrator/language/en-GB/en-GB.mod_multilangstatus.sys.ini
|
||||
/administrator/language/en-GB/en-GB.mod_online.ini
|
||||
/administrator/language/en-GB/en-GB.mod_online.sys.ini
|
||||
/administrator/language/en-GB/en-GB.mod_popular.ini
|
||||
/administrator/language/en-GB/en-GB.mod_popular.sys.ini
|
||||
/administrator/language/en-GB/en-GB.mod_privacy_dashboard.ini
|
||||
/administrator/language/en-GB/en-GB.mod_privacy_dashboard.sys.ini
|
||||
/administrator/language/en-GB/en-GB.mod_quickicon.ini
|
||||
/administrator/language/en-GB/en-GB.mod_quickicon.sys.ini
|
||||
/administrator/language/en-GB/en-GB.mod_sampledata.ini
|
||||
/administrator/language/en-GB/en-GB.mod_sampledata.sys.ini
|
||||
/administrator/language/en-GB/en-GB.mod_stats_admin.ini
|
||||
/administrator/language/en-GB/en-GB.mod_stats_admin.sys.ini
|
||||
/administrator/language/en-GB/en-GB.mod_status.ini
|
||||
/administrator/language/en-GB/en-GB.mod_status.sys.ini
|
||||
/administrator/language/en-GB/en-GB.mod_submenu.ini
|
||||
/administrator/language/en-GB/en-GB.mod_submenu.sys.ini
|
||||
/administrator/language/en-GB/en-GB.mod_title.ini
|
||||
/administrator/language/en-GB/en-GB.mod_title.sys.ini
|
||||
/administrator/language/en-GB/en-GB.mod_toolbar.ini
|
||||
/administrator/language/en-GB/en-GB.mod_toolbar.sys.ini
|
||||
/administrator/language/en-GB/en-GB.mod_unread.ini
|
||||
/administrator/language/en-GB/en-GB.mod_unread.sys.ini
|
||||
/administrator/language/en-GB/en-GB.mod_version.ini
|
||||
/administrator/language/en-GB/en-GB.mod_version.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_actionlog_joomla.ini
|
||||
/administrator/language/en-GB/en-GB.plg_actionlog_joomla.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_authentication_cookie.ini
|
||||
/administrator/language/en-GB/en-GB.plg_authentication_cookie.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_authentication_example.ini
|
||||
/administrator/language/en-GB/en-GB.plg_authentication_example.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_authentication_gmail.ini
|
||||
/administrator/language/en-GB/en-GB.plg_authentication_gmail.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_authentication_joomla.ini
|
||||
/administrator/language/en-GB/en-GB.plg_authentication_joomla.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_authentication_ldap.ini
|
||||
/administrator/language/en-GB/en-GB.plg_authentication_ldap.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_captcha_recaptcha.ini
|
||||
/administrator/language/en-GB/en-GB.plg_captcha_recaptcha.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_captcha_recaptcha_invisible.ini
|
||||
/administrator/language/en-GB/en-GB.plg_captcha_recaptcha_invisible.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_content_confirmconsent.ini
|
||||
/administrator/language/en-GB/en-GB.plg_content_confirmconsent.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_content_contact.ini
|
||||
/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
|
||||
/administrator/language/en-GB/en-GB.plg_content_finder.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_content_geshi.ini
|
||||
/administrator/language/en-GB/en-GB.plg_content_geshi.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_content_joomla.ini
|
||||
/administrator/language/en-GB/en-GB.plg_content_joomla.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_content_loadmodule.ini
|
||||
/administrator/language/en-GB/en-GB.plg_content_loadmodule.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_content_pagebreak.ini
|
||||
/administrator/language/en-GB/en-GB.plg_content_pagebreak.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_content_pagenavigation.ini
|
||||
/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
|
||||
/administrator/language/en-GB/en-GB.plg_editors-xtd_contact.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_editors-xtd_fields.ini
|
||||
/administrator/language/en-GB/en-GB.plg_editors-xtd_fields.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_editors-xtd_image.ini
|
||||
/administrator/language/en-GB/en-GB.plg_editors-xtd_image.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_editors-xtd_menu.ini
|
||||
/administrator/language/en-GB/en-GB.plg_editors-xtd_menu.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_editors-xtd_module.ini
|
||||
/administrator/language/en-GB/en-GB.plg_editors-xtd_module.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_editors-xtd_pagebreak.ini
|
||||
/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_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
|
||||
/administrator/language/en-GB/en-GB.plg_fields_calendar.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_checkboxes.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_checkboxes.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_color.ini
|
||||
/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_imagelist.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_imagelist.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_integer.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_integer.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_list.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_list.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_media.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_media.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_radio.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_radio.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_repeatable.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_repeatable.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_sql.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_sql.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_text.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_text.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_textarea.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_textarea.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_url.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_url.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_user.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_user.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_usergrouplist.ini
|
||||
/administrator/language/en-GB/en-GB.plg_fields_usergrouplist.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_finder_categories.ini
|
||||
/administrator/language/en-GB/en-GB.plg_finder_categories.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_finder_contacts.ini
|
||||
/administrator/language/en-GB/en-GB.plg_finder_contacts.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_finder_content.ini
|
||||
/administrator/language/en-GB/en-GB.plg_finder_content.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_finder_newsfeeds.ini
|
||||
/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_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_privacy_actionlogs.ini
|
||||
/administrator/language/en-GB/en-GB.plg_privacy_actionlogs.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_privacy_consents.ini
|
||||
/administrator/language/en-GB/en-GB.plg_privacy_consents.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_privacy_contact.ini
|
||||
/administrator/language/en-GB/en-GB.plg_privacy_contact.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_privacy_content.ini
|
||||
/administrator/language/en-GB/en-GB.plg_privacy_content.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_privacy_message.ini
|
||||
/administrator/language/en-GB/en-GB.plg_privacy_message.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_privacy_user.ini
|
||||
/administrator/language/en-GB/en-GB.plg_privacy_user.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_quickicon_extensionupdate.ini
|
||||
/administrator/language/en-GB/en-GB.plg_quickicon_extensionupdate.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_quickicon_joomlaupdate.ini
|
||||
/administrator/language/en-GB/en-GB.plg_quickicon_joomlaupdate.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_quickicon_phpversioncheck.ini
|
||||
/administrator/language/en-GB/en-GB.plg_quickicon_phpversioncheck.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_quickicon_privacycheck.ini
|
||||
/administrator/language/en-GB/en-GB.plg_quickicon_privacycheck.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_sampledata_blog.ini
|
||||
/administrator/language/en-GB/en-GB.plg_sampledata_blog.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_search_categories.ini
|
||||
/administrator/language/en-GB/en-GB.plg_search_categories.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_search_contacts.ini
|
||||
/administrator/language/en-GB/en-GB.plg_search_contacts.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_search_content.ini
|
||||
/administrator/language/en-GB/en-GB.plg_search_content.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_search_newsfeeds.ini
|
||||
/administrator/language/en-GB/en-GB.plg_search_newsfeeds.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_search_tags.ini
|
||||
/administrator/language/en-GB/en-GB.plg_search_tags.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_search_weblinks.ini
|
||||
/administrator/language/en-GB/en-GB.plg_search_weblinks.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_actionlogs.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_actionlogs.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_cache.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_cache.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_debug.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_debug.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_fields.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_fields.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_highlight.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_highlight.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_languagecode.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_languagecode.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_languagefilter.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_languagefilter.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_log.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_log.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_logout.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_logout.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_logrotation.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_logrotation.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_p3p.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_p3p.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_privacyconsent.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_privacyconsent.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_redirect.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_redirect.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_remember.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_remember.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_sef.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_sef.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_sessiongc.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_sessiongc.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_stats.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_stats.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_updatenotification.ini
|
||||
/administrator/language/en-GB/en-GB.plg_system_updatenotification.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_twofactorauth_totp.ini
|
||||
/administrator/language/en-GB/en-GB.plg_twofactorauth_totp.sys.ini
|
||||
/administrator/language/en-GB/en-GB.plg_twofactorauth_yubikey.ini
|
||||
/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.xml
|
||||
/administrator/language/en-GB/index.html
|
||||
/administrator/language/index.html
|
||||
/administrator/language/overrides/*
|
||||
/administrator/logs/*
|
||||
/administrator/manifests/files/*
|
||||
/administrator/manifests/libraries/*
|
||||
/administrator/manifests/packages/*
|
||||
/administrator/modules/mod_custom/*
|
||||
/administrator/modules/mod_feed/*
|
||||
/administrator/modules/mod_latest/*
|
||||
/administrator/modules/mod_latestactions/*
|
||||
/administrator/modules/mod_logged/*
|
||||
/administrator/modules/mod_login/*
|
||||
/administrator/modules/mod_menu/*
|
||||
/administrator/modules/mod_multilangstatus/*
|
||||
/administrator/modules/mod_online/*
|
||||
/administrator/modules/mod_popular/*
|
||||
/administrator/modules/mod_privacy_dashboard/*
|
||||
/administrator/modules/mod_quickicon/*
|
||||
/administrator/modules/mod_sampledata/*
|
||||
/administrator/modules/mod_stats_admin/*
|
||||
/administrator/modules/mod_status/*
|
||||
/administrator/modules/mod_submenu/*
|
||||
/administrator/modules/mod_title/*
|
||||
/administrator/modules/mod_toolbar/*
|
||||
/administrator/modules/mod_unread/*
|
||||
/administrator/modules/mod_version/*
|
||||
/administrator/templates/hathor/*
|
||||
/administrator/templates/isis/*
|
||||
/administrator/templates/system/*
|
||||
/bin/*
|
||||
/cache/*
|
||||
/cli/*
|
||||
/components/com_ajax/*
|
||||
/components/com_banners/*
|
||||
/components/com_config/*
|
||||
/components/com_contact/*
|
||||
/components/com_content/*
|
||||
/components/com_contenthistory/*
|
||||
/components/com_fields/*
|
||||
/components/com_finder/*
|
||||
/components/com_mailto/*
|
||||
/components/com_media/*
|
||||
/components/com_newsfeeds/*
|
||||
/components/com_privacy/*
|
||||
/components/com_search/*
|
||||
/components/com_tags/*
|
||||
/components/com_users/*
|
||||
/components/com_weblinks/*
|
||||
/components/com_wrapper/*
|
||||
/images/banners/*
|
||||
/images/headers/*
|
||||
/images/joomla*
|
||||
/images/sampledata/*
|
||||
/images/index.html
|
||||
/includes/*
|
||||
/installation/*
|
||||
/language/en-GB/en-GB.com_ajax.ini
|
||||
/language/en-GB/en-GB.com_config.ini
|
||||
/language/en-GB/en-GB.com_contact.ini
|
||||
/language/en-GB/en-GB.com_content.ini
|
||||
/language/en-GB/en-GB.com_finder.ini
|
||||
/language/en-GB/en-GB.com_mailto.ini
|
||||
/language/en-GB/en-GB.com_media.ini
|
||||
/language/en-GB/en-GB.com_messages.ini
|
||||
/language/en-GB/en-GB.com_newsfeeds.ini
|
||||
/language/en-GB/en-GB.com_privacy.ini
|
||||
/language/en-GB/en-GB.com_search.ini
|
||||
/language/en-GB/en-GB.com_tags.ini
|
||||
/language/en-GB/en-GB.com_users.ini
|
||||
/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.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_phpmailer.sys.ini
|
||||
/language/en-GB/en-GB.lib_phputf8.sys.ini
|
||||
/language/en-GB/en-GB.lib_simplepie.sys.ini
|
||||
/language/en-GB/en-GB.localise.php
|
||||
/language/en-GB/en-GB.mod_articles_archive.ini
|
||||
/language/en-GB/en-GB.mod_articles_archive.sys.ini
|
||||
/language/en-GB/en-GB.mod_articles_categories.ini
|
||||
/language/en-GB/en-GB.mod_articles_categories.sys.ini
|
||||
/language/en-GB/en-GB.mod_articles_category.ini
|
||||
/language/en-GB/en-GB.mod_articles_category.sys.ini
|
||||
/language/en-GB/en-GB.mod_articles_latest.ini
|
||||
/language/en-GB/en-GB.mod_articles_latest.sys.ini
|
||||
/language/en-GB/en-GB.mod_articles_news.ini
|
||||
/language/en-GB/en-GB.mod_articles_news.sys.ini
|
||||
/language/en-GB/en-GB.mod_articles_popular.ini
|
||||
/language/en-GB/en-GB.mod_articles_popular.sys.ini
|
||||
/language/en-GB/en-GB.mod_banners.ini
|
||||
/language/en-GB/en-GB.mod_banners.sys.ini
|
||||
/language/en-GB/en-GB.mod_breadcrumbs.ini
|
||||
/language/en-GB/en-GB.mod_breadcrumbs.sys.ini
|
||||
/language/en-GB/en-GB.mod_custom.ini
|
||||
/language/en-GB/en-GB.mod_custom.sys.ini
|
||||
/language/en-GB/en-GB.mod_feed.ini
|
||||
/language/en-GB/en-GB.mod_feed.sys.ini
|
||||
/language/en-GB/en-GB.mod_finder.ini
|
||||
/language/en-GB/en-GB.mod_finder.sys.ini
|
||||
/language/en-GB/en-GB.mod_footer.ini
|
||||
/language/en-GB/en-GB.mod_footer.sys.ini
|
||||
/language/en-GB/en-GB.mod_languages.ini
|
||||
/language/en-GB/en-GB.mod_languages.sys.ini
|
||||
/language/en-GB/en-GB.mod_login.ini
|
||||
/language/en-GB/en-GB.mod_login.sys.ini
|
||||
/language/en-GB/en-GB.mod_menu.ini
|
||||
/language/en-GB/en-GB.mod_menu.sys.ini
|
||||
/language/en-GB/en-GB.mod_random_image.ini
|
||||
/language/en-GB/en-GB.mod_random_image.sys.ini
|
||||
/language/en-GB/en-GB.mod_related_items.ini
|
||||
/language/en-GB/en-GB.mod_related_items.sys.ini
|
||||
/language/en-GB/en-GB.mod_search.ini
|
||||
/language/en-GB/en-GB.mod_search.sys.ini
|
||||
/language/en-GB/en-GB.mod_stats.ini
|
||||
/language/en-GB/en-GB.mod_stats.sys.ini
|
||||
/language/en-GB/en-GB.mod_syndicate.ini
|
||||
/language/en-GB/en-GB.mod_syndicate.sys.ini
|
||||
/language/en-GB/en-GB.mod_tags_popular.ini
|
||||
/language/en-GB/en-GB.mod_tags_popular.sys.ini
|
||||
/language/en-GB/en-GB.mod_tags_similar.ini
|
||||
/language/en-GB/en-GB.mod_tags_similar.sys.ini
|
||||
/language/en-GB/en-GB.mod_users_latest.ini
|
||||
/language/en-GB/en-GB.mod_users_latest.sys.ini
|
||||
/language/en-GB/en-GB.mod_weblinks.ini
|
||||
/language/en-GB/en-GB.mod_weblinks.sys.ini
|
||||
/language/en-GB/en-GB.mod_whosonline.ini
|
||||
/language/en-GB/en-GB.mod_whosonline.sys.ini
|
||||
/language/en-GB/en-GB.mod_wrapper.ini
|
||||
/language/en-GB/en-GB.mod_wrapper.sys.ini
|
||||
/language/en-GB/en-GB.tpl_atomic.ini
|
||||
/language/en-GB/en-GB.tpl_atomic.sys.ini
|
||||
/language/en-GB/en-GB.tpl_beez3.ini
|
||||
/language/en-GB/en-GB.tpl_beez3.sys.ini
|
||||
/language/en-GB/en-GB.tpl_beez5.ini
|
||||
/language/en-GB/en-GB.tpl_beez5.sys.ini
|
||||
/language/en-GB/en-GB.tpl_beez_20.ini
|
||||
/language/en-GB/en-GB.tpl_beez_20.sys.ini
|
||||
/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/index.html
|
||||
/language/index.html
|
||||
/language/overrides/*
|
||||
/layouts/joomla/*
|
||||
/libraries/cms/*
|
||||
/libraries/fof/*
|
||||
/libraries/idna_convert/*
|
||||
/libraries/joomla/*
|
||||
/libraries/legacy/*
|
||||
/libraries/php-encryption/*
|
||||
/libraries/phpass/*
|
||||
/libraries/phpmailer/*
|
||||
/libraries/phputf8/*
|
||||
/libraries/simplepie/*
|
||||
/libraries/vendor/*
|
||||
/libraries/classmap.php
|
||||
/libraries/cms.php
|
||||
/libraries/import.legacy.php
|
||||
/libraries/import.php
|
||||
/libraries/index.html
|
||||
/libraries/loader.php
|
||||
/media/cms/*
|
||||
/media/com_associations/*
|
||||
/media/com_contact/*
|
||||
/media/com_content/*
|
||||
/media/com_contenthistory/*
|
||||
/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/*
|
||||
/media/jui/*
|
||||
/media/mailto/*
|
||||
/media/media/*
|
||||
/media/mod_languages/*
|
||||
/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/*
|
||||
/modules/mod_articles_archive/*
|
||||
/modules/mod_articles_categories/*
|
||||
/modules/mod_articles_category/*
|
||||
/modules/mod_articles_latest/*
|
||||
/modules/mod_articles_news/*
|
||||
/modules/mod_articles_popular/*
|
||||
/modules/mod_banners/*
|
||||
/modules/mod_breadcrumbs/*
|
||||
/modules/mod_custom/*
|
||||
/modules/mod_feed/*
|
||||
/modules/mod_finder/*
|
||||
/modules/mod_footer/*
|
||||
/modules/mod_languages/*
|
||||
/modules/mod_login/*
|
||||
/modules/mod_menu/*
|
||||
/modules/mod_random_image/*
|
||||
/modules/mod_related_items/*
|
||||
/modules/mod_search/*
|
||||
/modules/mod_stats/*
|
||||
/modules/mod_syndicate/*
|
||||
/modules/mod_tags_popular/*
|
||||
/modules/mod_tags_similar/*
|
||||
/modules/mod_users_latest/*
|
||||
/modules/mod_weblinks/*
|
||||
/modules/mod_whosonline/*
|
||||
/modules/mod_wrapper/*
|
||||
/plugins/actionlog/joomla/*
|
||||
/plugins/authentication/cookie/*
|
||||
/plugins/authentication/example/*
|
||||
/plugins/authentication/gmail/*
|
||||
/plugins/authentication/joomla/*
|
||||
/plugins/authentication/ldap/*
|
||||
/plugins/captcha/recaptcha/*
|
||||
/plugins/captcha/recaptcha_invisible/*
|
||||
/plugins/content/confirmconsent/*
|
||||
/plugins/content/contact/*
|
||||
/plugins/content/emailcloak/*
|
||||
/plugins/content/example/*
|
||||
/plugins/content/fields/*
|
||||
/plugins/content/finder/*
|
||||
/plugins/content/geshi/*
|
||||
/plugins/content/joomla/*
|
||||
/plugins/content/loadmodule/*
|
||||
/plugins/content/pagebreak/*
|
||||
/plugins/content/pagenavigation/*
|
||||
/plugins/content/vote/*
|
||||
/plugins/editors/codemirror/*
|
||||
/plugins/editors/none/*
|
||||
/plugins/editors/tinymce/*
|
||||
/plugins/editors-xtd/article/*
|
||||
/plugins/editors-xtd/contact/*
|
||||
/plugins/editors-xtd/fields/*
|
||||
/plugins/editors-xtd/image/*
|
||||
/plugins/editors-xtd/menu/*
|
||||
/plugins/editors-xtd/module/*
|
||||
/plugins/editors-xtd/pagebreak/*
|
||||
/plugins/editors-xtd/readmore/*
|
||||
/plugins/extension/example/*
|
||||
/plugins/extension/joomla/*
|
||||
/plugins/fields/calendar/*
|
||||
/plugins/fields/checkboxes/*
|
||||
/plugins/fields/color/*
|
||||
/plugins/fields/editor/*
|
||||
/plugins/fields/imagelist/*
|
||||
/plugins/fields/integer/*
|
||||
/plugins/fields/list/*
|
||||
/plugins/fields/media/*
|
||||
/plugins/fields/radio/*
|
||||
/plugins/fields/repeatable/*
|
||||
/plugins/fields/sql/*
|
||||
/plugins/fields/text/*
|
||||
/plugins/fields/textarea/*
|
||||
/plugins/fields/url/*
|
||||
/plugins/fields/user/*
|
||||
/plugins/fields/usergrouplist/*
|
||||
/plugins/finder/categories/*
|
||||
/plugins/finder/contacts/*
|
||||
/plugins/finder/content/*
|
||||
/plugins/finder/newsfeeds/*
|
||||
/plugins/finder/tags/*
|
||||
/plugins/installer/folderinstaller/*
|
||||
/plugins/installer/packageinstaller/*
|
||||
/plugins/installer/urlinstaller/*
|
||||
/plugins/privacy/actionlogs/*
|
||||
/plugins/privacy/consents/*
|
||||
/plugins/privacy/contact/*
|
||||
/plugins/privacy/content/*
|
||||
/plugins/privacy/message/*
|
||||
/plugins/privacy/user/*
|
||||
/plugins/quickicon/extensionupdate/*
|
||||
/plugins/quickicon/joomlaupdate/*
|
||||
/plugins/quickicon/phpversioncheck/*
|
||||
/plugins/quickicon/privacycheck/*
|
||||
/plugins/quickicon/index.html
|
||||
/plugins/sampledata/blog/*
|
||||
/plugins/search/categories/*
|
||||
/plugins/search/contacts/*
|
||||
/plugins/search/content/*
|
||||
/plugins/search/newsfeeds/*
|
||||
/plugins/search/tags/*
|
||||
/plugins/search/weblinks/*
|
||||
/plugins/search/index.html
|
||||
/plugins/system/actionlogs/*
|
||||
/plugins/system/cache/*
|
||||
/plugins/system/debug/*
|
||||
/plugins/system/fields/*
|
||||
/plugins/system/highlight/*
|
||||
/plugins/system/languagecode/*
|
||||
/plugins/system/languagefilter/*
|
||||
/plugins/system/log/*
|
||||
/plugins/system/logout/*
|
||||
/plugins/system/logrotation/*
|
||||
/plugins/system/p3p/*
|
||||
/plugins/system/privacyconsent/*
|
||||
/plugins/system/redirect/*
|
||||
/plugins/system/remember/*
|
||||
/plugins/system/sef/*
|
||||
/plugins/system/sessiongc/*
|
||||
/plugins/system/stats/*
|
||||
/plugins/system/updatenotification/*
|
||||
/plugins/system/index.html
|
||||
/plugins/twofactorauth/totp/*
|
||||
/plugins/twofactorauth/yubikey/*
|
||||
/plugins/user/contactcreator/*
|
||||
/plugins/user/example/*
|
||||
/plugins/user/joomla/*
|
||||
/plugins/user/profile/*
|
||||
/plugins/user/terms/*
|
||||
/plugins/user/index.html
|
||||
/plugins/user/index.html
|
||||
/plugins/index.html
|
||||
/templates/beez3/*
|
||||
/templates/protostar/*
|
||||
/templates/system/*
|
||||
/templates/index.html
|
||||
/tmp/*
|
||||
/configuration.php
|
||||
/htaccess.txt
|
||||
/index.php
|
||||
/joomla.xml
|
||||
/LICENSE.txt
|
||||
/README.txt
|
||||
/robots.txt.dist
|
||||
/web.config.txt
|
||||
|
||||
# ============================================================
|
||||
# Keep-empty folders helper
|
||||
# ============================================================
|
||||
!.gitkeep
|
||||
|
||||
# ── MokoStandards sync (auto-appended) ────────────────────────────────
|
||||
/.claude
|
||||
|
||||
# ── MokoStandards sync (auto-appended) ────────────────────────────────
|
||||
!src/media/vendor/
|
||||
|
||||
20
.mokostandards
Normal file
20
.mokostandards
Normal file
@@ -0,0 +1,20 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
# 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.04.01
|
||||
# BRIEF: Governance attachment template — synced to .mokostandards in every governed repository
|
||||
# NOTE: Tokens replaced at sync time: mokoconsulting-tech, MokoCassiopeia, waas-component, 04.04.00
|
||||
#
|
||||
# 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.04.00"
|
||||
platform: "waas-component"
|
||||
governed_repo: "mokoconsulting-tech/MokoCassiopeia"
|
||||
@@ -1,98 +1,87 @@
|
||||
<!--
|
||||
Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
||||
<!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
|
||||
This file is part of a Moko Consulting project.
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
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 (./LICENSE.md).
|
||||
|
||||
# FILE INFORMATION
|
||||
DEFGROUP: Joomla.Template
|
||||
INGROUP: MokoCassiopeia.Governance
|
||||
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
||||
FILE: CODE_OF_CONDUCT.md
|
||||
VERSION: 03.06.03
|
||||
BRIEF: Contributor code of conduct for the MokoCassiopeia project.
|
||||
PATH: /CODE_OF_CONDUCT.md
|
||||
NOTE: This document defines behavioral expectations and enforcement processes.
|
||||
DEFGROUP:
|
||||
INGROUP: Project.Documentation
|
||||
REPO:
|
||||
VERSION: 04.04.01
|
||||
PATH: ./CODE_OF_CONDUCT.md
|
||||
BRIEF: Reference + packaging repo for Moko Consulting Developer GPT Other Default
|
||||
-->
|
||||
# Code of Conduct
|
||||
|
||||
## Code of Conduct
|
||||
## 1. Purpose
|
||||
|
||||
This Code of Conduct establishes expectations for behavior within the MokoCassiopeia project community. The objective is to maintain a professional, inclusive, and respectful environment aligned with open source governance best practices.
|
||||
The purpose of this Code of Conduct is to ensure a safe, inclusive, and respectful environment for all contributors and participants in Moko Consulting projects. This applies to all interactions, whether in repositories, issue trackers, documentation, meetings, or community spaces.
|
||||
|
||||
## Scope
|
||||
## 2. Our Standards
|
||||
|
||||
This Code of Conduct applies to all project spaces, including:
|
||||
Participants are expected to uphold behaviors that strengthen our community, including:
|
||||
|
||||
* GitHub repositories, issues, pull requests, discussions, and security advisories.
|
||||
* Project documentation, workflows, and release processes.
|
||||
* Any communication channels officially associated with the project.
|
||||
Demonstrating empathy and respect toward others.
|
||||
Being inclusive of diverse viewpoints and backgrounds.
|
||||
Gracefully accepting constructive feedback.
|
||||
Prioritizing collaboration over conflict.
|
||||
Showing professionalism in all interactions.
|
||||
|
||||
## Our Standards
|
||||
### Unacceptable behavior includes:
|
||||
|
||||
Participants are expected to:
|
||||
Harassment, discrimination, or derogatory comments.
|
||||
Threatening or violent language or actions.
|
||||
Disruptive, aggressive, or intentionally harmful behavior.
|
||||
Publishing others’ private information without permission.
|
||||
Any behavior that violates applicable laws.
|
||||
|
||||
* Communicate professionally and respectfully.
|
||||
* Provide constructive feedback focused on technical merit and project objectives.
|
||||
* Respect differing viewpoints, experience levels, and backgrounds.
|
||||
* Follow documented contribution, security, and governance policies.
|
||||
## 3. Responsibilities of Maintainers
|
||||
|
||||
Unacceptable behavior includes:
|
||||
Maintainers are responsible for:
|
||||
|
||||
* Harassment, discrimination, or exclusionary conduct.
|
||||
* Personal attacks, insults, or inflammatory comments.
|
||||
* Publishing private information without consent.
|
||||
* Disruptive behavior that materially interferes with project operations.
|
||||
Clarifying acceptable behavior.
|
||||
Taking appropriate corrective action when unacceptable behavior occurs.
|
||||
Removing, editing, or rejecting contributions that violate this Code.
|
||||
Temporarily or permanently banning contributors who engage in repeated or severe violations.
|
||||
|
||||
## Enforcement Responsibilities
|
||||
## 4. Scope
|
||||
|
||||
Project maintainers are responsible for:
|
||||
This Code applies to:
|
||||
|
||||
* Clarifying standards when questions arise.
|
||||
* Taking appropriate and proportionate corrective action when violations occur.
|
||||
* Maintaining confidentiality to the extent practical during investigations.
|
||||
All Moko Consulting repositories.
|
||||
All documentation and collaboration platforms.
|
||||
Public and private communication related to project activities.
|
||||
Any representation of Moko Consulting in online or offline spaces.
|
||||
|
||||
## Reporting
|
||||
## 5. Enforcement
|
||||
|
||||
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported through:
|
||||
Instances of misconduct may be reported to:
|
||||
**[hello@mokoconsulting.tech](mailto:hello@mokoconsulting.tech)**
|
||||
|
||||
* Email: `hello@mokoconsulting.tech` with subject `CODE OF CONDUCT: MokoCassiopeia`.
|
||||
All reports will be reviewed and investigated promptly and fairly. Maintainers are obligated to maintain confidentiality where possible.
|
||||
|
||||
Reports should include relevant context, links, screenshots, or other supporting information.
|
||||
Consequences may include:
|
||||
|
||||
## Enforcement Guidelines
|
||||
A warning.
|
||||
Required training or mediation.
|
||||
Temporary or permanent bans.
|
||||
Escalation to legal authorities when required.
|
||||
|
||||
Corrective actions may include, but are not limited to:
|
||||
## 6. Acknowledgements
|
||||
|
||||
* Private warning or request for corrective action.
|
||||
* Temporary or permanent restriction from project participation.
|
||||
* Removal of content that violates this Code of Conduct.
|
||||
This Code of Conduct is inspired by widely adopted community guidelines, including the Contributor Covenant and major open-source collaboration standards.
|
||||
|
||||
Decisions are made based on impact, severity, and pattern of behavior.
|
||||
## 7. Related Documents
|
||||
|
||||
## No Retaliation
|
||||
[Governance Guide](./docs-governance.md)
|
||||
[Contributor Guide](./docs-contributing.md)
|
||||
[Documentation Index](./docs-index.md)
|
||||
|
||||
Retaliation against individuals who report concerns in good faith is not tolerated. Any retaliatory behavior will be treated as a separate violation.
|
||||
|
||||
## Jurisdiction
|
||||
|
||||
This project is managed from Tennessee, USA. This statement is informational and does not constitute legal advice.
|
||||
|
||||
---
|
||||
|
||||
## Metadata
|
||||
|
||||
* **Document:** CODE_OF_CONDUCT.md
|
||||
* **Repository:** [https://github.com/mokoconsulting-tech/MokoCassiopeia](https://github.com/mokoconsulting-tech/MokoCassiopeia)
|
||||
* **Path:** /CODE_OF_CONDUCT.md
|
||||
* **Owner:** Moko Consulting
|
||||
* **Version:** 03.06.00
|
||||
* **Status:** Active
|
||||
* **Effective Date:** 2025-12-18
|
||||
* **Last Reviewed:** 2025-12-18
|
||||
|
||||
## Revision History
|
||||
|
||||
| Date | Change Summary | Author |
|
||||
| ---------- | ----------------------------------------------------------------------------- | --------------- |
|
||||
| 2025-12-18 | Initial publication of contributor conduct standards and enforcement process. | Moko Consulting |
|
||||
This Code of Conduct is a living document and may be updated following the established Change Management process.
|
||||
|
||||
187
CONTRIBUTING.md
187
CONTRIBUTING.md
@@ -1,145 +1,128 @@
|
||||
<!--
|
||||
Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
||||
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
|
||||
This file is part of a Moko Consulting project.
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the IMPLIED WARRANTY of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License (./LICENSE).
|
||||
|
||||
# FILE INFORMATION
|
||||
DEFGROUP: Joomla.Template
|
||||
INGROUP: MokoCassiopeia.Governance
|
||||
DEFGROUP: {{DEFGROUP}}
|
||||
INGROUP: Project.Documentation
|
||||
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
||||
FILE: CONTRIBUTING.md
|
||||
VERSION: 03.06.03
|
||||
BRIEF: Contribution guidelines for the MokoCassiopeia project.
|
||||
PATH: /CONTRIBUTING.md
|
||||
NOTE: This document defines contribution workflow, standards, and governance alignment.
|
||||
VERSION: 04.04.00
|
||||
PATH: ./CONTRIBUTING.md
|
||||
BRIEF: How to contribute; branch strategy, commit conventions, PR workflow, and release pipeline
|
||||
-->
|
||||
|
||||
## Contributing
|
||||
# Contributing
|
||||
|
||||
This document defines how to contribute to the MokoCassiopeia project. The goal is to ensure changes are reviewable, auditable, and aligned with project governance and release processes.
|
||||
Thank you for your interest in contributing to **MokoCassiopeia**!
|
||||
|
||||
## Scope
|
||||
This repository is governed by **[MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards)** — the authoritative source of coding standards, workflows, and policies for all Moko Consulting repositories.
|
||||
|
||||
These guidelines apply to all contributions, including:
|
||||
## Branch Strategy
|
||||
|
||||
* Source code changes
|
||||
* Documentation updates
|
||||
* Bug reports and enhancement proposals
|
||||
| Branch | Purpose | Deploys To |
|
||||
|--------|---------|------------|
|
||||
| `main` | Bleeding edge — all development merges here | CI only |
|
||||
| `dev/XX.YY.ZZ` | Feature development | Dev server (version: "development") |
|
||||
| `version/XX.YY` | Stable frozen snapshot | Demo + RS servers |
|
||||
|
||||
## Prerequisites
|
||||
### Development Workflow
|
||||
|
||||
Contributors are expected to:
|
||||
|
||||
* Have a working understanding of Joomla template structure.
|
||||
* Be familiar with Git and GitHub pull request workflows.
|
||||
* Review repository governance documents prior to submitting changes.
|
||||
* Set up the development environment using the provided tools.
|
||||
|
||||
### Quick Setup
|
||||
|
||||
For first-time contributors:
|
||||
|
||||
```bash
|
||||
# Clone the repository
|
||||
git clone https://github.com/mokoconsulting-tech/MokoCassiopeia.git
|
||||
cd MokoCassiopeia
|
||||
```
|
||||
1. Create branch: git checkout -b dev/XX.YY.ZZ/my-feature
|
||||
2. Develop + test (dev server auto-deploys on push)
|
||||
3. Open PR → main (squash merge only)
|
||||
4. Auto-release (version branch + tag + GitHub Release created automatically)
|
||||
```
|
||||
|
||||
See [docs/QUICK_START.md](./docs/QUICK_START.md) for detailed setup instructions.
|
||||
### Branch Naming
|
||||
|
||||
## Development Tools
|
||||
| Prefix | Use |
|
||||
|--------|-----|
|
||||
| `dev/XX.YY.ZZ` | Feature development (e.g., `dev/02.00.00/add-extrafields`) |
|
||||
| `version/XX.YY` | Stable release (auto-created, never manually pushed) |
|
||||
| `chore/` | Automated sync branches (managed by MokoStandards) |
|
||||
|
||||
The repository provides several tools to streamline development:
|
||||
> **Never use** `feature/`, `hotfix/`, or `release/` prefixes — they are not part of the MokoStandards branch strategy.
|
||||
|
||||
* **Pre-commit Hooks**: Automatic local validation before commits
|
||||
## Commit Conventions
|
||||
|
||||
## Contribution Workflow
|
||||
Use [conventional commits](https://www.conventionalcommits.org/):
|
||||
|
||||
1. Fork the repository.
|
||||
2. Create a branch from the active development branch.
|
||||
3. Make focused, minimal changes that address a single concern.
|
||||
4. Submit a pull request with a clear description of intent and impact.
|
||||
```
|
||||
feat(scope): add new extrafield for invoice tracking
|
||||
fix(sql): correct column type in llx_mytable
|
||||
docs(readme): update installation instructions
|
||||
chore(deps): bump enterprise library to 04.02.30
|
||||
```
|
||||
|
||||
Direct commits to protected branches are not permitted.
|
||||
**Valid types:** `feat` | `fix` | `docs` | `chore` | `ci` | `refactor` | `style` | `test` | `perf` | `revert` | `build`
|
||||
|
||||
## Branching and Versioning
|
||||
## Pull Request Workflow
|
||||
|
||||
* Development work occurs on designated development branches.
|
||||
* Releases are produced from versioned branches following repository standards.
|
||||
* Contributors should not bump version numbers unless explicitly requested.
|
||||
1. **Branch** from `main` using `dev/XX.YY.ZZ/description` format
|
||||
2. **Bump** the patch version in `README.md` before opening the PR
|
||||
3. **Title** must be a valid conventional commit subject line
|
||||
4. **Target** `main` — squash merge only (merge commits are disabled)
|
||||
5. **CI checks** must pass before merge
|
||||
|
||||
## Coding and Formatting Standards
|
||||
### What Happens on Merge
|
||||
|
||||
All contributions must:
|
||||
When your PR is merged to `main`, these workflows run automatically:
|
||||
|
||||
* Follow Joomla coding standards where applicable.
|
||||
* Conform to Moko Consulting repository standards for headers, metadata, and file structure.
|
||||
* Avoid introducing tabs, inconsistent path separators, or non portable assumptions.
|
||||
1. **sync-version-on-merge** — auto-bumps patch version, propagates to all file headers
|
||||
2. **auto-release** — creates `version/XX.YY` branch, git tag, and GitHub Release
|
||||
3. **deploy-demo / deploy-rs** — deploys to demo and RS servers (if `src/**` changed)
|
||||
|
||||
Automated checks may reject changes that do not meet these requirements.
|
||||
## Coding Standards
|
||||
|
||||
## Documentation Standards
|
||||
All contributions must follow [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards):
|
||||
|
||||
Documentation changes must:
|
||||
| Standard | Reference |
|
||||
|----------|-----------|
|
||||
| Coding Style | [coding-style-guide.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/coding-style-guide.md) |
|
||||
| File Headers | [file-header-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/file-header-standards.md) |
|
||||
| Branching | [branch-release-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/branch-release-strategy.md) |
|
||||
| Merge Strategy | [merge-strategy.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/merge-strategy.md) |
|
||||
| Scripting | [scripting-standards.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/policy/scripting-standards.md) |
|
||||
| Build & Release | [build-release.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/workflows/build-release.md) |
|
||||
|
||||
* Include required metadata and revision history sections.
|
||||
* Avoid embedding version numbers in revision history tables.
|
||||
* Preserve existing structure unless a structural change is explicitly proposed.
|
||||
## PR Checklist
|
||||
|
||||
## Commit Messages
|
||||
- [ ] Branch named `dev/XX.YY.ZZ/description`
|
||||
- [ ] Patch version bumped in `README.md`
|
||||
- [ ] Conventional commit format for PR title
|
||||
- [ ] All new files have FILE INFORMATION headers
|
||||
- [ ] `declare(strict_types=1)` in all PHP files
|
||||
- [ ] PHPDoc on all public methods
|
||||
- [ ] Tests pass
|
||||
- [ ] CHANGELOG.md updated
|
||||
- [ ] No secrets, tokens, or credentials committed
|
||||
|
||||
Commit messages should:
|
||||
## Custom Workflows
|
||||
|
||||
* Be concise and descriptive.
|
||||
* Focus on what changed and why.
|
||||
* Avoid referencing internal issue trackers unless required.
|
||||
Place repo-specific workflows in `.github/workflows/custom/` — they are **never overwritten or deleted** by MokoStandards sync:
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
Bug reports and enhancement requests should be filed as GitHub issues and include:
|
||||
|
||||
* Clear reproduction steps or use cases.
|
||||
* Expected versus actual behavior.
|
||||
* Relevant environment details.
|
||||
|
||||
Security related issues must follow the process defined in SECURITY.md and must not be reported publicly.
|
||||
|
||||
## Review Process
|
||||
|
||||
All pull requests are subject to review. Review criteria include:
|
||||
|
||||
* Technical correctness
|
||||
* Alignment with project goals
|
||||
* Maintainability and clarity
|
||||
* Risk introduced to release and update processes
|
||||
|
||||
Maintainers may request changes prior to approval.
|
||||
```
|
||||
.github/workflows/
|
||||
├── deploy-dev.yml ← Synced from MokoStandards
|
||||
├── auto-release.yml ← Synced from MokoStandards
|
||||
└── custom/ ← Your custom workflows (safe)
|
||||
└── my-custom-ci.yml
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
By contributing, you agree that your contributions will be licensed under GPL-3.0-or-later, consistent with the rest of the project.
|
||||
|
||||
## Code of Conduct
|
||||
|
||||
Participation in this project is governed by the Code of Conduct. Unacceptable behavior may result in contribution restrictions.
|
||||
By contributing, you agree that your contributions will be licensed under the [GPL-3.0-or-later](LICENSE) license.
|
||||
|
||||
---
|
||||
|
||||
## Metadata
|
||||
|
||||
* **Document:** CONTRIBUTING.md
|
||||
* **Repository:** [https://github.com/mokoconsulting-tech/MokoCassiopeia](https://github.com/mokoconsulting-tech/MokoCassiopeia)
|
||||
* **Path:** /CONTRIBUTING.md
|
||||
* **Owner:** Moko Consulting
|
||||
* **Version:** 03.06.00
|
||||
* **Status:** Active
|
||||
* **Effective Date:** 2025-12-18
|
||||
* **Last Reviewed:** 2025-12-18
|
||||
|
||||
## Revision History
|
||||
|
||||
| Date | Change Summary | Author |
|
||||
| ---------- | ------------------------------------------------------------------------- | --------------- |
|
||||
| 2025-12-18 | Initial publication of contribution guidelines and workflow expectations. | Moko Consulting |
|
||||
*This file is synced from [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards). Do not edit directly — changes will be overwritten on the next sync.*
|
||||
|
||||
12
README.md
12
README.md
@@ -13,13 +13,19 @@
|
||||
BRIEF: Documentation for MokoCassiopeia template
|
||||
-->
|
||||
|
||||
|
||||
[](https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/tag/v03)
|
||||
[](LICENSE)
|
||||
[](https://www.joomla.org)
|
||||
[](https://www.php.net)
|
||||
|
||||
# README - MokoCassiopeia (VERSION: 03.06.03)
|
||||
|
||||
**A Modern, Lightweight Joomla Template Based on Cassiopeia**
|
||||
|
||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||
[](https://www.joomla.org)
|
||||
[](https://www.php.net)
|
||||
: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
|
||||
](https://img.shields.io/badge/Joomla-4.4.x%20%7C%205.x-blue.svg)](https://www.joomla.org)
|
||||
](https://img.shields.io/badge/PHP-8.0%2B-blue.svg)](https://www.php.net)
|
||||
|
||||
MokoCassiopeia is a modern, lightweight enhancement layer built on top of Joomla's Cassiopeia template. It adds **Font Awesome 7**, **Bootstrap 5** helpers, an automatic **Table of Contents (TOC)** utility, advanced **Dark Mode** theming, and optional integrations for **Google Tag Manager** and **Google Analytics (GA4)**—all while maintaining minimal core template overrides for maximum upgrade compatibility.
|
||||
|
||||
|
||||
279
SECURITY.md
279
SECURITY.md
@@ -1,185 +1,240 @@
|
||||
<!--
|
||||
Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
||||
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
|
||||
This file is part of a Moko Consulting project.
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
# FILE INFORMATION
|
||||
DEFGROUP: Joomla.Template
|
||||
INGROUP: MokoCassiopeia.Governance
|
||||
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
||||
FILE: SECURITY.md
|
||||
VERSION: 03.06.02
|
||||
BRIEF: Security policy and vulnerability reporting process for MokoCassiopeia.
|
||||
DEFGROUP: [PROJECT_NAME]
|
||||
INGROUP: [PROJECT_NAME].Documentation
|
||||
REPO: [REPOSITORY_URL]
|
||||
PATH: /SECURITY.md
|
||||
NOTE: This policy is process oriented and does not replace secure engineering practices.
|
||||
VERSION: 04.04.01
|
||||
BRIEF: Security vulnerability reporting and handling policy
|
||||
-->
|
||||
|
||||
## Security Policy
|
||||
# Security Policy
|
||||
|
||||
This document defines how MokoCassiopeia handles vulnerability intake, triage, remediation, and disclosure. The objective is to reduce risk, protect downstream users, and preserve operational continuity with a verifiable audit trail.
|
||||
## Purpose and Scope
|
||||
|
||||
## Scope
|
||||
|
||||
This policy applies to:
|
||||
|
||||
* Repository source code, workflows, scripts, and build artifacts.
|
||||
* Release packaging (ZIP outputs) generated from the repository.
|
||||
* Configuration and metadata used for distribution (for example manifests and update metadata).
|
||||
|
||||
Out of scope:
|
||||
|
||||
* Vulnerabilities in upstream Joomla core, third party extensions, or external infrastructure not controlled by this repository.
|
||||
* Issues that require physical access to a host, compromised administrator credentials, or a compromised hosting provider, unless the repository materially increases impact.
|
||||
This document defines the security vulnerability reporting, response, and disclosure policy for [PROJECT_NAME] and all repositories governed by these standards. It establishes the authoritative process for responsible disclosure, assessment, remediation, and communication of security issues.
|
||||
|
||||
## Supported Versions
|
||||
|
||||
Security fixes are prioritized for:
|
||||
Security updates are provided for the following versions:
|
||||
|
||||
* The latest released version.
|
||||
* The current development line when it is actively used for release engineering.
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| [X.x.x] | :white_check_mark: |
|
||||
| < [X.0] | :x: |
|
||||
|
||||
Backports may be provided based on impact, deployment footprint, and engineering capacity.
|
||||
Only the current major version receives security updates. Users should upgrade to the latest supported version to receive security patches.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
Use one of the following channels:
|
||||
### Where to Report
|
||||
|
||||
* GitHub Security Advisories (preferred): use the repository security tab to submit a private report.
|
||||
* Email: send details to `hello@mokoconsulting.tech` with subject `SECURITY: MokoCassiopeia vulnerability report`.
|
||||
**DO NOT** create public GitHub issues for security vulnerabilities.
|
||||
|
||||
Do not file a public GitHub issue for suspected security vulnerabilities.
|
||||
Report security vulnerabilities privately to:
|
||||
|
||||
### What to include
|
||||
**Email**: `security@[DOMAIN]`
|
||||
|
||||
Provide enough detail to reproduce and triage:
|
||||
**Subject Line**: `[SECURITY] Brief Description`
|
||||
|
||||
* A clear description of the vulnerability and expected impact.
|
||||
* A minimal proof of concept or reproduction steps.
|
||||
* Affected versions, configuration assumptions, and environment details.
|
||||
* Any proposed mitigation or patch.
|
||||
* Your preferred contact details for follow up.
|
||||
### What to Include
|
||||
|
||||
## Triage and Response Targets
|
||||
A complete vulnerability report should include:
|
||||
|
||||
The project operates with response targets aligned to practical delivery realities:
|
||||
1. **Description**: Clear explanation of the vulnerability
|
||||
2. **Impact**: Potential security impact and severity assessment
|
||||
3. **Affected Versions**: Which versions are vulnerable
|
||||
4. **Reproduction Steps**: Detailed steps to reproduce the issue
|
||||
5. **Proof of Concept**: Code, configuration, or demonstration (if applicable)
|
||||
6. **Suggested Fix**: Proposed remediation (if known)
|
||||
7. **Disclosure Timeline**: Your expectations for public disclosure
|
||||
|
||||
* **Acknowledgement:** within 3 business days.
|
||||
* **Initial triage:** within 10 business days.
|
||||
* **Fix plan:** communicated once severity is confirmed.
|
||||
### Response Timeline
|
||||
|
||||
These targets are not guarantees. Complex issues, supply chain considerations, and coordination with upstream vendors may extend timelines.
|
||||
* **Initial Response**: Within 3 business days
|
||||
* **Assessment Complete**: Within 7 business days
|
||||
* **Fix Timeline**: Depends on severity (see below)
|
||||
* **Disclosure**: Coordinated with reporter
|
||||
|
||||
## Severity Assessment
|
||||
## Severity Classification
|
||||
|
||||
Issues are triaged based on business impact and technical exploitability, including:
|
||||
Vulnerabilities are classified using the following severity levels:
|
||||
|
||||
* Remote exploitability and required privileges.
|
||||
* Data confidentiality, integrity, and availability impact.
|
||||
* Likelihood of exploitation in typical Joomla deployments.
|
||||
* Exposure surface (public endpoints, administrator area, installation flows, and update mechanisms).
|
||||
### Critical
|
||||
* Remote code execution
|
||||
* Authentication bypass
|
||||
* Data breach or exposure of sensitive information
|
||||
* **Fix Timeline**: 7 days
|
||||
|
||||
When appropriate, industry standard scoring such as CVSS may be used for internal prioritization.
|
||||
### High
|
||||
* Privilege escalation
|
||||
* SQL injection or command injection
|
||||
* Cross-site scripting (XSS) with significant impact
|
||||
* **Fix Timeline**: 14 days
|
||||
|
||||
## Coordinated Disclosure
|
||||
### Medium
|
||||
* Information disclosure (limited scope)
|
||||
* Denial of service
|
||||
* Security misconfigurations with moderate impact
|
||||
* **Fix Timeline**: 30 days
|
||||
|
||||
The project follows coordinated vulnerability disclosure:
|
||||
### Low
|
||||
* Security best practice violations
|
||||
* Minor information leaks
|
||||
* Issues requiring user interaction or complex preconditions
|
||||
* **Fix Timeline**: 60 days or next release
|
||||
|
||||
* Reports are treated as confidential until remediation is available.
|
||||
* A public advisory may be published once a fix is released.
|
||||
* A reasonable embargo period is expected to enable patch distribution.
|
||||
## Remediation Process
|
||||
|
||||
If you believe disclosure is time sensitive due to active exploitation, include that assessment and any supporting indicators.
|
||||
1. **Acknowledgment**: Security team confirms receipt and begins investigation
|
||||
2. **Assessment**: Vulnerability is validated, severity assigned, and impact analyzed
|
||||
3. **Development**: Security patch is developed and tested
|
||||
4. **Review**: Patch undergoes security review and validation
|
||||
5. **Release**: Fixed version is released with security advisory
|
||||
6. **Disclosure**: Public disclosure follows coordinated timeline
|
||||
|
||||
## Security Updates and Advisories
|
||||
## Security Advisories
|
||||
|
||||
Security updates are distributed through:
|
||||
Security advisories are published via:
|
||||
|
||||
* GitHub releases for the repository.
|
||||
* GitHub Security Advisories when applicable.
|
||||
* GitHub Security Advisories
|
||||
* Release notes and CHANGELOG.md
|
||||
* Security mailing list (when established)
|
||||
|
||||
Advisories may include:
|
||||
Advisories include:
|
||||
|
||||
* Affected versions and fixed versions.
|
||||
* Mitigations and workarounds when a fix is not immediately available.
|
||||
* Upgrade guidance.
|
||||
* CVE identifier (if applicable)
|
||||
* Severity rating
|
||||
* Affected versions
|
||||
* Fixed versions
|
||||
* Mitigation steps
|
||||
* Attribution (with reporter consent)
|
||||
|
||||
## Dependencies and Supply Chain Controls
|
||||
## Security Best Practices
|
||||
|
||||
The project aims to manage supply chain risk through:
|
||||
For repositories adopting MokoStandards:
|
||||
|
||||
* Pinning and review of workflow dependencies where feasible.
|
||||
* Minimizing privileged GitHub token permissions.
|
||||
* Validating build inputs prior to packaging releases.
|
||||
### Required Controls
|
||||
|
||||
If you identify a supply chain issue (for example compromised action, dependency confusion, or malicious upstream artifact), report it as a vulnerability.
|
||||
* Enable GitHub security features (Dependabot, code scanning)
|
||||
* Implement branch protection on `main`
|
||||
* Require code review for all changes
|
||||
* Enforce signed commits (recommended)
|
||||
* Use secrets management (never commit credentials)
|
||||
* Maintain security documentation
|
||||
* Follow secure coding standards defined in `/docs/policy/`
|
||||
|
||||
## Secure Development and CI Expectations
|
||||
### CI/CD Security
|
||||
|
||||
Security posture is reinforced through operational controls:
|
||||
* Validate all inputs
|
||||
* Sanitize outputs
|
||||
* Use least privilege access
|
||||
* Pin dependencies with hash verification
|
||||
* Scan for vulnerabilities in dependencies
|
||||
* Audit third-party actions and tools
|
||||
|
||||
* CI validation for packaging inputs and manifest integrity.
|
||||
* Consistent path normalization and whitespace hygiene checks where required for release correctness.
|
||||
* Least privilege for GitHub Actions permissions.
|
||||
#### Automated Security Scanning
|
||||
|
||||
### Template Security Features
|
||||
All repositories MUST implement:
|
||||
|
||||
**Custom Head Content Injection**
|
||||
**CodeQL Analysis**:
|
||||
* Enabled for all supported languages (Python, JavaScript, TypeScript, Java, C/C++, C#, Go, Ruby)
|
||||
* Runs on: push to main, pull requests, weekly schedule
|
||||
* Query sets: `security-extended` and `security-and-quality`
|
||||
* Configuration: `.github/workflows/codeql-analysis.yml`
|
||||
|
||||
The template provides Custom Head Code fields (`custom_head_start` and `custom_head_end`) that allow administrators to inject custom HTML, CSS, and JavaScript code. This is an intentional feature for:
|
||||
**Dependabot Security Updates**:
|
||||
* Weekly scans for vulnerable dependencies
|
||||
* Automated pull requests for security patches
|
||||
* Configuration: `.github/dependabot.yml`
|
||||
|
||||
* Adding analytics scripts (Google Analytics, Google Tag Manager)
|
||||
* Custom meta tags
|
||||
* Third-party integrations
|
||||
* Custom styling
|
||||
**Secret Scanning**:
|
||||
* Enabled by default with push protection
|
||||
* Prevents accidental credential commits
|
||||
* Partner patterns enabled
|
||||
|
||||
**Security Considerations:**
|
||||
**Dependency Review**:
|
||||
* Required for all pull requests
|
||||
* Blocks introduction of known vulnerable dependencies
|
||||
* Automatic license compliance checking
|
||||
|
||||
* These fields use `filter="raw"` to allow HTML/JS injection
|
||||
* **Access is restricted to Joomla administrators only** via template configuration
|
||||
* This is not an XSS vulnerability as it requires administrator privileges
|
||||
* Administrators should only add trusted code from verified sources
|
||||
* Regular security audits should review custom head content
|
||||
See [Security Scanning Policy](docs/policy/security-scanning.md) for detailed requirements.
|
||||
|
||||
This policy does not guarantee that all vulnerabilities will be prevented. It defines how risk is managed when issues are discovered.
|
||||
### Dependency Management
|
||||
|
||||
## Safe Harbor
|
||||
* Keep dependencies up to date
|
||||
* Monitor security advisories for dependencies
|
||||
* Remove unused dependencies
|
||||
* Audit new dependencies before adoption
|
||||
* Document security-critical dependencies
|
||||
|
||||
The project supports good faith security research. When you:
|
||||
## Compliance and Governance
|
||||
|
||||
* Avoid privacy violations, data destruction, and service disruption.
|
||||
* Limit testing to systems you own or have explicit permission to test.
|
||||
* Provide a reasonable window for coordinated disclosure.
|
||||
This security policy is binding for all repositories governed by MokoStandards. Deviations require documented justification and approval from the Security Owner.
|
||||
|
||||
Then the project will treat your report as a constructive security contribution.
|
||||
Security policies are reviewed and updated at least annually or following significant security incidents.
|
||||
|
||||
Jurisdiction note: this repository is managed from Tennessee, USA. This note is informational only and does not constitute legal advice.
|
||||
## Attribution and Recognition
|
||||
|
||||
## Public Communications
|
||||
We acknowledge and appreciate responsible disclosure. With your permission, we will:
|
||||
|
||||
Only maintainers will publish security advisories or public statements for confirmed vulnerabilities. Public communication will focus on actionable remediation and operational risk reduction.
|
||||
* Credit you in security advisories
|
||||
* List you in CHANGELOG.md for the fix release
|
||||
* Recognize your contribution publicly (if desired)
|
||||
|
||||
## Acknowledgements
|
||||
## Contact and Escalation
|
||||
|
||||
If you want credit, include the name or handle to list in an advisory. If you prefer anonymity, state that explicitly.
|
||||
* **Security Team**: security@[DOMAIN]
|
||||
* **Primary Contact**: [CONTACT_EMAIL]
|
||||
* **Escalation**: For urgent matters requiring immediate attention, contact the maintainer directly via GitHub
|
||||
|
||||
## Out of Scope
|
||||
|
||||
The following are explicitly out of scope:
|
||||
|
||||
* Issues in third-party dependencies (report directly to maintainers)
|
||||
* Social engineering attacks
|
||||
* Physical security issues
|
||||
* Denial of service via resource exhaustion without amplification
|
||||
* Issues requiring physical access to systems
|
||||
* Theoretical vulnerabilities without proof of exploitability
|
||||
|
||||
---
|
||||
|
||||
## Metadata
|
||||
|
||||
* **Document:** SECURITY.md
|
||||
* **Repository:** [https://github.com/mokoconsulting-tech/MokoCassiopeia](https://github.com/mokoconsulting-tech/MokoCassiopeia)
|
||||
* **Path:** /SECURITY.md
|
||||
* **Owner:** Moko Consulting
|
||||
* **Version:** 03.06.00
|
||||
* **Status:** Active
|
||||
* **Effective Date:** 2025-12-18
|
||||
* **Last Reviewed:** 2025-12-18
|
||||
| Field | Value |
|
||||
| ------------ | ----------------------------------------------- |
|
||||
| Document | Security Policy |
|
||||
| Path | /SECURITY.md |
|
||||
| Repository | [REPOSITORY_URL] |
|
||||
| Owner | [OWNER_NAME] |
|
||||
| Scope | Security vulnerability handling |
|
||||
| Applies To | All repositories governed by MokoStandards |
|
||||
| Status | Active |
|
||||
| Effective | [YYYY-MM-DD] |
|
||||
|
||||
## Revision History
|
||||
|
||||
| Date | Change Summary | Author |
|
||||
| ---------- | ------------------------------------------------------------------------------------------------ | --------------- |
|
||||
| 2026-01-30 | Added Template Security Features section documenting custom head content injection controls. | Copilot Agent |
|
||||
| 2025-12-18 | Initial publication of security policy, intake channels, triage targets, and disclosure process. | Moko Consulting |
|
||||
| Date | Change Description | Author |
|
||||
| ---------- | ------------------------------------------------- | --------------- |
|
||||
| [YYYY-MM-DD] | Initial creation | [AUTHOR_NAME] |
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
"prefer-stable": true,
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"mokoconsulting-tech/enterprise": "^4.0"
|
||||
"mokoconsulting-tech/enterprise": "dev-version/04.02.00"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^10.5",
|
||||
|
||||
119
docs/update-server.md
Normal file
119
docs/update-server.md
Normal file
@@ -0,0 +1,119 @@
|
||||
<!--
|
||||
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
|
||||
This file is part of a Moko Consulting project.
|
||||
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# FILE INFORMATION
|
||||
DEFGROUP: MokoCassiopeia.Documentation
|
||||
INGROUP: MokoStandards.Templates
|
||||
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
||||
PATH: /docs/update-server.md
|
||||
VERSION: 04.04.00
|
||||
BRIEF: How this extension's Joomla update server file (update.xml) is managed
|
||||
-->
|
||||
|
||||
# Joomla Update Server
|
||||
|
||||
[](https://github.com/mokoconsulting-tech/MokoStandards)
|
||||
|
||||
This document explains how `update.xml` is automatically managed for this Joomla extension following the [Joomla Update Server specification](https://docs.joomla.org/Deploying_an_Update_Server).
|
||||
|
||||
## How It Works
|
||||
|
||||
Joomla checks for extension updates by fetching an XML file from the URL defined in the `<updateservers>` tag in the extension's XML manifest. MokoStandards generates this file automatically.
|
||||
|
||||
### Automatic Generation
|
||||
|
||||
| Event | Workflow | `<tag>` | `<version>` |
|
||||
|-------|----------|---------|-------------|
|
||||
| Merge to `main` | `auto-release.yml` | `stable` | `XX.YY.ZZ` |
|
||||
| Push to `dev/**` | `deploy-dev.yml` | `development` | `development` |
|
||||
| Push to `rc/**` | `deploy-dev.yml` | `rc` | `XX.YY.ZZ-rc` |
|
||||
|
||||
### Generated XML Structure
|
||||
|
||||
```xml
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<updates>
|
||||
<update>
|
||||
<name>Extension Name</name>
|
||||
<description>Extension Name update</description>
|
||||
<element>com_extensionname</element>
|
||||
<type>component</type>
|
||||
<version>01.02.03</version>
|
||||
<client>site</client>
|
||||
<folder>system</folder> <!-- plugins only -->
|
||||
<tags>
|
||||
<tag>stable</tag>
|
||||
</tags>
|
||||
<infourl title="Extension Name">https://github.com/.../releases/tag/v01.02.03</infourl>
|
||||
<downloads>
|
||||
<downloadurl type="full" format="zip">https://github.com/.../releases/download/v01.02.03/com_ext-01.02.03.zip</downloadurl>
|
||||
</downloads>
|
||||
<targetplatform name="joomla" version="((5\.[0-9])|(6\.[0-9]))" />
|
||||
<php_minimum>8.2</php_minimum> <!-- if present in manifest -->
|
||||
<maintainer>Moko Consulting</maintainer>
|
||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
||||
</update>
|
||||
</updates>
|
||||
```
|
||||
|
||||
### Metadata Source
|
||||
|
||||
All metadata is extracted from the extension's XML manifest (`src/*.xml`) at build time:
|
||||
|
||||
| XML Element | Source | Notes |
|
||||
|-------------|--------|-------|
|
||||
| `<name>` | `<name>` in manifest | Extension display name |
|
||||
| `<element>` | `<element>` in manifest | Must match installed extension identifier |
|
||||
| `<type>` | `type` attribute on `<extension>` | `component`, `module`, `plugin`, `library`, `package`, `template` |
|
||||
| `<client>` | `client` attribute on `<extension>` | `site` or `administrator` — **required for plugins and modules** |
|
||||
| `<folder>` | `group` attribute on `<extension>` | Plugin group (e.g., `system`, `content`) — **required for plugins** |
|
||||
| `<targetplatform>` | `<targetplatform>` in manifest | Falls back to Joomla 5.x / 6.x if not specified |
|
||||
| `<php_minimum>` | `<php_minimum>` in manifest | Included only if present |
|
||||
|
||||
### Extension Manifest Setup
|
||||
|
||||
Your XML manifest must include an `<updateservers>` tag pointing to the `update.xml` on the `main` branch:
|
||||
|
||||
```xml
|
||||
<extension type="component" client="site" method="upgrade">
|
||||
<name>My Extension</name>
|
||||
<element>com_myextension</element>
|
||||
<!-- ... -->
|
||||
<updateservers>
|
||||
<server type="extension" name="My Extension Updates">
|
||||
https://raw.githubusercontent.com/mokoconsulting-tech/MokoCassiopeia/main/update.xml
|
||||
</server>
|
||||
</updateservers>
|
||||
</extension>
|
||||
```
|
||||
|
||||
### Branch Lifecycle
|
||||
|
||||
```
|
||||
dev/XX.YY.ZZ → rc/XX.YY.ZZ → main → version/XX.YY
|
||||
(development) (rc) (stable) (frozen snapshot)
|
||||
```
|
||||
|
||||
1. **Development** (`dev/**`): `update.xml` with `<tag>development</tag>`, download points to branch archive
|
||||
2. **Release Candidate** (`rc/**`): `update.xml` with `<tag>rc</tag>`, version set to `XX.YY.ZZ-rc`
|
||||
3. **Stable Release** (merge to `main`): `update.xml` with `<tag>stable</tag>`, download points to GitHub Release asset
|
||||
4. **Frozen Snapshot** (`version/XX.YY`): immutable, never force-pushed
|
||||
|
||||
### Health Checks
|
||||
|
||||
The `repo_health.yml` workflow verifies on every commit:
|
||||
|
||||
- `update.xml` exists in the repository root
|
||||
- XML manifest exists with `<extension>` tag
|
||||
- `<version>`, `<name>`, `<author>`, `<namespace>` tags present
|
||||
- Extension `type` attribute is valid
|
||||
- Language `.ini` files exist
|
||||
- `index.html` directory listing protection in `src/`, `src/admin/`, `src/site/`
|
||||
|
||||
---
|
||||
|
||||
*Managed by [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards). See [docs/workflows/update-server.md](https://github.com/mokoconsulting-tech/MokoStandards/blob/main/docs/workflows/update-server.md) for the full specification.*
|
||||
Reference in New Issue
Block a user