Compare commits

..

3 Commits

Author SHA1 Message Date
gitea-actions[bot] 5461bccd49 chore(version): pre-release bump to 01.00.01-dev [skip ci] 2026-06-23 17:44:39 +00:00
Jonathan Miller 167ae2dc06 chore: align version format to MokoStandards XX.YY.ZZ (01.00.00)
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Secret Scan (pull_request) Successful in 6s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 12s
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 32s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 12s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
mokocli tools require two-digit zero-padded version groups (01.00.00)
per MokoStandards convention. Fixes auto-release pipeline failure.

Changed in: pkg manifest, component manifest, both module manifests,
and CHANGELOG.md.

Authored-by: Moko Consulting
2026-06-23 12:42:06 -05:00
Jonathan Miller 20250db9e0 chore: sync workflows from Template-Joomla (metadata first-class fields)
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 25s
Joomla: Extension CI / Release Readiness Check (pull_request) Failing after 6s
Joomla: Extension CI / Lint & Validate (pull_request) Failing after 16s
Universal: PR Check / Branch Policy (pull_request) Successful in 3s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Universal: PR Check / Secret Scan (pull_request) Successful in 8s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Joomla: Metadata Validation / Validate Joomla Metadata (pull_request) Successful in 58s
Joomla: Extension CI / Tests (PHP 8.2) (pull_request) Has been cancelled
Joomla: Extension CI / Tests (PHP 8.3) (pull_request) Has been cancelled
Joomla: Extension CI / PHPStan Analysis (pull_request) Has been cancelled
Joomla: Extension CI / Build RC Pre-Release (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Pull latest workflow versions from MokoConsulting/Template-Joomla.
These use the updated mokocli tools with manifest metadata as
first-class fields instead of README FILE INFORMATION blocks.

Updated: ci-joomla.yml, pre-release.yml, workflow-sync-trigger.yml
Added: version-set.yml
Removed: deploy-manual.yml, update-server.yml, composer-publish.yml
  (superseded by auto-release.yml pipeline)

Authored-by: Moko Consulting
2026-06-23 12:33:57 -05:00
14 changed files with 894 additions and 617 deletions
+466 -467
View File
@@ -1,467 +1,466 @@
# 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: mokocli.Release
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli # REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli
# 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, synchronize, closed] types: [opened, closed]
branches: branches:
- main - main
paths-ignore: paths-ignore:
- '.mokogitea/workflows/**' - '.mokogitea/workflows/**'
- '*.md' - '*.md'
- 'wiki/**' - 'wiki/**'
- '.editorconfig' - '.editorconfig'
- '.gitignore' - '.gitignore'
- '.gitattributes' - '.gitattributes'
- '.gitmessage' - '.gitmessage'
- 'LICENSE' - 'LICENSE'
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.action == 'synchronize' && 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 mokocli 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/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then echo Using pre-installed /opt/mokocli
echo Using pre-installed /opt/mokocli echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV
echo MOKO_CLI=/opt/mokocli/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/mokocli 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}/mokocli.git git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/mokocli
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/mokocli cd /tmp/mokocli
cd /tmp/mokocli 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/mokocli/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: Update RC release notes from CHANGELOG.md run: |
run: | API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
# Extract [Unreleased] section from changelog
# Extract [Unreleased] section from changelog NOTES=""
NOTES="" if [ -f "CHANGELOG.md" ]; then
if [ -f "CHANGELOG.md" ]; then NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md) fi
fi [ -z "$NOTES" ] && NOTES="Release candidate"
[ -z "$NOTES" ] && NOTES="Release candidate"
# Find the RC release and update its body
# Find the RC release and update its body RELEASE_ID=$(curl -sf -H "Authorization: token ${TOKEN}" \
RELEASE_ID=$(curl -sf -H "Authorization: token ${TOKEN}" \ "${API_BASE}/releases/tags/release-candidate" \
"${API_BASE}/releases/tags/release-candidate" \ | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
| python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
if [ -n "$RELEASE_ID" ]; then
if [ -n "$RELEASE_ID" ]; then python3 -c "
python3 -c " import json, urllib.request
import json, urllib.request body = open('/dev/stdin').read()
body = open('/dev/stdin').read() payload = json.dumps({'body': body}).encode()
payload = json.dumps({'body': body}).encode() req = urllib.request.Request(
req = urllib.request.Request( '${API_BASE}/releases/${RELEASE_ID}',
'${API_BASE}/releases/${RELEASE_ID}', data=payload, method='PATCH',
data=payload, method='PATCH', headers={
headers={ 'Authorization': 'token ${TOKEN}',
'Authorization': 'token ${TOKEN}', 'Content-Type': 'application/json'
'Content-Type': 'application/json' })
}) urllib.request.urlopen(req)
urllib.request.urlopen(req) " <<< "$NOTES"
" <<< "$NOTES" echo "RC release notes updated from CHANGELOG.md"
echo "RC release notes updated from CHANGELOG.md" fi
fi
- name: Summary
- name: Summary if: always()
if: always() run: |
run: | echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY
echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY echo "Branch renamed to rc, minor bump, RC release built" >> $GITHUB_STEP_SUMMARY
echo "Branch renamed to rc, minor bump, RC release built" >> $GITHUB_STEP_SUMMARY
# ── Merged PR → Build & Release (or promote RC to stable) ─────────────────────────
# ── Merged PR → Build & Release (or promote RC to stable) ───────────────────────── release:
release: name: Build & Release Pipeline
name: Build & Release Pipeline runs-on: release
runs-on: release if: >-
if: >- github.event.pull_request.merged == true ||
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: 0
fetch-depth: 0
- name: Configure git for bot pushes
- name: Configure git for bot pushes run: |
run: | 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: Check for merge conflict markers
- name: Check for merge conflict markers run: |
run: | 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)
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) if [ -n "$CONFLICTS" ]; then
if [ -n "$CONFLICTS" ]; then echo "::error::Merge conflict markers found — aborting release"
echo "::error::Merge conflict markers found — aborting release" echo "## Release Blocked: Conflict Markers" >> $GITHUB_STEP_SUMMARY
echo "## Release Blocked: Conflict Markers" >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY
echo "$CONFLICTS" >> $GITHUB_STEP_SUMMARY echo '```' >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY exit 1
exit 1 fi
fi echo "No conflict markers found"
echo "No conflict markers found"
- name: Setup mokocli tools
- name: Setup mokocli 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 COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}'
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}' run: |
run: | if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then echo Using pre-installed /opt/mokocli
echo Using pre-installed /opt/mokocli echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV
echo MOKO_CLI=/opt/mokocli/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/mokocli 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}/mokocli.git git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/mokocli
git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/mokocli cd /tmp/mokocli
cd /tmp/mokocli 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/mokocli/cli >> $GITHUB_ENV fi
fi
- name: "Detect platform"
- name: "Detect platform" id: platform
id: platform run: |
run: | php ${MOKO_CLI}/platform_detect.php --path . --github-output 2>/dev/null || true
php ${MOKO_CLI}/platform_detect.php --path . --github-output 2>/dev/null || true php ${MOKO_CLI}/manifest_read.php --path . --github-output 2>/dev/null || true
php ${MOKO_CLI}/manifest_read.php --path . --github-output 2>/dev/null || true
- name: "Determine version bump level"
- name: "Determine version bump level" id: bump
id: bump run: |
run: | # Fix/patch branches: version was already bumped by pre-release, just strip suffix
# Fix/patch branches: version was already bumped by pre-release, just strip suffix # Feature/dev branches: bump minor for the new stable release
# Feature/dev branches: bump minor for the new stable release HEAD_REF="${{ github.event.pull_request.head.ref || 'dev' }}"
HEAD_REF="${{ github.event.pull_request.head.ref || 'dev' }}" case "$HEAD_REF" in
case "$HEAD_REF" in fix/*|patch/*|hotfix/*|bugfix/*) BUMP="none" ;;
fix/*|patch/*|hotfix/*|bugfix/*) BUMP="none" ;; *) BUMP="minor" ;;
*) BUMP="minor" ;; esac
esac echo "level=${BUMP}" >> "$GITHUB_OUTPUT"
echo "level=${BUMP}" >> "$GITHUB_OUTPUT" echo "Bump level: ${BUMP} (from branch: ${HEAD_REF})"
echo "Bump level: ${BUMP} (from branch: ${HEAD_REF})"
- name: "Publish stable release"
- name: "Publish stable release" run: |
run: | BUMP_FLAG=""
BUMP_FLAG="" if [ "${{ steps.bump.outputs.level }}" != "none" ]; then
if [ "${{ steps.bump.outputs.level }}" != "none" ]; then BUMP_FLAG="--bump ${{ steps.bump.outputs.level }}"
BUMP_FLAG="--bump ${{ steps.bump.outputs.level }}" fi
fi php ${MOKO_CLI}/release_publish.php \
php ${MOKO_CLI}/release_publish.php \ --path . --stability stable ${BUMP_FLAG} --branch main \
--path . --stability stable ${BUMP_FLAG} --branch main \ --token "${{ secrets.MOKOGITEA_TOKEN }}"
--token "${{ secrets.MOKOGITEA_TOKEN }}"
- name: "Read published version"
- name: "Read published version" id: version
id: version run: |
run: | VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "")
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "") VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//') [ -z "$VERSION" ] && VERSION="00.00.00" && echo "skip=true" >> "$GITHUB_OUTPUT"
[ -z "$VERSION" ] && VERSION="00.00.00" && echo "skip=true" >> "$GITHUB_OUTPUT" echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT" PLATFORM="${{ steps.platform.outputs.platform }}"
PLATFORM="${{ steps.platform.outputs.platform }}" if [[ "$PLATFORM" == joomla* ]]; then
if [[ "$PLATFORM" == joomla* ]]; then echo "tag=stable" >> "$GITHUB_OUTPUT"
echo "tag=stable" >> "$GITHUB_OUTPUT" echo "release_tag=stable" >> "$GITHUB_OUTPUT"
echo "release_tag=stable" >> "$GITHUB_OUTPUT" else
else echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT"
echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT" echo "release_tag=v${VERSION}" >> "$GITHUB_OUTPUT"
echo "release_tag=v${VERSION}" >> "$GITHUB_OUTPUT" fi
fi echo "branch=main" >> "$GITHUB_OUTPUT"
echo "branch=main" >> "$GITHUB_OUTPUT" echo "Published version: ${VERSION}"
echo "Published version: ${VERSION}"
- name: "Create semver tag for non-Joomla repos"
- name: "Create semver tag for non-Joomla repos" id: semver
id: semver if: |
if: | steps.version.outputs.skip != 'true' &&
steps.version.outputs.skip != 'true' && !startsWith(steps.platform.outputs.platform, 'joomla')
!startsWith(steps.platform.outputs.platform, 'joomla') run: |
run: | VERSION="${{ steps.version.outputs.version }}"
VERSION="${{ steps.version.outputs.version }}" API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" SEMVER_TAG="v${VERSION}"
SEMVER_TAG="v${VERSION}"
echo "Creating semver tag: ${SEMVER_TAG}"
echo "Creating semver tag: ${SEMVER_TAG}"
# Create the git tag via API
# Create the git tag via API HTTP_CODE=$(curl -sf -o /dev/null -w "%{http_code}" \
HTTP_CODE=$(curl -sf -o /dev/null -w "%{http_code}" \ -X POST -H "Authorization: token ${TOKEN}" \
-X POST -H "Authorization: token ${TOKEN}" \ -H "Content-Type: application/json" \
-H "Content-Type: application/json" \ "${API_BASE}/tags" \
"${API_BASE}/tags" \ -d "{\"tag_name\":\"${SEMVER_TAG}\",\"target\":\"main\",\"message\":\"Release ${VERSION}\"}" 2>/dev/null || echo "000")
-d "{\"tag_name\":\"${SEMVER_TAG}\",\"target\":\"main\",\"message\":\"Release ${VERSION}\"}" 2>/dev/null || echo "000")
if [ "$HTTP_CODE" = "201" ] || [ "$HTTP_CODE" = "200" ]; then
if [ "$HTTP_CODE" = "201" ] || [ "$HTTP_CODE" = "200" ]; then echo "Created semver tag: ${SEMVER_TAG}"
echo "Created semver tag: ${SEMVER_TAG}" elif [ "$HTTP_CODE" = "409" ]; then
elif [ "$HTTP_CODE" = "409" ]; then echo "Semver tag ${SEMVER_TAG} already exists (skipped)"
echo "Semver tag ${SEMVER_TAG} already exists (skipped)" else
else echo "::warning::Failed to create semver tag ${SEMVER_TAG} (HTTP ${HTTP_CODE})"
echo "::warning::Failed to create semver tag ${SEMVER_TAG} (HTTP ${HTTP_CODE})" fi
fi
echo "semver_tag=${SEMVER_TAG}" >> "$GITHUB_OUTPUT"
echo "semver_tag=${SEMVER_TAG}" >> "$GITHUB_OUTPUT"
- name: Update release notes and promote changelog
- name: Update release notes and promote changelog run: |
run: | API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
# Get the stable release info (version and ID)
# Get the stable release info (version and ID) RELEASE_JSON=$(curl -sf -H "Authorization: token ${TOKEN}" \
RELEASE_JSON=$(curl -sf -H "Authorization: token ${TOKEN}" \ "${API_BASE}/releases/tags/stable" 2>/dev/null || echo '{}')
"${API_BASE}/releases/tags/stable" 2>/dev/null || echo '{}') RELEASE_ID=$(python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" <<< "$RELEASE_JSON" 2>/dev/null || true)
RELEASE_ID=$(python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" <<< "$RELEASE_JSON" 2>/dev/null || true) # Extract version from release name (e.g. "06.17.00" or "v06.17.00")
# Extract version from release name (e.g. "06.17.00" or "v06.17.00") VERSION=$(python3 -c "
VERSION=$(python3 -c " import json, sys, re
import json, sys, re r = json.load(sys.stdin)
r = json.load(sys.stdin) name = r.get('name', '')
name = r.get('name', '') m = re.search(r'(\d+\.\d+\.\d+)', name)
m = re.search(r'(\d+\.\d+\.\d+)', name) print(m.group(1) if m else '')
print(m.group(1) if m else '') " <<< "$RELEASE_JSON" 2>/dev/null || true)
" <<< "$RELEASE_JSON" 2>/dev/null || true)
# Extract [Unreleased] section from changelog
# Extract [Unreleased] section from changelog NOTES=""
NOTES="" if [ -f "CHANGELOG.md" ]; then
if [ -f "CHANGELOG.md" ]; then NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md) fi
fi [ -z "$NOTES" ] && NOTES="Stable release"
[ -z "$NOTES" ] && NOTES="Stable release"
# Update release body via API
# Update release body via API if [ -n "$RELEASE_ID" ]; then
if [ -n "$RELEASE_ID" ]; then python3 -c "
python3 -c " import json, urllib.request
import json, urllib.request body = open('/dev/stdin').read()
body = open('/dev/stdin').read() payload = json.dumps({'body': body}).encode()
payload = json.dumps({'body': body}).encode() req = urllib.request.Request(
req = urllib.request.Request( '${API_BASE}/releases/${RELEASE_ID}',
'${API_BASE}/releases/${RELEASE_ID}', data=payload, method='PATCH',
data=payload, method='PATCH', headers={
headers={ 'Authorization': 'token ${TOKEN}',
'Authorization': 'token ${TOKEN}', 'Content-Type': 'application/json'
'Content-Type': 'application/json' })
}) urllib.request.urlopen(req)
urllib.request.urlopen(req) " <<< "$NOTES"
" <<< "$NOTES" echo "Release notes updated from CHANGELOG.md"
echo "Release notes updated from CHANGELOG.md" fi
fi
# Promote [Unreleased] → [version] in CHANGELOG.md and reset
# Promote [Unreleased] → [version] in CHANGELOG.md and reset if [ -n "$VERSION" ] && [ -f "CHANGELOG.md" ]; then
if [ -n "$VERSION" ] && [ -f "CHANGELOG.md" ]; then DATE=$(date +%Y-%m-%d)
DATE=$(date +%Y-%m-%d) python3 -c "
python3 -c " import sys
import sys version, date = sys.argv[1], sys.argv[2]
version, date = sys.argv[1], sys.argv[2] content = open('CHANGELOG.md').read()
content = open('CHANGELOG.md').read() old = '## [Unreleased]'
old = '## [Unreleased]' new = f'## [Unreleased]\n\n## [{version}] --- {date}'
new = f'## [Unreleased]\n\n## [{version}] --- {date}' content = content.replace(old, new, 1)
content = content.replace(old, new, 1) open('CHANGELOG.md', 'w').write(content)
open('CHANGELOG.md', 'w').write(content) " "$VERSION" "$DATE"
" "$VERSION" "$DATE" git add CHANGELOG.md
git add CHANGELOG.md git commit -m "chore: promote changelog [Unreleased] → [${VERSION}]" || true
git commit -m "chore: promote changelog [Unreleased] → [${VERSION}]" || true git push origin main || true
git push origin main || true echo "Changelog promoted: [Unreleased] → [${VERSION}]"
echo "Changelog promoted: [Unreleased] → [${VERSION}]" fi
fi
# -- STEP 9: Mirror to GitHub (stable only) --------------------------------
# -- STEP 9: Mirror to GitHub (stable only) -------------------------------- - name: "Step 9: Mirror release to GitHub"
- name: "Step 9: Mirror release to GitHub" if: >-
if: >- steps.version.outputs.skip != 'true' &&
steps.version.outputs.skip != 'true' && secrets.GH_MIRROR_TOKEN != ''
secrets.GH_MIRROR_TOKEN != '' continue-on-error: true
continue-on-error: true run: |
run: | VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
RELEASE_TAG="${{ steps.version.outputs.release_tag }}" GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" php ${MOKO_CLI}/release_mirror.php \
php ${MOKO_CLI}/release_mirror.php \ --version "$VERSION" --tag "$RELEASE_TAG" \
--version "$VERSION" --tag "$RELEASE_TAG" \ --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ --gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \
--gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \ --branch main 2>&1 || true
--branch main 2>&1 || true echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY
echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY
# -- STEP 10: Sync main branch to GitHub mirror ----------------------------
# -- STEP 10: Sync main branch to GitHub mirror ---------------------------- - name: "Step 10: Push main to GitHub mirror"
- name: "Step 10: Push main to GitHub mirror" if: >-
if: >- steps.version.outputs.skip != 'true' &&
steps.version.outputs.skip != 'true' && secrets.GH_MIRROR_TOKEN != ''
secrets.GH_MIRROR_TOKEN != '' continue-on-error: true
continue-on-error: true run: |
run: | GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1)
GH_ORG=$(echo "$GH_REPO" | cut -d/ -f1) GH_NAME=$(echo "$GH_REPO" | cut -d/ -f2)
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 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 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 fetch origin main --depth=1 git push github origin/main:refs/heads/main --force 2>/dev/null \
git push github origin/main:refs/heads/main --force 2>/dev/null \ && echo "main branch pushed to GitHub mirror" \
&& echo "main branch pushed to GitHub mirror" \ || echo "WARNING: GitHub mirror push failed"
|| echo "WARNING: GitHub mirror push failed"
- name: "Step 11: Delete rc branch and recreate dev from main"
- name: "Step 11: Delete rc branch and recreate dev from main" if: steps.version.outputs.skip != 'true'
if: steps.version.outputs.skip != 'true' continue-on-error: true
continue-on-error: true run: |
run: | API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
# Delete rc branch (ephemeral — created by promote-rc)
# Delete rc branch (ephemeral — created by promote-rc) curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ "${API_BASE}/branches/rc" 2>/dev/null \
"${API_BASE}/branches/rc" 2>/dev/null \ && echo "Deleted rc branch" || echo "rc branch not found"
&& echo "Deleted rc branch" || echo "rc branch not found"
# Delete dev branch
# Delete dev branch curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ "${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch"
"${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch"
# Recreate dev from main (now includes version bump + changelog promotion)
# Recreate dev from main (now includes version bump + changelog promotion) curl -sf -X POST -H "Authorization: token ${TOKEN}" \
curl -sf -X POST -H "Authorization: token ${TOKEN}" \ -H "Content-Type: application/json" \
-H "Content-Type: application/json" \ "${API_BASE}/branches" \
"${API_BASE}/branches" \ -d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main"
-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
echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY
- name: "Step 12: Create version branch from main"
- name: "Step 12: Create version branch from main" if: steps.version.outputs.skip != 'true'
if: steps.version.outputs.skip != 'true' continue-on-error: true
continue-on-error: true run: |
run: | API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" BRANCH_NAME="version/${VERSION}"
BRANCH_NAME="version/${VERSION}" MAIN_SHA=$(git rev-parse HEAD)
MAIN_SHA=$(git rev-parse HEAD)
# Delete old version branch if it exists (same version re-release)
# 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}"
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
# 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"
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
echo "Version branch created: ${BRANCH_NAME} (${MAIN_SHA})" >> $GITHUB_STEP_SUMMARY
# -- Dolibarr post-release: Reset dev version -----------------------------
# -- Dolibarr post-release: Reset dev version ----------------------------- - name: "Post-release: Reset dev version"
- name: "Post-release: Reset dev version" if: steps.version.outputs.skip != 'true'
if: steps.version.outputs.skip != 'true' continue-on-error: true
continue-on-error: true run: |
run: | API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" php ${MOKO_CLI}/version_reset_dev.php \
php ${MOKO_CLI}/version_reset_dev.php \ --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \ --branch dev --path . 2>&1 || true
--branch dev --path . 2>&1 || true
# -- Summary --------------------------------------------------------------
# -- Summary -------------------------------------------------------------- - name: Pipeline Summary
- name: Pipeline Summary if: always()
if: always() run: |
run: | VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" PLATFORM="${{ steps.platform.outputs.platform }}"
PLATFORM="${{ steps.platform.outputs.platform }}" if [ "${{ steps.version.outputs.skip }}" = "true" ]; then
if [ "${{ steps.version.outputs.skip }}" = "true" ]; then echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY
echo "## Release Skipped" >> $GITHUB_STEP_SUMMARY echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY
echo "No VERSION in README.md" >> $GITHUB_STEP_SUMMARY elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then
elif [ "${{ steps.check.outputs.already_released }}" = "true" ]; then echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY
echo "## Already Released — ${VERSION}" >> $GITHUB_STEP_SUMMARY else
else echo "" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY
echo "## Build & Release Complete (${PLATFORM})" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY
echo "| Step | Result |" >> $GITHUB_STEP_SUMMARY echo "|------|--------|" >> $GITHUB_STEP_SUMMARY
echo "|------|--------|" >> $GITHUB_STEP_SUMMARY echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Platform | \`${PLATFORM}\` |" >> $GITHUB_STEP_SUMMARY echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $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
echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY fi
fi
+331
View File
@@ -164,6 +164,75 @@ jobs:
echo "**Manifest validation passed.**" >> $GITHUB_STEP_SUMMARY echo "**Manifest validation passed.**" >> $GITHUB_STEP_SUMMARY
fi fi
- name: Update server & packaging checks
continue-on-error: true
run: |
echo "### Update Server & Packaging" >> $GITHUB_STEP_SUMMARY
WARNINGS=0
# Find the extension manifest
MANIFEST=""
for XML_FILE in $(find . -maxdepth 2 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do
if grep -q "<extension" "$XML_FILE" 2>/dev/null; then
MANIFEST="$XML_FILE"
break
fi
done
if [ -z "$MANIFEST" ]; then
echo "No manifest found — skipping." >> $GITHUB_STEP_SUMMARY
else
EXT_TYPE=$(grep -oP '<extension[^>]*\btype="\K[^"]+' "$MANIFEST" | head -1)
# 1. Check <updateservers> exists and uses MokoGitea update server
if ! grep -q '<updateservers>' "$MANIFEST" 2>/dev/null; then
echo "::warning file=${MANIFEST}::Missing \`<updateservers>\` tag — extension will not receive OTA updates"
echo "- **Missing** \`<updateservers>\` — extension will not receive OTA updates" >> $GITHUB_STEP_SUMMARY
WARNINGS=$((WARNINGS + 1))
else
SERVER_URL=$(grep -oP '<server[^>]*>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1)
if [ -z "$SERVER_URL" ]; then
echo "::warning file=${MANIFEST}::\`<updateservers>\` is empty — no server URL defined"
echo "- **Empty** \`<updateservers>\` — no server URL defined" >> $GITHUB_STEP_SUMMARY
WARNINGS=$((WARNINGS + 1))
elif ! echo "$SERVER_URL" | grep -q 'git\.mokoconsulting\.tech'; then
echo "::warning file=${MANIFEST}::Update server does not use MokoGitea engine: ${SERVER_URL}"
echo "- **Non-MokoGitea update server:** \`${SERVER_URL}\`" >> $GITHUB_STEP_SUMMARY
echo " Expected: \`https://git.mokoconsulting.tech/{org}/{repo}/updates.xml\`" >> $GITHUB_STEP_SUMMARY
WARNINGS=$((WARNINGS + 1))
else
echo "- \`<updateservers>\`: MokoGitea engine ✓" >> $GITHUB_STEP_SUMMARY
fi
fi
# 2. Check <dlid> tag exists
if ! grep -q '<dlid' "$MANIFEST" 2>/dev/null; then
echo "::warning file=${MANIFEST}::Missing \`<dlid>\` tag — download ID authentication is not configured"
echo "- **Missing** \`<dlid>\` — download ID authentication not configured" >> $GITHUB_STEP_SUMMARY
WARNINGS=$((WARNINGS + 1))
else
echo "- \`<dlid>\`: present ✓" >> $GITHUB_STEP_SUMMARY
fi
# 3. For packages: check <childuninstall> tag
if [ "$EXT_TYPE" = "package" ]; then
if ! grep -q '<childuninstall>' "$MANIFEST" 2>/dev/null; then
echo "::warning file=${MANIFEST}::Package is missing \`<childuninstall>\` — child extensions will not be removed on uninstall"
echo "- **Missing** \`<childuninstall>\` — child extensions will remain when package is uninstalled" >> $GITHUB_STEP_SUMMARY
WARNINGS=$((WARNINGS + 1))
else
echo "- \`<childuninstall>\`: present ✓" >> $GITHUB_STEP_SUMMARY
fi
fi
fi
echo "" >> $GITHUB_STEP_SUMMARY
if [ "$WARNINGS" -gt 0 ]; then
echo "**${WARNINGS} packaging warning(s).** These won't block CI but should be addressed." >> $GITHUB_STEP_SUMMARY
else
echo "**Update server & packaging checks passed.**" >> $GITHUB_STEP_SUMMARY
fi
- name: Check language files referenced in manifest - name: Check language files referenced in manifest
run: | run: |
echo "### Language File Check" >> $GITHUB_STEP_SUMMARY echo "### Language File Check" >> $GITHUB_STEP_SUMMARY
@@ -647,6 +716,268 @@ jobs:
echo "**Service provider check passed.**" >> $GITHUB_STEP_SUMMARY echo "**Service provider check passed.**" >> $GITHUB_STEP_SUMMARY
fi fi
- name: Script file reference check
run: |
echo "### Script File Reference" >> $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")
SCRIPT_FILE=$(grep -oP '<scriptfile>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1)
if [ -z "$SCRIPT_FILE" ]; then
echo "No \`<scriptfile>\` referenced — skipping." >> $GITHUB_STEP_SUMMARY
elif [ ! -f "${MANIFEST_DIR}/${SCRIPT_FILE}" ]; then
echo "::error file=${MANIFEST}::Manifest references \`<scriptfile>${SCRIPT_FILE}</scriptfile>\` but file does not exist"
echo "- **Missing** \`${SCRIPT_FILE}\` — referenced in \`<scriptfile>\` but not found" >> $GITHUB_STEP_SUMMARY
ERRORS=$((ERRORS + 1))
else
echo "- \`${SCRIPT_FILE}\`: present ✓" >> $GITHUB_STEP_SUMMARY
fi
fi
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${ERRORS}" -gt 0 ]; then
echo "**${ERRORS} script file issue(s).**" >> $GITHUB_STEP_SUMMARY
exit 1
else
echo "**Script file reference check passed.**" >> $GITHUB_STEP_SUMMARY
fi
- name: Media folder validation
run: |
echo "### Media Folder Validation" >> $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 <media> tag and its folder/filename children
MEDIA_DEST=$(grep -oP '<media[^>]*\bdestination="\K[^"]+' "$MANIFEST" 2>/dev/null | head -1)
MEDIA_FOLDER=$(grep -oP '<media[^>]*\bfolder="\K[^"]+' "$MANIFEST" 2>/dev/null | head -1)
if [ -z "$MEDIA_DEST" ] && [ -z "$MEDIA_FOLDER" ]; then
echo "No \`<media>\` tag found — skipping." >> $GITHUB_STEP_SUMMARY
else
if [ -n "$MEDIA_FOLDER" ] && [ ! -d "${MANIFEST_DIR}/${MEDIA_FOLDER}" ]; then
echo "::error file=${MANIFEST}::\`<media folder=\"${MEDIA_FOLDER}\">\` references missing directory"
echo "- **Missing** media folder \`${MEDIA_FOLDER}\`" >> $GITHUB_STEP_SUMMARY
ERRORS=$((ERRORS + 1))
else
echo "- Media folder \`${MEDIA_FOLDER:-(inline)}\`: present ✓" >> $GITHUB_STEP_SUMMARY
# Check child references inside <media> block
if [ -n "$MEDIA_FOLDER" ]; then
MEDIA_FOLDERS=$(sed -n '/<media /,/<\/media>/p' "$MANIFEST" | grep -oP '<folder>\K[^<]+' 2>/dev/null || true)
for F in $MEDIA_FOLDERS; do
if [ ! -d "${MANIFEST_DIR}/${MEDIA_FOLDER}/${F}" ]; then
echo "- **Missing** media subfolder \`${MEDIA_FOLDER}/${F}\`" >> $GITHUB_STEP_SUMMARY
ERRORS=$((ERRORS + 1))
fi
done
MEDIA_FILES=$(sed -n '/<media /,/<\/media>/p' "$MANIFEST" | grep -oP '<filename>\K[^<]+' 2>/dev/null || true)
for F in $MEDIA_FILES; do
if [ ! -f "${MANIFEST_DIR}/${MEDIA_FOLDER}/${F}" ]; then
echo "- **Missing** media file \`${MEDIA_FOLDER}/${F}\`" >> $GITHUB_STEP_SUMMARY
ERRORS=$((ERRORS + 1))
fi
done
fi
fi
fi
fi
echo "" >> $GITHUB_STEP_SUMMARY
if [ "${ERRORS}" -gt 0 ]; then
echo "**${ERRORS} media reference issue(s).**" >> $GITHUB_STEP_SUMMARY
exit 1
else
echo "**Media folder validation passed.**" >> $GITHUB_STEP_SUMMARY
fi
- name: Target platform check
continue-on-error: true
run: |
echo "### Target Platform Check" >> $GITHUB_STEP_SUMMARY
WARNINGS=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
# Check updates.xml for targetplatform if it exists
if [ -f "updates.xml" ]; then
if ! grep -q '<targetplatform' "updates.xml" 2>/dev/null; then
echo "::warning file=updates.xml::No \`<targetplatform>\` found — Joomla updater cannot filter by compatible version"
echo "- **Missing** \`<targetplatform>\` in updates.xml" >> $GITHUB_STEP_SUMMARY
WARNINGS=$((WARNINGS + 1))
else
echo "- \`<targetplatform>\` in updates.xml: present ✓" >> $GITHUB_STEP_SUMMARY
fi
fi
# Check manifest for minimum PHP/Joomla version hints
if ! grep -qP '<php_minimum>|targetplatform|joomla.*version' "$MANIFEST" 2>/dev/null; then
echo "::warning file=${MANIFEST}::No minimum Joomla or PHP version constraint found in manifest"
echo "- **Missing** version constraints (\`<php_minimum>\` or \`<targetplatform>\`)" >> $GITHUB_STEP_SUMMARY
WARNINGS=$((WARNINGS + 1))
else
echo "- Version constraints in manifest: present ✓" >> $GITHUB_STEP_SUMMARY
fi
fi
echo "" >> $GITHUB_STEP_SUMMARY
if [ "$WARNINGS" -gt 0 ]; then
echo "**${WARNINGS} target platform warning(s).**" >> $GITHUB_STEP_SUMMARY
else
echo "**Target platform check passed.**" >> $GITHUB_STEP_SUMMARY
fi
- name: Changelog URL check
continue-on-error: true
run: |
echo "### Changelog URL Check" >> $GITHUB_STEP_SUMMARY
WARNINGS=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
if ! grep -q '<changelogurl>' "$MANIFEST" 2>/dev/null; then
echo "::warning file=${MANIFEST}::Missing \`<changelogurl>\` — Joomla updater will not display changelogs"
echo "- **Missing** \`<changelogurl>\` — Joomla 4+ shows changelogs in the update manager when this is set" >> $GITHUB_STEP_SUMMARY
WARNINGS=$((WARNINGS + 1))
else
CHANGELOG_URL=$(grep -oP '<changelogurl>\K[^<]+' "$MANIFEST" | head -1)
echo "- \`<changelogurl>\`: \`${CHANGELOG_URL}\` ✓" >> $GITHUB_STEP_SUMMARY
fi
fi
echo "" >> $GITHUB_STEP_SUMMARY
if [ "$WARNINGS" -gt 0 ]; then
echo "**${WARNINGS} changelog URL warning(s).**" >> $GITHUB_STEP_SUMMARY
else
echo "**Changelog URL check passed.**" >> $GITHUB_STEP_SUMMARY
fi
- name: Duplicate file references check
continue-on-error: true
run: |
echo "### Duplicate File References" >> $GITHUB_STEP_SUMMARY
WARNINGS=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
# Extract all <filename> and <folder> references
ALL_REFS=$(grep -oP '<(filename|folder)[^>]*>\K[^<]+' "$MANIFEST" 2>/dev/null | sort || true)
if [ -z "$ALL_REFS" ]; then
echo "No file/folder references found — skipping." >> $GITHUB_STEP_SUMMARY
else
DUPES=$(echo "$ALL_REFS" | uniq -d)
if [ -n "$DUPES" ]; then
while IFS= read -r DUP; do
COUNT=$(echo "$ALL_REFS" | grep -cx "$DUP")
echo "::warning file=${MANIFEST}::Duplicate reference: \`${DUP}\` appears ${COUNT} times (may be valid if in different sections)"
echo "- **Duplicate:** \`${DUP}\` (${COUNT}x) — check if cross-section" >> $GITHUB_STEP_SUMMARY
WARNINGS=$((WARNINGS + 1))
done <<< "$DUPES"
else
TOTAL=$(echo "$ALL_REFS" | wc -l)
echo "All ${TOTAL} file/folder references are unique." >> $GITHUB_STEP_SUMMARY
fi
fi
fi
echo "" >> $GITHUB_STEP_SUMMARY
if [ "$WARNINGS" -gt 0 ]; then
echo "**${WARNINGS} duplicate reference(s) found.** Review for cross-section validity." >> $GITHUB_STEP_SUMMARY
else
echo "**Duplicate file references check passed.**" >> $GITHUB_STEP_SUMMARY
fi
- name: Empty language keys check
continue-on-error: true
run: |
echo "### Empty Language Keys" >> $GITHUB_STEP_SUMMARY
WARNINGS=0
LANG_FILES=$(find . -name "*.ini" -not -path "./.git/*" -not -path "./vendor/*" 2>/dev/null)
if [ -z "$LANG_FILES" ]; then
echo "No .ini language files found — skipping." >> $GITHUB_STEP_SUMMARY
else
TOTAL_FILES=0
for FILE in $LANG_FILES; do
TOTAL_FILES=$((TOTAL_FILES + 1))
# Find lines with KEY= but no value (empty or whitespace-only after =)
EMPTY_KEYS=$(grep -nP '^[A-Z_]+=\s*$' "$FILE" 2>/dev/null || true)
if [ -n "$EMPTY_KEYS" ]; then
COUNT=$(echo "$EMPTY_KEYS" | wc -l)
echo "::warning file=${FILE}::${COUNT} empty language key(s)"
echo "- \`${FILE}\`: ${COUNT} empty key(s)" >> $GITHUB_STEP_SUMMARY
while IFS= read -r LINE; do
LINE_NUM=$(echo "$LINE" | cut -d: -f1)
KEY=$(echo "$LINE" | cut -d: -f2 | cut -d= -f1)
echo " - Line ${LINE_NUM}: \`${KEY}\`" >> $GITHUB_STEP_SUMMARY
done <<< "$EMPTY_KEYS"
WARNINGS=$((WARNINGS + COUNT))
fi
done
if [ "$WARNINGS" -eq 0 ]; then
echo "All ${TOTAL_FILES} language file(s) have populated keys." >> $GITHUB_STEP_SUMMARY
fi
fi
echo "" >> $GITHUB_STEP_SUMMARY
if [ "$WARNINGS" -gt 0 ]; then
echo "**${WARNINGS} empty language key(s) across ${TOTAL_FILES} file(s).**" >> $GITHUB_STEP_SUMMARY
else
echo "**Empty language keys 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
-126
View File
@@ -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
+1 -1
View File
@@ -5,7 +5,7 @@
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Automation # INGROUP: mokocli.Automation
# VERSION: 01.00.00 # VERSION: 01.00.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"
-16
View File
@@ -88,20 +88,8 @@ jobs:
php ${MOKO_CLI}/platform_detect.php --path . --github-output 2>/dev/null || true php ${MOKO_CLI}/platform_detect.php --path . --github-output 2>/dev/null || true
php ${MOKO_CLI}/manifest_read.php --path . --github-output php ${MOKO_CLI}/manifest_read.php --path . --github-output
- name: Check platform eligibility (Joomla only)
id: eligibility
run: |
PLATFORM="${{ steps.platform.outputs.platform }}"
if [[ "$PLATFORM" == joomla* ]] || [[ "$PLATFORM" == "joomla" ]]; then
echo "proceed=true" >> "$GITHUB_OUTPUT"
else
echo "proceed=false" >> "$GITHUB_OUTPUT"
echo "::notice::Platform '$PLATFORM' — non-Joomla, skipping pre-release auto-bump"
fi
- name: Resolve metadata and bump version - name: Resolve metadata and bump version
id: meta id: meta
if: steps.eligibility.outputs.proceed == 'true'
run: | run: |
# Auto-detect stability from branch name on push, or use input on dispatch # Auto-detect stability from branch name on push, or use input on dispatch
if [ "${{ github.event_name }}" = "push" ]; then if [ "${{ github.event_name }}" = "push" ]; then
@@ -178,7 +166,6 @@ jobs:
- name: Create release - name: Create release
id: release id: release
if: steps.eligibility.outputs.proceed == 'true'
run: | run: |
TAG="${{ steps.meta.outputs.tag }}" TAG="${{ steps.meta.outputs.tag }}"
VERSION="${{ steps.meta.outputs.version }}" VERSION="${{ steps.meta.outputs.version }}"
@@ -189,7 +176,6 @@ jobs:
--repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease --repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease
- name: Update release notes from CHANGELOG.md - name: Update release notes from CHANGELOG.md
if: steps.eligibility.outputs.proceed == 'true'
run: | run: |
TAG="${{ steps.meta.outputs.tag }}" TAG="${{ steps.meta.outputs.tag }}"
VERSION="${{ steps.meta.outputs.version }}" VERSION="${{ steps.meta.outputs.version }}"
@@ -226,7 +212,6 @@ jobs:
- name: Build package and upload - name: Build package and upload
id: package id: package
if: steps.eligibility.outputs.proceed == 'true'
run: | run: |
VERSION="${{ steps.meta.outputs.version }}" VERSION="${{ steps.meta.outputs.version }}"
TAG="${{ steps.meta.outputs.tag }}" TAG="${{ steps.meta.outputs.tag }}"
@@ -240,7 +225,6 @@ jobs:
# No need to build, commit, or sync updates.xml from workflows # No need to build, commit, or sync updates.xml from workflows
- name: "Delete lesser pre-release channels (cascade)" - name: "Delete lesser pre-release channels (cascade)"
if: steps.eligibility.outputs.proceed == 'true'
continue-on-error: true continue-on-error: true
run: | run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
+82
View File
@@ -0,0 +1,82 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Security
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards
# PATH: /.gitea/workflows/security-audit.yml
# VERSION: 01.00.00
# BRIEF: Dependency vulnerability scanning for composer and npm packages
name: "Universal: Security Audit"
on:
schedule:
- cron: '0 6 * * 1' # Weekly on Monday at 06:00 UTC
pull_request:
branches:
- main
paths:
- 'composer.json'
- 'composer.lock'
- 'package.json'
- 'package-lock.json'
workflow_dispatch:
permissions:
contents: read
env:
NTFY_URL: ${{ vars.NTFY_URL || 'https://ntfy.mokoconsulting.tech' }}
NTFY_TOPIC: ${{ vars.NTFY_TOPIC || 'gitea-security' }}
jobs:
audit:
name: Dependency Audit
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Composer audit
if: hashFiles('composer.lock') != ''
run: |
echo "=== Composer Security Audit ==="
if ! command -v composer &> /dev/null; then
sudo apt-get update -qq
sudo apt-get install -y -qq php-cli composer >/dev/null 2>&1
fi
composer audit --format=plain 2>&1 | tee /tmp/composer-audit.txt
RESULT=$?
if [ $RESULT -ne 0 ]; then
echo "::warning::Composer vulnerabilities found"
echo "composer_vulnerable=true" >> "$GITHUB_ENV"
else
echo "No known vulnerabilities in composer dependencies"
fi
- name: NPM audit
if: hashFiles('package-lock.json') != ''
run: |
echo "=== NPM Security Audit ==="
npm audit --production 2>&1 | tee /tmp/npm-audit.txt || true
if npm audit --production 2>&1 | grep -q "found 0 vulnerabilities"; then
echo "No known vulnerabilities in npm dependencies"
else
echo "::warning::NPM vulnerabilities found"
echo "npm_vulnerable=true" >> "$GITHUB_ENV"
fi
- name: Notify on vulnerabilities
if: env.composer_vulnerable == 'true' || env.npm_vulnerable == 'true'
run: |
REPO="${{ github.event.repository.name }}"
curl -sS \
-H "Title: ${REPO} has vulnerable dependencies" \
-H "Tags: lock,warning" \
-H "Priority: high" \
-d "Security audit found vulnerabilities. Review dependency updates." \
"${NTFY_URL}/${NTFY_TOPIC}" || true
@@ -45,6 +45,13 @@ jobs:
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT" echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
echo "Platform: ${PLATFORM:-all}" echo "Platform: ${PLATFORM:-all}"
- 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: Clone mokocli - name: Clone mokocli
env: env:
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
+1 -1
View File
@@ -20,7 +20,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- CSV import view accessible from admin toolbar and submenu - CSV import view accessible from admin toolbar and submenu
- Language strings for directions, geocoding feedback, and import UI - Language strings for directions, geocoding feedback, and import UI
## [1.0.0] - 2026-06-23 ## [01.00.00] - 2026-06-23
### Added ### Added
- Admin `LocationController` (FormController) for single-record save/cancel/apply - Admin `LocationController` (FormController) for single-record save/cancel/apply
+1 -1
View File
@@ -14,7 +14,7 @@
DEFGROUP: DEFGROUP:
INGROUP: Project.Documentation INGROUP: Project.Documentation
REPO: REPO:
VERSION: 04.04.01 VERSION: 01.00.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
View File
@@ -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: 04.04.01 VERSION: 01.00.01
BRIEF: Security vulnerability reporting and handling policy BRIEF: Security vulnerability reporting and handling policy
--> -->
@@ -15,7 +15,7 @@
--> -->
<extension type="component" method="upgrade"> <extension type="component" method="upgrade">
<name>com_mokosuitestorelocator</name> <name>com_mokosuitestorelocator</name>
<version>1.0.0</version> <version>01.00.01</version>
<creationDate>2026-06-23</creationDate> <creationDate>2026-06-23</creationDate>
<author>Moko Consulting</author> <author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail> <authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -14,7 +14,7 @@
--> -->
<extension type="module" client="site" method="upgrade"> <extension type="module" client="site" method="upgrade">
<name>mod_mokosuitestorelocator_map</name> <name>mod_mokosuitestorelocator_map</name>
<version>1.0.0</version> <version>01.00.01</version>
<creationDate>2026-06-23</creationDate> <creationDate>2026-06-23</creationDate>
<author>Moko Consulting</author> <author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail> <authorEmail>hello@mokoconsulting.tech</authorEmail>
@@ -14,7 +14,7 @@
--> -->
<extension type="module" client="site" method="upgrade"> <extension type="module" client="site" method="upgrade">
<name>mod_mokosuitestorelocator_search</name> <name>mod_mokosuitestorelocator_search</name>
<version>1.0.0</version> <version>01.00.01</version>
<creationDate>2026-06-23</creationDate> <creationDate>2026-06-23</creationDate>
<author>Moko Consulting</author> <author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail> <authorEmail>hello@mokoconsulting.tech</authorEmail>
+1 -1
View File
@@ -18,7 +18,7 @@
<extension type="package" method="upgrade"> <extension type="package" method="upgrade">
<name>pkg_mokosuitestorelocator</name> <name>pkg_mokosuitestorelocator</name>
<packagename>mokosuitestorelocator</packagename> <packagename>mokosuitestorelocator</packagename>
<version>1.0.0</version> <version>01.00.01</version>
<creationDate>2026-06-23</creationDate> <creationDate>2026-06-23</creationDate>
<author>Moko Consulting</author> <author>Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail> <authorEmail>hello@mokoconsulting.tech</authorEmail>