chore: Sync MokoStandards v04.04 #110
1
.github/.mokostandards
vendored
Normal file
1
.github/.mokostandards
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
platform: waas-component
|
||||||
378
.github/CLAUDE.md
vendored
378
.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]
|
> [!IMPORTANT]
|
||||||
> **🔧 AI Self-Update Required on First Use**
|
> **🔧 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`) |
|
> | `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>` |
|
> | `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_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_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`) |
|
> | `{{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 name: **{{EXTENSION_NAME}}**
|
||||||
Extension type: **{{EXTENSION_TYPE}}** (`{{EXTENSION_ELEMENT}}`)
|
Extension type: **{{EXTENSION_TYPE}}** (`{{EXTENSION_ELEMENT}}`)
|
||||||
Repository URL: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
Platform: **Joomla 4.x / MokoWaaS**
|
||||||
|
|
||||||
This repository is governed by [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards) — the single source of truth for coding standards, file-header policies, GitHub Actions workflows, and Terraform configuration templates across all Moko Consulting repositories.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Repo Structure
|
## Primary Language
|
||||||
|
|
||||||
```
|
**PHP** (≥ 7.4) is the primary language for this Joomla extension. JavaScript may be used for frontend enhancements. YAML uses 2-space indentation. All other text files use tabs per `.editorconfig`.
|
||||||
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
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 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`.
|
Every new file needs a copyright header as its first content.
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
**PHP:**
|
**PHP:**
|
||||||
```php
|
```php
|
||||||
@@ -162,47 +80,141 @@ Every new file **must** have a copyright header as its first content. JSON files
|
|||||||
* DEFGROUP: MokoCassiopeia.{{EXTENSION_TYPE}}
|
* DEFGROUP: MokoCassiopeia.{{EXTENSION_TYPE}}
|
||||||
* INGROUP: MokoCassiopeia
|
* INGROUP: MokoCassiopeia
|
||||||
* REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
* REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
||||||
* PATH: /site/controllers/item.php
|
* PATH: /path/to/file.php
|
||||||
* VERSION: XX.YY.ZZ
|
* VERSION: XX.YY.ZZ
|
||||||
* BRIEF: One-line description of file purpose
|
* BRIEF: One-line description of purpose
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defined('_JEXEC') or die;
|
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 |
|
- **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.
|
||||||
| PHP class | `PascalCase` | `ItemModel` |
|
- Version format is zero-padded semver: `XX.YY.ZZ` (e.g. `01.02.03`).
|
||||||
| PHP method / function | `camelCase` | `getItems()` |
|
- Never hardcode a specific version in document body text — use the badge or FILE INFORMATION header only.
|
||||||
| 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
|
### 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
|
<!-- In updates.xml — prepend a new <update> block for every release.
|
||||||
|
Note: the backslash in version="4\.[0-9]+" is a literal backslash character
|
||||||
Format: `<prefix>/<MAJOR.MINOR.PATCH>[/description]`
|
in the XML attribute value. Joomla's update server treats the value as a
|
||||||
|
regular expression, so \. matches a literal dot. -->
|
||||||
Approved prefixes: `dev/` · `rc/` · `version/` · `patch/` · `copilot/` · `dependabot/`
|
<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).
|
Every workflow must use **`secrets.GH_TOKEN`** (the org-level Personal Access Token).
|
||||||
|
|
||||||
@@ -217,58 +229,76 @@ env:
|
|||||||
```
|
```
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
# ❌ Wrong — never use these
|
# ❌ Wrong — never use these in workflows
|
||||||
token: ${{ github.token }}
|
token: ${{ github.token }}
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# Keeping Documentation Current
|
## MokoStandards Reference
|
||||||
|
|
||||||
| Change type | Documentation to update |
|
This repository is governed by [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards). Authoritative policies:
|
||||||
|-------------|------------------------|
|
|
||||||
| 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)
|
|
||||||
|
|
||||||
| Document | Purpose |
|
| 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 |
|
| [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 |
|
| [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 |
|
| [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 |
|
| [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 |
|
| [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
|
# Combined with branch protection (require PR reviews), this prevents
|
||||||
# unauthorized modifications to workflows, configs, and governance files.
|
# unauthorized modifications to workflows, configs, and governance files.
|
||||||
|
|
||||||
# ── Workflows (synced from MokoStandards — must not be manually edited) ──
|
# ── Synced workflows (managed by MokoStandards — do not edit manually) ────
|
||||||
/.github/workflows/ @jmiller-moko
|
/.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 configuration ─────────────────────────────────────────────────
|
||||||
/.github/ISSUE_TEMPLATE/ @jmiller-moko
|
/.github/ISSUE_TEMPLATE/ @jmiller-moko
|
||||||
@@ -23,7 +41,7 @@
|
|||||||
/composer.json @jmiller-moko
|
/composer.json @jmiller-moko
|
||||||
/phpstan.neon @jmiller-moko
|
/phpstan.neon @jmiller-moko
|
||||||
/Makefile @jmiller-moko
|
/Makefile @jmiller-moko
|
||||||
/.ftp_ignore @jmiller-moko
|
/.ftpignore @jmiller-moko
|
||||||
/.gitignore @jmiller-moko
|
/.gitignore @jmiller-moko
|
||||||
/.gitattributes @jmiller-moko
|
/.gitattributes @jmiller-moko
|
||||||
/.editorconfig @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]
|
> [!IMPORTANT]
|
||||||
> **🔧 AI Self-Update Required on First Use**
|
> **🔧 AI Self-Update Required on First Use**
|
||||||
>
|
>
|
||||||
@@ -103,13 +122,13 @@ BRIEF: One-line description
|
|||||||
|
|
||||||
### Joomla Version Alignment
|
### 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
|
```xml
|
||||||
<!-- In manifest.xml — must match README.md version -->
|
<!-- In manifest.xml — must match README.md version -->
|
||||||
<version>01.02.04</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
|
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
|
in the XML attribute value. Joomla's update server treats the value as a
|
||||||
regular expression, so \. matches a literal dot. -->
|
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/
|
MokoCassiopeia/
|
||||||
├── manifest.xml # Joomla installer manifest (root — required)
|
├── 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
|
├── site/ # Frontend (site) code
|
||||||
│ ├── controller.php
|
│ ├── controller.php
|
||||||
│ ├── controllers/
|
│ ├── 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:
|
The `manifest.xml` must reference it via:
|
||||||
```xml
|
```xml
|
||||||
<updateservers>
|
<updateservers>
|
||||||
<server type="extension" priority="1" name="{{EXTENSION_NAME}}">
|
<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>
|
</server>
|
||||||
</updateservers>
|
</updateservers>
|
||||||
```
|
```
|
||||||
|
|
||||||
**Rules:**
|
**Rules:**
|
||||||
- Every release must prepend a new `<update>` block at the top of `update.xml` — old entries must be preserved below.
|
- Every release must prepend a new `<update>` block at the top of `updates.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`.
|
- 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).
|
- 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.
|
- `<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
|
## manifest.xml Rules
|
||||||
|
|
||||||
- Lives at the repo root as `manifest.xml` (not inside `site/` or `admin/`).
|
- 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`.
|
- `<version>` tag must be kept in sync with `README.md` version and `updates.xml`.
|
||||||
- Must include `<updateservers>` block pointing to this repo's `update.xml`.
|
- Must include `<updateservers>` block pointing to this repo's `updates.xml`.
|
||||||
- Must include `<files folder="site">` and `<administration>` sections.
|
- Must include `<files folder="site">` and `<administration>` sections.
|
||||||
- Joomla 4.x requires `<namespace path="src">Moko\{{EXTENSION_NAME}}</namespace>` for namespaced extensions.
|
- 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 |
|
| Change type | Documentation to update |
|
||||||
|-------------|------------------------|
|
|-------------|------------------------|
|
||||||
| New or renamed PHP class/method | PHPDoc block; `docs/api/` entry |
|
| 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 or changed manifest.xml | Update `updates.xml` version; bump README.md version |
|
||||||
| New release | Prepend `<update>` block to `update.xml`; update CHANGELOG.md; 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` |
|
| New or changed workflow | `docs/workflows/<workflow-name>.md` |
|
||||||
| Any modified file | Update the `VERSION` field in that file's `FILE INFORMATION` block |
|
| 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 |
|
| **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 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 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 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
|
# INGROUP: MokoStandards.Security
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /.github/dependabot.yml
|
# PATH: /.github/dependabot.yml
|
||||||
# VERSION: 01.00.00
|
# VERSION: 03.09.03
|
||||||
# BRIEF: Dependabot configuration for automated dependency updates and security patches
|
# BRIEF: Dependabot configuration for automated dependency updates and security patches
|
||||||
# NOTE: Monitors GitHub Actions for vulnerabilities and keeps ecosystem secure
|
# 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
|
||||||
191
.github/workflows/auto-dev-issue.yml
vendored
191
.github/workflows/auto-dev-issue.yml
vendored
@@ -9,14 +9,22 @@
|
|||||||
# INGROUP: MokoStandards.Automation
|
# INGROUP: MokoStandards.Automation
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /templates/workflows/shared/auto-dev-issue.yml.template
|
# PATH: /templates/workflows/shared/auto-dev-issue.yml.template
|
||||||
# VERSION: 04.04.01
|
# VERSION: 04.06.00
|
||||||
# BRIEF: Auto-create tracking issue when a dev/** or rc/** branch is pushed
|
# 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.
|
# 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:
|
on:
|
||||||
|
# Auto-create on RC branch creation
|
||||||
create:
|
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:
|
env:
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
@@ -30,15 +38,23 @@ jobs:
|
|||||||
name: Create version tracking issue
|
name: Create version tracking issue
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: >-
|
if: >-
|
||||||
github.event.ref_type == 'branch' &&
|
(github.event_name == 'workflow_dispatch') ||
|
||||||
(startsWith(github.event.ref, 'dev/') || startsWith(github.event.ref, 'rc/'))
|
(github.event.ref_type == 'branch' &&
|
||||||
|
(startsWith(github.event.ref, 'rc/') ||
|
||||||
|
startsWith(github.event.ref, 'alpha/') ||
|
||||||
|
startsWith(github.event.ref, 'beta/')))
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Create tracking issue
|
- name: Create tracking issue and sub-issues
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
run: |
|
run: |
|
||||||
BRANCH="${{ github.event.ref }}"
|
# 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 }}"
|
REPO="${{ github.repository }}"
|
||||||
ACTOR="${{ github.actor }}"
|
ACTOR="${{ github.actor }}"
|
||||||
NOW=$(date -u '+%Y-%m-%d %H:%M UTC')
|
NOW=$(date -u '+%Y-%m-%d %H:%M UTC')
|
||||||
@@ -49,6 +65,16 @@ jobs:
|
|||||||
BRANCH_TYPE="Release Candidate"
|
BRANCH_TYPE="Release Candidate"
|
||||||
LABEL_TYPE="type: release"
|
LABEL_TYPE="type: release"
|
||||||
TITLE_PREFIX="rc"
|
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
|
else
|
||||||
VERSION="${BRANCH#dev/}"
|
VERSION="${BRANCH#dev/}"
|
||||||
BRANCH_TYPE="Development"
|
BRANCH_TYPE="Development"
|
||||||
@@ -58,45 +84,124 @@ jobs:
|
|||||||
|
|
||||||
TITLE="${TITLE_PREFIX}(${VERSION}): ${BRANCH_TYPE} tracking for ${BRANCH}"
|
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
|
# 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)
|
--jq ".[] | select(.title | startswith(\"${TITLE_PREFIX}(${VERSION})\")) | .number" 2>/dev/null | head -1)
|
||||||
|
|
||||||
if [ -n "$EXISTING" ]; then
|
if [ -n "$EXISTING" ]; then
|
||||||
echo "ℹ️ Issue #${EXISTING} already exists for ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
echo "ℹ️ Issue #${EXISTING} already exists for ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
||||||
else
|
exit 0
|
||||||
ISSUE_URL=$(gh issue create \
|
|
||||||
--repo "$REPO" \
|
|
||||||
--title "$TITLE" \
|
|
||||||
--body "$BODY" \
|
|
||||||
--label "${LABEL_TYPE},version" \
|
|
||||||
--assignee "jmiller-moko" 2>&1)
|
|
||||||
echo "✅ Created tracking issue: ${ISSUE_URL}" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
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
|
||||||
|
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 "$PARENT_BODY" \
|
||||||
|
--label "${LABEL_TYPE},version" \
|
||||||
|
--assignee "jmiller-moko" 2>&1)
|
||||||
|
|
||||||
|
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
|
||||||
|
|||||||
547
.github/workflows/auto-release.yml
vendored
547
.github/workflows/auto-release.yml
vendored
@@ -6,29 +6,31 @@
|
|||||||
# DEFGROUP: GitHub.Workflow
|
# DEFGROUP: GitHub.Workflow
|
||||||
# INGROUP: MokoStandards.Release
|
# INGROUP: MokoStandards.Release
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /templates/workflows/shared/auto-release.yml.template
|
# PATH: /templates/workflows/joomla/auto-release.yml.template
|
||||||
# VERSION: 04.04.01
|
# VERSION: 04.06.00
|
||||||
# BRIEF: Unified build & release pipeline — version branch, platform version, badges, tag, release
|
# BRIEF: Joomla build & release — ZIP package, updates.xml, SHA-256 checksum
|
||||||
#
|
#
|
||||||
# ╔════════════════════════════════════════════════════════════════════════╗
|
# +========================================================================+
|
||||||
# ║ BUILD & RELEASE PIPELINE ║
|
# | BUILD & RELEASE PIPELINE (JOOMLA) |
|
||||||
# ╠════════════════════════════════════════════════════════════════════════╣
|
# +========================================================================+
|
||||||
# ║ ║
|
# | |
|
||||||
# ║ Triggers on push to main (skips bot commits + [skip ci]): ║
|
# | Triggers on push to main (skips bot commits + [skip ci]): |
|
||||||
# ║ ║
|
# | |
|
||||||
# ║ Every push: ║
|
# | Every push: |
|
||||||
# ║ 1. Read version from README.md ║
|
# | 1. Read version from README.md |
|
||||||
# ║ 3. Set platform version (Dolibarr $this->version, Joomla <version>)║
|
# | 3. Set platform version (Joomla <version>) |
|
||||||
# ║ 4. Update [VERSION: XX.YY.ZZ] badges in markdown files ║
|
# | 4. Update [VERSION: XX.YY.ZZ] badges in markdown files |
|
||||||
# ║ 5. Write update.txt / update.xml ║
|
# | 5. Write updates.xml (Joomla update server XML) |
|
||||||
# ║ 6. Create git tag vXX.YY.ZZ ║
|
# | 6. Create git tag vXX.YY.ZZ |
|
||||||
# ║ 7a. Patch: update existing GitHub Release for this minor ║
|
# | 7a. Patch: update existing GitHub Release for this minor |
|
||||||
# ║ ║
|
# | 8. Build ZIP, upload asset, write SHA-256 to updates.xml |
|
||||||
# ║ Minor releases only (patch == 00): ║
|
# | |
|
||||||
# ║ 2. Create/update version/XX.YY branch (patches update in-place) ║
|
# | Every version change: archives main -> version/XX.YY branch |
|
||||||
# ║ 7b. Create new GitHub Release ║
|
# | Patch 00 = development (no release). First release = patch 01. |
|
||||||
# ║ ║
|
# | First release only (patch == 01): |
|
||||||
# ╚════════════════════════════════════════════════════════════════════════╝
|
# | 7b. Create new GitHub Release |
|
||||||
|
# | |
|
||||||
|
# +========================================================================+
|
||||||
|
|
||||||
name: Build & Release
|
name: Build & Release
|
||||||
|
|
||||||
@@ -37,6 +39,9 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- master
|
- master
|
||||||
|
paths:
|
||||||
|
- 'src/**'
|
||||||
|
- 'htdocs/**'
|
||||||
|
|
||||||
env:
|
env:
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
@@ -64,19 +69,19 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||||
run: |
|
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" \
|
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||||
/tmp/mokostandards
|
/tmp/mokostandards
|
||||||
cd /tmp/mokostandards
|
cd /tmp/mokostandards
|
||||||
composer install --no-dev --no-interaction --quiet
|
composer install --no-dev --no-interaction --quiet
|
||||||
|
|
||||||
# ── STEP 1: Read version ───────────────────────────────────────────
|
# -- STEP 1: Read version -----------------------------------------------
|
||||||
- name: "Step 1: Read version from README.md"
|
- name: "Step 1: Read version from README.md"
|
||||||
id: version
|
id: version
|
||||||
run: |
|
run: |
|
||||||
VERSION=$(php /tmp/mokostandards/api/cli/version_read.php --path . 2>/dev/null)
|
VERSION=$(php /tmp/mokostandards/api/cli/version_read.php --path . 2>/dev/null)
|
||||||
if [ -z "$VERSION" ]; then
|
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"
|
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
@@ -84,24 +89,34 @@ jobs:
|
|||||||
MINOR=$(echo "$VERSION" | awk -F. '{printf "%s.%s", $1, $2}')
|
MINOR=$(echo "$VERSION" | awk -F. '{printf "%s.%s", $1, $2}')
|
||||||
PATCH=$(echo "$VERSION" | awk -F. '{print $3}')
|
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 "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||||
echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT"
|
echo "branch=version/${MAJOR}" >> "$GITHUB_OUTPUT"
|
||||||
echo "branch=version/${MINOR}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "minor=$MINOR" >> "$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
|
if [ "$PATCH" = "00" ]; then
|
||||||
echo "is_minor=true" >> "$GITHUB_OUTPUT"
|
echo "skip=true" >> "$GITHUB_OUTPUT"
|
||||||
echo "✅ Version: $VERSION (minor release — full pipeline)"
|
|
||||||
else
|
|
||||||
echo "is_minor=false" >> "$GITHUB_OUTPUT"
|
echo "is_minor=false" >> "$GITHUB_OUTPUT"
|
||||||
echo "✅ Version: $VERSION (patch — platform version + badges only)"
|
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 (first release — full pipeline)"
|
||||||
|
else
|
||||||
|
echo "is_minor=false" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "Version: $VERSION (patch — platform version + badges only)"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Check if already released
|
- name: Check if already released
|
||||||
if: steps.version.outputs.skip != 'true'
|
if: steps.version.outputs.skip != 'true'
|
||||||
id: check
|
id: check
|
||||||
run: |
|
run: |
|
||||||
TAG="${{ steps.version.outputs.tag }}"
|
TAG="${{ steps.version.outputs.release_tag }}"
|
||||||
BRANCH="${{ steps.version.outputs.branch }}"
|
BRANCH="${{ steps.version.outputs.branch }}"
|
||||||
|
|
||||||
TAG_EXISTS=false
|
TAG_EXISTS=false
|
||||||
@@ -119,102 +134,109 @@ jobs:
|
|||||||
echo "already_released=false" >> "$GITHUB_OUTPUT"
|
echo "already_released=false" >> "$GITHUB_OUTPUT"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── SANITY CHECKS ────────────────────────────────────────────────────
|
# -- SANITY CHECKS -------------------------------------------------------
|
||||||
- name: "Sanity: Platform-specific validation"
|
- name: "Sanity: Pre-release validation"
|
||||||
if: >-
|
if: >-
|
||||||
steps.version.outputs.skip != 'true' &&
|
steps.version.outputs.skip != 'true' &&
|
||||||
steps.check.outputs.already_released != 'true'
|
steps.check.outputs.already_released != 'true'
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null)
|
|
||||||
ERRORS=0
|
ERRORS=0
|
||||||
|
|
||||||
echo "## 🔍 Pre-Release Sanity Checks" >> $GITHUB_STEP_SUMMARY
|
echo "## Pre-Release Sanity Checks (Joomla)" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "Platform: \`${PLATFORM}\`" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "" >> $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
|
# Common checks
|
||||||
if [ ! -f "LICENSE" ]; then
|
if [ ! -f "LICENSE" ]; then
|
||||||
echo "❌ Missing LICENSE file" >> $GITHUB_STEP_SUMMARY
|
echo "- Missing LICENSE file" >> $GITHUB_STEP_SUMMARY
|
||||||
ERRORS=$((ERRORS+1))
|
ERRORS=$((ERRORS+1))
|
||||||
else
|
else
|
||||||
echo "✅ LICENSE" >> $GITHUB_STEP_SUMMARY
|
echo "- LICENSE present" >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -d "src" ]; then
|
if [ ! -d "src" ] && [ ! -d "htdocs" ]; then
|
||||||
echo "⚠️ No src/ directory" >> $GITHUB_STEP_SUMMARY
|
echo "- Warning: No src/ or htdocs/ directory" >> $GITHUB_STEP_SUMMARY
|
||||||
else
|
else
|
||||||
echo "✅ src/ directory" >> $GITHUB_STEP_SUMMARY
|
echo "- Source directory present" >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Dolibarr-specific checks
|
# -- Joomla: manifest version drift --------
|
||||||
if [ "$PLATFORM" = "crm-module" ]; then
|
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||||
MOD_FILE=$(find src htdocs -path "*/core/modules/mod*.class.php" -print -quit 2>/dev/null)
|
if [ -n "$MANIFEST" ]; then
|
||||||
if [ -z "$MOD_FILE" ]; then
|
XML_VER=$(grep -oP '<version>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1)
|
||||||
echo "❌ No module descriptor (src/core/modules/mod*.class.php)" >> $GITHUB_STEP_SUMMARY
|
if [ -n "$XML_VER" ] && [ "$XML_VER" != "$VERSION" ]; then
|
||||||
|
echo "- Manifest drift: \`${XML_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
ERRORS=$((ERRORS+1))
|
ERRORS=$((ERRORS+1))
|
||||||
else
|
else
|
||||||
echo "✅ Module descriptor: \`${MOD_FILE}\`" >> $GITHUB_STEP_SUMMARY
|
echo "- Manifest version: \`${VERSION}\`" >> $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
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Joomla-specific checks
|
# -- Joomla: XML manifest existence --------
|
||||||
if [ "$PLATFORM" = "waas-component" ]; then
|
if [ -z "$MANIFEST" ]; then
|
||||||
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
echo "- No Joomla XML manifest found" >> $GITHUB_STEP_SUMMARY
|
||||||
if [ -z "$MANIFEST" ]; then
|
ERRORS=$((ERRORS+1))
|
||||||
echo "❌ No Joomla XML manifest found" >> $GITHUB_STEP_SUMMARY
|
else
|
||||||
ERRORS=$((ERRORS+1))
|
echo "- Manifest: \`${MANIFEST}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
else
|
|
||||||
echo "✅ Manifest: \`${MANIFEST}\`" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
# Check extension type
|
# -- Joomla: extension type check --------
|
||||||
TYPE=$(grep -oP '<extension[^>]+type="\K[^"]+' "$MANIFEST" 2>/dev/null)
|
TYPE=$(grep -oP '<extension[^>]+type="\K[^"]+' "$MANIFEST" 2>/dev/null)
|
||||||
echo "✅ Extension type: ${TYPE:-unknown}" >> $GITHUB_STEP_SUMMARY
|
echo "- Extension type: ${TYPE:-unknown}" >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
if [ "$ERRORS" -gt 0 ]; then
|
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
|
else
|
||||||
echo "**✅ All sanity checks passed**" >> $GITHUB_STEP_SUMMARY
|
echo "**All sanity checks passed**" >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── STEP 2: Create or update version/XX.YY branch ──────────────────
|
# -- STEP 2: Create or update version/XX.YY archive branch ---------------
|
||||||
- name: "Step 2: Version branch"
|
# Always runs — every version change on main archives to version/XX.YY
|
||||||
if: >-
|
- name: "Step 2: Version archive branch"
|
||||||
steps.version.outputs.skip != 'true' &&
|
if: steps.check.outputs.already_released != 'true'
|
||||||
steps.check.outputs.already_released != 'true'
|
|
||||||
run: |
|
run: |
|
||||||
BRANCH="${{ steps.version.outputs.branch }}"
|
BRANCH="${{ steps.version.outputs.branch }}"
|
||||||
IS_MINOR="${{ steps.version.outputs.is_minor }}"
|
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 checkout -b "$BRANCH" 2>/dev/null || git checkout "$BRANCH"
|
||||||
git push origin "$BRANCH" --force
|
git push origin "$BRANCH" --force
|
||||||
echo "🌿 Created branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
|
echo "Created archive branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
|
||||||
else
|
|
||||||
git push origin HEAD:"$BRANCH" --force
|
|
||||||
echo "📝 Updated branch: ${BRANCH} (patch)" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── STEP 3: Set platform version ───────────────────────────────────
|
# -- STEP 3: Set platform version ----------------------------------------
|
||||||
- name: "Step 3: Set platform version"
|
- name: "Step 3: Set platform version"
|
||||||
if: >-
|
if: >-
|
||||||
steps.version.outputs.skip != 'true' &&
|
steps.version.outputs.skip != 'true' &&
|
||||||
@@ -224,7 +246,7 @@ jobs:
|
|||||||
php /tmp/mokostandards/api/cli/version_set_platform.php \
|
php /tmp/mokostandards/api/cli/version_set_platform.php \
|
||||||
--path . --version "$VERSION" --branch main
|
--path . --version "$VERSION" --branch main
|
||||||
|
|
||||||
# ── STEP 4: Update version badges ──────────────────────────────────
|
# -- STEP 4: Update version badges ----------------------------------------
|
||||||
- name: "Step 4: Update version badges"
|
- name: "Step 4: Update version badges"
|
||||||
if: >-
|
if: >-
|
||||||
steps.version.outputs.skip != 'true' &&
|
steps.version.outputs.skip != 'true' &&
|
||||||
@@ -237,107 +259,119 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# ── STEP 5: Write update files (Dolibarr: update.txt / Joomla: update.xml)
|
# -- STEP 5: Write updates.xml (Joomla update server) ---------------------
|
||||||
- name: "Step 5: Write update files"
|
- name: "Step 5: Write updates.xml"
|
||||||
if: >-
|
if: >-
|
||||||
steps.version.outputs.skip != 'true' &&
|
steps.version.outputs.skip != 'true' &&
|
||||||
steps.check.outputs.already_released != 'true'
|
steps.check.outputs.already_released != 'true'
|
||||||
run: |
|
run: |
|
||||||
PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null)
|
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
REPO="${{ github.repository }}"
|
REPO="${{ github.repository }}"
|
||||||
|
|
||||||
if [ "$PLATFORM" = "crm-module" ]; then
|
# -- Parse extension metadata from XML manifest ----------------
|
||||||
printf '%s' "$VERSION" > update.txt
|
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||||
echo "📦 update.txt: ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
if [ -z "$MANIFEST" ]; then
|
||||||
|
echo "Warning: No Joomla XML manifest found — skipping updates.xml" >> $GITHUB_STEP_SUMMARY
|
||||||
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$PLATFORM" = "waas-component" ]; then
|
EXT_NAME=$(grep -oP '<name>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "${{ github.event.repository.name }}")
|
||||||
# ── Parse extension metadata from XML manifest ──────────────
|
EXT_TYPE=$(grep -oP '<extension[^>]+type="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "component")
|
||||||
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
EXT_ELEMENT=$(grep -oP '<element>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "")
|
||||||
if [ -z "$MANIFEST" ]; then
|
EXT_CLIENT=$(grep -oP '<extension[^>]+client="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "")
|
||||||
echo "⚠️ No Joomla XML manifest found — skipping update.xml" >> $GITHUB_STEP_SUMMARY
|
EXT_FOLDER=$(grep -oP '<extension[^>]+group="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "")
|
||||||
else
|
TARGET_PLATFORM=$(grep -oP '<targetplatform[^/]*/>' "$MANIFEST" 2>/dev/null | head -1 || echo "")
|
||||||
EXT_NAME=$(grep -oP '<name>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "${{ github.event.repository.name }}")
|
PHP_MINIMUM=$(grep -oP '<php_minimum>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "")
|
||||||
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 "")
|
|
||||||
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 "")
|
|
||||||
|
|
||||||
# Derive element from manifest filename if not in XML
|
# Derive element from manifest filename if not in XML
|
||||||
if [ -z "$EXT_ELEMENT" ]; then
|
if [ -z "$EXT_ELEMENT" ]; then
|
||||||
EXT_ELEMENT=$(basename "$MANIFEST" .xml)
|
EXT_ELEMENT=$(basename "$MANIFEST" .xml)
|
||||||
fi
|
|
||||||
|
|
||||||
# Build client tag: plugins and frontend modules need <client>site</client>
|
|
||||||
CLIENT_TAG=""
|
|
||||||
if [ -n "$EXT_CLIENT" ]; then
|
|
||||||
CLIENT_TAG="<client>${EXT_CLIENT}</client>"
|
|
||||||
elif [ "$EXT_TYPE" = "module" ] || [ "$EXT_TYPE" = "plugin" ]; then
|
|
||||||
CLIENT_TAG="<client>site</client>"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Build folder tag for plugins (required for Joomla to match the update)
|
|
||||||
FOLDER_TAG=""
|
|
||||||
if [ -n "$EXT_FOLDER" ] && [ "$EXT_TYPE" = "plugin" ]; then
|
|
||||||
FOLDER_TAG="<folder>${EXT_FOLDER}</folder>"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Build targetplatform (fallback to Joomla 5+6 if not in manifest)
|
|
||||||
if [ -z "$TARGET_PLATFORM" ]; then
|
|
||||||
TARGET_PLATFORM=$(printf '<targetplatform name="joomla" version="5.*" %s>' "/")
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Build php_minimum tag
|
|
||||||
PHP_TAG=""
|
|
||||||
if [ -n "$PHP_MINIMUM" ]; then
|
|
||||||
PHP_TAG="<php_minimum>${PHP_MINIMUM}</php_minimum>"
|
|
||||||
fi
|
|
||||||
|
|
||||||
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) ───────────────────────
|
|
||||||
{
|
|
||||||
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>"
|
|
||||||
printf '%s\n' " <element>${EXT_ELEMENT}</element>"
|
|
||||||
printf '%s\n' " <type>${EXT_TYPE}</type>"
|
|
||||||
printf '%s\n' " <version>${VERSION}</version>"
|
|
||||||
[ -n "$CLIENT_TAG" ] && printf '%s\n' " ${CLIENT_TAG}"
|
|
||||||
[ -n "$FOLDER_TAG" ] && printf '%s\n' " ${FOLDER_TAG}"
|
|
||||||
printf '%s\n' ' <tags>'
|
|
||||||
printf '%s\n' ' <tag>stable</tag>'
|
|
||||||
printf '%s\n' ' </tags>'
|
|
||||||
printf '%s\n' " <infourl title=\"${EXT_NAME}\">${INFO_URL}</infourl>"
|
|
||||||
printf '%s\n' ' <downloads>'
|
|
||||||
printf '%s\n' " <downloadurl type=\"full\" format=\"zip\">${DOWNLOAD_URL}</downloadurl>"
|
|
||||||
printf '%s\n' ' </downloads>'
|
|
||||||
printf '%s\n' " ${TARGET_PLATFORM}"
|
|
||||||
[ -n "$PHP_TAG" ] && printf '%s\n' " ${PHP_TAG}"
|
|
||||||
printf '%s\n' ' <maintainer>Moko Consulting</maintainer>'
|
|
||||||
printf '%s\n' ' <maintainerurl>https://mokoconsulting.tech</maintainerurl>'
|
|
||||||
printf '%s\n' ' </update>'
|
|
||||||
printf '%s\n' '</updates>'
|
|
||||||
} > update.xml
|
|
||||||
|
|
||||||
echo "📦 update.xml: ${VERSION} (stable) — ${EXT_TYPE}/${EXT_ELEMENT}" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Commit all changes ─────────────────────────────────────────────
|
# Build client tag: plugins and frontend modules need <client>site</client>
|
||||||
|
CLIENT_TAG=""
|
||||||
|
if [ -n "$EXT_CLIENT" ]; then
|
||||||
|
CLIENT_TAG="<client>${EXT_CLIENT}</client>"
|
||||||
|
elif [ "$EXT_TYPE" = "module" ] || [ "$EXT_TYPE" = "plugin" ]; then
|
||||||
|
CLIENT_TAG="<client>site</client>"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build folder tag for plugins (required for Joomla to match the update)
|
||||||
|
FOLDER_TAG=""
|
||||||
|
if [ -n "$EXT_FOLDER" ] && [ "$EXT_TYPE" = "plugin" ]; then
|
||||||
|
FOLDER_TAG="<folder>${EXT_FOLDER}</folder>"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# Build php_minimum tag
|
||||||
|
PHP_TAG=""
|
||||||
|
if [ -n "$PHP_MINIMUM" ]; then
|
||||||
|
PHP_TAG="<php_minimum>${PHP_MINIMUM}</php_minimum>"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DOWNLOAD_URL="https://github.com/${REPO}/releases/download/v${VERSION}/${EXT_ELEMENT}-${VERSION}.zip"
|
||||||
|
INFO_URL="https://github.com/${REPO}/releases/tag/v${VERSION}"
|
||||||
|
|
||||||
|
# -- Build stable entry to temp file ─────────────────────────
|
||||||
|
{
|
||||||
|
printf '%s\n' ' <update>'
|
||||||
|
printf '%s\n' " <name>${EXT_NAME}</name>"
|
||||||
|
printf '%s\n' " <description>${EXT_NAME} update</description>"
|
||||||
|
printf '%s\n' " <element>${EXT_ELEMENT}</element>"
|
||||||
|
printf '%s\n' " <type>${EXT_TYPE}</type>"
|
||||||
|
printf '%s\n' " <version>${VERSION}</version>"
|
||||||
|
[ -n "$CLIENT_TAG" ] && printf '%s\n' " ${CLIENT_TAG}"
|
||||||
|
[ -n "$FOLDER_TAG" ] && printf '%s\n' " ${FOLDER_TAG}"
|
||||||
|
printf '%s\n' ' <tags>'
|
||||||
|
printf '%s\n' ' <tag>stable</tag>'
|
||||||
|
printf '%s\n' ' </tags>'
|
||||||
|
printf '%s\n' " <infourl title=\"${EXT_NAME}\">${INFO_URL}</infourl>"
|
||||||
|
printf '%s\n' ' <downloads>'
|
||||||
|
printf '%s\n' " <downloadurl type=\"full\" format=\"zip\">${DOWNLOAD_URL}</downloadurl>"
|
||||||
|
printf '%s\n' ' </downloads>'
|
||||||
|
printf '%s\n' " ${TARGET_PLATFORM}"
|
||||||
|
[ -n "$PHP_TAG" ] && printf '%s\n' " ${PHP_TAG}"
|
||||||
|
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>'
|
||||||
|
} > updates.xml
|
||||||
|
|
||||||
|
echo "updates.xml: ${VERSION} (stable + rc/dev preserved)" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
# -- Commit all changes ---------------------------------------------------
|
||||||
- name: Commit release changes
|
- name: Commit release changes
|
||||||
if: >-
|
if: >-
|
||||||
steps.version.outputs.skip != 'true' &&
|
steps.version.outputs.skip != 'true' &&
|
||||||
steps.check.outputs.already_released != 'true'
|
steps.check.outputs.already_released != 'true'
|
||||||
run: |
|
run: |
|
||||||
if git diff --quiet && git diff --cached --quiet; then
|
if git diff --quiet && git diff --cached --quiet; then
|
||||||
echo "ℹ️ No changes to commit"
|
echo "No changes to commit"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
@@ -348,18 +382,25 @@ jobs:
|
|||||||
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
|
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>"
|
||||||
git push
|
git push
|
||||||
|
|
||||||
# ── STEP 6: Create tag ─────────────────────────────────────────────
|
# -- STEP 6: Create tag ---------------------------------------------------
|
||||||
- name: "Step 6: Create git tag"
|
- name: "Step 6: Create git tag"
|
||||||
if: >-
|
if: >-
|
||||||
steps.version.outputs.skip != 'true' &&
|
steps.version.outputs.skip != 'true' &&
|
||||||
steps.check.outputs.tag_exists != 'true'
|
steps.check.outputs.tag_exists != 'true' &&
|
||||||
|
steps.version.outputs.is_minor == 'true'
|
||||||
run: |
|
run: |
|
||||||
TAG="${{ steps.version.outputs.tag }}"
|
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||||
git tag "$TAG"
|
# Only create the major release tag if it doesn't exist yet
|
||||||
git push origin "$TAG"
|
if ! git rev-parse "$RELEASE_TAG" >/dev/null 2>&1; then
|
||||||
echo "🏷️ Tag: ${TAG}" >> $GITHUB_STEP_SUMMARY
|
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"
|
- name: "Step 7: GitHub Release"
|
||||||
if: >-
|
if: >-
|
||||||
steps.version.outputs.skip != 'true' &&
|
steps.version.outputs.skip != 'true' &&
|
||||||
@@ -368,67 +409,129 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
TAG="${{ steps.version.outputs.tag }}"
|
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||||
BRANCH="${{ steps.version.outputs.branch }}"
|
BRANCH="${{ steps.version.outputs.branch }}"
|
||||||
IS_MINOR="${{ steps.version.outputs.is_minor }}"
|
MAJOR="${{ steps.version.outputs.major }}"
|
||||||
|
|
||||||
# Derive the minor version base (XX.YY.00)
|
|
||||||
MINOR_BASE=$(echo "$VERSION" | sed 's/\.[0-9]*$/.00/')
|
|
||||||
MINOR_TAG="v${MINOR_BASE}"
|
|
||||||
|
|
||||||
NOTES=$(php /tmp/mokostandards/api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null)
|
NOTES=$(php /tmp/mokostandards/api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null)
|
||||||
[ -z "$NOTES" ] && NOTES="Release ${VERSION}"
|
[ -z "$NOTES" ] && NOTES="Release ${VERSION}"
|
||||||
echo "$NOTES" > /tmp/release_notes.md
|
echo "$NOTES" > /tmp/release_notes.md
|
||||||
|
|
||||||
if [ "$IS_MINOR" = "true" ]; then
|
# Check if the major release already exists
|
||||||
# Minor release: create new GitHub Release
|
EXISTING=$(gh release view "$RELEASE_TAG" --json tagName -q .tagName 2>/dev/null || true)
|
||||||
gh release create "$TAG" \
|
|
||||||
--title "${VERSION}" \
|
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 \
|
--notes-file /tmp/release_notes.md \
|
||||||
--target "$BRANCH"
|
--target "$BRANCH"
|
||||||
echo "🚀 Release created: ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
echo "Release created: ${RELEASE_TAG} (${VERSION})" >> $GITHUB_STEP_SUMMARY
|
||||||
else
|
else
|
||||||
# Patch release: update the existing minor release with new tag
|
# Append version notes to existing major release
|
||||||
# Find the latest release for this minor version
|
CURRENT_NOTES=$(gh release view "$RELEASE_TAG" --json body -q .body 2>/dev/null || true)
|
||||||
EXISTING=$(gh release view "$MINOR_TAG" --json tagName -q .tagName 2>/dev/null || true)
|
{
|
||||||
if [ -n "$EXISTING" ]; then
|
echo "$CURRENT_NOTES"
|
||||||
# Update existing release body with patch info
|
echo ""
|
||||||
CURRENT_NOTES=$(gh release view "$MINOR_TAG" --json body -q .body 2>/dev/null || true)
|
echo "---"
|
||||||
{
|
echo "### ${VERSION}"
|
||||||
echo "$CURRENT_NOTES"
|
echo ""
|
||||||
echo ""
|
cat /tmp/release_notes.md
|
||||||
echo "---"
|
} > /tmp/updated_notes.md
|
||||||
echo "### Patch ${VERSION}"
|
|
||||||
echo ""
|
|
||||||
cat /tmp/release_notes.md
|
|
||||||
} > /tmp/updated_notes.md
|
|
||||||
|
|
||||||
gh release edit "$MINOR_TAG" \
|
gh release edit "$RELEASE_TAG" \
|
||||||
--title "${MINOR_BASE} (latest: ${VERSION})" \
|
--title "v${MAJOR} (latest: ${VERSION})" \
|
||||||
--notes-file /tmp/updated_notes.md
|
--notes-file /tmp/updated_notes.md
|
||||||
echo "📝 Release updated: ${MINOR_BASE} → patch ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
echo "Release updated: ${RELEASE_TAG} -> ${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
|
|
||||||
fi
|
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
|
- name: Pipeline Summary
|
||||||
if: always()
|
if: always()
|
||||||
run: |
|
run: |
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
VERSION="${{ steps.version.outputs.version }}"
|
||||||
if [ "${{ steps.version.outputs.skip }}" = "true" ]; then
|
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
|
echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY
|
||||||
elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then
|
elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then
|
||||||
echo "## ℹ️ Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
||||||
else
|
else
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "## ✅ Build & Release Complete" >> $GITHUB_STEP_SUMMARY
|
echo "## Build & Release Complete (Joomla)" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY
|
echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "|------|--------|" >> $GITHUB_STEP_SUMMARY
|
echo "|------|--------|" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|||||||
291
.github/workflows/auto-update-sha.yml
vendored
291
.github/workflows/auto-update-sha.yml
vendored
@@ -1,147 +1,144 @@
|
|||||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: GitHub.Workflow
|
# DEFGROUP: GitHub.Workflow
|
||||||
# INGROUP: MokoCassiopeia.Automation
|
# INGROUP: MokoCassiopeia.Automation
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
# REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
||||||
# PATH: /.github/workflows/auto-update-sha.yml
|
# 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
|
# BRIEF: Automatically update SHA-256 hash in updates.xml after release
|
||||||
# NOTE: Ensures updates.xml stays synchronized with release packages
|
# NOTE: Ensures updates.xml stays synchronized with release packages
|
||||||
|
|
||||||
name: Auto-Update SHA Hash
|
name: Auto-Update SHA Hash
|
||||||
|
|
||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
tag:
|
tag:
|
||||||
description: 'Release tag to update SHA for (e.g., 03.08.03)'
|
description: 'Release tag to update SHA for (e.g., 03.08.03)'
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update-sha:
|
update-sha:
|
||||||
name: Update SHA-256 Hash in updates.xml
|
name: Update SHA-256 Hash in updates.xml
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout Repository
|
- name: Checkout Repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
ref: main
|
ref: main
|
||||||
|
|
||||||
- name: Get release tag
|
- name: Get release tag
|
||||||
id: tag
|
id: tag
|
||||||
run: |
|
run: |
|
||||||
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
|
||||||
TAG="${{ inputs.tag }}"
|
TAG="${{ inputs.tag }}"
|
||||||
else
|
else
|
||||||
TAG="${{ github.event.release.tag_name }}"
|
TAG="${{ github.event.release.tag_name }}"
|
||||||
fi
|
fi
|
||||||
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
echo "tag=${TAG}" >> $GITHUB_OUTPUT
|
||||||
echo "Processing release: ${TAG}"
|
echo "Processing release: ${TAG}"
|
||||||
|
|
||||||
- name: Download release package
|
- name: Download release package
|
||||||
run: |
|
run: |
|
||||||
TAG="${{ steps.tag.outputs.tag }}"
|
TAG="${{ steps.tag.outputs.tag }}"
|
||||||
PACKAGE_NAME="mokocassiopeia-src-${TAG}.zip"
|
PACKAGE_NAME="mokocassiopeia-src-${TAG}.zip"
|
||||||
DOWNLOAD_URL="https://github.com/${{ github.repository }}/releases/download/${TAG}/${PACKAGE_NAME}"
|
DOWNLOAD_URL="https://github.com/${{ github.repository }}/releases/download/${TAG}/${PACKAGE_NAME}"
|
||||||
|
|
||||||
echo "Downloading: ${DOWNLOAD_URL}"
|
echo "Downloading: ${DOWNLOAD_URL}"
|
||||||
curl -L -o "${PACKAGE_NAME}" "${DOWNLOAD_URL}"
|
curl -L -o "${PACKAGE_NAME}" "${DOWNLOAD_URL}"
|
||||||
|
|
||||||
if [ ! -f "${PACKAGE_NAME}" ]; then
|
if [ ! -f "${PACKAGE_NAME}" ]; then
|
||||||
echo "Error: Failed to download package"
|
echo "Error: Failed to download package"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "PACKAGE_NAME=${PACKAGE_NAME}" >> $GITHUB_ENV
|
echo "PACKAGE_NAME=${PACKAGE_NAME}" >> $GITHUB_ENV
|
||||||
|
|
||||||
- name: Calculate SHA-256 hash
|
- name: Calculate SHA-256 hash
|
||||||
id: sha
|
id: sha
|
||||||
run: |
|
run: |
|
||||||
SHA256_HASH=$(sha256sum "${PACKAGE_NAME}" | cut -d' ' -f1)
|
SHA256_HASH=$(sha256sum "${PACKAGE_NAME}" | cut -d' ' -f1)
|
||||||
echo "sha256=${SHA256_HASH}" >> $GITHUB_OUTPUT
|
echo "sha256=${SHA256_HASH}" >> $GITHUB_OUTPUT
|
||||||
echo "SHA-256 Hash: ${SHA256_HASH}"
|
echo "SHA-256 Hash: ${SHA256_HASH}"
|
||||||
|
|
||||||
- name: Update updates.xml
|
- name: Update updates.xml
|
||||||
run: |
|
run: |
|
||||||
TAG="${{ steps.tag.outputs.tag }}"
|
TAG="${{ steps.tag.outputs.tag }}"
|
||||||
SHA256="${{ steps.sha.outputs.sha256 }}"
|
SHA256="${{ steps.sha.outputs.sha256 }}"
|
||||||
DATE=$(date +%Y-%m-%d)
|
DATE=$(date +%Y-%m-%d)
|
||||||
|
|
||||||
# Update version
|
# Update version
|
||||||
sed -i "s|<version>.*</version>|<version>${TAG}</version>|" updates.xml
|
sed -i "s|<version>.*</version>|<version>${TAG}</version>|" updates.xml
|
||||||
|
|
||||||
# Update creation date
|
# Update creation date
|
||||||
sed -i "s|<creationDate>.*</creationDate>|<creationDate>${DATE}</creationDate>|" updates.xml
|
sed -i "s|<creationDate>.*</creationDate>|<creationDate>${DATE}</creationDate>|" updates.xml
|
||||||
|
|
||||||
# Update download URL
|
# Update download URL
|
||||||
sed -i "s|<downloadurl type='full' format='zip'>.*</downloadurl>|<downloadurl type='full' format='zip'>https://github.com/${{ github.repository }}/releases/download/${TAG}/mokocassiopeia-src-${TAG}.zip</downloadurl>|" updates.xml
|
sed -i "s|<downloadurl type='full' format='zip'>.*</downloadurl>|<downloadurl type='full' format='zip'>https://github.com/${{ github.repository }}/releases/download/${TAG}/mokocassiopeia-src-${TAG}.zip</downloadurl>|" updates.xml
|
||||||
|
|
||||||
# Update or add SHA-256 hash
|
# Update or add SHA-256 hash
|
||||||
if grep -q "<sha256>" updates.xml; then
|
if grep -q "<sha256>" updates.xml; then
|
||||||
sed -i "s|<sha256>.*</sha256>|<sha256>sha256:${SHA256}</sha256>|" updates.xml
|
sed -i "s|<sha256>.*</sha256>|<sha256>sha256:${SHA256}</sha256>|" updates.xml
|
||||||
else
|
else
|
||||||
# Add SHA-256 after downloadurl
|
# Add SHA-256 after downloadurl
|
||||||
sed -i "/<\/downloadurl>/a\ <sha256>sha256:${SHA256}<\/sha256>" updates.xml
|
sed -i "/<\/downloadurl>/a\ <sha256>sha256:${SHA256}<\/sha256>" updates.xml
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "Updated updates.xml with:"
|
echo "Updated updates.xml with:"
|
||||||
echo " Version: ${TAG}"
|
echo " Version: ${TAG}"
|
||||||
echo " Date: ${DATE}"
|
echo " Date: ${DATE}"
|
||||||
echo " SHA-256: ${SHA256}"
|
echo " SHA-256: ${SHA256}"
|
||||||
|
|
||||||
- name: Check for changes
|
- name: Check for changes
|
||||||
id: changes
|
id: changes
|
||||||
run: |
|
run: |
|
||||||
if git diff --quiet updates.xml; then
|
if git diff --quiet updates.xml; then
|
||||||
echo "has_changes=false" >> $GITHUB_OUTPUT
|
echo "has_changes=false" >> $GITHUB_OUTPUT
|
||||||
echo "No changes to updates.xml"
|
echo "No changes to updates.xml"
|
||||||
else
|
else
|
||||||
echo "has_changes=true" >> $GITHUB_OUTPUT
|
echo "has_changes=true" >> $GITHUB_OUTPUT
|
||||||
echo "Changes detected in updates.xml"
|
echo "Changes detected in updates.xml"
|
||||||
git diff updates.xml
|
git diff updates.xml
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Commit and push changes
|
- name: Commit and push changes
|
||||||
if: steps.changes.outputs.has_changes == 'true'
|
if: steps.changes.outputs.has_changes == 'true'
|
||||||
run: |
|
run: |
|
||||||
TAG="${{ steps.tag.outputs.tag }}"
|
TAG="${{ steps.tag.outputs.tag }}"
|
||||||
|
|
||||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||||
git config --local user.name "github-actions[bot]"
|
git config --local user.name "github-actions[bot]"
|
||||||
|
|
||||||
git add updates.xml
|
git add updates.xml
|
||||||
git commit -m "chore: Update SHA-256 hash for release ${TAG}
|
git commit -m "chore: Update SHA-256 hash for release ${TAG} - SHA: ${{ steps.sha.outputs.sha256 }}"
|
||||||
|
|
||||||
Auto-generated by auto-update-sha workflow
|
git push origin main
|
||||||
SHA-256: ${{ steps.sha.outputs.sha256 }}"
|
|
||||||
|
echo "Successfully updated updates.xml with SHA-256 hash for release ${TAG}"
|
||||||
git push origin main
|
|
||||||
|
- name: Summary
|
||||||
echo "Successfully updated updates.xml with SHA-256 hash for release ${TAG}"
|
if: steps.changes.outputs.has_changes == 'true'
|
||||||
|
run: |
|
||||||
- name: Summary
|
echo "### SHA-256 Hash Updated Successfully" >> $GITHUB_STEP_SUMMARY
|
||||||
if: steps.changes.outputs.has_changes == 'true'
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
run: |
|
echo "- Release: ${{ steps.tag.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "### SHA-256 Hash Updated Successfully" >> $GITHUB_STEP_SUMMARY
|
echo "- SHA-256: \`${{ steps.sha.outputs.sha256 }}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "- File: updates.xml" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "- Release: ${{ steps.tag.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "- SHA-256: \`${{ steps.sha.outputs.sha256 }}\`" >> $GITHUB_STEP_SUMMARY
|
echo "The Joomla update server will now provide the correct package hash." >> $GITHUB_STEP_SUMMARY
|
||||||
echo "- File: updates.xml" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
- name: Summary (no changes)
|
||||||
echo "The Joomla update server will now provide the correct package hash." >> $GITHUB_STEP_SUMMARY
|
if: steps.changes.outputs.has_changes == 'false'
|
||||||
|
run: |
|
||||||
- name: Summary (no changes)
|
echo "### No Updates Needed" >> $GITHUB_STEP_SUMMARY
|
||||||
if: steps.changes.outputs.has_changes == 'false'
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
run: |
|
echo "updates.xml already contains the correct SHA-256 hash for release ${{ steps.tag.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "### No Updates Needed" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "updates.xml already contains the correct SHA-256 hash for release ${{ steps.tag.outputs.tag }}" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|||||||
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
|
# INGROUP: MokoStandards.Security
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /templates/workflows/generic/codeql-analysis.yml.template
|
# 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)
|
# BRIEF: CodeQL security scanning workflow (generic — all repo types)
|
||||||
# NOTE: Deployed to .github/workflows/codeql-analysis.yml in governed repos.
|
# NOTE: Deployed to .github/workflows/codeql-analysis.yml in governed repos.
|
||||||
# CodeQL does not support PHP directly; JavaScript scans JSON/YAML/shell.
|
# CodeQL does not support PHP directly; JavaScript scans JSON/YAML/shell.
|
||||||
|
|||||||
39
.github/workflows/deploy-demo.yml
vendored
39
.github/workflows/deploy-demo.yml
vendored
@@ -22,7 +22,7 @@
|
|||||||
# INGROUP: MokoStandards.Deploy
|
# INGROUP: MokoStandards.Deploy
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /templates/workflows/shared/deploy-demo.yml.template
|
# 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
|
# 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.
|
# 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.
|
# 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-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
|
# 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
|
# full remote destination: DEMO_FTP_PATH/DEMO_FTP_SUFFIX
|
||||||
# Ignore rules: Place a .ftp_ignore file in the repository root. Each non-empty,
|
# Ignore rules: Place a .ftpignore file in the repository root. Each non-empty,
|
||||||
# non-comment line is a regex pattern tested against the relative path
|
# non-comment line is a glob pattern tested against the relative path
|
||||||
# of each file (e.g. "subdir/file.txt"). The .gitignore is also
|
# of each file (e.g. "subdir/file.txt"). The .gitignore is NOT used.
|
||||||
# respected automatically.
|
|
||||||
# Required org-level secret: DEMO_FTP_KEY (preferred) or DEMO_FTP_PASSWORD
|
# 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.
|
# Access control: only users with admin or maintain role on the repository may deploy.
|
||||||
@@ -195,8 +194,8 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
SOURCE_DIR: ${{ steps.source.outputs.dir }}
|
SOURCE_DIR: ${{ steps.source.outputs.dir }}
|
||||||
run: |
|
run: |
|
||||||
# ── Convert a gitignore-style glob line to an ERE pattern ──────────────
|
# ── Convert a ftpignore-style glob line to an ERE pattern ──────────────
|
||||||
ftp_ignore_to_regex() {
|
ftpignore_to_regex() {
|
||||||
local line="$1"
|
local line="$1"
|
||||||
local anchored=false
|
local anchored=false
|
||||||
# Strip inline comments and whitespace
|
# Strip inline comments and whitespace
|
||||||
@@ -226,15 +225,15 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Read .ftp_ignore (gitignore-style globs) ─────────────────────────
|
# ── Read .ftpignore (ftpignore-style globs) ─────────────────────────
|
||||||
IGNORE_PATTERNS=()
|
IGNORE_PATTERNS=()
|
||||||
IGNORE_SOURCES=()
|
IGNORE_SOURCES=()
|
||||||
if [ -f ".ftp_ignore" ]; then
|
if [ -f ".ftpignore" ]; then
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
[[ "$line" =~ ^[[:space:]]*$ || "$line" =~ ^[[:space:]]*# ]] && continue
|
[[ "$line" =~ ^[[:space:]]*$ || "$line" =~ ^[[:space:]]*# ]] && continue
|
||||||
regex=$(ftp_ignore_to_regex "$line")
|
regex=$(ftpignore_to_regex "$line")
|
||||||
[ -n "$regex" ] && IGNORE_PATTERNS+=("$regex") && IGNORE_SOURCES+=("$line")
|
[ -n "$regex" ] && IGNORE_PATTERNS+=("$regex") && IGNORE_SOURCES+=("$line")
|
||||||
done < ".ftp_ignore"
|
done < ".ftpignore"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Walk src/ and classify every file ────────────────────────────────
|
# ── Walk src/ and classify every file ────────────────────────────────
|
||||||
@@ -245,17 +244,11 @@ jobs:
|
|||||||
SKIP=false
|
SKIP=false
|
||||||
for i in "${!IGNORE_PATTERNS[@]}"; do
|
for i in "${!IGNORE_PATTERNS[@]}"; do
|
||||||
if echo "$rel" | grep -qE "${IGNORE_PATTERNS[$i]}" 2>/dev/null; then
|
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
|
SKIP=true; break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
$SKIP && continue
|
$SKIP && continue
|
||||||
if [ -f ".gitignore" ]; then
|
|
||||||
git check-ignore -q "$rel" 2>/dev/null && {
|
|
||||||
IGNORED_FILES+=("$rel | .gitignore")
|
|
||||||
continue
|
|
||||||
} || true
|
|
||||||
fi
|
|
||||||
WILL_UPLOAD+=("$rel")
|
WILL_UPLOAD+=("$rel")
|
||||||
done < <(find "$SOURCE_DIR" -type f -print0 | sort -z)
|
done < <(find "$SOURCE_DIR" -type f -print0 | sort -z)
|
||||||
|
|
||||||
@@ -426,7 +419,7 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||||
run: |
|
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" \
|
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||||
/tmp/mokostandards
|
/tmp/mokostandards
|
||||||
cd /tmp/mokostandards
|
cd /tmp/mokostandards
|
||||||
@@ -637,8 +630,12 @@ jobs:
|
|||||||
DEPLOY_ARGS+=(--key-passphrase "$SFTP_PASSWORD")
|
DEPLOY_ARGS+=(--key-passphrase "$SFTP_PASSWORD")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
php /tmp/mokostandards/api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
|
PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null || true)
|
||||||
# (deploy-sftp.php handles dotfile skipping and .ftp_ignore natively)
|
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
|
||||||
# Remove temp files that should never be left behind
|
# Remove temp files that should never be left behind
|
||||||
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
||||||
|
|
||||||
|
|||||||
111
.github/workflows/deploy-dev.yml
vendored
111
.github/workflows/deploy-dev.yml
vendored
@@ -22,7 +22,7 @@
|
|||||||
# INGROUP: MokoStandards.Deploy
|
# INGROUP: MokoStandards.Deploy
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /templates/workflows/shared/deploy-dev.yml.template
|
# 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
|
# 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.
|
# 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.
|
# 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-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
|
# 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
|
# full remote destination: DEV_FTP_PATH/DEV_FTP_SUFFIX
|
||||||
# Ignore rules: Place a .ftp_ignore file in the repository root. Each non-empty,
|
# Ignore rules: Place a .ftpignore file in the repository root. Each non-empty,
|
||||||
# non-comment line is a regex pattern tested against the relative path
|
# non-comment line is a glob pattern tested against the relative path
|
||||||
# of each file (e.g. "subdir/file.txt"). The .gitignore is also
|
# of each file (e.g. "subdir/file.txt"). The .gitignore is NOT used.
|
||||||
# respected automatically.
|
|
||||||
# Required org-level secret: DEV_FTP_KEY (preferred) or DEV_FTP_PASSWORD
|
# 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.
|
# Access control: only users with admin or maintain role on the repository may deploy.
|
||||||
@@ -200,8 +199,8 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
SOURCE_DIR: ${{ steps.source.outputs.dir }}
|
SOURCE_DIR: ${{ steps.source.outputs.dir }}
|
||||||
run: |
|
run: |
|
||||||
# ── Convert a gitignore-style glob line to an ERE pattern ──────────────
|
# ── Convert a ftpignore-style glob line to an ERE pattern ──────────────
|
||||||
ftp_ignore_to_regex() {
|
ftpignore_to_regex() {
|
||||||
local line="$1"
|
local line="$1"
|
||||||
local anchored=false
|
local anchored=false
|
||||||
# Strip inline comments and whitespace
|
# Strip inline comments and whitespace
|
||||||
@@ -231,15 +230,15 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Read .ftp_ignore (gitignore-style globs) ─────────────────────────
|
# ── Read .ftpignore (ftpignore-style globs) ─────────────────────────
|
||||||
IGNORE_PATTERNS=()
|
IGNORE_PATTERNS=()
|
||||||
IGNORE_SOURCES=()
|
IGNORE_SOURCES=()
|
||||||
if [ -f ".ftp_ignore" ]; then
|
if [ -f ".ftpignore" ]; then
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
[[ "$line" =~ ^[[:space:]]*$ || "$line" =~ ^[[:space:]]*# ]] && continue
|
[[ "$line" =~ ^[[:space:]]*$ || "$line" =~ ^[[:space:]]*# ]] && continue
|
||||||
regex=$(ftp_ignore_to_regex "$line")
|
regex=$(ftpignore_to_regex "$line")
|
||||||
[ -n "$regex" ] && IGNORE_PATTERNS+=("$regex") && IGNORE_SOURCES+=("$line")
|
[ -n "$regex" ] && IGNORE_PATTERNS+=("$regex") && IGNORE_SOURCES+=("$line")
|
||||||
done < ".ftp_ignore"
|
done < ".ftpignore"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Walk src/ and classify every file ────────────────────────────────
|
# ── Walk src/ and classify every file ────────────────────────────────
|
||||||
@@ -250,17 +249,11 @@ jobs:
|
|||||||
SKIP=false
|
SKIP=false
|
||||||
for i in "${!IGNORE_PATTERNS[@]}"; do
|
for i in "${!IGNORE_PATTERNS[@]}"; do
|
||||||
if echo "$rel" | grep -qE "${IGNORE_PATTERNS[$i]}" 2>/dev/null; then
|
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
|
SKIP=true; break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
$SKIP && continue
|
$SKIP && continue
|
||||||
if [ -f ".gitignore" ]; then
|
|
||||||
git check-ignore -q "$rel" 2>/dev/null && {
|
|
||||||
IGNORED_FILES+=("$rel | .gitignore")
|
|
||||||
continue
|
|
||||||
} || true
|
|
||||||
fi
|
|
||||||
WILL_UPLOAD+=("$rel")
|
WILL_UPLOAD+=("$rel")
|
||||||
done < <(find "$SOURCE_DIR" -type f -print0 | sort -z)
|
done < <(find "$SOURCE_DIR" -type f -print0 | sort -z)
|
||||||
|
|
||||||
@@ -431,7 +424,7 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||||
run: |
|
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" \
|
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||||
/tmp/mokostandards
|
/tmp/mokostandards
|
||||||
cd /tmp/mokostandards
|
cd /tmp/mokostandards
|
||||||
@@ -582,6 +575,10 @@ jobs:
|
|||||||
> /tmp/sftp-config.json
|
> /tmp/sftp-config.json
|
||||||
fi
|
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 ────────────────────────────
|
# ── Run deploy-sftp.php from MokoStandards ────────────────────────────
|
||||||
DEPLOY_ARGS=(--path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json)
|
DEPLOY_ARGS=(--path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json)
|
||||||
if [ "$USE_PASSPHRASE" = "true" ]; then
|
if [ "$USE_PASSPHRASE" = "true" ]; then
|
||||||
@@ -662,74 +659,20 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
php /tmp/mokostandards/api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
|
# Use Joomla-aware deploy for waas-component (routes files to correct Joomla dirs)
|
||||||
# (deploy-sftp.php handles dotfile skipping and .ftp_ignore natively)
|
# 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[@]}"
|
||||||
|
fi
|
||||||
|
# (both scripts handle dotfile skipping and .ftpignore natively)
|
||||||
# Remove temp files that should never be left behind
|
# Remove temp files that should never be left behind
|
||||||
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
||||||
|
|
||||||
- name: Create or update failure issue
|
# Dev deploys fail silently — no issue creation.
|
||||||
if: failure()
|
# Demo and RS deploys create failure issues (production-facing).
|
||||||
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
|
|
||||||
|
|
||||||
- name: Deployment summary
|
- name: Deployment summary
|
||||||
if: always()
|
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
|
||||||
39
.github/workflows/deploy-rs.yml
vendored
39
.github/workflows/deploy-rs.yml
vendored
@@ -22,7 +22,7 @@
|
|||||||
# INGROUP: MokoStandards.Deploy
|
# INGROUP: MokoStandards.Deploy
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /templates/workflows/shared/deploy-rs.yml.template
|
# 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
|
# 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.
|
# 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.
|
# 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-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
|
# 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
|
# full remote destination: RS_FTP_PATH/RS_FTP_SUFFIX
|
||||||
# Ignore rules: Place a .ftp_ignore file in the repository root. Each non-empty,
|
# Ignore rules: Place a .ftpignore file in the repository root. Each non-empty,
|
||||||
# non-comment line is a regex pattern tested against the relative path
|
# non-comment line is a glob pattern tested against the relative path
|
||||||
# of each file (e.g. "subdir/file.txt"). The .gitignore is also
|
# of each file (e.g. "subdir/file.txt"). The .gitignore is NOT used.
|
||||||
# respected automatically.
|
|
||||||
# Required org-level secret: RS_FTP_KEY (preferred) or RS_FTP_PASSWORD
|
# 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.
|
# Access control: only users with admin or maintain role on the repository may deploy.
|
||||||
@@ -195,8 +194,8 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
SOURCE_DIR: ${{ steps.source.outputs.dir }}
|
SOURCE_DIR: ${{ steps.source.outputs.dir }}
|
||||||
run: |
|
run: |
|
||||||
# ── Convert a gitignore-style glob line to an ERE pattern ──────────────
|
# ── Convert a ftpignore-style glob line to an ERE pattern ──────────────
|
||||||
ftp_ignore_to_regex() {
|
ftpignore_to_regex() {
|
||||||
local line="$1"
|
local line="$1"
|
||||||
local anchored=false
|
local anchored=false
|
||||||
# Strip inline comments and whitespace
|
# Strip inline comments and whitespace
|
||||||
@@ -226,15 +225,15 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# ── Read .ftp_ignore (gitignore-style globs) ─────────────────────────
|
# ── Read .ftpignore (ftpignore-style globs) ─────────────────────────
|
||||||
IGNORE_PATTERNS=()
|
IGNORE_PATTERNS=()
|
||||||
IGNORE_SOURCES=()
|
IGNORE_SOURCES=()
|
||||||
if [ -f ".ftp_ignore" ]; then
|
if [ -f ".ftpignore" ]; then
|
||||||
while IFS= read -r line; do
|
while IFS= read -r line; do
|
||||||
[[ "$line" =~ ^[[:space:]]*$ || "$line" =~ ^[[:space:]]*# ]] && continue
|
[[ "$line" =~ ^[[:space:]]*$ || "$line" =~ ^[[:space:]]*# ]] && continue
|
||||||
regex=$(ftp_ignore_to_regex "$line")
|
regex=$(ftpignore_to_regex "$line")
|
||||||
[ -n "$regex" ] && IGNORE_PATTERNS+=("$regex") && IGNORE_SOURCES+=("$line")
|
[ -n "$regex" ] && IGNORE_PATTERNS+=("$regex") && IGNORE_SOURCES+=("$line")
|
||||||
done < ".ftp_ignore"
|
done < ".ftpignore"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# ── Walk src/ and classify every file ────────────────────────────────
|
# ── Walk src/ and classify every file ────────────────────────────────
|
||||||
@@ -245,17 +244,11 @@ jobs:
|
|||||||
SKIP=false
|
SKIP=false
|
||||||
for i in "${!IGNORE_PATTERNS[@]}"; do
|
for i in "${!IGNORE_PATTERNS[@]}"; do
|
||||||
if echo "$rel" | grep -qE "${IGNORE_PATTERNS[$i]}" 2>/dev/null; then
|
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
|
SKIP=true; break
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
$SKIP && continue
|
$SKIP && continue
|
||||||
if [ -f ".gitignore" ]; then
|
|
||||||
git check-ignore -q "$rel" 2>/dev/null && {
|
|
||||||
IGNORED_FILES+=("$rel | .gitignore")
|
|
||||||
continue
|
|
||||||
} || true
|
|
||||||
fi
|
|
||||||
WILL_UPLOAD+=("$rel")
|
WILL_UPLOAD+=("$rel")
|
||||||
done < <(find "$SOURCE_DIR" -type f -print0 | sort -z)
|
done < <(find "$SOURCE_DIR" -type f -print0 | sort -z)
|
||||||
|
|
||||||
@@ -407,7 +400,7 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||||
run: |
|
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" \
|
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||||
/tmp/mokostandards
|
/tmp/mokostandards
|
||||||
cd /tmp/mokostandards
|
cd /tmp/mokostandards
|
||||||
@@ -564,8 +557,12 @@ jobs:
|
|||||||
DEPLOY_ARGS+=(--key-passphrase "$SFTP_PASSWORD")
|
DEPLOY_ARGS+=(--key-passphrase "$SFTP_PASSWORD")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
php /tmp/mokostandards/api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
|
PLATFORM=$(php /tmp/mokostandards/api/cli/platform_detect.php --path . 2>/dev/null || true)
|
||||||
# (deploy-sftp.php handles dotfile skipping and .ftp_ignore natively)
|
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
|
||||||
# Remove temp files that should never be left behind
|
# Remove temp files that should never be left behind
|
||||||
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
rm -f /tmp/deploy_key /tmp/sftp-config.json
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
# INGROUP: MokoStandards.Firewall
|
# INGROUP: MokoStandards.Firewall
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /templates/workflows/shared/enterprise-firewall-setup.yml.template
|
# 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
|
# 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.
|
# 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
|
# INGROUP: MokoCassiopeia.Release
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
# REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
||||||
# PATH: /.github/workflows/release.yml
|
# PATH: /.github/workflows/release.yml
|
||||||
# VERSION: 01.00.00
|
# VERSION: 03.09.03
|
||||||
# BRIEF: Automated release workflow for MokoCassiopeia Joomla template
|
# BRIEF: Automated release workflow for MokoCassiopeia Joomla template
|
||||||
# NOTE: Creates release packages and publishes to GitHub Releases
|
# 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
|
# INGROUP: MokoStandards.Validation
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /.github/workflows/repo_health.yml
|
# 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.
|
# BRIEF: Enforces repository guardrails by validating release configuration, scripts governance, tooling availability, and core repository health artifacts.
|
||||||
# NOTE: Field is user-managed.
|
# NOTE: Field is user-managed.
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -29,7 +29,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
profile:
|
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
|
required: true
|
||||||
default: all
|
default: all
|
||||||
type: choice
|
type: choice
|
||||||
@@ -39,19 +39,7 @@ on:
|
|||||||
- scripts
|
- scripts
|
||||||
- repo
|
- repo
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
|
||||||
- .github/workflows/**
|
|
||||||
- scripts/**
|
|
||||||
- docs/**
|
|
||||||
- dev/**
|
|
||||||
push:
|
push:
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
paths:
|
|
||||||
- .github/workflows/**
|
|
||||||
- scripts/**
|
|
||||||
- docs/**
|
|
||||||
- dev/**
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
@@ -68,7 +56,7 @@ env:
|
|||||||
|
|
||||||
# Repo health policy
|
# Repo health policy
|
||||||
# Files are listed as-is; directories must end with a trailing slash.
|
# 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_OPTIONAL_FILES: SECURITY.md,GOVERNANCE.md,.editorconfig,.gitattributes,.gitignore,README.md,docs/
|
||||||
REPO_DISALLOWED_DIRS:
|
REPO_DISALLOWED_DIRS:
|
||||||
REPO_DISALLOWED_FILES: TODO.md,todo.md
|
REPO_DISALLOWED_FILES: TODO.md,todo.md
|
||||||
@@ -82,6 +70,7 @@ env:
|
|||||||
WORKFLOWS_DIR: .github/workflows
|
WORKFLOWS_DIR: .github/workflows
|
||||||
SHELLCHECK_PATTERN: '*.sh'
|
SHELLCHECK_PATTERN: '*.sh'
|
||||||
SPDX_FILE_GLOBS: '*.sh,*.php,*.js,*.ts,*.css,*.xml,*.yml,*.yaml'
|
SPDX_FILE_GLOBS: '*.sh,*.php,*.js,*.ts,*.css,*.xml,*.yml,*.yaml'
|
||||||
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
access_check:
|
access_check:
|
||||||
@@ -173,7 +162,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
@@ -268,7 +257,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
@@ -382,7 +371,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
@@ -412,6 +401,15 @@ jobs:
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
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 required_artifacts <<< "${REPO_REQUIRED_ARTIFACTS}"
|
||||||
IFS=',' read -r -a optional_files <<< "${REPO_OPTIONAL_FILES}"
|
IFS=',' read -r -a optional_files <<< "${REPO_OPTIONAL_FILES}"
|
||||||
IFS=',' read -r -a disallowed_dirs <<< "${REPO_DISALLOWED_DIRS}"
|
IFS=',' read -r -a disallowed_dirs <<< "${REPO_DISALLOWED_DIRS}"
|
||||||
@@ -561,6 +559,73 @@ jobs:
|
|||||||
} >> "${GITHUB_STEP_SUMMARY}"
|
} >> "${GITHUB_STEP_SUMMARY}"
|
||||||
fi
|
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_enabled="${EXTENDED_CHECKS:-true}"
|
||||||
extended_findings=()
|
extended_findings=()
|
||||||
|
|
||||||
|
|||||||
4
.github/workflows/repository-cleanup.yml
vendored
4
.github/workflows/repository-cleanup.yml
vendored
@@ -9,7 +9,7 @@
|
|||||||
# INGROUP: MokoStandards.Maintenance
|
# INGROUP: MokoStandards.Maintenance
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /templates/workflows/shared/repository-cleanup.yml.template
|
# 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
|
# 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.
|
# 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.
|
# 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 }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
run: |
|
run: |
|
||||||
REPO="${{ github.repository }}"
|
REPO="${{ github.repository }}"
|
||||||
CURRENT="chore/sync-mokostandards-v04.04"
|
CURRENT="chore/sync-mokostandards-v04.05"
|
||||||
echo "## 🌿 Branch Cleanup" >> $GITHUB_STEP_SUMMARY
|
echo "## 🌿 Branch Cleanup" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "" >> $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
|
# INGROUP: MokoStandards.Compliance
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /.github/workflows/standards-compliance.yml
|
# PATH: /.github/workflows/standards-compliance.yml
|
||||||
# VERSION: 04.04.01
|
# VERSION: 04.06.00
|
||||||
# BRIEF: MokoStandards compliance validation workflow
|
# BRIEF: MokoStandards compliance validation workflow
|
||||||
# NOTE: Validates repository structure, documentation, and coding standards
|
# NOTE: Validates repository structure, documentation, and coding standards
|
||||||
|
|
||||||
@@ -163,7 +163,11 @@ jobs:
|
|||||||
--include="*.php" --include="*.py" --include="*.js" --include="*.ts" \
|
--include="*.php" --include="*.py" --include="*.js" --include="*.ts" \
|
||||||
--exclude-dir=".git" --exclude-dir="vendor" --exclude-dir="node_modules" 2>/dev/null | \
|
--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 -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
|
scan_pattern "Secret assignments" "⚠️" /tmp/secrets1.txt
|
||||||
|
|
||||||
# Pattern 2: Private keys
|
# Pattern 2: Private keys
|
||||||
@@ -500,10 +504,18 @@ jobs:
|
|||||||
tools: composer
|
tools: composer
|
||||||
coverage: none
|
coverage: none
|
||||||
|
|
||||||
- name: Install API Package
|
- name: Setup MokoStandards tools
|
||||||
run: composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
|
|
||||||
env:
|
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
|
- name: Run Version Consistency Check
|
||||||
id: version_check
|
id: version_check
|
||||||
@@ -512,18 +524,15 @@ jobs:
|
|||||||
echo "## 🔢 Version Consistency Validation" >> $GITHUB_STEP_SUMMARY
|
echo "## 🔢 Version Consistency Validation" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
# Use PHP enterprise library for version consistency check
|
# Use MokoStandards tools (no Composer needed on the governed repo)
|
||||||
if [ -f "vendor/bin/moko" ]; then
|
if [ -f "/tmp/mokostandards/api/validate/check_version_consistency.php" ]; 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
|
|
||||||
php /tmp/mokostandards/api/validate/check_version_consistency.php --path . --verbose 2>&1 | tee /tmp/version-check.log
|
php /tmp/mokostandards/api/validate/check_version_consistency.php --path . --verbose 2>&1 | tee /tmp/version-check.log
|
||||||
EXIT_CODE=${PIPESTATUS[0]}
|
EXIT_CODE=${PIPESTATUS[0]}
|
||||||
elif [ -f "api/validate/check_version_consistency.php" ]; then
|
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
|
php api/validate/check_version_consistency.php --path . --verbose 2>&1 | tee /tmp/version-check.log
|
||||||
EXIT_CODE=${PIPESTATUS[0]}
|
EXIT_CODE=${PIPESTATUS[0]}
|
||||||
else
|
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
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -1960,17 +1969,39 @@ jobs:
|
|||||||
coverage: none
|
coverage: none
|
||||||
|
|
||||||
- name: Install API Package
|
- name: Install API Package
|
||||||
run: composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
|
|
||||||
env:
|
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
|
- name: Check Enterprise Readiness
|
||||||
id: enterprise_check
|
id: enterprise_check
|
||||||
run: |
|
run: |
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
SCRIPT=""
|
||||||
if [ -f "api/validate/check_enterprise_readiness.php" ]; then
|
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=$?
|
EXIT_CODE=$?
|
||||||
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
@@ -2010,17 +2041,39 @@ jobs:
|
|||||||
coverage: none
|
coverage: none
|
||||||
|
|
||||||
- name: Install API Package
|
- name: Install API Package
|
||||||
run: composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
|
|
||||||
env:
|
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
|
- name: Check Repository Health
|
||||||
id: health_check
|
id: health_check
|
||||||
run: |
|
run: |
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
|
SCRIPT=""
|
||||||
if [ -f "api/validate/check_repo_health.php" ]; then
|
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=$?
|
EXIT_CODE=$?
|
||||||
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
@@ -2481,8 +2534,8 @@ jobs:
|
|||||||
echo ""
|
echo ""
|
||||||
echo "✅ SUCCESS: Repository is fully MokoStandards compliant"
|
echo "✅ SUCCESS: Repository is fully MokoStandards compliant"
|
||||||
|
|
||||||
- name: Create tracking issue for standards violations
|
- name: Create or reopen tracking issue for standards violations
|
||||||
if: failure() && github.event_name == 'push'
|
if: failure()
|
||||||
env:
|
env:
|
||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
run: |
|
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
|
# INGROUP: MokoStandards.Automation
|
||||||
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
|
||||||
# PATH: /templates/workflows/shared/sync-version-on-merge.yml.template
|
# 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
|
# 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.
|
# 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.
|
# README.md is the single source of truth for the repository version.
|
||||||
@@ -58,7 +58,7 @@ jobs:
|
|||||||
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
|
||||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || github.token }}"}}'
|
||||||
run: |
|
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" \
|
"https://x-access-token:${GH_TOKEN}@github.com/mokoconsulting-tech/MokoStandards.git" \
|
||||||
/tmp/mokostandards
|
/tmp/mokostandards
|
||||||
cd /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
|
# Environment and secrets
|
||||||
# ============================================================
|
# ============================================================
|
||||||
@@ -23,10 +28,7 @@ secrets/
|
|||||||
*.log
|
*.log
|
||||||
*.pid
|
*.pid
|
||||||
*.seed
|
*.seed
|
||||||
*.sql
|
|
||||||
*.sql.gz
|
|
||||||
*.sqlite
|
|
||||||
*.sqlite3
|
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# OS / Editor / IDE cruft
|
# OS / Editor / IDE cruft
|
||||||
@@ -43,6 +45,7 @@ System Volume Information/
|
|||||||
Icon?
|
Icon?
|
||||||
.idea/
|
.idea/
|
||||||
.settings/
|
.settings/
|
||||||
|
.claude/
|
||||||
.vscode/*
|
.vscode/*
|
||||||
!.vscode/tasks.json
|
!.vscode/tasks.json
|
||||||
!.vscode/settings.json.example
|
!.vscode/settings.json.example
|
||||||
@@ -70,6 +73,17 @@ todo*
|
|||||||
# SFTP / sync tools
|
# SFTP / sync tools
|
||||||
# ============================================================
|
# ============================================================
|
||||||
sftp-config*.json
|
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
|
# Replit / cloud IDE
|
||||||
@@ -138,6 +152,7 @@ package-lock.json
|
|||||||
# PHP / Composer tooling
|
# PHP / Composer tooling
|
||||||
# ============================================================
|
# ============================================================
|
||||||
vendor/
|
vendor/
|
||||||
|
!src/media/vendor/
|
||||||
composer.lock
|
composer.lock
|
||||||
*.phar
|
*.phar
|
||||||
codeception.phar
|
codeception.phar
|
||||||
@@ -185,727 +200,3 @@ venv/
|
|||||||
*.coverage
|
*.coverage
|
||||||
hypothesis/
|
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) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||||
Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
|
||||||
|
|
||||||
This file is part of a Moko Consulting project.
|
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
|
||||||
|
|
||||||
# FILE INFORMATION
|
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.
|
||||||
DEFGROUP: Joomla.Template
|
|
||||||
INGROUP: MokoCassiopeia.Governance
|
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.
|
||||||
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
|
||||||
FILE: CODE_OF_CONDUCT.md
|
You should have received a copy of the GNU General Public License (./LICENSE.md).
|
||||||
VERSION: 03.06.03
|
|
||||||
BRIEF: Contributor code of conduct for the MokoCassiopeia project.
|
# FILE INFORMATION
|
||||||
PATH: /CODE_OF_CONDUCT.md
|
DEFGROUP:
|
||||||
NOTE: This document defines behavioral expectations and enforcement processes.
|
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.
|
Demonstrating empathy and respect toward others.
|
||||||
* Project documentation, workflows, and release processes.
|
Being inclusive of diverse viewpoints and backgrounds.
|
||||||
* Any communication channels officially associated with the project.
|
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.
|
## 3. Responsibilities of Maintainers
|
||||||
* Provide constructive feedback focused on technical merit and project objectives.
|
|
||||||
* Respect differing viewpoints, experience levels, and backgrounds.
|
|
||||||
* Follow documented contribution, security, and governance policies.
|
|
||||||
|
|
||||||
Unacceptable behavior includes:
|
Maintainers are responsible for:
|
||||||
|
|
||||||
* Harassment, discrimination, or exclusionary conduct.
|
Clarifying acceptable behavior.
|
||||||
* Personal attacks, insults, or inflammatory comments.
|
Taking appropriate corrective action when unacceptable behavior occurs.
|
||||||
* Publishing private information without consent.
|
Removing, editing, or rejecting contributions that violate this Code.
|
||||||
* Disruptive behavior that materially interferes with project operations.
|
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.
|
All Moko Consulting repositories.
|
||||||
* Taking appropriate and proportionate corrective action when violations occur.
|
All documentation and collaboration platforms.
|
||||||
* Maintaining confidentiality to the extent practical during investigations.
|
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.
|
This Code of Conduct is inspired by widely adopted community guidelines, including the Contributor Covenant and major open-source collaboration standards.
|
||||||
* Temporary or permanent restriction from project participation.
|
|
||||||
* Removal of content that violates this Code of Conduct.
|
|
||||||
|
|
||||||
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.
|
This Code of Conduct is a living document and may be updated following the established Change Management process.
|
||||||
|
|
||||||
## 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 |
|
|
||||||
|
|||||||
189
CONTRIBUTING.md
189
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.
|
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).
|
||||||
|
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
DEFGROUP: Joomla.Template
|
DEFGROUP: {{DEFGROUP}}
|
||||||
INGROUP: MokoCassiopeia.Governance
|
INGROUP: Project.Documentation
|
||||||
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
||||||
FILE: CONTRIBUTING.md
|
VERSION: 04.04.00
|
||||||
VERSION: 03.06.03
|
PATH: ./CONTRIBUTING.md
|
||||||
BRIEF: Contribution guidelines for the MokoCassiopeia project.
|
BRIEF: How to contribute; branch strategy, commit conventions, PR workflow, and release pipeline
|
||||||
PATH: /CONTRIBUTING.md
|
-->
|
||||||
NOTE: This document defines contribution workflow, standards, and governance alignment.
|
|
||||||
-->
|
|
||||||
|
|
||||||
## 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
|
| Branch | Purpose | Deploys To |
|
||||||
* Documentation updates
|
|--------|---------|------------|
|
||||||
* Bug reports and enhancement proposals
|
| `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:
|
```
|
||||||
|
1. Create branch: git checkout -b dev/XX.YY.ZZ/my-feature
|
||||||
* Have a working understanding of Joomla template structure.
|
2. Develop + test (dev server auto-deploys on push)
|
||||||
* Be familiar with Git and GitHub pull request workflows.
|
3. Open PR → main (squash merge only)
|
||||||
* Review repository governance documents prior to submitting changes.
|
4. Auto-release (version branch + tag + GitHub Release created automatically)
|
||||||
* 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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
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.
|
feat(scope): add new extrafield for invoice tracking
|
||||||
3. Make focused, minimal changes that address a single concern.
|
fix(sql): correct column type in llx_mytable
|
||||||
4. Submit a pull request with a clear description of intent and impact.
|
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.
|
1. **Branch** from `main` using `dev/XX.YY.ZZ/description` format
|
||||||
* Releases are produced from versioned branches following repository standards.
|
2. **Bump** the patch version in `README.md` before opening the PR
|
||||||
* Contributors should not bump version numbers unless explicitly requested.
|
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.
|
1. **sync-version-on-merge** — auto-bumps patch version, propagates to all file headers
|
||||||
* Conform to Moko Consulting repository standards for headers, metadata, and file structure.
|
2. **auto-release** — creates `version/XX.YY` branch, git tag, and GitHub Release
|
||||||
* Avoid introducing tabs, inconsistent path separators, or non portable assumptions.
|
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.
|
## PR Checklist
|
||||||
* Avoid embedding version numbers in revision history tables.
|
|
||||||
* Preserve existing structure unless a structural change is explicitly proposed.
|
|
||||||
|
|
||||||
## 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.
|
Place repo-specific workflows in `.github/workflows/custom/` — they are **never overwritten or deleted** by MokoStandards sync:
|
||||||
* Focus on what changed and why.
|
|
||||||
* Avoid referencing internal issue trackers unless required.
|
|
||||||
|
|
||||||
## Reporting Issues
|
```
|
||||||
|
.github/workflows/
|
||||||
Bug reports and enhancement requests should be filed as GitHub issues and include:
|
├── deploy-dev.yml ← Synced from MokoStandards
|
||||||
|
├── auto-release.yml ← Synced from MokoStandards
|
||||||
* Clear reproduction steps or use cases.
|
└── custom/ ← Your custom workflows (safe)
|
||||||
* Expected versus actual behavior.
|
└── my-custom-ci.yml
|
||||||
* 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.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
By contributing, you agree that your contributions will be licensed under GPL-3.0-or-later, consistent with the rest of the project.
|
By contributing, you agree that your contributions will be licensed under the [GPL-3.0-or-later](LICENSE) license.
|
||||||
|
|
||||||
## Code of Conduct
|
|
||||||
|
|
||||||
Participation in this project is governed by the Code of Conduct. Unacceptable behavior may result in contribution restrictions.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Metadata
|
*This file is synced from [MokoStandards](https://github.com/mokoconsulting-tech/MokoStandards). Do not edit directly — changes will be overwritten on the next sync.*
|
||||||
|
|
||||||
* **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 |
|
|
||||||
|
|||||||
12
README.md
12
README.md
@@ -13,13 +13,19 @@
|
|||||||
BRIEF: Documentation for MokoCassiopeia template
|
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)
|
# README - MokoCassiopeia (VERSION: 03.06.03)
|
||||||
|
|
||||||
**A Modern, Lightweight Joomla Template Based on Cassiopeia**
|
**A Modern, Lightweight Joomla Template Based on Cassiopeia**
|
||||||
|
|
||||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
|
||||||
[](https://www.joomla.org)
|
](https://img.shields.io/badge/Joomla-4.4.x%20%7C%205.x-blue.svg)](https://www.joomla.org)
|
||||||
[](https://www.php.net)
|
](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.
|
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.
|
||||||
|
|
||||||
|
|||||||
287
SECURITY.md
287
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.
|
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
|
||||||
|
|
||||||
# FILE INFORMATION
|
This program is free software; you can redistribute it and/or modify
|
||||||
DEFGROUP: Joomla.Template
|
it under the terms of the GNU General Public License as published by
|
||||||
INGROUP: MokoCassiopeia.Governance
|
the Free Software Foundation; either version 3 of the License, or
|
||||||
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
|
(at your option) any later version.
|
||||||
FILE: SECURITY.md
|
|
||||||
VERSION: 03.06.02
|
This program is distributed in the hope that it will be useful,
|
||||||
BRIEF: Security policy and vulnerability reporting process for MokoCassiopeia.
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
PATH: /SECURITY.md
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
NOTE: This policy is process oriented and does not replace secure engineering practices.
|
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: [PROJECT_NAME]
|
||||||
|
INGROUP: [PROJECT_NAME].Documentation
|
||||||
|
REPO: [REPOSITORY_URL]
|
||||||
|
PATH: /SECURITY.md
|
||||||
|
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 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.
|
||||||
|
|
||||||
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.
|
|
||||||
|
|
||||||
## Supported Versions
|
## Supported Versions
|
||||||
|
|
||||||
Security fixes are prioritized for:
|
Security updates are provided for the following versions:
|
||||||
|
|
||||||
* The latest released version.
|
| Version | Supported |
|
||||||
* The current development line when it is actively used for release engineering.
|
| ------- | ------------------ |
|
||||||
|
| [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
|
## 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.
|
**DO NOT** create public GitHub issues for security vulnerabilities.
|
||||||
* Email: send details to `hello@mokoconsulting.tech` with subject `SECURITY: MokoCassiopeia vulnerability report`.
|
|
||||||
|
|
||||||
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.
|
### What to Include
|
||||||
* 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.
|
|
||||||
|
|
||||||
## 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.
|
### Response Timeline
|
||||||
* **Initial triage:** within 10 business days.
|
|
||||||
* **Fix plan:** communicated once severity is confirmed.
|
|
||||||
|
|
||||||
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.
|
### Critical
|
||||||
* Data confidentiality, integrity, and availability impact.
|
* Remote code execution
|
||||||
* Likelihood of exploitation in typical Joomla deployments.
|
* Authentication bypass
|
||||||
* Exposure surface (public endpoints, administrator area, installation flows, and update mechanisms).
|
* 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.
|
## Remediation Process
|
||||||
* A public advisory may be published once a fix is released.
|
|
||||||
* A reasonable embargo period is expected to enable patch distribution.
|
|
||||||
|
|
||||||
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
|
||||||
* GitHub Security Advisories when applicable.
|
* Release notes and CHANGELOG.md
|
||||||
|
* Security mailing list (when established)
|
||||||
|
|
||||||
Advisories may include:
|
Advisories include:
|
||||||
|
|
||||||
* Affected versions and fixed versions.
|
* CVE identifier (if applicable)
|
||||||
* Mitigations and workarounds when a fix is not immediately available.
|
* Severity rating
|
||||||
* Upgrade guidance.
|
* 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.
|
### Required Controls
|
||||||
* Minimizing privileged GitHub token permissions.
|
|
||||||
* Validating build inputs prior to packaging releases.
|
|
||||||
|
|
||||||
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.
|
#### Automated Security Scanning
|
||||||
* Consistent path normalization and whitespace hygiene checks where required for release correctness.
|
|
||||||
* Least privilege for GitHub Actions permissions.
|
|
||||||
|
|
||||||
### 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)
|
**Secret Scanning**:
|
||||||
* Custom meta tags
|
* Enabled by default with push protection
|
||||||
* Third-party integrations
|
* Prevents accidental credential commits
|
||||||
* Custom styling
|
* 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
|
See [Security Scanning Policy](docs/policy/security-scanning.md) for detailed requirements.
|
||||||
* **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
|
|
||||||
|
|
||||||
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.
|
This security policy is binding for all repositories governed by MokoStandards. Deviations require documented justification and approval from the Security Owner.
|
||||||
* Limit testing to systems you own or have explicit permission to test.
|
|
||||||
* Provide a reasonable window for coordinated disclosure.
|
|
||||||
|
|
||||||
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
|
## Metadata
|
||||||
|
|
||||||
* **Document:** SECURITY.md
|
| Field | Value |
|
||||||
* **Repository:** [https://github.com/mokoconsulting-tech/MokoCassiopeia](https://github.com/mokoconsulting-tech/MokoCassiopeia)
|
| ------------ | ----------------------------------------------- |
|
||||||
* **Path:** /SECURITY.md
|
| Document | Security Policy |
|
||||||
* **Owner:** Moko Consulting
|
| Path | /SECURITY.md |
|
||||||
* **Version:** 03.06.00
|
| Repository | [REPOSITORY_URL] |
|
||||||
* **Status:** Active
|
| Owner | [OWNER_NAME] |
|
||||||
* **Effective Date:** 2025-12-18
|
| Scope | Security vulnerability handling |
|
||||||
* **Last Reviewed:** 2025-12-18
|
| Applies To | All repositories governed by MokoStandards |
|
||||||
|
| Status | Active |
|
||||||
|
| Effective | [YYYY-MM-DD] |
|
||||||
|
|
||||||
## Revision History
|
## Revision History
|
||||||
|
|
||||||
| Date | Change Summary | Author |
|
| Date | Change Description | Author |
|
||||||
| ---------- | ------------------------------------------------------------------------------------------------ | --------------- |
|
| ---------- | ------------------------------------------------- | --------------- |
|
||||||
| 2026-01-30 | Added Template Security Features section documenting custom head content injection controls. | Copilot Agent |
|
| [YYYY-MM-DD] | Initial creation | [AUTHOR_NAME] |
|
||||||
| 2025-12-18 | Initial publication of security policy, intake channels, triage targets, and disclosure process. | Moko Consulting |
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
"prefer-stable": true,
|
"prefer-stable": true,
|
||||||
"require": {
|
"require": {
|
||||||
"php": ">=8.1",
|
"php": ">=8.1",
|
||||||
"mokoconsulting-tech/enterprise": "^4.0"
|
"mokoconsulting-tech/enterprise": "dev-version/04.02.00"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^10.5",
|
"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