Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c8718525f2 | |||
| 49469566e8 | |||
| c2e2dccf2f | |||
| 0fded28b31 | |||
| 63352b7c91 | |||
| dd2671043d |
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
MokoStandards Repository Manifest
|
||||||
|
Schema: https://standards.mokoconsulting.tech/moko-platform/1.0
|
||||||
|
See: https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/MANIFEST-STANDARD
|
||||||
|
-->
|
||||||
|
<moko-platform xmlns="https://standards.mokoconsulting.tech/moko-platform/1.0" schema-version="1.0">
|
||||||
|
<identity>
|
||||||
|
<name>MokoJoomHero</name>
|
||||||
|
<display-name>Package - MokoJoomHero</display-name>
|
||||||
|
<org>MokoConsulting</org>
|
||||||
|
<description>A Joomla Module designed to provide a random image from a folder with content on top as a Hero.</description>
|
||||||
|
<version>01.19.01</version>
|
||||||
|
<license spdx="GPL-3.0-or-later">GNU General Public License v3</license>
|
||||||
|
</identity>
|
||||||
|
<governance>
|
||||||
|
<platform>joomla</platform>
|
||||||
|
<standards-version>09.01.00</standards-version>
|
||||||
|
<standards-source>https://git.mokoconsulting.tech/MokoConsulting/moko-platform</standards-source>
|
||||||
|
<last-synced>2026-05-30T15:00:00+00:00</last-synced>
|
||||||
|
</governance>
|
||||||
|
<build>
|
||||||
|
<language>PHP</language>
|
||||||
|
<package-type>joomla-extension</package-type>
|
||||||
|
<entry-point>src/</entry-point>
|
||||||
|
</build>
|
||||||
|
<deploy>
|
||||||
|
<source-dir>src/</source-dir>
|
||||||
|
<remote-subdir>modules/mod_mokojoomhero</remote-subdir>
|
||||||
|
<dev-host>mokoconsulting_dev@waas.dev.mokoconsulting.tech</dev-host>
|
||||||
|
<demo-host>mokoconsulting_demo@waas.demo.mokoconsulting.tech</demo-host>
|
||||||
|
</deploy>
|
||||||
|
</moko-platform>
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
#
|
|
||||||
# FILE INFORMATION
|
|
||||||
# DEFGROUP: Gitea.Workflow
|
|
||||||
# INGROUP: mokocli.Release
|
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
|
||||||
# PATH: /.mokogitea/workflows/auto-bump.yml
|
|
||||||
# VERSION: 09.02.00
|
|
||||||
# BRIEF: Auto patch-bump version on every push to dev (skips merge commits)
|
|
||||||
|
|
||||||
name: "Universal: Auto Version Bump"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- dev
|
|
||||||
- rc
|
|
||||||
- 'feature/**'
|
|
||||||
- 'patch/**'
|
|
||||||
|
|
||||||
env:
|
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
||||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
bump:
|
|
||||||
name: Version Bump
|
|
||||||
runs-on: release
|
|
||||||
if: >-
|
|
||||||
!contains(github.event.head_commit.message, '[skip ci]') &&
|
|
||||||
!contains(github.event.head_commit.message, '[skip bump]') &&
|
|
||||||
!startsWith(github.event.head_commit.message, 'Merge pull request')
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
|
||||||
with:
|
|
||||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
|
||||||
fetch-depth: 1
|
|
||||||
|
|
||||||
- name: Setup mokocli tools
|
|
||||||
run: |
|
|
||||||
if ! command -v composer &> /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
|
|
||||||
if [ -d "/opt/mokocli/cli" ]; then
|
|
||||||
echo "MOKO_CLI=/opt/mokocli/cli" >> "$GITHUB_ENV"
|
|
||||||
else
|
|
||||||
git clone --depth 1 --branch main --quiet \
|
|
||||||
"https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/mokocli.git" \
|
|
||||||
/tmp/mokocli
|
|
||||||
cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet
|
|
||||||
echo "MOKO_CLI=/tmp/mokocli/cli" >> "$GITHUB_ENV"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Bump version
|
|
||||||
run: |
|
|
||||||
php ${MOKO_CLI}/version_auto_bump.php \
|
|
||||||
--path . --branch "${GITHUB_REF_NAME}" \
|
|
||||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
|
||||||
--repo-url "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
|
||||||
@@ -1,421 +1,324 @@
|
|||||||
# 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: Gitea.Workflow
|
# DEFGROUP: Gitea.Workflow
|
||||||
# INGROUP: mokocli.Release
|
# INGROUP: moko-platform.Release
|
||||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli
|
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
|
||||||
# PATH: /templates/workflows/universal/auto-release.yml.template
|
# PATH: /templates/workflows/universal/auto-release.yml.template
|
||||||
# VERSION: 05.00.00
|
# VERSION: 05.00.00
|
||||||
# BRIEF: Universal build & release � detects platform from manifest.xml
|
# BRIEF: Universal build & release � detects platform from manifest.xml
|
||||||
#
|
#
|
||||||
# +========================================================================+
|
# +========================================================================+
|
||||||
# | UNIVERSAL BUILD & RELEASE PIPELINE |
|
# | UNIVERSAL BUILD & RELEASE PIPELINE |
|
||||||
# +========================================================================+
|
# +========================================================================+
|
||||||
# | |
|
# | |
|
||||||
# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. |
|
# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. |
|
||||||
# | |
|
# | |
|
||||||
# | Platform-specific: |
|
# | Platform-specific: |
|
||||||
# | joomla: XML manifest, type-prefixed packages |
|
# | joomla: XML manifest, type-prefixed packages |
|
||||||
# | dolibarr: mod*.class.php, update.txt, dev version reset |
|
# | dolibarr: mod*.class.php, update.txt, dev version reset |
|
||||||
# | generic: README-only, no update stream |
|
# | generic: README-only, no update stream |
|
||||||
# | |
|
# | |
|
||||||
# +========================================================================+
|
# +========================================================================+
|
||||||
|
|
||||||
name: "Universal: Build & Release"
|
name: "Universal: Build & Release"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, closed]
|
types: [opened, closed]
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
action:
|
action:
|
||||||
description: 'Action to perform'
|
description: 'Action to perform'
|
||||||
required: false
|
required: false
|
||||||
type: choice
|
type: choice
|
||||||
default: release
|
default: release
|
||||||
options:
|
options:
|
||||||
- release
|
- release
|
||||||
- promote-rc
|
- promote-rc
|
||||||
|
|
||||||
env:
|
env:
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
||||||
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
|
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
|
||||||
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
|
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# ── PR Opened → Rename branch to RC and build RC release ─────────────────────
|
# ── PR Opened → Rename branch to RC and build RC release ─────────────────────
|
||||||
promote-rc:
|
promote-rc:
|
||||||
name: Promote to RC
|
name: Promote to RC
|
||||||
runs-on: release
|
runs-on: release
|
||||||
if: >-
|
if: >-
|
||||||
(github.event.action == 'opened' && github.event.pull_request.merged != true) ||
|
(github.event.action == 'opened' && github.event.pull_request.merged != true) ||
|
||||||
(github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc')
|
(github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc')
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
with:
|
with:
|
||||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
fetch-depth: 1
|
fetch-depth: 1
|
||||||
|
|
||||||
- name: Setup mokocli tools
|
- name: Setup moko-platform tools
|
||||||
env:
|
env:
|
||||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||||
run: |
|
run: |
|
||||||
if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
|
if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then
|
||||||
echo Using pre-installed /opt/mokocli
|
echo Using pre-installed /opt/moko-platform
|
||||||
echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV
|
echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV
|
||||||
else
|
else
|
||||||
echo Falling back to fresh clone
|
echo Falling back to fresh clone
|
||||||
if ! command -v composer > /dev/null 2>&1; then
|
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
|
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
|
fi
|
||||||
rm -rf /tmp/mokocli
|
rm -rf /tmp/moko-platform-api
|
||||||
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git
|
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/mokocli
|
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api
|
||||||
cd /tmp/mokocli
|
cd /tmp/moko-platform-api
|
||||||
composer install --no-dev --no-interaction --quiet
|
composer install --no-dev --no-interaction --quiet
|
||||||
echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV
|
echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Rename branch to rc
|
- name: Rename branch to rc
|
||||||
run: |
|
run: |
|
||||||
php ${MOKO_CLI}/branch_rename.php \
|
php ${MOKO_CLI}/branch_rename.php \
|
||||||
--from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \
|
--from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \
|
||||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||||
--api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \
|
--api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \
|
||||||
--pr "${{ github.event.pull_request.number }}"
|
--pr "${{ github.event.pull_request.number }}"
|
||||||
|
|
||||||
- name: Checkout rc and configure git
|
- name: Checkout rc and configure git
|
||||||
run: |
|
run: |
|
||||||
git fetch origin rc
|
git fetch origin rc
|
||||||
git checkout rc
|
git checkout rc
|
||||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||||
git config --local user.name "gitea-actions[bot]"
|
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 remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||||
|
|
||||||
- name: Publish RC release
|
- name: Publish RC release
|
||||||
run: |
|
run: |
|
||||||
php ${MOKO_CLI}/release_publish.php \
|
php ${MOKO_CLI}/release_publish.php \
|
||||||
--path . --stability rc --bump minor --branch rc \
|
--path . --stability rc --bump minor --branch rc \
|
||||||
--token "${{ secrets.MOKOGITEA_TOKEN }}"
|
--token "${{ secrets.MOKOGITEA_TOKEN }}"
|
||||||
|
|
||||||
- name: Update RC release notes from CHANGELOG.md
|
- name: Summary
|
||||||
run: |
|
if: always()
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
run: |
|
||||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo "Branch renamed to rc, minor bump, RC release built" >> $GITHUB_STEP_SUMMARY
|
||||||
# Extract [Unreleased] section from changelog
|
|
||||||
NOTES=""
|
# ── Merged PR → Build & Release (or promote RC to stable) ────────────────────
|
||||||
if [ -f "CHANGELOG.md" ]; then
|
release:
|
||||||
NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
|
name: Build & Release Pipeline
|
||||||
fi
|
runs-on: release
|
||||||
[ -z "$NOTES" ] && NOTES="Release candidate"
|
if: >-
|
||||||
|
github.event.pull_request.merged == true ||
|
||||||
# Find the RC release and update its body
|
(github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc')
|
||||||
RELEASE_ID=$(curl -sf -H "Authorization: token ${TOKEN}" \
|
|
||||||
"${API_BASE}/releases/tags/release-candidate" \
|
steps:
|
||||||
| python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
if [ -n "$RELEASE_ID" ]; then
|
with:
|
||||||
python3 -c "
|
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
import json, urllib.request
|
fetch-depth: 0
|
||||||
body = open('/dev/stdin').read()
|
|
||||||
payload = json.dumps({'body': body}).encode()
|
- name: Configure git for bot pushes
|
||||||
req = urllib.request.Request(
|
run: |
|
||||||
'${API_BASE}/releases/${RELEASE_ID}',
|
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
||||||
data=payload, method='PATCH',
|
git config --local user.name "gitea-actions[bot]"
|
||||||
headers={
|
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
||||||
'Authorization': 'token ${TOKEN}',
|
|
||||||
'Content-Type': 'application/json'
|
- name: Check for merge conflict markers
|
||||||
})
|
run: |
|
||||||
urllib.request.urlopen(req)
|
CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true)
|
||||||
" <<< "$NOTES"
|
if [ -n "$CONFLICTS" ]; then
|
||||||
echo "RC release notes updated from CHANGELOG.md"
|
echo "::error::Merge conflict markers found — aborting release"
|
||||||
fi
|
echo "## Release Blocked: Conflict Markers" >> $GITHUB_STEP_SUMMARY
|
||||||
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||||
- name: Summary
|
echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY
|
||||||
if: always()
|
echo '```' >> $GITHUB_STEP_SUMMARY
|
||||||
run: |
|
exit 1
|
||||||
echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY
|
fi
|
||||||
echo "Branch renamed to rc, minor bump, RC release built" >> $GITHUB_STEP_SUMMARY
|
echo "No conflict markers found"
|
||||||
|
|
||||||
# ── Merged PR → Build & Release (or promote RC to stable) ────────────────────
|
- name: Setup moko-platform tools
|
||||||
release:
|
env:
|
||||||
name: Build & Release Pipeline
|
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
runs-on: release
|
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||||
if: >-
|
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}'
|
||||||
github.event.pull_request.merged == true ||
|
run: |
|
||||||
(github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc')
|
if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then
|
||||||
|
echo Using pre-installed /opt/moko-platform
|
||||||
steps:
|
echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV
|
||||||
- name: Checkout repository
|
else
|
||||||
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
echo Falling back to fresh clone
|
||||||
with:
|
if ! command -v composer > /dev/null 2>&1; then
|
||||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
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
|
||||||
fetch-depth: 0
|
fi
|
||||||
|
rm -rf /tmp/moko-platform-api
|
||||||
- name: Configure git for bot pushes
|
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git
|
||||||
run: |
|
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api
|
||||||
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
|
cd /tmp/moko-platform-api
|
||||||
git config --local user.name "gitea-actions[bot]"
|
composer install --no-dev --no-interaction --quiet
|
||||||
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
|
echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
|
||||||
|
fi
|
||||||
- name: Check for merge conflict markers
|
|
||||||
run: |
|
- name: "Publish stable release"
|
||||||
CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true)
|
run: |
|
||||||
if [ -n "$CONFLICTS" ]; then
|
php ${MOKO_CLI}/release_publish.php \
|
||||||
echo "::error::Merge conflict markers found — aborting release"
|
--path . --stability stable --bump minor --branch main \
|
||||||
echo "## Release Blocked: Conflict Markers" >> $GITHUB_STEP_SUMMARY
|
--token "${{ secrets.MOKOGITEA_TOKEN }}"
|
||||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY
|
- name: Update release notes from CHANGELOG.md
|
||||||
echo '```' >> $GITHUB_STEP_SUMMARY
|
run: |
|
||||||
exit 1
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
fi
|
|
||||||
echo "No conflict markers found"
|
# Extract [Unreleased] section from changelog
|
||||||
|
if [ -f "CHANGELOG.md" ]; then
|
||||||
- name: Setup mokocli tools
|
NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
|
||||||
env:
|
[ -z "$NOTES" ] && NOTES="Stable release"
|
||||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
else
|
||||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
NOTES="Stable release"
|
||||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}'
|
fi
|
||||||
run: |
|
|
||||||
if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
|
# Update release body via API
|
||||||
echo Using pre-installed /opt/mokocli
|
RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \
|
||||||
echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV
|
"${API_BASE}/releases/tags/stable" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
|
||||||
else
|
|
||||||
echo Falling back to fresh clone
|
if [ -n "$RELEASE_ID" ]; then
|
||||||
if ! command -v composer > /dev/null 2>&1; then
|
python3 -c "
|
||||||
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
|
import json, urllib.request
|
||||||
fi
|
body = open('/dev/stdin').read()
|
||||||
rm -rf /tmp/mokocli
|
payload = json.dumps({'body': body}).encode()
|
||||||
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git
|
req = urllib.request.Request(
|
||||||
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/mokocli
|
'${API_BASE}/releases/${RELEASE_ID}',
|
||||||
cd /tmp/mokocli
|
data=payload, method='PATCH',
|
||||||
composer install --no-dev --no-interaction --quiet
|
headers={
|
||||||
echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV
|
'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}',
|
||||||
fi
|
'Content-Type': 'application/json'
|
||||||
|
})
|
||||||
- name: "Detect platform"
|
urllib.request.urlopen(req)
|
||||||
id: platform
|
" <<< "$NOTES"
|
||||||
run: |
|
echo "Release notes updated from CHANGELOG.md"
|
||||||
php ${MOKO_CLI}/platform_detect.php --path . --github-output 2>/dev/null || true
|
fi
|
||||||
php ${MOKO_CLI}/manifest_read.php --path . --github-output 2>/dev/null || true
|
|
||||||
|
# -- STEP 9: Mirror to GitHub (stable only) --------------------------------
|
||||||
- name: "Determine version bump level"
|
- name: "Step 9: Mirror release to GitHub"
|
||||||
id: bump
|
if: >-
|
||||||
run: |
|
steps.version.outputs.skip != 'true' &&
|
||||||
# Fix/patch branches: version was already bumped by pre-release, just strip suffix
|
secrets.GH_MIRROR_TOKEN != ''
|
||||||
# Feature/dev branches: bump minor for the new stable release
|
continue-on-error: true
|
||||||
HEAD_REF="${{ github.event.pull_request.head.ref || 'dev' }}"
|
run: |
|
||||||
case "$HEAD_REF" in
|
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||||
fix/*|patch/*|hotfix/*|bugfix/*) BUMP="none" ;;
|
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
||||||
*) BUMP="minor" ;;
|
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
|
||||||
esac
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
echo "level=${BUMP}" >> "$GITHUB_OUTPUT"
|
php ${MOKO_CLI}/release_mirror.php \
|
||||||
echo "Bump level: ${BUMP} (from branch: ${HEAD_REF})"
|
--version "$VERSION" --tag "$RELEASE_TAG" \
|
||||||
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
||||||
- name: "Publish stable release"
|
--gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \
|
||||||
run: |
|
--branch main 2>&1 || true
|
||||||
BUMP_FLAG=""
|
echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY
|
||||||
if [ "${{ steps.bump.outputs.level }}" != "none" ]; then
|
|
||||||
BUMP_FLAG="--bump ${{ steps.bump.outputs.level }}"
|
# -- STEP 10: Sync main branch to GitHub mirror ----------------------------
|
||||||
fi
|
- name: "Step 10: Push main to GitHub mirror"
|
||||||
php ${MOKO_CLI}/release_publish.php \
|
if: >-
|
||||||
--path . --stability stable ${BUMP_FLAG} --branch main \
|
steps.version.outputs.skip != 'true' &&
|
||||||
--token "${{ secrets.MOKOGITEA_TOKEN }}"
|
secrets.GH_MIRROR_TOKEN != ''
|
||||||
|
continue-on-error: true
|
||||||
- name: "Read published version"
|
run: |
|
||||||
id: version
|
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
|
||||||
run: |
|
GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1)
|
||||||
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "")
|
GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2)
|
||||||
VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
|
git remote add github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \
|
||||||
[ -z "$VERSION" ] && VERSION="00.00.00" && echo "skip=true" >> "$GITHUB_OUTPUT"
|
git remote set-url github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git"
|
||||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
git fetch origin main --depth=1
|
||||||
echo "tag=stable" >> "$GITHUB_OUTPUT"
|
git push github origin/main:refs/heads/main --force 2>/dev/null \
|
||||||
echo "release_tag=stable" >> "$GITHUB_OUTPUT"
|
&& echo "main branch pushed to GitHub mirror" \
|
||||||
echo "branch=main" >> "$GITHUB_OUTPUT"
|
|| echo "WARNING: GitHub mirror push failed"
|
||||||
echo "Published version: ${VERSION}"
|
|
||||||
|
- name: "Step 11: Delete rc branch and recreate dev from main"
|
||||||
- name: Update release notes and promote changelog
|
if: steps.version.outputs.skip != 'true'
|
||||||
run: |
|
continue-on-error: true
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
run: |
|
||||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
|
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||||
# Get the stable release info (version and ID)
|
|
||||||
RELEASE_JSON=$(curl -sf -H "Authorization: token ${TOKEN}" \
|
# Delete rc branch (ephemeral — created by promote-rc)
|
||||||
"${API_BASE}/releases/tags/stable" 2>/dev/null || echo '{}')
|
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
|
||||||
RELEASE_ID=$(python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" <<< "$RELEASE_JSON" 2>/dev/null || true)
|
"${API_BASE}/branches/rc" 2>/dev/null \
|
||||||
# Extract version from release name (e.g. "06.17.00" or "v06.17.00")
|
&& echo "Deleted rc branch" || echo "rc branch not found"
|
||||||
VERSION=$(python3 -c "
|
|
||||||
import json, sys, re
|
# Delete dev branch
|
||||||
r = json.load(sys.stdin)
|
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
|
||||||
name = r.get('name', '')
|
"${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch"
|
||||||
m = re.search(r'(\d+\.\d+\.\d+)', name)
|
|
||||||
print(m.group(1) if m else '')
|
# Recreate dev from main (now includes version bump + changelog promotion)
|
||||||
" <<< "$RELEASE_JSON" 2>/dev/null || true)
|
curl -sf -X POST -H "Authorization: token ${TOKEN}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
# Extract [Unreleased] section from changelog
|
"${API_BASE}/branches" \
|
||||||
NOTES=""
|
-d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main"
|
||||||
if [ -f "CHANGELOG.md" ]; then
|
|
||||||
NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
|
echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
|
||||||
[ -z "$NOTES" ] && NOTES="Stable release"
|
- name: "Step 12: Create version branch from main"
|
||||||
|
if: steps.version.outputs.skip != 'true'
|
||||||
# Update release body via API
|
continue-on-error: true
|
||||||
if [ -n "$RELEASE_ID" ]; then
|
run: |
|
||||||
python3 -c "
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
import json, urllib.request
|
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
||||||
body = open('/dev/stdin').read()
|
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||||
payload = json.dumps({'body': body}).encode()
|
BRANCH_NAME="version/${VERSION}"
|
||||||
req = urllib.request.Request(
|
MAIN_SHA=$(git rev-parse HEAD)
|
||||||
'${API_BASE}/releases/${RELEASE_ID}',
|
|
||||||
data=payload, method='PATCH',
|
# Delete old version branch if it exists (same version re-release)
|
||||||
headers={
|
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${BRANCH_NAME}" 2>/dev/null && echo "Deleted old ${BRANCH_NAME}"
|
||||||
'Authorization': 'token ${TOKEN}',
|
|
||||||
'Content-Type': 'application/json'
|
# Create version/XX.YY.ZZ from main
|
||||||
})
|
curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/branches" -d "{\"new_branch_name\":\"${BRANCH_NAME}\",\"old_branch_name\":\"main\"}" 2>/dev/null && echo "Created ${BRANCH_NAME} from main (${MAIN_SHA})" || echo "WARNING: ${BRANCH_NAME} creation failed"
|
||||||
urllib.request.urlopen(req)
|
|
||||||
" <<< "$NOTES"
|
echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "Release notes updated from CHANGELOG.md"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Promote [Unreleased] → [version] in CHANGELOG.md and reset
|
# -- Dolibarr post-release: Reset dev version -----------------------------
|
||||||
if [ -n "$VERSION" ] && [ -f "CHANGELOG.md" ]; then
|
- name: "Post-release: Reset dev version"
|
||||||
DATE=$(date +%Y-%m-%d)
|
if: steps.version.outputs.skip != 'true'
|
||||||
python3 -c "
|
continue-on-error: true
|
||||||
import sys
|
run: |
|
||||||
version, date = sys.argv[1], sys.argv[2]
|
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||||
content = open('CHANGELOG.md').read()
|
php ${MOKO_CLI}/version_reset_dev.php \
|
||||||
old = '## [Unreleased]'
|
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \
|
||||||
new = f'## [Unreleased]\n\n## [{version}] --- {date}'
|
--branch dev --path . 2>&1 || true
|
||||||
content = content.replace(old, new, 1)
|
|
||||||
open('CHANGELOG.md', 'w').write(content)
|
# -- Summary --------------------------------------------------------------
|
||||||
" "$VERSION" "$DATE"
|
- name: Pipeline Summary
|
||||||
git add CHANGELOG.md
|
if: always()
|
||||||
git commit -m "chore: promote changelog [Unreleased] → [${VERSION}]" || true
|
run: |
|
||||||
git push origin main || true
|
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
||||||
echo "Changelog promoted: [Unreleased] → [${VERSION}]"
|
PLATFORM="${{ steps.platform.outputs.platform }}"
|
||||||
fi
|
if [ "${{ steps.version.outputs.skip }}" = "true" ]; then
|
||||||
|
echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY
|
||||||
# -- STEP 9: Mirror to GitHub (stable only) --------------------------------
|
echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY
|
||||||
- name: "Step 9: Mirror release to GitHub"
|
elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then
|
||||||
if: >-
|
echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
||||||
steps.version.outputs.skip != 'true' &&
|
else
|
||||||
secrets.GH_MIRROR_TOKEN != ''
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
continue-on-error: true
|
echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY
|
||||||
run: |
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY
|
||||||
RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
|
echo "|------|--------|" >> $GITHUB_STEP_SUMMARY
|
||||||
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
|
echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
php ${MOKO_CLI}/release_mirror.php \
|
echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
--version "$VERSION" --tag "$RELEASE_TAG" \
|
echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
|
echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY
|
||||||
--gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \
|
fi
|
||||||
--branch main 2>&1 || true
|
|
||||||
echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
# -- STEP 10: Sync main branch to GitHub mirror ----------------------------
|
|
||||||
- name: "Step 10: Push main to GitHub mirror"
|
|
||||||
if: >-
|
|
||||||
steps.version.outputs.skip != 'true' &&
|
|
||||||
secrets.GH_MIRROR_TOKEN != ''
|
|
||||||
continue-on-error: true
|
|
||||||
run: |
|
|
||||||
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
|
|
||||||
GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1)
|
|
||||||
GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2)
|
|
||||||
git remote add github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git" 2>/dev/null || \
|
|
||||||
git remote set-url github "https://x-access-token:${{ secrets.GH_MIRROR_TOKEN }}@github.com/${GH_ORG}/${GH_NAME}.git"
|
|
||||||
git fetch origin main --depth=1
|
|
||||||
git push github origin/main:refs/heads/main --force 2>/dev/null \
|
|
||||||
&& echo "main branch pushed to GitHub mirror" \
|
|
||||||
|| echo "WARNING: GitHub mirror push failed"
|
|
||||||
|
|
||||||
- name: "Step 11: Delete rc branch and recreate dev from main"
|
|
||||||
if: steps.version.outputs.skip != 'true'
|
|
||||||
continue-on-error: true
|
|
||||||
run: |
|
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
|
||||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
|
||||||
|
|
||||||
# Delete rc branch (ephemeral — created by promote-rc)
|
|
||||||
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
|
|
||||||
"${API_BASE}/branches/rc" 2>/dev/null \
|
|
||||||
&& echo "Deleted rc branch" || echo "rc branch not found"
|
|
||||||
|
|
||||||
# Delete dev branch
|
|
||||||
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
|
|
||||||
"${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch"
|
|
||||||
|
|
||||||
# Recreate dev from main (now includes version bump + changelog promotion)
|
|
||||||
curl -sf -X POST -H "Authorization: token ${TOKEN}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
"${API_BASE}/branches" \
|
|
||||||
-d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main"
|
|
||||||
|
|
||||||
echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
- name: "Step 12: Create version branch from main"
|
|
||||||
if: steps.version.outputs.skip != 'true'
|
|
||||||
continue-on-error: true
|
|
||||||
run: |
|
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
|
||||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
|
||||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
|
||||||
BRANCH_NAME="version/${VERSION}"
|
|
||||||
MAIN_SHA=$(git rev-parse HEAD)
|
|
||||||
|
|
||||||
# Delete old version branch if it exists (same version re-release)
|
|
||||||
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" "${API_BASE}/branches/${BRANCH_NAME}" 2>/dev/null && echo "Deleted old ${BRANCH_NAME}"
|
|
||||||
|
|
||||||
# Create version/XX.YY.ZZ from main
|
|
||||||
curl -sf -X POST -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" "${API_BASE}/branches" -d "{\"new_branch_name\":\"${BRANCH_NAME}\",\"old_branch_name\":\"main\"}" 2>/dev/null && echo "Created ${BRANCH_NAME} from main (${MAIN_SHA})" || echo "WARNING: ${BRANCH_NAME} creation failed"
|
|
||||||
|
|
||||||
echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# -- Dolibarr post-release: Reset dev version -----------------------------
|
|
||||||
- name: "Post-release: Reset dev version"
|
|
||||||
if: steps.version.outputs.skip != 'true'
|
|
||||||
continue-on-error: true
|
|
||||||
run: |
|
|
||||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
|
||||||
php ${MOKO_CLI}/version_reset_dev.php \
|
|
||||||
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \
|
|
||||||
--branch dev --path . 2>&1 || true
|
|
||||||
|
|
||||||
# -- Summary --------------------------------------------------------------
|
|
||||||
- name: Pipeline Summary
|
|
||||||
if: always()
|
|
||||||
run: |
|
|
||||||
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
|
|
||||||
PLATFORM="${{ steps.platform.outputs.platform }}"
|
|
||||||
if [ "${{ steps.version.outputs.skip }}" = "true" ]; then
|
|
||||||
echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY
|
|
||||||
elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then
|
|
||||||
echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "|------|--------|" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: Gitea.Workflow
|
# DEFGROUP: Gitea.Workflow
|
||||||
# INGROUP: MokoStandards.Universal
|
# INGROUP: MokoStandards.Universal
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||||
# PATH: /.mokogitea/workflows/branch-cleanup.yml
|
# PATH: /.mokogitea/workflows/branch-cleanup.yml
|
||||||
# VERSION: 01.00.00
|
# VERSION: 01.00.00
|
||||||
# BRIEF: Delete feature branches after PR merge
|
# BRIEF: Delete feature branches after PR merge
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
# DISABLED — auto-release Step 11 recreates dev from main after every release.
|
|
||||||
# Cascade-dev is redundant and causes version conflicts when both main and dev
|
|
||||||
# have different version numbers in templateDetails.xml / manifest.xml.
|
|
||||||
name: "Cascade Main → Dev (DISABLED)"
|
|
||||||
on: workflow_dispatch
|
|
||||||
jobs:
|
|
||||||
noop:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- run: echo "Cascade disabled — auto-release handles dev recreation"
|
|
||||||
@@ -1,191 +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:
|
|
||||||
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
|
|
||||||
@@ -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 mokocli tools
|
- name: Clone MokoStandards
|
||||||
env:
|
env:
|
||||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN || secrets.GA_TOKEN || github.token }}
|
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN || secrets.MOKOGITEA_TOKEN || github.token }}
|
||||||
|
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' }}
|
MOKO_CLONE_HOST: ${{ secrets.MOKOGITEA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }}
|
||||||
run: |
|
run: |
|
||||||
if [ -d "/opt/mokocli" ] || [ -d "/tmp/mokocli" ]; then
|
git clone --depth 1 --branch main --quiet \
|
||||||
echo "mokocli 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}/mokocli.git" \
|
|
||||||
/tmp/mokocli 2>/dev/null || echo "mokocli 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 \
|
||||||
@@ -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
|
||||||
@@ -245,417 +225,14 @@ jobs:
|
|||||||
echo "All ${CHECKED} directories contain index.html." >> $GITHUB_STEP_SUMMARY
|
echo "All ${CHECKED} directories contain index.html." >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Check config.xml and access.xml for components
|
|
||||||
run: |
|
|
||||||
echo "### Component Config & ACL Check" >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=0
|
|
||||||
|
|
||||||
# Find all component manifests (XML with type="component")
|
|
||||||
COMP_MANIFESTS=$(find . -maxdepth 4 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*" -exec grep -l '<extension[^>]*type="component"' {} ; 2>/dev/null || true)
|
|
||||||
|
|
||||||
if [ -z "$COMP_MANIFESTS" ]; then
|
|
||||||
echo "No component extensions found — skipping." >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
for MANIFEST in $COMP_MANIFESTS; do
|
|
||||||
COMP_DIR=$(dirname "$MANIFEST")
|
|
||||||
COMP_NAME=$(basename "$COMP_DIR")
|
|
||||||
echo "Component: `${COMP_NAME}` (manifest: `${MANIFEST}`)" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
# Check access.xml exists
|
|
||||||
ACCESS_FILE=$(find "$COMP_DIR" -name "access.xml" -not -path "./.git/*" 2>/dev/null | head -1)
|
|
||||||
if [ -z "$ACCESS_FILE" ]; then
|
|
||||||
echo "- Missing `access.xml` — ACL permissions will not work." >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=$((ERRORS + 1))
|
|
||||||
else
|
|
||||||
if command -v php &> /dev/null; then
|
|
||||||
if ! php -r "@simplexml_load_file('$ACCESS_FILE') ?: exit(1);" 2>/dev/null; then
|
|
||||||
echo "- `access.xml` is not well-formed XML." >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=$((ERRORS + 1))
|
|
||||||
else
|
|
||||||
for ACTION in core.admin core.manage; do
|
|
||||||
if ! grep -q "name=\"${ACTION}\"" "$ACCESS_FILE" 2>/dev/null; then
|
|
||||||
echo "- `access.xml` missing required action: `${ACTION}`" >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=$((ERRORS + 1))
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
echo "- `access.xml`: valid" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check config.xml exists
|
|
||||||
CONFIG_FILE=$(find "$COMP_DIR" -name "config.xml" -not -path "./.git/*" 2>/dev/null | head -1)
|
|
||||||
if [ -z "$CONFIG_FILE" ]; then
|
|
||||||
echo "- Missing `config.xml` — component Options page will be empty." >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=$((ERRORS + 1))
|
|
||||||
else
|
|
||||||
if command -v php &> /dev/null; then
|
|
||||||
if ! php -r "@simplexml_load_file('$CONFIG_FILE') ?: exit(1);" 2>/dev/null; then
|
|
||||||
echo "- `config.xml` is not well-formed XML." >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=$((ERRORS + 1))
|
|
||||||
else
|
|
||||||
echo "- `config.xml`: valid" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
if [ "${ERRORS}" -gt 0 ]; then
|
|
||||||
echo "**${ERRORS} config/ACL issue(s) found.**" >> $GITHUB_STEP_SUMMARY
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "**Component config & ACL check passed.**" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: SQL schema validation
|
|
||||||
run: |
|
|
||||||
echo "### SQL Schema Validation" >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=0
|
|
||||||
|
|
||||||
# Find SQL files in source/htdocs
|
|
||||||
SQL_FILES=$(find . -name "*.sql" -path "*/sql/*" -not -path "./.git/*" -not -path "./vendor/*" 2>/dev/null)
|
|
||||||
if [ -z "$SQL_FILES" ]; then
|
|
||||||
echo "No SQL files found — skipping." >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
echo "Found $(echo "$SQL_FILES" | wc -l) SQL file(s)" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
for FILE in $SQL_FILES; do
|
|
||||||
# Basic syntax check: balanced parentheses, no empty files
|
|
||||||
SIZE=$(wc -c < "$FILE" | tr -d ' ')
|
|
||||||
if [ "$SIZE" -eq 0 ]; then
|
|
||||||
echo "- Empty SQL file: \`${FILE}\`" >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=$((ERRORS + 1))
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check for common SQL errors
|
|
||||||
if grep -qP '^\s*$' "$FILE" && [ "$SIZE" -lt 5 ]; then
|
|
||||||
echo "- Whitespace-only SQL file: \`${FILE}\`" >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=$((ERRORS + 1))
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "- \`${FILE}\`: ${SIZE} bytes" >> $GITHUB_STEP_SUMMARY
|
|
||||||
done
|
|
||||||
|
|
||||||
# Check update SQL files follow version numbering pattern
|
|
||||||
UPDATE_DIR=$(find . -path "*/sql/updates/mysql" -type d -not -path "./.git/*" 2>/dev/null | head -1)
|
|
||||||
if [ -n "$UPDATE_DIR" ]; then
|
|
||||||
BAD_NAMES=0
|
|
||||||
for UFILE in "$UPDATE_DIR"/*.sql; do
|
|
||||||
[ ! -f "$UFILE" ] && continue
|
|
||||||
BASENAME=$(basename "$UFILE" .sql)
|
|
||||||
if ! echo "$BASENAME" | grep -qP '^\d+\.\d+\.\d+'; then
|
|
||||||
echo "- Update file \`${UFILE}\` does not follow version naming (expected X.Y.Z.sql)" >> $GITHUB_STEP_SUMMARY
|
|
||||||
BAD_NAMES=$((BAD_NAMES + 1))
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
if [ "$BAD_NAMES" -gt 0 ]; then
|
|
||||||
ERRORS=$((ERRORS + BAD_NAMES))
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
if [ "${ERRORS}" -gt 0 ]; then
|
|
||||||
echo "**${ERRORS} SQL issue(s) found.**" >> $GITHUB_STEP_SUMMARY
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "**SQL schema validation passed.**" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Manifest file references check
|
|
||||||
run: |
|
|
||||||
echo "### Manifest File References" >> $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 [ -z "$MANIFEST" ]; then
|
|
||||||
echo "No manifest found — skipping." >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
MANIFEST_DIR=$(dirname "$MANIFEST")
|
|
||||||
|
|
||||||
# Check <filename> references
|
|
||||||
FILENAMES=$(grep -oP '<filename[^>]*>\K[^<]+' "$MANIFEST" 2>/dev/null || true)
|
|
||||||
for F in $FILENAMES; do
|
|
||||||
if [ ! -f "${MANIFEST_DIR}/${F}" ] && [ ! -d "${MANIFEST_DIR}/${F}" ]; then
|
|
||||||
echo "- Missing: \`${F}\` (referenced in manifest)" >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=$((ERRORS + 1))
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Check <folder> references
|
|
||||||
FOLDERS=$(grep -oP '<folder[^>]*>\K[^<]+' "$MANIFEST" 2>/dev/null || true)
|
|
||||||
for F in $FOLDERS; do
|
|
||||||
if [ ! -d "${MANIFEST_DIR}/${F}" ]; then
|
|
||||||
echo "- Missing folder: \`${F}\` (referenced in manifest)" >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=$((ERRORS + 1))
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Check <file> references in package manifests (ZIP files won't exist in source)
|
|
||||||
EXT_TYPE=$(grep -oP '<extension[^>]*\btype="\K[^"]+' "$MANIFEST" | head -1)
|
|
||||||
if [ "$EXT_TYPE" != "package" ]; then
|
|
||||||
FILES=$(grep -oP '<file[^>]*>\K[^<]+' "$MANIFEST" 2>/dev/null || true)
|
|
||||||
for F in $FILES; do
|
|
||||||
if [ ! -f "${MANIFEST_DIR}/${F}" ]; then
|
|
||||||
echo "- Missing file: \`${F}\` (referenced in manifest)" >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=$((ERRORS + 1))
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
if [ "${ERRORS}" -gt 0 ]; then
|
|
||||||
echo "**${ERRORS} missing file reference(s).**" >> $GITHUB_STEP_SUMMARY
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "**Manifest file references check passed.**" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Form XML validation
|
|
||||||
run: |
|
|
||||||
echo "### Form XML Validation" >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=0
|
|
||||||
|
|
||||||
FORM_FILES=$(find . -name "*.xml" -path "*/forms/*" -not -path "./.git/*" -not -path "./vendor/*" 2>/dev/null)
|
|
||||||
if [ -z "$FORM_FILES" ]; then
|
|
||||||
echo "No form XML files found — skipping." >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
echo "Found $(echo "$FORM_FILES" | wc -l) form file(s)" >> $GITHUB_STEP_SUMMARY
|
|
||||||
for FILE in $FORM_FILES; do
|
|
||||||
if command -v php &> /dev/null; then
|
|
||||||
if ! php -r "@simplexml_load_file('$FILE') ?: exit(1);" 2>/dev/null; then
|
|
||||||
echo "- \`${FILE}\`: malformed XML" >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=$((ERRORS + 1))
|
|
||||||
else
|
|
||||||
# Check for valid Joomla form structure
|
|
||||||
if ! grep -qE '<form|<field|<fieldset' "$FILE" 2>/dev/null; then
|
|
||||||
echo "- \`${FILE}\`: no \`<form>\`, \`<field>\`, or \`<fieldset>\` elements found" >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=$((ERRORS + 1))
|
|
||||||
else
|
|
||||||
echo "- \`${FILE}\`: valid" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
if [ "${ERRORS}" -gt 0 ]; then
|
|
||||||
echo "**${ERRORS} form XML issue(s).**" >> $GITHUB_STEP_SUMMARY
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "**Form XML validation passed.**" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Deprecated Joomla API check
|
|
||||||
continue-on-error: true
|
|
||||||
run: |
|
|
||||||
echo "### Deprecated Joomla API Check" >> $GITHUB_STEP_SUMMARY
|
|
||||||
WARNINGS=0
|
|
||||||
|
|
||||||
SRC_DIR=""
|
|
||||||
for DIR in source/ src/ htdocs/; do
|
|
||||||
[ -d "$DIR" ] && SRC_DIR="$DIR" && break
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -z "$SRC_DIR" ]; then
|
|
||||||
echo "No source directory found — skipping." >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
# Joomla 3/4 deprecated patterns that break in Joomla 6
|
|
||||||
PATTERNS=(
|
|
||||||
'JFactory::'
|
|
||||||
'JText::'
|
|
||||||
'JHtml::'
|
|
||||||
'JRoute::'
|
|
||||||
'JUri::'
|
|
||||||
'JLog::'
|
|
||||||
'JTable::'
|
|
||||||
'JInput'
|
|
||||||
'CMSFactory::\$application'
|
|
||||||
'JApplicationCms'
|
|
||||||
)
|
|
||||||
|
|
||||||
for PATTERN in "${PATTERNS[@]}"; do
|
|
||||||
HITS=$(grep -rnl "$PATTERN" "$SRC_DIR" --include="*.php" 2>/dev/null || true)
|
|
||||||
if [ -n "$HITS" ]; then
|
|
||||||
COUNT=$(echo "$HITS" | wc -l)
|
|
||||||
echo "- \`${PATTERN}\` found in ${COUNT} file(s)" >> $GITHUB_STEP_SUMMARY
|
|
||||||
WARNINGS=$((WARNINGS + COUNT))
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
if [ "$WARNINGS" -gt 0 ]; then
|
|
||||||
echo "**${WARNINGS} deprecated API usage(s) found.** These will break in Joomla 6." >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
echo "**No deprecated APIs found.**" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Template output escaping check
|
|
||||||
continue-on-error: true
|
|
||||||
run: |
|
|
||||||
echo "### Template Output Escaping" >> $GITHUB_STEP_SUMMARY
|
|
||||||
WARNINGS=0
|
|
||||||
|
|
||||||
TMPL_FILES=$(find . -name "*.php" -path "*/tmpl/*" -not -path "./.git/*" -not -path "./vendor/*" 2>/dev/null)
|
|
||||||
if [ -z "$TMPL_FILES" ]; then
|
|
||||||
echo "No template files found — skipping." >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
echo "Found $(echo "$TMPL_FILES" | wc -l) template file(s)" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
for FILE in $TMPL_FILES; do
|
|
||||||
# Check for unescaped output: <?= $var ?> or echo $var without escape()
|
|
||||||
UNESCAPED=$(grep -nP '<\?=\s*\$(?!this->escape)' "$FILE" 2>/dev/null || true)
|
|
||||||
if [ -n "$UNESCAPED" ]; then
|
|
||||||
HITS=$(echo "$UNESCAPED" | wc -l)
|
|
||||||
echo "- \`${FILE}\`: ${HITS} unescaped \`<?= \$var ?>\` output(s) — use \`<?= \$this->escape(\$var) ?>\`" >> $GITHUB_STEP_SUMMARY
|
|
||||||
WARNINGS=$((WARNINGS + HITS))
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check for echo without escaping in template context
|
|
||||||
RAW_ECHO=$(grep -nP '^\s*echo\s+\$(?!this->escape)' "$FILE" 2>/dev/null || true)
|
|
||||||
if [ -n "$RAW_ECHO" ]; then
|
|
||||||
HITS=$(echo "$RAW_ECHO" | wc -l)
|
|
||||||
echo "- \`${FILE}\`: ${HITS} raw \`echo \$var\` — consider \`echo \$this->escape(\$var)\`" >> $GITHUB_STEP_SUMMARY
|
|
||||||
WARNINGS=$((WARNINGS + HITS))
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
if [ "$WARNINGS" -gt 0 ]; then
|
|
||||||
echo "**${WARNINGS} potential XSS risk(s) in templates.** Review unescaped output." >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
echo "**All template output appears properly escaped.**" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Namespace consistency check
|
|
||||||
run: |
|
|
||||||
echo "### Namespace Consistency" >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=0
|
|
||||||
|
|
||||||
# Find component/plugin manifests with <namespace> tags
|
|
||||||
MANIFESTS=$(find . -maxdepth 4 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*" -exec grep -l '<namespace' {} \; 2>/dev/null || true)
|
|
||||||
|
|
||||||
if [ -z "$MANIFESTS" ]; then
|
|
||||||
echo "No manifests with \`<namespace>\` found — skipping." >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
for MANIFEST in $MANIFESTS; do
|
|
||||||
NS_PATH=$(grep -oP '<namespace[^>]*>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1)
|
|
||||||
[ -z "$NS_PATH" ] && continue
|
|
||||||
MANIFEST_DIR=$(dirname "$MANIFEST")
|
|
||||||
|
|
||||||
echo "Manifest: \`${MANIFEST}\` → namespace \`${NS_PATH}\`" >> $GITHUB_STEP_SUMMARY
|
|
||||||
|
|
||||||
# Check PHP files have matching namespace
|
|
||||||
while IFS= read -r -d '' PHP_FILE; do
|
|
||||||
FILE_NS=$(grep -oP '^\s*namespace\s+\K[^;]+' "$PHP_FILE" 2>/dev/null | head -1)
|
|
||||||
[ -z "$FILE_NS" ] && continue
|
|
||||||
|
|
||||||
# Namespace should start with the manifest namespace path
|
|
||||||
if ! echo "$FILE_NS" | grep -qF "${NS_PATH}"; then
|
|
||||||
echo "- \`${PHP_FILE}\`: namespace \`${FILE_NS}\` doesn't match manifest \`${NS_PATH}\`" >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=$((ERRORS + 1))
|
|
||||||
fi
|
|
||||||
done < <(find "$MANIFEST_DIR" -name "*.php" -path "*/src/*" -not -path "./vendor/*" -print0 2>/dev/null)
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
if [ "${ERRORS}" -gt 0 ]; then
|
|
||||||
echo "**${ERRORS} namespace mismatch(es).**" >> $GITHUB_STEP_SUMMARY
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "**Namespace consistency check passed.**" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: SPDX license header check
|
|
||||||
continue-on-error: true
|
|
||||||
run: |
|
|
||||||
echo "### SPDX License Headers" >> $GITHUB_STEP_SUMMARY
|
|
||||||
MISSING=0
|
|
||||||
|
|
||||||
SRC_DIR=""
|
|
||||||
for DIR in source/ src/ htdocs/; do
|
|
||||||
[ -d "$DIR" ] && SRC_DIR="$DIR" && break
|
|
||||||
done
|
|
||||||
|
|
||||||
if [ -z "$SRC_DIR" ]; then
|
|
||||||
echo "No source directory found — skipping." >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
TOTAL=0
|
|
||||||
while IFS= read -r -d '' FILE; do
|
|
||||||
TOTAL=$((TOTAL + 1))
|
|
||||||
if ! head -10 "$FILE" | grep -qi "SPDX"; then
|
|
||||||
echo "- Missing SPDX header: \`${FILE}\`" >> $GITHUB_STEP_SUMMARY
|
|
||||||
MISSING=$((MISSING + 1))
|
|
||||||
fi
|
|
||||||
done < <(find "$SRC_DIR" -name "*.php" -not -path "./vendor/*" -print0)
|
|
||||||
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
if [ "$MISSING" -gt 0 ]; then
|
|
||||||
echo "**${MISSING}/${TOTAL} PHP file(s) missing SPDX license header.**" >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
echo "**All ${TOTAL} PHP files have SPDX headers.**" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Service provider check
|
|
||||||
run: |
|
|
||||||
echo "### Service Provider Check" >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=0
|
|
||||||
|
|
||||||
PROVIDERS=$(find . -name "provider.php" -path "*/services/*" -not -path "./.git/*" -not -path "./vendor/*" 2>/dev/null)
|
|
||||||
if [ -z "$PROVIDERS" ]; then
|
|
||||||
echo "No service providers found — skipping." >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
for FILE in $PROVIDERS; do
|
|
||||||
# Must return a ServiceProviderInterface
|
|
||||||
if ! grep -qP 'ServiceProviderInterface|ComponentInterface|MVCFactoryInterface|DispatcherInterface' "$FILE" 2>/dev/null; then
|
|
||||||
echo "- \`${FILE}\`: does not reference ServiceProviderInterface or component interfaces" >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=$((ERRORS + 1))
|
|
||||||
else
|
|
||||||
echo "- \`${FILE}\`: valid service provider" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Must have return statement
|
|
||||||
if ! grep -qP '^\s*return\s+new\s+' "$FILE" 2>/dev/null; then
|
|
||||||
echo "- \`${FILE}\`: missing \`return new ...\` statement" >> $GITHUB_STEP_SUMMARY
|
|
||||||
ERRORS=$((ERRORS + 1))
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
if [ "${ERRORS}" -gt 0 ]; then
|
|
||||||
echo "**${ERRORS} service provider issue(s).**" >> $GITHUB_STEP_SUMMARY
|
|
||||||
exit 1
|
|
||||||
else
|
|
||||||
echo "**Service provider check passed.**" >> $GITHUB_STEP_SUMMARY
|
|
||||||
fi
|
|
||||||
|
|
||||||
release-readiness:
|
release-readiness:
|
||||||
name: Release Readiness Check
|
name: Release Readiness Check
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: github.event_name == 'pull_request' && github.base_ref == 'main'
|
if: github.event_name == 'pull_request' && github.base_ref == 'main'
|
||||||
continue-on-error: true
|
|
||||||
|
|
||||||
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: |
|
||||||
@@ -761,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 \
|
||||||
@@ -811,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
|
||||||
@@ -890,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,76 +0,0 @@
|
|||||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
|
|
||||||
name: "Publish to Composer"
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
tags:
|
|
||||||
- 'v*'
|
|
||||||
- '[0-9]*.[0-9]*.[0-9]*'
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
workflow_dispatch:
|
|
||||||
|
|
||||||
env:
|
|
||||||
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish:
|
|
||||||
name: Publish Package
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: >-
|
|
||||||
!contains(github.event.head_commit.message, '[skip ci]') &&
|
|
||||||
!contains(github.event.head_commit.message, '[skip publish]')
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup PHP
|
|
||||||
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
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: composer install --no-dev --no-interaction --prefer-dist --quiet
|
|
||||||
|
|
||||||
- name: Determine version
|
|
||||||
id: version
|
|
||||||
run: |
|
|
||||||
VERSION=$(php -r "echo json_decode(file_get_contents('composer.json'))->version;")
|
|
||||||
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "Package version: ${VERSION}"
|
|
||||||
|
|
||||||
# Gitea Composer Registry — auto-publishes from tags
|
|
||||||
# The tag push itself registers the package at:
|
|
||||||
# https://git.mokoconsulting.tech/api/packages/MokoConsulting/composer
|
|
||||||
- name: Verify Gitea registry
|
|
||||||
run: |
|
|
||||||
echo "Gitea Composer registry auto-publishes from tags."
|
|
||||||
echo "Package available at: ${GITEA_URL}/api/packages/MokoConsulting/composer"
|
|
||||||
echo "Install: composer require mokoconsulting/mokocli"
|
|
||||||
|
|
||||||
# Packagist — notify of new version
|
|
||||||
- name: Notify Packagist
|
|
||||||
if: secrets.PACKAGIST_TOKEN != ''
|
|
||||||
run: |
|
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
|
||||||
echo "Notifying Packagist of version ${VERSION}..."
|
|
||||||
curl -sf -X POST \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d '{"repository":{"url":"https://git.mokoconsulting.tech/MokoConsulting/mokocli"}}' \
|
|
||||||
"https://packagist.org/api/update-package?username=mokoconsulting&apiToken=${{ secrets.PACKAGIST_TOKEN }}" \
|
|
||||||
&& echo "Packagist notified" \
|
|
||||||
|| echo "::warning::Packagist notification failed (package may not be registered yet)"
|
|
||||||
|
|
||||||
- name: Summary
|
|
||||||
run: |
|
|
||||||
VERSION="${{ steps.version.outputs.version }}"
|
|
||||||
echo "## Composer Package Published" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "| Registry | Status |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "|----------|--------|" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "| Gitea | \`composer require mokoconsulting/mokocli:${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "| Packagist | \`composer require mokoconsulting/mokocli\` |" >> $GITHUB_STEP_SUMMARY
|
|
||||||
@@ -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
|
||||||
@@ -25,6 +25,10 @@
|
|||||||
name: "Universal: Secret Scanning"
|
name: "Universal: Secret Scanning"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- 'dev/**'
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 5 * * 1' # Weekly Monday 05:00 UTC
|
- cron: '0 5 * * 1' # Weekly Monday 05:00 UTC
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: Gitea.Workflow
|
# DEFGROUP: Gitea.Workflow
|
||||||
# INGROUP: mokocli.Automation
|
# INGROUP: moko-platform.Automation
|
||||||
# VERSION: 01.00.00
|
# VERSION: 01.19.01
|
||||||
# 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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -96,32 +96,6 @@ jobs:
|
|||||||
echo "Branch policy: OK (${HEAD} → ${BASE})"
|
echo "Branch policy: OK (${HEAD} → ${BASE})"
|
||||||
echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY
|
echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
# ── Secret Scanning ──────────────────────────────────────────────────
|
|
||||||
gitleaks:
|
|
||||||
name: Secret Scan
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- name: Install Gitleaks
|
|
||||||
run: |
|
|
||||||
GITLEAKS_VERSION="8.21.2"
|
|
||||||
curl -sSL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \
|
|
||||||
| tar -xz -C /usr/local/bin gitleaks
|
|
||||||
|
|
||||||
- name: Scan PR commits for secrets
|
|
||||||
run: |
|
|
||||||
if gitleaks detect --source . --verbose \
|
|
||||||
--log-opts=${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} 2>&1; then
|
|
||||||
echo "**No secrets detected.**" >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
echo "::error::Potential secrets detected in PR commits"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# ── Code Validation ────────────────────────────────────────────────────
|
# ── Code Validation ────────────────────────────────────────────────────
|
||||||
validate:
|
validate:
|
||||||
name: Validate PR
|
name: Validate PR
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
#
|
|
||||||
# FILE INFORMATION
|
|
||||||
# DEFGROUP: Gitea.Workflow
|
|
||||||
# INGROUP: mokocli.Validation
|
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
|
||||||
# PATH: /templates/workflows/joomla/pr-metadata-check.yml.template
|
|
||||||
# VERSION: 01.00.00
|
|
||||||
# BRIEF: Validate MokoGitea metadata matches Joomla extension manifest on PRs
|
|
||||||
|
|
||||||
name: "Joomla: Metadata Validation"
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [opened, synchronize, reopened, converted_to_draft, ready_for_review]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
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:
|
|
||||||
validate-metadata:
|
|
||||||
name: "Validate Joomla Metadata"
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- name: Setup mokocli tools
|
|
||||||
env:
|
|
||||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
|
||||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
|
||||||
run: |
|
|
||||||
if [ -f /opt/mokocli/cli/joomla_metadata_validate.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
|
|
||||||
echo Using pre-installed /opt/mokocli
|
|
||||||
echo MOKO_CLI=/opt/mokocli/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/mokocli
|
|
||||||
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git
|
|
||||||
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/mokocli
|
|
||||||
cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet
|
|
||||||
echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Validate metadata against Joomla manifest
|
|
||||||
env:
|
|
||||||
GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
|
||||||
run: |
|
|
||||||
php ${MOKO_CLI}/joomla_metadata_validate.php \
|
|
||||||
--path . \
|
|
||||||
--token "${GITEA_TOKEN}" \
|
|
||||||
--org "${GITEA_ORG}" \
|
|
||||||
--repo "${GITEA_REPO}" \
|
|
||||||
--api-base "${GITEA_URL}/api/v1" \
|
|
||||||
--ci
|
|
||||||
|
|
||||||
if [ $? -ne 0 ]; then
|
|
||||||
echo "::error::Joomla metadata mismatch — update delivery will fail. Run 'php cli/joomla_metadata_validate.php' locally to see details."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
@@ -4,8 +4,8 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: Gitea.Workflow
|
# DEFGROUP: Gitea.Workflow
|
||||||
# INGROUP: mokocli.Release
|
# INGROUP: moko-platform.Release
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
# 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: Auto pre-release on push to dev/alpha/beta/rc branches
|
||||||
@@ -60,25 +60,25 @@ jobs:
|
|||||||
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
token: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
ref: ${{ github.ref_name }}
|
ref: ${{ github.ref_name }}
|
||||||
|
|
||||||
- name: Setup mokocli tools
|
- name: Setup moko-platform tools
|
||||||
env:
|
env:
|
||||||
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||||
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
|
||||||
run: |
|
run: |
|
||||||
# Use pre-installed /opt/mokocli if available (updated by cron every 6h)
|
# Use pre-installed /opt/moko-platform if available (updated by cron every 6h)
|
||||||
if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/cli/manifest_element.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
|
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/mokocli
|
echo Using pre-installed /opt/moko-platform
|
||||||
echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV
|
echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV
|
||||||
else
|
else
|
||||||
echo Falling back to fresh clone
|
echo Falling back to fresh clone
|
||||||
if ! command -v composer > /dev/null 2>&1; then
|
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
|
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
|
fi
|
||||||
rm -rf /tmp/mokocli
|
rm -rf /tmp/moko-platform-api
|
||||||
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git
|
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/mokocli
|
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api
|
||||||
cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet
|
cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
|
||||||
echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV
|
echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
|
||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Detect platform
|
- name: Detect platform
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
#
|
|
||||||
# FILE INFORMATION
|
|
||||||
# DEFGROUP: Gitea.Workflow
|
|
||||||
# INGROUP: mokocli.Universal
|
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
|
||||||
# PATH: /.mokogitea/workflows/rc-revert.yml
|
|
||||||
# VERSION: 09.23.00
|
|
||||||
# BRIEF: Rename rc/ branch back to dev/ when PR is closed without merge
|
|
||||||
|
|
||||||
name: "RC Revert"
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [closed]
|
|
||||||
|
|
||||||
env:
|
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
revert:
|
|
||||||
name: Rename rc/ back to dev/
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: >-
|
|
||||||
github.event.pull_request.merged == false &&
|
|
||||||
startsWith(github.event.pull_request.head.ref, 'rc/')
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Rename branch
|
|
||||||
run: |
|
|
||||||
BRANCH="${{ github.event.pull_request.head.ref }}"
|
|
||||||
SUFFIX="${BRANCH#rc/}"
|
|
||||||
DEV_BRANCH="dev/${SUFFIX}"
|
|
||||||
API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches"
|
|
||||||
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
|
|
||||||
|
|
||||||
# Create dev/ branch from rc/ branch
|
|
||||||
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X POST \
|
|
||||||
-H "Authorization: token ${TOKEN}" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
-d "{\"new_branch_name\": \"${DEV_BRANCH}\", \"old_branch_name\": \"${BRANCH}\"}" \
|
|
||||||
"${API}" 2>/dev/null || true)
|
|
||||||
|
|
||||||
if [ "$STATUS" = "201" ]; then
|
|
||||||
echo "Created branch: ${DEV_BRANCH}" >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
echo "::error::Failed to create ${DEV_BRANCH} from ${BRANCH} (HTTP ${STATUS})"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Delete rc/ branch
|
|
||||||
ENCODED=$(php -r "echo rawurlencode('${BRANCH}');")
|
|
||||||
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
|
|
||||||
-H "Authorization: token ${TOKEN}" \
|
|
||||||
"${API}/${ENCODED}" 2>/dev/null || true)
|
|
||||||
|
|
||||||
if [ "$STATUS" = "204" ]; then
|
|
||||||
echo "Deleted branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
|
|
||||||
else
|
|
||||||
echo "::warning::Failed to delete ${BRANCH} (HTTP ${STATUS})"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "### RC Reverted" >> $GITHUB_STEP_SUMMARY
|
|
||||||
echo "${BRANCH} → ${DEV_BRANCH}" >> $GITHUB_STEP_SUMMARY
|
|
||||||
@@ -7,8 +7,8 @@
|
|||||||
#
|
#
|
||||||
# FILE INFORMATION
|
# FILE INFORMATION
|
||||||
# DEFGROUP: Gitea.Workflow
|
# DEFGROUP: Gitea.Workflow
|
||||||
# INGROUP: mokocli.Validation
|
# INGROUP: moko-platform.Validation
|
||||||
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli
|
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
|
||||||
# PATH: /templates/workflows/joomla/repo_health.yml.template
|
# PATH: /templates/workflows/joomla/repo_health.yml.template
|
||||||
# VERSION: 09.23.00
|
# VERSION: 09.23.00
|
||||||
# BRIEF: Enforces repository guardrails by validating scripts governance, tooling availability, and core repository health artifacts.
|
# BRIEF: Enforces repository guardrails by validating scripts governance, tooling availability, and core repository health artifacts.
|
||||||
@@ -33,8 +33,7 @@ on:
|
|||||||
- scripts
|
- scripts
|
||||||
- repo
|
- repo
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
push:
|
||||||
- main
|
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
@@ -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 }}
|
||||||
|
|
||||||
|
|||||||
@@ -1,73 +0,0 @@
|
|||||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
|
||||||
#
|
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
#
|
|
||||||
# FILE INFORMATION
|
|
||||||
# DEFGROUP: Gitea.Workflow
|
|
||||||
# INGROUP: mokocli.Universal
|
|
||||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
|
||||||
# PATH: /.mokogitea/workflows/workflow-sync-trigger.yml
|
|
||||||
# VERSION: 01.01.00
|
|
||||||
# BRIEF: Trigger workflow sync to live repos when a PR is merged to main
|
|
||||||
|
|
||||||
name: "Universal: Workflow Sync Trigger"
|
|
||||||
|
|
||||||
on:
|
|
||||||
pull_request:
|
|
||||||
types: [closed]
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
|
|
||||||
env:
|
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
sync:
|
|
||||||
name: Sync workflows to live repos
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
if: >-
|
|
||||||
github.event.pull_request.merged == true &&
|
|
||||||
!contains(github.event.pull_request.title, '[skip sync]')
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Determine platform from repo name
|
|
||||||
id: platform
|
|
||||||
run: |
|
|
||||||
REPO="${{ github.event.repository.name }}"
|
|
||||||
case "$REPO" in
|
|
||||||
Template-Joomla) PLATFORM="joomla" ;;
|
|
||||||
Template-Dolibarr) PLATFORM="dolibarr" ;;
|
|
||||||
Template-Go) PLATFORM="go" ;;
|
|
||||||
Template-MCP) PLATFORM="mcp" ;;
|
|
||||||
Template-Generic) PLATFORM="" ;;
|
|
||||||
*) PLATFORM="" ;;
|
|
||||||
esac
|
|
||||||
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "Platform: ${PLATFORM:-all}"
|
|
||||||
|
|
||||||
- name: Clone mokocli
|
|
||||||
env:
|
|
||||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
|
||||||
run: |
|
|
||||||
GITEA_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
|
|
||||||
git clone --depth 1 "${GITEA_URL}/MokoConsulting/mokocli.git" /tmp/mokocli
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
run: |
|
|
||||||
cd /tmp/mokocli
|
|
||||||
composer install --no-dev --no-interaction --quiet 2>/dev/null || true
|
|
||||||
|
|
||||||
- name: Run workflow sync
|
|
||||||
env:
|
|
||||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
|
||||||
run: |
|
|
||||||
ARGS="--token ${MOKOGITEA_TOKEN}"
|
|
||||||
ARGS="${ARGS} --org ${{ vars.GITEA_ORG || github.repository_owner }}"
|
|
||||||
ARGS="${ARGS} --phase repos"
|
|
||||||
|
|
||||||
PLATFORM="${{ steps.platform.outputs.platform }}"
|
|
||||||
if [ -n "$PLATFORM" ]; then
|
|
||||||
ARGS="${ARGS} --platform-filter ${PLATFORM}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
php /tmp/mokocli/cli/workflow_sync.php ${ARGS}
|
|
||||||
+16
-4
@@ -1,10 +1,6 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
## [01.21.00] --- 2026-06-19
|
|
||||||
|
|
||||||
## [01.20.00] --- 2026-06-04
|
|
||||||
|
|
||||||
## [01.19.00] --- 2026-06-04
|
## [01.19.00] --- 2026-06-04
|
||||||
|
|
||||||
|
|
||||||
@@ -18,3 +14,19 @@
|
|||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
- Skip overlay background on solid colour and gradient hero modes — gradient was invisible behind 50% black overlay
|
- Skip overlay background on solid colour and gradient hero modes — gradient was invisible behind 50% black overlay
|
||||||
|
|
||||||
|
## [01.14] - 2026-06-04
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Update server URL to new Gitea release system
|
||||||
|
- Use pretty name format for update server entry
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Add missing `<updateservers>` section to module manifest
|
||||||
|
|
||||||
|
## [01.13] - 2026-06-04
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Restructure from package extension back to standalone site module
|
||||||
|
- Remove system plugin (`plg_system_mokojoomhero`) and package wrapper
|
||||||
|
- Simplify build target for module ZIP
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@
|
|||||||
DEFGROUP:
|
DEFGROUP:
|
||||||
INGROUP: Project.Documentation
|
INGROUP: Project.Documentation
|
||||||
REPO:
|
REPO:
|
||||||
VERSION: 01.21.00
|
VERSION: 01.19.01
|
||||||
PATH: ./CODE_OF_CONDUCT.md
|
PATH: ./CODE_OF_CONDUCT.md
|
||||||
BRIEF: Reference + packaging repo for Moko Consulting Developer GPT Other Default
|
BRIEF: Reference + packaging repo for Moko Consulting Developer GPT Other Default
|
||||||
-->
|
-->
|
||||||
|
|||||||
+1
-1
@@ -23,7 +23,7 @@ DEFGROUP: [PROJECT_NAME]
|
|||||||
INGROUP: [PROJECT_NAME].Documentation
|
INGROUP: [PROJECT_NAME].Documentation
|
||||||
REPO: [REPOSITORY_URL]
|
REPO: [REPOSITORY_URL]
|
||||||
PATH: /SECURITY.md
|
PATH: /SECURITY.md
|
||||||
VERSION: 01.21.00
|
VERSION: 01.19.01
|
||||||
BRIEF: Security vulnerability reporting and handling policy
|
BRIEF: Security vulnerability reporting and handling policy
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
; INGROUP: MokoJoomHero.Module
|
; INGROUP: MokoJoomHero.Module
|
||||||
; REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
|
; REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
|
||||||
; PATH: /src/language/en-GB/mod_mokojoomhero.ini
|
; PATH: /src/language/en-GB/mod_mokojoomhero.ini
|
||||||
; VERSION: 01.21.00
|
; VERSION: 01.19.01
|
||||||
; BRIEF: Language strings for MokoJoomHero module (frontend + admin form fields)
|
; BRIEF: Language strings for MokoJoomHero module (frontend + admin form fields)
|
||||||
|
|
||||||
MOD_MOKOJOOMHERO_NO_CONTENT="Add content to this module to display it over the hero image."
|
MOD_MOKOJOOMHERO_NO_CONTENT="Add content to this module to display it over the hero image."
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
; INGROUP: MokoJoomHero.Module
|
; INGROUP: MokoJoomHero.Module
|
||||||
; REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
|
; REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
|
||||||
; PATH: /src/language/en-GB/mod_mokojoomhero.sys.ini
|
; PATH: /src/language/en-GB/mod_mokojoomhero.sys.ini
|
||||||
; VERSION: 01.21.00
|
; VERSION: 01.19.01
|
||||||
; BRIEF: System language strings — used in admin Extension Manager and Module Manager
|
; BRIEF: System language strings — used in admin Extension Manager and Module Manager
|
||||||
|
|
||||||
MOD_MOKOJOOMHERO="Module - MokoJoomHero"
|
MOD_MOKOJOOMHERO="Module - MokoJoomHero"
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
; INGROUP: MokoJoomHero.Module
|
; INGROUP: MokoJoomHero.Module
|
||||||
; REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
|
; REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
|
||||||
; PATH: /src/language/en-US/mod_mokojoomhero.ini
|
; PATH: /src/language/en-US/mod_mokojoomhero.ini
|
||||||
; VERSION: 01.21.00
|
; VERSION: 01.19.01
|
||||||
; BRIEF: Language strings for MokoJoomHero module (en-US, frontend + admin form fields)
|
; BRIEF: Language strings for MokoJoomHero module (en-US, frontend + admin form fields)
|
||||||
|
|
||||||
MOD_MOKOJOOMHERO_NO_CONTENT="Add content to this module to display it over the hero image."
|
MOD_MOKOJOOMHERO_NO_CONTENT="Add content to this module to display it over the hero image."
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
; INGROUP: MokoJoomHero.Module
|
; INGROUP: MokoJoomHero.Module
|
||||||
; REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
|
; REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
|
||||||
; PATH: /src/language/en-US/mod_mokojoomhero.sys.ini
|
; PATH: /src/language/en-US/mod_mokojoomhero.sys.ini
|
||||||
; VERSION: 01.21.00
|
; VERSION: 01.19.01
|
||||||
; BRIEF: System language strings — used in admin Extension Manager and Module Manager (en-US)
|
; BRIEF: System language strings — used in admin Extension Manager and Module Manager (en-US)
|
||||||
|
|
||||||
MOD_MOKOJOOMHERO="Module - MokoJoomHero"
|
MOD_MOKOJOOMHERO="Module - MokoJoomHero"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* INGROUP: MokoJoomHero.Module
|
* INGROUP: MokoJoomHero.Module
|
||||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
|
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
|
||||||
* PATH: /src/media/css/mod_mokojoomhero.css
|
* PATH: /src/media/css/mod_mokojoomhero.css
|
||||||
* VERSION: 01.21.00
|
* VERSION: 01.19.01
|
||||||
* BRIEF: Hero module stylesheet — slideshow, video, colour/gradient, overlay, card, mute toggle, responsive
|
* BRIEF: Hero module stylesheet — slideshow, video, colour/gradient, overlay, card, mute toggle, responsive
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
* INGROUP: MokoJoomHero.Module
|
* INGROUP: MokoJoomHero.Module
|
||||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
|
* REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoJoomHero
|
||||||
* PATH: /src/media/js/mod_mokojoomhero.js
|
* PATH: /src/media/js/mod_mokojoomhero.js
|
||||||
* VERSION: 01.21.00
|
* VERSION: 01.19.01
|
||||||
* BRIEF: Hero module JavaScript — slideshow crossfade, video viewport control, mute toggle
|
* BRIEF: Hero module JavaScript — slideshow crossfade, video viewport control, mute toggle
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
<authorUrl>https://mokoconsulting.tech</authorUrl>
|
||||||
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
|
<copyright>Copyright (C) 2026 Moko Consulting. All rights reserved.</copyright>
|
||||||
<license>GPL-3.0-or-later</license>
|
<license>GPL-3.0-or-later</license>
|
||||||
<version>01.21.00</version>
|
<version>01.19.01-dev</version>
|
||||||
<description>Random hero image slideshow, video backgrounds, solid colour/gradient, parallax, content animations, A/B testing, scheduling, and overlay with card support. Free and open source. By Moko Consulting.</description>
|
<description>Random hero image slideshow, video backgrounds, solid colour/gradient, parallax, content animations, A/B testing, scheduling, and overlay with card support. Free and open source. By Moko Consulting.</description>
|
||||||
|
|
||||||
<scriptfile>script.php</scriptfile>
|
<scriptfile>script.php</scriptfile>
|
||||||
|
|||||||
Reference in New Issue
Block a user