Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6c6f813c26 | |||
| cebbb33d87 | |||
| 8b7ea2e2c2 | |||
| ce9b5f0e5c | |||
| 59875466f4 | |||
| 32cb7cecd6 | |||
| f573ac0a77 | |||
| 24e42d9132 | |||
| d957022fc1 | |||
| 82d5beb0f0 | |||
| 9968c81660 | |||
| 6802b256d0 | |||
| d0ea5e43c0 | |||
| 7b9ea92faa | |||
| 9f628201ad | |||
| 86f341bd72 |
@@ -0,0 +1,63 @@
|
|||||||
|
# MokoOnyx
|
||||||
|
|
||||||
|
Joomla site template — successor to MokoCassiopeia. Base template for all WaaS client deployments.
|
||||||
|
|
||||||
|
## Quick Reference
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|---|---|
|
||||||
|
| **Element** | `tpl_mokoonyx` |
|
||||||
|
| **Type** | Joomla site template |
|
||||||
|
| **Language** | PHP 8.1+ / CSS / JS |
|
||||||
|
| **Branch** | develop on `dev`, merge to `main` (protected) |
|
||||||
|
| **Wiki** | [MokoOnyx Wiki](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/wiki) |
|
||||||
|
|
||||||
|
## Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make build # Build template ZIP
|
||||||
|
make lint # Run linters
|
||||||
|
make validate # Validate structure
|
||||||
|
make release # Full release pipeline
|
||||||
|
make minify # Minify CSS/JS assets
|
||||||
|
make clean # Clean build artifacts
|
||||||
|
composer install # Install PHP dependencies
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
Joomla **site template** — the base layer that client theme packages override:
|
||||||
|
|
||||||
|
- `src/templateDetails.xml` — template manifest
|
||||||
|
- `src/index.php` — main template entry point
|
||||||
|
- `src/error.php` — error page
|
||||||
|
- `src/offline.php` — maintenance page
|
||||||
|
- `src/component.php` — print/component-only layout
|
||||||
|
- `src/html/` — template overrides for core components
|
||||||
|
- `src/media/css/` — base stylesheets
|
||||||
|
- `src/media/js/` — base scripts
|
||||||
|
- `src/media/images/` — template images
|
||||||
|
- `src/language/` — translations
|
||||||
|
|
||||||
|
### Client Theme Packages
|
||||||
|
|
||||||
|
Client repos (`client-clarksvillefurs`, `client-optainfunding`, etc.) install `type="file"` packages that overlay client-specific CSS, images, and JS into the MokoOnyx media directory. MokoOnyx provides the structure; client themes customize the appearance.
|
||||||
|
|
||||||
|
### Minification
|
||||||
|
|
||||||
|
`MokoMinifyHelper` handles runtime CSS/JS minification in Joomla. Build-time minification via `make minify`. Never commit `*.min.css`/`*.min.js` — they're generated.
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- **Never commit** `.claude/`, `.mcp.json`, `TODO.md`, `*.min.css`/`*.min.js`
|
||||||
|
- **Attribution**: `Authored-by: Moko Consulting`
|
||||||
|
- **Workflow directory**: `.mokogitea/` (not `.gitea/` or `.github/`)
|
||||||
|
- **Minification**: handled at build time (CI) and runtime (MokoMinifyHelper)
|
||||||
|
- **Wiki**: documentation lives in the Gitea wiki, not `docs/` files
|
||||||
|
- **Standards**: [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home)
|
||||||
|
|
||||||
|
## Coding Standards
|
||||||
|
|
||||||
|
- PHP 8.1+ minimum
|
||||||
|
- SPDX license headers on all PHP files
|
||||||
|
- `defined('_JEXEC') or die;` on all PHP files
|
||||||
@@ -9,7 +9,7 @@
|
|||||||
<display-name>Template - MokoOnyx</display-name>
|
<display-name>Template - MokoOnyx</display-name>
|
||||||
<org>MokoConsulting</org>
|
<org>MokoConsulting</org>
|
||||||
<description>MokoOnyx - Joomla site template (successor to MokoCassiopeia)</description>
|
<description>MokoOnyx - Joomla site template (successor to MokoCassiopeia)</description>
|
||||||
<version>02.20.00</version>
|
<version>02.19.07</version>
|
||||||
<license spdx="GPL-3.0-or-later">GNU General Public License v3</license>
|
<license spdx="GPL-3.0-or-later">GNU General Public License v3</license>
|
||||||
</identity>
|
</identity>
|
||||||
<governance>
|
<governance>
|
||||||
|
|||||||
@@ -171,27 +171,10 @@ jobs:
|
|||||||
echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
|
echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: "Determine version bump level"
|
|
||||||
id: bump
|
|
||||||
run: |
|
|
||||||
# Fix/patch branches: version was already bumped by pre-release, just strip suffix
|
|
||||||
# Feature/dev branches: bump minor for the new stable release
|
|
||||||
HEAD_REF="${{ github.event.pull_request.head.ref || 'dev' }}"
|
|
||||||
case "$HEAD_REF" in
|
|
||||||
fix/*|patch/*|hotfix/*|bugfix/*) BUMP="none" ;;
|
|
||||||
*) BUMP="minor" ;;
|
|
||||||
esac
|
|
||||||
echo "level=${BUMP}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "Bump level: ${BUMP} (from branch: ${HEAD_REF})"
|
|
||||||
|
|
||||||
- name: "Publish stable release"
|
- name: "Publish stable release"
|
||||||
run: |
|
run: |
|
||||||
BUMP_FLAG=""
|
|
||||||
if [ "${{ steps.bump.outputs.level }}" != "none" ]; then
|
|
||||||
BUMP_FLAG="--bump ${{ steps.bump.outputs.level }}"
|
|
||||||
fi
|
|
||||||
php ${MOKO_CLI}/release_publish.php \
|
php ${MOKO_CLI}/release_publish.php \
|
||||||
--path . --stability stable ${BUMP_FLAG} --branch main \
|
--path . --stability stable --bump minor --branch main \
|
||||||
--token "${{ secrets.MOKOGITEA_TOKEN }}"
|
--token "${{ secrets.MOKOGITEA_TOKEN }}"
|
||||||
|
|
||||||
- name: Update release notes from CHANGELOG.md
|
- name: Update release notes from CHANGELOG.md
|
||||||
|
|||||||
@@ -1,204 +0,0 @@
|
|||||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
#
|
|
||||||
# FILE INFORMATION
|
|
||||||
# DEFGROUP: Gitea.Workflow
|
|
||||||
# INGROUP: MokoStandards.CI
|
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
|
||||||
# PATH: /.gitea/workflows/ci-generic.yml
|
|
||||||
# VERSION: 01.00.00
|
|
||||||
# BRIEF: CI pipeline — lint, validate, and test for generic projects (PHP + Node.js)
|
|
||||||
|
|
||||||
name: "Generic: Project CI"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- dev
|
|
||||||
- dev/**
|
|
||||||
- rc/**
|
|
||||||
- version/**
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- dev
|
|
||||||
- dev/**
|
|
||||||
- rc/**
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
env:
|
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
# ── Lint & Validate ───────────────────────────────────────────────────
|
|
||||||
lint:
|
|
||||||
name: Lint & Validate
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Detect toolchain
|
|
||||||
id: detect
|
|
||||||
run: |
|
|
||||||
HAS_PHP=false
|
|
||||||
HAS_NODE=false
|
|
||||||
[ -f "composer.json" ] && HAS_PHP=true
|
|
||||||
[ -f "package.json" ] && HAS_NODE=true
|
|
||||||
echo "has_php=$HAS_PHP" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "has_node=$HAS_NODE" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "Toolchain: PHP=$HAS_PHP Node=$HAS_NODE"
|
|
||||||
|
|
||||||
- name: Setup PHP
|
|
||||||
if: steps.detect.outputs.has_php == 'true'
|
|
||||||
run: |
|
|
||||||
if ! command -v php &> /dev/null; then
|
|
||||||
sudo apt-get update -qq
|
|
||||||
sudo apt-get install -y -qq php-cli php-mbstring php-xml >/dev/null 2>&1
|
|
||||||
fi
|
|
||||||
php -v
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
if: steps.detect.outputs.has_node == 'true'
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '20'
|
|
||||||
|
|
||||||
- name: Install PHP dependencies
|
|
||||||
if: steps.detect.outputs.has_php == 'true'
|
|
||||||
run: |
|
|
||||||
if [ -f "composer.json" ]; then
|
|
||||||
composer install --no-interaction --prefer-dist --quiet 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Install Node.js dependencies
|
|
||||||
if: steps.detect.outputs.has_node == 'true'
|
|
||||||
run: |
|
|
||||||
if [ -f "package.json" ]; then
|
|
||||||
npm ci --quiet 2>/dev/null || npm install --quiet 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: PHP syntax check
|
|
||||||
if: steps.detect.outputs.has_php == 'true'
|
|
||||||
run: |
|
|
||||||
ERRORS=0
|
|
||||||
while IFS= read -r -d '' file; do
|
|
||||||
if ! php -l "$file" 2>&1 | grep -q "No syntax errors"; then
|
|
||||||
echo "::error file=${file}::PHP syntax error"
|
|
||||||
ERRORS=$((ERRORS + 1))
|
|
||||||
fi
|
|
||||||
done < <(find . -name "*.php" -not -path "./.git/*" -not -path "./vendor/*" -not -path "./node_modules/*" -print0)
|
|
||||||
|
|
||||||
echo "## PHP Lint" >> $GITHUB_STEP_SUMMARY
|
|
||||||
if [ "$ERRORS" -eq 0 ]; then
|
|
||||||
echo "All PHP files passed syntax check." >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
echo "${ERRORS} file(s) with syntax errors." >> $GITHUB_STEP_SUMMARY
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: TypeScript/JavaScript lint
|
|
||||||
if: steps.detect.outputs.has_node == 'true'
|
|
||||||
run: |
|
|
||||||
if [ -f "node_modules/.bin/eslint" ]; then
|
|
||||||
npx eslint src/ --quiet 2>&1 || { echo "::error::ESLint errors found"; exit 1; }
|
|
||||||
echo "## ESLint" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "All files passed ESLint." >> $GITHUB_STEP_SUMMARY
|
|
||||||
elif [ -f ".eslintrc.json" ] || [ -f ".eslintrc.js" ] || [ -f "eslint.config.js" ]; then
|
|
||||||
echo "::warning::ESLint config found but eslint not installed"
|
|
||||||
else
|
|
||||||
echo "No ESLint configured — skipping"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: TypeScript compile check
|
|
||||||
if: steps.detect.outputs.has_node == 'true'
|
|
||||||
run: |
|
|
||||||
if [ -f "tsconfig.json" ] && [ -f "node_modules/.bin/tsc" ]; then
|
|
||||||
npx tsc --noEmit 2>&1 || { echo "::error::TypeScript compilation errors"; exit 1; }
|
|
||||||
echo "## TypeScript" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "TypeScript compilation passed." >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: PHPStan static analysis
|
|
||||||
if: steps.detect.outputs.has_php == 'true'
|
|
||||||
run: |
|
|
||||||
if [ -f "phpstan.neon" ] && [ -f "vendor/bin/phpstan" ]; then
|
|
||||||
vendor/bin/phpstan analyse --no-progress 2>&1 || { echo "::warning::PHPStan found issues"; }
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── Tests ─────────────────────────────────────────────────────────────
|
|
||||||
test:
|
|
||||||
name: Tests
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
needs: lint
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Detect toolchain
|
|
||||||
id: detect
|
|
||||||
run: |
|
|
||||||
HAS_PHP=false
|
|
||||||
HAS_NODE=false
|
|
||||||
[ -f "composer.json" ] && HAS_PHP=true
|
|
||||||
[ -f "package.json" ] && HAS_NODE=true
|
|
||||||
echo "has_php=$HAS_PHP" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "has_node=$HAS_NODE" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Setup PHP
|
|
||||||
if: steps.detect.outputs.has_php == 'true'
|
|
||||||
run: |
|
|
||||||
if ! command -v php &> /dev/null; then
|
|
||||||
sudo apt-get update -qq
|
|
||||||
sudo apt-get install -y -qq php-cli php-mbstring php-xml >/dev/null 2>&1
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Setup Node.js
|
|
||||||
if: steps.detect.outputs.has_node == 'true'
|
|
||||||
uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: '20'
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
[ -f "composer.json" ] && composer install --no-interaction --prefer-dist --quiet 2>/dev/null || true
|
|
||||||
[ -f "package.json" ] && { npm ci --quiet 2>/dev/null || npm install --quiet 2>/dev/null || true; }
|
|
||||||
|
|
||||||
- name: Run PHP tests
|
|
||||||
if: steps.detect.outputs.has_php == 'true'
|
|
||||||
run: |
|
|
||||||
if [ -f "vendor/bin/phpunit" ]; then
|
|
||||||
vendor/bin/phpunit --testdox 2>&1
|
|
||||||
echo "## PHPUnit" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "Tests passed." >> $GITHUB_STEP_SUMMARY
|
|
||||||
elif [ -f "phpunit.xml" ] || [ -f "phpunit.xml.dist" ]; then
|
|
||||||
echo "::warning::PHPUnit config found but phpunit not installed"
|
|
||||||
else
|
|
||||||
echo "No PHPUnit configured — skipping"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Run Node.js tests
|
|
||||||
if: steps.detect.outputs.has_node == 'true'
|
|
||||||
run: |
|
|
||||||
if jq -e '.scripts.test' package.json > /dev/null 2>&1; then
|
|
||||||
npm test 2>&1
|
|
||||||
echo "## Node.js Tests" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "Tests passed." >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
echo "No test script in package.json — skipping"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Build check
|
|
||||||
run: |
|
|
||||||
if [ -f "Makefile" ]; then
|
|
||||||
make build 2>&1 || echo "::warning::Build failed or not configured"
|
|
||||||
elif [ -f "package.json" ] && jq -e '.scripts.build' package.json > /dev/null 2>&1; then
|
|
||||||
npm run build 2>&1 || echo "::warning::Build failed"
|
|
||||||
fi
|
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API
|
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API
|
||||||
# PATH: /templates/workflows/joomla/ci-joomla.yml.template
|
# PATH: /templates/workflows/joomla/ci-joomla.yml.template
|
||||||
# VERSION: 04.06.00
|
# VERSION: 04.06.00
|
||||||
# BRIEF: CI workflow for Joomla extensions — lint, validate, test
|
# BRIEF: CI workflow for Joomla extensions -- lint, validate, test
|
||||||
|
|
||||||
name: "Joomla: Extension CI"
|
name: "Joomla: Extension CI"
|
||||||
|
|
||||||
@@ -35,32 +35,25 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
run: |
|
run: |
|
||||||
if ! command -v php &> /dev/null; then
|
|
||||||
sudo apt-get update -qq
|
|
||||||
sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1
|
|
||||||
fi
|
|
||||||
php -v && composer --version
|
php -v && composer --version
|
||||||
|
|
||||||
- name: Setup moko-platform tools
|
- name: Clone MokoStandards
|
||||||
env:
|
env:
|
||||||
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN || github.token }}
|
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN || secrets.MOKOGITEA_TOKEN || github.token }}
|
||||||
MOKO_CLONE_HOST: ${{ secrets.GA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }}
|
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN || secrets.MOKOGITEA_TOKEN || github.token }}
|
||||||
|
MOKO_CLONE_HOST: ${{ secrets.MOKOGITEA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }}
|
||||||
run: |
|
run: |
|
||||||
if [ -d "/tmp/moko-platform" ] || [ -d "/opt/moko-platform" ]; then
|
git clone --depth 1 --branch main --quiet \
|
||||||
echo "moko-platform already available on runner — skipping clone"
|
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \
|
||||||
else
|
/tmp/mokostandards-api
|
||||||
git clone --depth 1 --branch main --quiet \
|
|
||||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \
|
|
||||||
/tmp/moko-platform 2>/dev/null || echo "moko-platform clone skipped — continuing without it"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
env:
|
env:
|
||||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || secrets.GA_TOKEN || github.token }}"}}'
|
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.MOKOGITEA_TOKEN || github.token }}"}}'
|
||||||
run: |
|
run: |
|
||||||
if [ -f "composer.json" ]; then
|
if [ -f "composer.json" ]; then
|
||||||
composer install \
|
composer install \
|
||||||
@@ -68,7 +61,7 @@ jobs:
|
|||||||
--prefer-dist \
|
--prefer-dist \
|
||||||
--optimize-autoloader
|
--optimize-autoloader
|
||||||
else
|
else
|
||||||
echo "No composer.json found — skipping dependency install"
|
echo "No composer.json found -- skipping dependency install"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: PHP syntax check
|
- name: PHP syntax check
|
||||||
@@ -131,8 +124,8 @@ jobs:
|
|||||||
echo "Manifest is well-formed XML." >> $GITHUB_STEP_SUMMARY
|
echo "Manifest is well-formed XML." >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check required tags: name, version, author
|
# Check required tags: name, version, author, namespace (Joomla 5+)
|
||||||
for TAG in name version author; do
|
for TAG in name version author namespace; do
|
||||||
if ! grep -q "<${TAG}>" "$MANIFEST" 2>/dev/null; then
|
if ! grep -q "<${TAG}>" "$MANIFEST" 2>/dev/null; then
|
||||||
echo "Missing required tag: \`<${TAG}>\`" >> $GITHUB_STEP_SUMMARY
|
echo "Missing required tag: \`<${TAG}>\`" >> $GITHUB_STEP_SUMMARY
|
||||||
ERRORS=$((ERRORS + 1))
|
ERRORS=$((ERRORS + 1))
|
||||||
@@ -140,19 +133,6 @@ jobs:
|
|||||||
echo "Found required tag: \`<${TAG}>\`" >> $GITHUB_STEP_SUMMARY
|
echo "Found required tag: \`<${TAG}>\`" >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
# Namespace is required for components/plugins but not packages
|
|
||||||
EXT_TYPE=$(grep -oP '<extension[^>]*\btype="\K[^"]+' "$MANIFEST" | head -1)
|
|
||||||
if [ "$EXT_TYPE" != "package" ]; then
|
|
||||||
if ! grep -q "<namespace" "$MANIFEST" 2>/dev/null; then
|
|
||||||
echo "Missing required tag: \`<namespace>\` (required for Joomla 5+ ${EXT_TYPE} extensions)" >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=$((ERRORS + 1))
|
|
||||||
else
|
|
||||||
echo "Found required tag: \`<namespace>\`" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Package extension — \`<namespace>\` not required." >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "${ERRORS}" -gt 0 ]; then
|
if [ "${ERRORS}" -gt 0 ]; then
|
||||||
@@ -181,7 +161,7 @@ jobs:
|
|||||||
# Extract language file references from manifest
|
# Extract language file references from manifest
|
||||||
LANG_FILES=$(grep -oP 'language\s+tag="[^"]*"[^>]*>\K[^<]+' "$MANIFEST" 2>/dev/null || true)
|
LANG_FILES=$(grep -oP 'language\s+tag="[^"]*"[^>]*>\K[^<]+' "$MANIFEST" 2>/dev/null || true)
|
||||||
if [ -z "$LANG_FILES" ]; then
|
if [ -z "$LANG_FILES" ]; then
|
||||||
echo "No language file references found in manifest — skipping." >> $GITHUB_STEP_SUMMARY
|
echo "No language file references found in manifest -- skipping." >> $GITHUB_STEP_SUMMARY
|
||||||
else
|
else
|
||||||
while IFS= read -r LANG_FILE; do
|
while IFS= read -r LANG_FILE; do
|
||||||
LANG_FILE=$(echo "$LANG_FILE" | xargs)
|
LANG_FILE=$(echo "$LANG_FILE" | xargs)
|
||||||
@@ -205,7 +185,7 @@ jobs:
|
|||||||
done <<< "$LANG_FILES"
|
done <<< "$LANG_FILES"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
echo "No manifest found — skipping language check." >> $GITHUB_STEP_SUMMARY
|
echo "No manifest found -- skipping language check." >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "${ERRORS}" -gt 0 ]; then
|
if [ "${ERRORS}" -gt 0 ]; then
|
||||||
@@ -236,7 +216,7 @@ jobs:
|
|||||||
done
|
done
|
||||||
|
|
||||||
if [ "${CHECKED}" -eq 0 ]; then
|
if [ "${CHECKED}" -eq 0 ]; then
|
||||||
echo "No src/ or htdocs/ directories found — skipping." >> $GITHUB_STEP_SUMMARY
|
echo "No src/ or htdocs/ directories found -- skipping." >> $GITHUB_STEP_SUMMARY
|
||||||
elif [ "${MISSING}" -gt 0 ]; then
|
elif [ "${MISSING}" -gt 0 ]; then
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "**${MISSING} director(ies) missing index.html out of ${CHECKED} checked.**" >> $GITHUB_STEP_SUMMARY
|
echo "**${MISSING} director(ies) missing index.html out of ${CHECKED} checked.**" >> $GITHUB_STEP_SUMMARY
|
||||||
@@ -252,7 +232,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
|
|
||||||
- name: Validate release readiness
|
- name: Validate release readiness
|
||||||
run: |
|
run: |
|
||||||
@@ -358,19 +338,15 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
|
|
||||||
- name: Setup PHP ${{ matrix.php }}
|
- name: Setup PHP ${{ matrix.php }}
|
||||||
run: |
|
run: |
|
||||||
if ! command -v php &> /dev/null; then
|
|
||||||
sudo apt-get update -qq
|
|
||||||
sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1
|
|
||||||
fi
|
|
||||||
php -v && composer --version
|
php -v && composer --version
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
env:
|
env:
|
||||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || secrets.GA_TOKEN || github.token }}"}}'
|
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.MOKOGITEA_TOKEN || github.token }}"}}'
|
||||||
run: |
|
run: |
|
||||||
if [ -f "composer.json" ]; then
|
if [ -f "composer.json" ]; then
|
||||||
composer install \
|
composer install \
|
||||||
@@ -378,7 +354,7 @@ jobs:
|
|||||||
--prefer-dist \
|
--prefer-dist \
|
||||||
--optimize-autoloader
|
--optimize-autoloader
|
||||||
else
|
else
|
||||||
echo "No composer.json found — skipping dependency install"
|
echo "No composer.json found -- skipping dependency install"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
@@ -390,14 +366,14 @@ jobs:
|
|||||||
if [ $EXIT -eq 0 ]; then
|
if [ $EXIT -eq 0 ]; then
|
||||||
echo "All tests passed." >> $GITHUB_STEP_SUMMARY
|
echo "All tests passed." >> $GITHUB_STEP_SUMMARY
|
||||||
else
|
else
|
||||||
echo "Test failures detected — see log." >> $GITHUB_STEP_SUMMARY
|
echo "Test failures detected -- see log." >> $GITHUB_STEP_SUMMARY
|
||||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||||
cat /tmp/test-output.log >> $GITHUB_STEP_SUMMARY
|
cat /tmp/test-output.log >> $GITHUB_STEP_SUMMARY
|
||||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
exit $EXIT
|
exit $EXIT
|
||||||
else
|
else
|
||||||
echo "No phpunit.xml found — skipping tests." >> $GITHUB_STEP_SUMMARY
|
echo "No phpunit.xml found -- skipping tests." >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
static-analysis:
|
static-analysis:
|
||||||
@@ -408,19 +384,14 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
|
|
||||||
- name: Setup PHP
|
- name: Setup PHP
|
||||||
run: |
|
run: php -v && composer --version
|
||||||
if ! command -v php &> /dev/null; then
|
|
||||||
sudo apt-get update -qq
|
|
||||||
sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1
|
|
||||||
fi
|
|
||||||
php -v && composer --version
|
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
env:
|
env:
|
||||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_TOKEN || secrets.GA_TOKEN || github.token }}"}}'
|
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.MOKOGITEA_TOKEN || github.token }}"}}'
|
||||||
run: |
|
run: |
|
||||||
if [ -f "composer.json" ]; then
|
if [ -f "composer.json" ]; then
|
||||||
composer install --no-interaction --prefer-dist --optimize-autoloader
|
composer install --no-interaction --prefer-dist --optimize-autoloader
|
||||||
@@ -451,7 +422,7 @@ jobs:
|
|||||||
done
|
done
|
||||||
|
|
||||||
if [ -z "$SRC_DIR" ]; then
|
if [ -z "$SRC_DIR" ]; then
|
||||||
echo "No source directory found (src/, htdocs/, lib/) — skipping." >> $GITHUB_STEP_SUMMARY
|
echo "No source directory found (src/, htdocs/, lib/) -- skipping." >> $GITHUB_STEP_SUMMARY
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -461,7 +432,7 @@ jobs:
|
|||||||
echo "Using project PHPStan config." >> $GITHUB_STEP_SUMMARY
|
echo "Using project PHPStan config." >> $GITHUB_STEP_SUMMARY
|
||||||
else
|
else
|
||||||
ARGS="$ARGS --level=3"
|
ARGS="$ARGS --level=3"
|
||||||
echo "No phpstan.neon found — using level 3 (type inference)." >> $GITHUB_STEP_SUMMARY
|
echo "No phpstan.neon found -- using level 3 (type inference)." >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
$PHPSTAN $ARGS 2>&1 | tee /tmp/phpstan-output.txt
|
$PHPSTAN $ARGS 2>&1 | tee /tmp/phpstan-output.txt
|
||||||
@@ -487,14 +458,10 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Trigger pre-release build
|
- name: Trigger pre-release build
|
||||||
env:
|
env:
|
||||||
GA_TOKEN: ${{ secrets.GA_TOKEN }}
|
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
REPO: ${{ github.repository }}
|
REPO: ${{ github.repository }}
|
||||||
BRANCH: ${{ github.head_ref }}
|
BRANCH: ${{ github.head_ref }}
|
||||||
run: |
|
run: |
|
||||||
curl -s -X POST \
|
curl -s -X POST "${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${REPO}/actions/workflows/pre-release.yml/dispatches" -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" -d "{\"ref\":\"${BRANCH}\",\"inputs\":{\"stability\":\"release-candidate\"}}"
|
||||||
"${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${REPO}/actions/workflows/pre-release.yml/dispatches" \
|
|
||||||
-H "Authorization: token ${GA_TOKEN}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "{\"ref\":\"${BRANCH}\",\"inputs\":{\"stability\":\"release-candidate\"}}"
|
|
||||||
echo "### Pre-Release" >> $GITHUB_STEP_SUMMARY
|
echo "### Pre-Release" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "Triggered RC build on branch \`${BRANCH}\`" >> $GITHUB_STEP_SUMMARY
|
echo "Triggered RC build on branch \`${BRANCH}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: Gitea.Workflow
|
# DEFGROUP: Gitea.Workflow
|
||||||
# INGROUP: MokoStandards.Maintenance
|
# INGROUP: moko-platform.Maintenance
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||||
# PATH: /.gitea/workflows/cleanup.yml
|
# PATH: /.gitea/workflows/cleanup.yml
|
||||||
# VERSION: 01.00.00
|
# VERSION: 01.00.00
|
||||||
# BRIEF: Scheduled cleanup — delete merged branches and old workflow runs
|
# BRIEF: Scheduled cleanup — delete merged branches and old workflow runs
|
||||||
@@ -33,17 +33,17 @@ jobs:
|
|||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
token: ${{ secrets.GA_TOKEN }}
|
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
|
|
||||||
- name: Delete merged branches
|
- name: Delete merged branches
|
||||||
env:
|
env:
|
||||||
GA_TOKEN: ${{ secrets.GA_TOKEN }}
|
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
echo "=== Merged Branch Cleanup ==="
|
echo "=== Merged Branch Cleanup ==="
|
||||||
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||||
|
|
||||||
# List branches via API
|
# List branches via API
|
||||||
BRANCHES=$(curl -sS -H "Authorization: token ${GA_TOKEN}" \
|
BRANCHES=$(curl -sS -H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
"${API}/branches?limit=50" | jq -r '.[].name')
|
"${API}/branches?limit=50" | jq -r '.[].name')
|
||||||
|
|
||||||
DELETED=0
|
DELETED=0
|
||||||
@@ -56,7 +56,7 @@ jobs:
|
|||||||
# Check if branch is merged into main
|
# Check if branch is merged into main
|
||||||
if git merge-base --is-ancestor "origin/${BRANCH}" origin/main 2>/dev/null; then
|
if git merge-base --is-ancestor "origin/${BRANCH}" origin/main 2>/dev/null; then
|
||||||
echo " Deleting merged branch: ${BRANCH}"
|
echo " Deleting merged branch: ${BRANCH}"
|
||||||
curl -sS -X DELETE -H "Authorization: token ${GA_TOKEN}" \
|
curl -sS -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
"${API}/branches/${BRANCH}" 2>/dev/null || true
|
"${API}/branches/${BRANCH}" 2>/dev/null || true
|
||||||
DELETED=$((DELETED + 1))
|
DELETED=$((DELETED + 1))
|
||||||
fi
|
fi
|
||||||
@@ -66,20 +66,20 @@ jobs:
|
|||||||
|
|
||||||
- name: Clean old workflow runs
|
- name: Clean old workflow runs
|
||||||
env:
|
env:
|
||||||
GA_TOKEN: ${{ secrets.GA_TOKEN }}
|
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
run: |
|
run: |
|
||||||
echo "=== Workflow Run Cleanup ==="
|
echo "=== Workflow Run Cleanup ==="
|
||||||
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||||
CUTOFF=$(date -d "30 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -v-30d +%Y-%m-%dT%H:%M:%SZ)
|
CUTOFF=$(date -d "30 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -v-30d +%Y-%m-%dT%H:%M:%SZ)
|
||||||
|
|
||||||
# Get old completed runs
|
# Get old completed runs
|
||||||
RUNS=$(curl -sS -H "Authorization: token ${GA_TOKEN}" \
|
RUNS=$(curl -sS -H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
"${API}/actions/runs?status=completed&limit=50" | \
|
"${API}/actions/runs?status=completed&limit=50" | \
|
||||||
jq -r ".workflow_runs[] | select(.created_at < \"${CUTOFF}\") | .id" 2>/dev/null)
|
jq -r ".workflow_runs[] | select(.created_at < \"${CUTOFF}\") | .id" 2>/dev/null)
|
||||||
|
|
||||||
DELETED=0
|
DELETED=0
|
||||||
for RUN_ID in $RUNS; do
|
for RUN_ID in $RUNS; do
|
||||||
curl -sS -X DELETE -H "Authorization: token ${GA_TOKEN}" \
|
curl -sS -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \
|
||||||
"${API}/actions/runs/${RUN_ID}" 2>/dev/null || true
|
"${API}/actions/runs/${RUN_ID}" 2>/dev/null || true
|
||||||
DELETED=$((DELETED + 1))
|
DELETED=$((DELETED + 1))
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -1,126 +0,0 @@
|
|||||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
#
|
|
||||||
# FILE INFORMATION
|
|
||||||
# DEFGROUP: Gitea.Workflow
|
|
||||||
# INGROUP: MokoStandards.Deploy
|
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API
|
|
||||||
# PATH: /templates/workflows/joomla/deploy-manual.yml.template
|
|
||||||
# VERSION: 04.07.00
|
|
||||||
# BRIEF: Manual SFTP deploy to dev server for Joomla repos
|
|
||||||
|
|
||||||
name: "Universal: 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
|
|
||||||
run: |
|
|
||||||
php -v && composer --version
|
|
||||||
|
|
||||||
- name: Setup MokoStandards tools
|
|
||||||
env:
|
|
||||||
GA_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
|
|
||||||
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
|
|
||||||
MOKO_CLONE_HOST: ${{ secrets.GA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }}
|
|
||||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}'
|
|
||||||
run: |
|
|
||||||
git clone --depth 1 --branch main --quiet \
|
|
||||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \
|
|
||||||
/tmp/mokostandards-api 2>/dev/null || true
|
|
||||||
if [ -d "/tmp/mokostandards-api" ] && [ -f "/tmp/mokostandards-api/composer.json" ]; then
|
|
||||||
cd /tmp/mokostandards-api && 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 }}
|
|
||||||
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%/}"
|
|
||||||
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
|
|
||||||
@@ -4,8 +4,8 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: Gitea.Workflow
|
# DEFGROUP: Gitea.Workflow
|
||||||
# INGROUP: MokoStandards.Security
|
# INGROUP: moko-platform.Security
|
||||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API
|
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
|
||||||
# PATH: /templates/workflows/gitleaks.yml.template
|
# PATH: /templates/workflows/gitleaks.yml.template
|
||||||
# VERSION: 01.00.00
|
# VERSION: 01.00.00
|
||||||
# BRIEF: Secret scanning — detect leaked credentials, API keys, and tokens
|
# BRIEF: Secret scanning — detect leaked credentials, API keys, and tokens
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: Gitea.Workflow
|
# DEFGROUP: Gitea.Workflow
|
||||||
# INGROUP: moko-platform.Automation
|
# INGROUP: moko-platform.Automation
|
||||||
# VERSION: 01.00.00
|
# VERSION: 02.19.07
|
||||||
# BRIEF: Auto-create feature branch when an issue is opened
|
# BRIEF: Auto-create feature branch when an issue is opened
|
||||||
|
|
||||||
name: "Universal: Issue Branch"
|
name: "Universal: Issue Branch"
|
||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Create branch and comment
|
- name: Create branch and comment
|
||||||
run: |
|
run: |
|
||||||
TOKEN="${{ secrets.GA_TOKEN }}"
|
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||||
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
|
||||||
ISSUE_NUM="${{ github.event.issue.number }}"
|
ISSUE_NUM="${{ github.event.issue.number }}"
|
||||||
ISSUE_TITLE="${{ github.event.issue.title }}"
|
ISSUE_TITLE="${{ github.event.issue.title }}"
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: Gitea.Workflow
|
# DEFGROUP: Gitea.Workflow
|
||||||
# INGROUP: MokoStandards.Notifications
|
# INGROUP: moko-platform.Notifications
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||||
# PATH: /.gitea/workflows/notify.yml
|
# PATH: /.gitea/workflows/notify.yml
|
||||||
# VERSION: 01.00.00
|
# VERSION: 01.00.00
|
||||||
# BRIEF: Push notifications via ntfy on release success or workflow failure
|
# BRIEF: Push notifications via ntfy on release success or workflow failure
|
||||||
|
|||||||
@@ -8,4 +8,236 @@
|
|||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||||
# PATH: /templates/workflows/universal/pre-release.yml.template
|
# PATH: /templates/workflows/universal/pre-release.yml.template
|
||||||
# VERSION: 05.01.00
|
# VERSION: 05.01.00
|
||||||
# BRIEF: Auto pre-release on push to dev/alpha/beta/rc branches
|
# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch
|
||||||
|
|
||||||
|
name: "Universal: Pre-Release"
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [closed]
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
pull_request_target:
|
||||||
|
types: [synchronize, opened, reopened]
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
stability:
|
||||||
|
description: 'Pre-release channel'
|
||||||
|
required: true
|
||||||
|
type: choice
|
||||||
|
options:
|
||||||
|
- development
|
||||||
|
- alpha
|
||||||
|
- beta
|
||||||
|
- release-candidate
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
env:
|
||||||
|
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||||
|
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
|
||||||
|
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
name: "Build Pre-Release (${{ inputs.stability || 'development' }})"
|
||||||
|
runs-on: release
|
||||||
|
if: >-
|
||||||
|
github.event_name == 'workflow_dispatch' ||
|
||||||
|
(github.event_name == 'pull_request' && github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'dev') ||
|
||||||
|
(github.event_name == 'pull_request_target' && github.event.pull_request.base.ref == 'main')
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
|
ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || '' }}
|
||||||
|
|
||||||
|
- name: Setup moko-platform tools
|
||||||
|
env:
|
||||||
|
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
|
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||||
|
run: |
|
||||||
|
# Use pre-installed /opt/moko-platform if available (updated by cron every 6h)
|
||||||
|
if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/cli/manifest_element.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then
|
||||||
|
echo Using pre-installed /opt/moko-platform
|
||||||
|
echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV
|
||||||
|
else
|
||||||
|
echo Falling back to fresh clone
|
||||||
|
if ! command -v composer > /dev/null 2>&1; then
|
||||||
|
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1
|
||||||
|
fi
|
||||||
|
rm -rf /tmp/moko-platform-api
|
||||||
|
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git
|
||||||
|
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api
|
||||||
|
cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
|
||||||
|
echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Detect platform
|
||||||
|
id: platform
|
||||||
|
run: |
|
||||||
|
php ${MOKO_CLI}/manifest_read.php --path . --github-output
|
||||||
|
|
||||||
|
- name: Resolve metadata and bump version
|
||||||
|
id: meta
|
||||||
|
run: |
|
||||||
|
# Auto-detect stability: RC for PRs targeting main, else use input or default to development
|
||||||
|
if [ "${{ github.event_name }}" = "pull_request_target" ] && [ "${{ github.event.pull_request.base.ref }}" = "main" ]; then
|
||||||
|
STABILITY="release-candidate"
|
||||||
|
else
|
||||||
|
STABILITY="${{ inputs.stability || 'development' }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$STABILITY" in
|
||||||
|
development) SUFFIX="-dev"; TAG="development" ;;
|
||||||
|
alpha) SUFFIX="-alpha"; TAG="alpha" ;;
|
||||||
|
beta) SUFFIX="-beta"; TAG="beta" ;;
|
||||||
|
release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Bump version via CLI: patch for dev/alpha/beta, minor for RC
|
||||||
|
case "$STABILITY" in
|
||||||
|
release-candidate) BUMP="minor" ;;
|
||||||
|
*) BUMP="patch" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
php ${MOKO_CLI}/version_bump.php --path . $([ "$BUMP" = "minor" ] && echo "--minor") 2>/dev/null || true
|
||||||
|
|
||||||
|
# Set stability suffix and verify consistency
|
||||||
|
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "00.00.01")
|
||||||
|
VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
|
||||||
|
|
||||||
|
php ${MOKO_CLI}/version_set_platform.php \
|
||||||
|
--path . --version "$VERSION" --branch "${{ github.ref_name }}" --stability "$STABILITY" 2>/dev/null || true
|
||||||
|
php ${MOKO_CLI}/version_check.php --path . --fix 2>/dev/null || true
|
||||||
|
|
||||||
|
# Ensure licensing tags (updateservers, dlid) if enabled in manifest.xml
|
||||||
|
php ${MOKO_CLI}/manifest_licensing.php --path . --fix 2>/dev/null || true
|
||||||
|
|
||||||
|
# Append suffix for output
|
||||||
|
if [ -n "$SUFFIX" ]; then
|
||||||
|
VERSION="${VERSION}${SUFFIX}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Commit version bump
|
||||||
|
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||||
|
git config --local user.name "gitea-actions[bot]"
|
||||||
|
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||||
|
git add -A
|
||||||
|
git diff --cached --quiet || {
|
||||||
|
git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
|
||||||
|
git push origin HEAD 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Auto-detect element via manifest_element.php
|
||||||
|
php ${MOKO_CLI}/manifest_element.php \
|
||||||
|
--path . --version "$VERSION" --stability "$STABILITY" \
|
||||||
|
--repo "${GITEA_REPO}" --github-output
|
||||||
|
|
||||||
|
# Read back element outputs
|
||||||
|
EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
|
||||||
|
ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
|
||||||
|
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
|
||||||
|
[ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip"
|
||||||
|
|
||||||
|
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
|
||||||
|
|
||||||
|
- name: Create release
|
||||||
|
id: release
|
||||||
|
run: |
|
||||||
|
TAG="${{ steps.meta.outputs.tag }}"
|
||||||
|
VERSION="${{ steps.meta.outputs.version }}"
|
||||||
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
|
php ${MOKO_CLI}/release_create.php \
|
||||||
|
--path . --version "$VERSION" --tag "$TAG" \
|
||||||
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||||
|
--repo "${GITEA_REPO}" --branch dev --prerelease
|
||||||
|
|
||||||
|
- name: Update release notes from CHANGELOG.md
|
||||||
|
run: |
|
||||||
|
TAG="${{ steps.meta.outputs.tag }}"
|
||||||
|
VERSION="${{ steps.meta.outputs.version }}"
|
||||||
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
|
|
||||||
|
# Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading)
|
||||||
|
if [ -f "CHANGELOG.md" ]; then
|
||||||
|
NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
|
||||||
|
[ -z "$NOTES" ] && NOTES="Release ${VERSION}"
|
||||||
|
else
|
||||||
|
NOTES="Release ${VERSION}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Update release body via API
|
||||||
|
RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||||
|
"${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
||||||
|
|
||||||
|
if [ -n "$RELEASE_ID" ]; then
|
||||||
|
python3 -c "
|
||||||
|
import json, urllib.request
|
||||||
|
body = open('/dev/stdin').read()
|
||||||
|
payload = json.dumps({'body': body}).encode()
|
||||||
|
req = urllib.request.Request(
|
||||||
|
'${API_BASE}/releases/${RELEASE_ID}',
|
||||||
|
data=payload, method='PATCH',
|
||||||
|
headers={
|
||||||
|
'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}',
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
})
|
||||||
|
urllib.request.urlopen(req)
|
||||||
|
" <<< "$NOTES"
|
||||||
|
echo "Release notes updated from CHANGELOG.md"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build package and upload
|
||||||
|
id: package
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.meta.outputs.version }}"
|
||||||
|
TAG="${{ steps.meta.outputs.tag }}"
|
||||||
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
|
php ${MOKO_CLI}/release_package.php \
|
||||||
|
--path . --version "$VERSION" --tag "$TAG" \
|
||||||
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||||
|
--repo "${GITEA_REPO}" --output /tmp || true
|
||||||
|
|
||||||
|
# updates.xml is generated dynamically by MokoGitea license server
|
||||||
|
# No need to build, commit, or sync updates.xml from workflows
|
||||||
|
|
||||||
|
- name: "Delete lesser pre-release channels (cascade)"
|
||||||
|
continue-on-error: true
|
||||||
|
run: |
|
||||||
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
|
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||||
|
|
||||||
|
php ${MOKO_CLI}/release_cascade.php \
|
||||||
|
--stability "${{ steps.meta.outputs.stability }}" \
|
||||||
|
--token "${TOKEN}" \
|
||||||
|
--api-base "${API_BASE}"
|
||||||
|
|
||||||
|
- name: Summary
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
VERSION="${{ steps.meta.outputs.version }}"
|
||||||
|
STABILITY="${{ steps.meta.outputs.stability }}"
|
||||||
|
ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
|
||||||
|
SHA256="${{ steps.package.outputs.sha256_zip }}"
|
||||||
|
echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: Gitea.Workflow
|
# DEFGROUP: Gitea.Workflow
|
||||||
# INGROUP: MokoStandards.Security
|
# INGROUP: moko-platform.Security
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||||
# PATH: /.gitea/workflows/security-audit.yml
|
# PATH: /.gitea/workflows/security-audit.yml
|
||||||
# VERSION: 01.00.00
|
# VERSION: 01.00.00
|
||||||
# BRIEF: Dependency vulnerability scanning for composer and npm packages
|
# BRIEF: Dependency vulnerability scanning for composer and npm packages
|
||||||
@@ -80,3 +80,19 @@ jobs:
|
|||||||
-H "Priority: high" \
|
-H "Priority: high" \
|
||||||
-d "Security audit found vulnerabilities. Review dependency updates." \
|
-d "Security audit found vulnerabilities. Review dependency updates." \
|
||||||
"${NTFY_URL}/${NTFY_TOPIC}" || true
|
"${NTFY_URL}/${NTFY_TOPIC}" || true
|
||||||
|
|
||||||
|
|
||||||
|
- name: Joomla version audit
|
||||||
|
if: always()
|
||||||
|
run: |
|
||||||
|
if [ -f "monitoring/joomla-version-audit.php" ] && [ -n "$JOOMLA_SITES" ]; then
|
||||||
|
echo "$JOOMLA_SITES" > /tmp/sites.json
|
||||||
|
php monitoring/joomla-version-audit.php --sites /tmp/sites.json || true
|
||||||
|
echo "### Joomla Version Audit" >> $GITHUB_STEP_SUMMARY
|
||||||
|
rm -f /tmp/sites.json
|
||||||
|
else
|
||||||
|
echo "Joomla audit skipped (no script or JOOMLA_SITES_JSON not configured)"
|
||||||
|
fi
|
||||||
|
env:
|
||||||
|
JOOMLA_SITES: ${{ vars.JOOMLA_SITES_JSON }}
|
||||||
|
|
||||||
|
|||||||
+2
-5
@@ -8,16 +8,13 @@
|
|||||||
DEFGROUP: Joomla.Template.Site
|
DEFGROUP: Joomla.Template.Site
|
||||||
INGROUP: MokoOnyx.Documentation
|
INGROUP: MokoOnyx.Documentation
|
||||||
PATH: ./CHANGELOG.md
|
PATH: ./CHANGELOG.md
|
||||||
VERSION: 02.20.00
|
VERSION: 02.19.07
|
||||||
BRIEF: Changelog file documenting version history of MokoOnyx
|
BRIEF: Changelog file documenting version history of MokoOnyx
|
||||||
-->
|
-->
|
||||||
|
|
||||||
# Changelog — MokoOnyx (VERSION: 02.20.00)
|
# Changelog — MokoOnyx (VERSION: 02.19.07)
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
## [02.20.00] --- 2026-06-04
|
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Strip Joomla-injected `p-2` padding class from Font Awesome icons in all menu overrides (default, mainmenu, horizontal)
|
- Strip Joomla-injected `p-2` padding class from Font Awesome icons in all menu overrides (default, mainmenu, horizontal)
|
||||||
|
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
# CLAUDE.md
|
|
||||||
|
|
||||||
This file provides guidance to Claude Code when working with this repository.
|
|
||||||
|
|
||||||
## Project Overview
|
|
||||||
|
|
||||||
**MokoOnyx** -- MokoOnyx - Joomla site template (successor to MokoCassiopeia)
|
|
||||||
|
|
||||||
| Field | Value |
|
|
||||||
|---|---|
|
|
||||||
| **Platform** | joomla |
|
|
||||||
| **Language** | PHP |
|
|
||||||
| **Default branch** | main |
|
|
||||||
| **License** | GPL-3.0-or-later |
|
|
||||||
| **Wiki** | [MokoOnyx Wiki](https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/wiki) |
|
|
||||||
| **Standards** | [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home) |
|
|
||||||
|
|
||||||
## Common Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
composer install # Install PHP dependencies
|
|
||||||
```
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
This is a Joomla extension. Key directories:
|
|
||||||
- `src/` -- extension source (deployed to Joomla)
|
|
||||||
- `src/*.xml` -- manifest file (version, files, params)
|
|
||||||
- `src/src/` or `src/services/` -- PHP classes
|
|
||||||
- `src/language/` -- translation strings
|
|
||||||
- `src/media/` -- CSS/JS/images
|
|
||||||
|
|
||||||
## Rules
|
|
||||||
|
|
||||||
- **Workflow directory**: `.mokogitea/` (not `.gitea/` or `.github/`)
|
|
||||||
|
|
||||||
- **Never commit** `.claude/`, `.mcp.json`, `TODO.md`, or `*.min.css`/`*.min.js`
|
|
||||||
- **Attribution**: use `Authored-by: Moko Consulting` in commits
|
|
||||||
- **Branch strategy**: develop on `dev`, merge to `main` for release
|
|
||||||
- **Minification**: handled at build time (CI) and runtime (MokoMinifyHelper for Joomla templates)
|
|
||||||
- **Wiki**: documentation lives in the Gitea wiki, not in `docs/` files
|
|
||||||
- **Standards**: this repo follows [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home)
|
|
||||||
+1
-1
@@ -10,7 +10,7 @@
|
|||||||
INGROUP: MokoOnyx.Governance
|
INGROUP: MokoOnyx.Governance
|
||||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
|
REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
|
||||||
FILE: SECURITY.md
|
FILE: SECURITY.md
|
||||||
VERSION: 02.20.00
|
VERSION: 02.19.07
|
||||||
BRIEF: Security policy and vulnerability reporting process for MokoOnyx.
|
BRIEF: Security policy and vulnerability reporting process for MokoOnyx.
|
||||||
PATH: /SECURITY.md
|
PATH: /SECURITY.md
|
||||||
NOTE: This policy is process oriented and does not replace secure engineering practices.
|
NOTE: This policy is process oriented and does not replace secure engineering practices.
|
||||||
|
|||||||
@@ -236,11 +236,11 @@ use Joomla\CMS\Log\Log;
|
|||||||
|
|
||||||
// Update the update server
|
// Update the update server
|
||||||
try {
|
try {
|
||||||
$onyxUpdatesUrl = 'https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/updates.xml';
|
$onyxUpdatesUrl = 'https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/updates.xml';
|
||||||
$query = $db->getQuery(true)
|
$query = $db->getQuery(true)
|
||||||
->update('#__update_sites')
|
->update('#__update_sites')
|
||||||
->set($db->quoteName('location') . ' = ' . $db->quote($onyxUpdatesUrl))
|
->set($db->quoteName('location') . ' = ' . $db->quote($onyxUpdatesUrl))
|
||||||
->set($db->quoteName('name') . ' = ' . $db->quote($newDisplay))
|
->set($db->quoteName('name') . ' = ' . $db->quote('Template - MokoOnyx'))
|
||||||
->where($db->quoteName('location') . ' LIKE ' . $db->quote('%MokoCassiopeia%'));
|
->where($db->quoteName('location') . ' LIKE ' . $db->quote('%MokoCassiopeia%'));
|
||||||
$db->setQuery($query)->execute();
|
$db->setQuery($query)->execute();
|
||||||
$n = $db->getAffectedRows();
|
$n = $db->getAffectedRows();
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
* INGROUP: MokoOnyx
|
* INGROUP: MokoOnyx
|
||||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
|
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
|
||||||
* PATH: /html/layouts/joomla/module/card.php
|
* PATH: /html/layouts/joomla/module/card.php
|
||||||
* VERSION: 02.20.00
|
* VERSION: 02.19.07
|
||||||
* BRIEF: Custom card module chrome — renders module titles for all modules
|
* BRIEF: Custom card module chrome — renders module titles for all modules
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
* INGROUP: MokoOnyx.Layouts
|
* INGROUP: MokoOnyx.Layouts
|
||||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
|
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
|
||||||
* PATH: /src/html/layouts/mokoonyx/article-metadata.php
|
* PATH: /src/html/layouts/mokoonyx/article-metadata.php
|
||||||
* VERSION: 02.20.00
|
* VERSION: 02.19.07
|
||||||
* BRIEF: Article metadata footer layout -- renders jcfields grouped by field group
|
* BRIEF: Article metadata footer layout -- renders jcfields grouped by field group
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
* INGROUP: MokoOnyx.Accessibility
|
* INGROUP: MokoOnyx.Accessibility
|
||||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
|
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx
|
||||||
* PATH: ./media/css/a11y-high-contrast.css
|
* PATH: ./media/css/a11y-high-contrast.css
|
||||||
* VERSION: 02.20.00
|
* VERSION: 02.19.07
|
||||||
* BRIEF: High-contrast stylesheet for accessibility toolbar
|
* BRIEF: High-contrast stylesheet for accessibility toolbar
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
+127
@@ -34,6 +34,9 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface
|
|||||||
private const OLD_DISPLAY = 'MokoCassiopeia';
|
private const OLD_DISPLAY = 'MokoCassiopeia';
|
||||||
private const NEW_DISPLAY = 'MokoOnyx';
|
private const NEW_DISPLAY = 'MokoOnyx';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public function preflight(string $type, InstallerAdapter $parent): bool
|
public function preflight(string $type, InstallerAdapter $parent): bool
|
||||||
{
|
{
|
||||||
if (version_compare(PHP_VERSION, self::MIN_PHP, '<')) {
|
if (version_compare(PHP_VERSION, self::MIN_PHP, '<')) {
|
||||||
@@ -83,11 +86,16 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface
|
|||||||
public function uninstall(InstallerAdapter $parent): bool
|
public function uninstall(InstallerAdapter $parent): bool
|
||||||
{
|
{
|
||||||
$this->logMessage('MokoOnyx template uninstalled.');
|
$this->logMessage('MokoOnyx template uninstalled.');
|
||||||
|
$this->saveDownloadKey();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function postflight(string $type, InstallerAdapter $parent): bool
|
public function postflight(string $type, InstallerAdapter $parent): bool
|
||||||
{
|
{
|
||||||
|
$this->restoreDownloadKey();
|
||||||
|
$this->warnMissingLicenseKey();
|
||||||
|
|
||||||
if ($type === 'install' || $type === 'update') {
|
if ($type === 'install' || $type === 'update') {
|
||||||
$this->migrateFromCassiopeia();
|
$this->migrateFromCassiopeia();
|
||||||
$this->replaceCassiopeiaReferences();
|
$this->replaceCassiopeiaReferences();
|
||||||
@@ -533,6 +541,37 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove faulty "template-mokoonyx" duplicate (wrong element name from bad install)
|
||||||
|
$faultyId = (int) $db->setQuery(
|
||||||
|
$db->getQuery(true)
|
||||||
|
->select('extension_id')
|
||||||
|
->from('#__extensions')
|
||||||
|
->where($db->quoteName('element') . ' = ' . $db->quote('template-mokoonyx'))
|
||||||
|
->where($db->quoteName('type') . ' = ' . $db->quote('template'))
|
||||||
|
)->loadResult();
|
||||||
|
|
||||||
|
if ($faultyId) {
|
||||||
|
$db->setQuery(
|
||||||
|
$db->getQuery(true)
|
||||||
|
->delete('#__update_sites_extensions')
|
||||||
|
->where('extension_id = ' . $faultyId)
|
||||||
|
)->execute();
|
||||||
|
|
||||||
|
$db->setQuery(
|
||||||
|
$db->getQuery(true)
|
||||||
|
->delete('#__extensions')
|
||||||
|
->where('extension_id = ' . $faultyId)
|
||||||
|
)->execute();
|
||||||
|
|
||||||
|
$db->setQuery(
|
||||||
|
$db->getQuery(true)
|
||||||
|
->delete('#__template_styles')
|
||||||
|
->where($db->quoteName('template') . ' = ' . $db->quote('template-mokoonyx'))
|
||||||
|
)->execute();
|
||||||
|
|
||||||
|
$this->logMessage('Removed faulty template-mokoonyx duplicate extension.');
|
||||||
|
}
|
||||||
|
|
||||||
// Remove stale MokoCassiopeia if not set as default
|
// Remove stale MokoCassiopeia if not set as default
|
||||||
$oldExt = (int) $db->setQuery(
|
$oldExt = (int) $db->setQuery(
|
||||||
$db->getQuery(true)
|
$db->getQuery(true)
|
||||||
@@ -653,4 +692,92 @@ class Tpl_MokoonyxInstallerScript implements InstallerScriptInterface
|
|||||||
|
|
||||||
Log::add($message, $priorities[$priority] ?? Log::INFO, 'mokoonyx');
|
Log::add($message, $priorities[$priority] ?? Log::INFO, 'mokoonyx');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private ?string $savedDownloadKey = null;
|
||||||
|
|
||||||
|
private function saveDownloadKey(): void
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$db = \Joomla\CMS\Factory::getDbo();
|
||||||
|
$db->setQuery(
|
||||||
|
$db->getQuery(true)
|
||||||
|
->select($db->quoteName('us.extra_query'))
|
||||||
|
->from($db->quoteName('#__update_sites', 'us'))
|
||||||
|
->join('INNER', $db->quoteName('#__update_sites_extensions', 'use') . ' ON use.update_site_id = us.update_site_id')
|
||||||
|
->join('INNER', $db->quoteName('#__extensions', 'e') . ' ON e.extension_id = use.extension_id')
|
||||||
|
->where($db->quoteName('e.element') . ' = ' . $db->quote('mokoonyx'))
|
||||||
|
->setLimit(1)
|
||||||
|
);
|
||||||
|
$key = $db->loadResult();
|
||||||
|
if (!empty($key)) { $this->savedDownloadKey = $key; }
|
||||||
|
}
|
||||||
|
catch (\Throwable $e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function restoreDownloadKey(): void
|
||||||
|
{
|
||||||
|
if ($this->savedDownloadKey === null) { return; }
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$db = \Joomla\CMS\Factory::getDbo();
|
||||||
|
$db->setQuery(
|
||||||
|
$db->getQuery(true)
|
||||||
|
->select($db->quoteName('us.update_site_id'))
|
||||||
|
->from($db->quoteName('#__update_sites', 'us'))
|
||||||
|
->join('INNER', $db->quoteName('#__update_sites_extensions', 'use') . ' ON use.update_site_id = us.update_site_id')
|
||||||
|
->join('INNER', $db->quoteName('#__extensions', 'e') . ' ON e.extension_id = use.extension_id')
|
||||||
|
->where($db->quoteName('e.element') . ' = ' . $db->quote('mokoonyx'))
|
||||||
|
->setLimit(1)
|
||||||
|
);
|
||||||
|
$siteId = (int) $db->loadResult();
|
||||||
|
if ($siteId > 0)
|
||||||
|
{
|
||||||
|
$db->setQuery(
|
||||||
|
$db->getQuery(true)
|
||||||
|
->update($db->quoteName('#__update_sites'))
|
||||||
|
->set($db->quoteName('extra_query') . ' = ' . $db->quote($this->savedDownloadKey))
|
||||||
|
->where($db->quoteName('update_site_id') . ' = ' . $siteId)
|
||||||
|
)->execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (\Throwable $e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function warnMissingLicenseKey(): void
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$db = \Joomla\CMS\Factory::getDbo();
|
||||||
|
$db->setQuery(
|
||||||
|
$db->getQuery(true)
|
||||||
|
->select([$db->quoteName('update_site_id'), $db->quoteName('extra_query')])
|
||||||
|
->from($db->quoteName('#__update_sites'))
|
||||||
|
->where('(' . $db->quoteName('name') . ' LIKE ' . $db->quote('%MokoOnyx%') . ' OR ' . $db->quoteName('location') . ' LIKE ' . $db->quote('%MokoOnyx%') . ')')
|
||||||
|
->setLimit(1)
|
||||||
|
);
|
||||||
|
$site = $db->loadObject();
|
||||||
|
|
||||||
|
if ($site)
|
||||||
|
{
|
||||||
|
$eq = (string) ($site->extra_query ?? '');
|
||||||
|
if (!empty($eq) && strpos($eq, 'dlid=') !== false) { parse_str($eq, $p); if (!empty($p['dlid'])) { return; } }
|
||||||
|
$editUrl = 'index.php?option=com_installer&task=updatesite.edit&update_site_id=' . (int) $site->update_site_id;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$editUrl = 'index.php?option=com_installer&view=updatesites';
|
||||||
|
}
|
||||||
|
|
||||||
|
\Joomla\CMS\Factory::getApplication()->enqueueMessage(
|
||||||
|
'<strong>Moko Consulting License Key Required</strong> — '
|
||||||
|
. 'No download key is configured. Updates will not be available until a valid license key is entered. '
|
||||||
|
. '<a href="' . $editUrl . '" class="btn btn-sm btn-warning ms-2">Enter License Key</a>',
|
||||||
|
'warning'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
catch (\Throwable $e) {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
</updateservers>
|
</updateservers>
|
||||||
<dlid prefix="dlid=" suffix=""/>
|
<dlid prefix="dlid=" suffix=""/>
|
||||||
<name>mokoonyx</name>
|
<name>mokoonyx</name>
|
||||||
<version>02.20.00</version>
|
<version>02.19.07-dev</version>
|
||||||
<scriptfile>script.php</scriptfile>
|
<scriptfile>script.php</scriptfile>
|
||||||
<creationDate>2026-05-16</creationDate>
|
<creationDate>2026-05-16</creationDate>
|
||||||
<author>Jonathan Miller || Moko Consulting</author>
|
<author>Jonathan Miller || Moko Consulting</author>
|
||||||
|
|||||||
-48
@@ -1,48 +0,0 @@
|
|||||||
<?xml version='1.0' encoding='UTF-8'?>
|
|
||||||
<!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
||||||
SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
VERSION: 02.19.06-dev
|
|
||||||
-->
|
|
||||||
|
|
||||||
<updates>
|
|
||||||
<update>
|
|
||||||
<name>Template - MokoOnyx</name>
|
|
||||||
<description>Template - MokoOnyx development build.</description>
|
|
||||||
<element>mokoonyx</element>
|
|
||||||
<type>template</type>
|
|
||||||
<client>site</client>
|
|
||||||
<version>02.19.06-dev</version>
|
|
||||||
<creationDate>2026-06-04</creationDate>
|
|
||||||
<infourl title='Template - MokoOnyx'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/development</infourl>
|
|
||||||
<downloads>
|
|
||||||
<downloadurl type='full' format='zip'>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/development/tpl_mokoonyx-02.19.06-dev.zip</downloadurl>
|
|
||||||
</downloads>
|
|
||||||
<sha256>718dd24295a5c8e3a0c855b01f86bf619bae84a4f7ab6e037c5cd5bfaadb2e30</sha256>
|
|
||||||
<tags><tag>dev</tag></tags>
|
|
||||||
<changelogurl>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/CHANGELOG.md</changelogurl>
|
|
||||||
<maintainer>Moko Consulting</maintainer>
|
|
||||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
|
||||||
<targetplatform name="joomla" version="(5|6)\..*" />
|
|
||||||
<php_minimum>8.1.0</php_minimum>
|
|
||||||
</update>
|
|
||||||
<update>
|
|
||||||
<name>Template - MokoOnyx</name>
|
|
||||||
<description>Template - MokoOnyx stable build.</description>
|
|
||||||
<element>mokoonyx</element>
|
|
||||||
<type>template</type>
|
|
||||||
<client>site</client>
|
|
||||||
<version>02.20.00</version>
|
|
||||||
<creationDate>2026-06-04</creationDate>
|
|
||||||
<infourl title="Template - MokoOnyx">https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/tag/stable</infourl>
|
|
||||||
<downloads>
|
|
||||||
<downloadurl type="full" format="zip">https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/releases/download/stable/tpl_mokoonyx-02.20.00.zip</downloadurl>
|
|
||||||
</downloads>
|
|
||||||
<sha256>00851a8a0ce9f64b873cbfbf4495422e78788d303ad5a4ac13f48122a8bc9e30</sha256>
|
|
||||||
<tags><tag>stable</tag></tags>
|
|
||||||
<changelogurl>https://git.mokoconsulting.tech/MokoConsulting/MokoOnyx/raw/branch/main/CHANGELOG.md</changelogurl>
|
|
||||||
<maintainer>Moko Consulting</maintainer>
|
|
||||||
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
|
|
||||||
<targetplatform name="joomla" version="(5|6)\..*"/>
|
|
||||||
<php_minimum>8.1.0</php_minimum>
|
|
||||||
</update>
|
|
||||||
</updates>
|
|
||||||
Reference in New Issue
Block a user