chore(deps)(deps): bump the github-actions group with 6 updates #119

Closed
dependabot[bot] wants to merge 4 commits from dependabot/github_actions/github-actions-b940f8027b into main
16 changed files with 262 additions and 277 deletions

3
.github/CODEOWNERS vendored
View File

@@ -11,7 +11,7 @@
# ── Synced workflows (managed by MokoStandards — do not edit manually) ────
/.github/workflows/deploy-dev.yml @jmiller-moko
/.github/workflows/deploy-demo.yml @jmiller-moko
/.github/workflows/deploy-rs.yml @jmiller-moko
/.github/workflows/deploy-manual.yml @jmiller-moko
/.github/workflows/auto-release.yml @jmiller-moko
/.github/workflows/auto-dev-issue.yml @jmiller-moko
/.github/workflows/auto-assign.yml @jmiller-moko
@@ -27,6 +27,7 @@
/.github/workflows/ci-dolibarr.yml @jmiller-moko
/.github/workflows/publish-to-mokodolimods.yml @jmiller-moko
/.github/workflows/changelog-validation.yml @jmiller-moko
/.github/workflows/branch-freeze.yml @jmiller-moko
# Custom workflows in .github/workflows/ not listed above are repo-owned.
# ── GitHub configuration ─────────────────────────────────────────────────

View File

@@ -6,7 +6,7 @@
# INGROUP: MokoStandards.Workflows.Shared
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /.github/workflows/auto-assign.yml
# VERSION: 03.09.03
# VERSION: 04.06.00
# BRIEF: Auto-assign jmiller-moko to unassigned issues and PRs every 15 minutes
name: Auto-Assign Issues & PRs

View File

@@ -9,7 +9,7 @@
# INGROUP: MokoStandards.Automation
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /templates/workflows/shared/auto-dev-issue.yml.template
# VERSION: 03.09.03
# VERSION: 04.06.00
# BRIEF: Auto-create tracking issue with sub-issues for dev/rc branch workflow
# NOTE: Synced via bulk-repo-sync to .github/workflows/auto-dev-issue.yml in all governed repos.

View File

@@ -7,7 +7,7 @@
# INGROUP: MokoStandards.Release
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /templates/workflows/joomla/auto-release.yml.template
# VERSION: 03.09.03
# VERSION: 04.06.00
# BRIEF: Joomla build & release — ZIP package, updates.xml, SHA-256 checksum
#
# +========================================================================+
@@ -35,13 +35,14 @@
name: Build & Release
on:
push:
pull_request:
types: [closed]
branches:
- main
- master
paths:
- 'src/**'
- 'htdocs/**'
workflow_dispatch:
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
@@ -54,8 +55,7 @@ jobs:
name: Build & Release Pipeline
runs-on: ubuntu-latest
if: >-
!contains(github.event.head_commit.message, '[skip ci]') &&
github.actor != 'github-actions[bot]'
github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
steps:
- name: Checkout repository
@@ -147,7 +147,7 @@ jobs:
echo "" >> $GITHUB_STEP_SUMMARY
# -- Version drift check (must pass before release) --------
README_VER=$(grep -oP 'VERSION:\s*\K[\d.]+' README.md 2>/dev/null | head -1)
README_VER=$(sed -n 's/.*VERSION:[[:space:]]*\([0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\).*/\1/p' README.md 2>/dev/null | head -1)
if [ "$README_VER" != "$VERSION" ]; then
echo "- Version drift: README says \`${README_VER}\` but releasing \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
ERRORS=$((ERRORS+1))
@@ -156,7 +156,7 @@ jobs:
fi
# Check CHANGELOG version matches
CL_VER=$(grep -oP 'VERSION:\s*\K[\d.]+' CHANGELOG.md 2>/dev/null | head -1)
CL_VER=$(sed -n 's/.*VERSION:[[:space:]]*\([0-9][0-9]\.[0-9][0-9]\.[0-9][0-9]\).*/\1/p' CHANGELOG.md 2>/dev/null | head -1)
if [ -n "$CL_VER" ] && [ "$CL_VER" != "$VERSION" ]; then
echo "- CHANGELOG drift: \`${CL_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
ERRORS=$((ERRORS+1))
@@ -164,7 +164,7 @@ jobs:
# Check composer.json version if present
if [ -f "composer.json" ]; then
COMP_VER=$(grep -oP '"version"\s*:\s*"\K[^"]+' composer.json 2>/dev/null | head -1)
COMP_VER=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' composer.json 2>/dev/null | head -1)
if [ -n "$COMP_VER" ] && [ "$COMP_VER" != "$VERSION" ]; then
echo "- composer.json drift: \`${COMP_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
ERRORS=$((ERRORS+1))
@@ -188,7 +188,7 @@ jobs:
# -- Joomla: manifest version drift --------
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
if [ -n "$MANIFEST" ]; then
XML_VER=$(grep -oP '<version>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1)
XML_VER=$(sed -n 's/.*<version>\([^<]*\)<\/version>.*/\1/p' "$MANIFEST" 2>/dev/null | head -1)
if [ -n "$XML_VER" ] && [ "$XML_VER" != "$VERSION" ]; then
echo "- Manifest drift: \`${XML_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
ERRORS=$((ERRORS+1))
@@ -205,7 +205,7 @@ jobs:
echo "- Manifest: \`${MANIFEST}\`" >> $GITHUB_STEP_SUMMARY
# -- Joomla: extension type check --------
TYPE=$(grep -oP '<extension[^>]+type="\K[^"]+' "$MANIFEST" 2>/dev/null)
TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" 2>/dev/null)
echo "- Extension type: ${TYPE:-unknown}" >> $GITHUB_STEP_SUMMARY
fi
@@ -275,17 +275,22 @@ jobs:
exit 0
fi
EXT_NAME=$(grep -oP '<name>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "${{ github.event.repository.name }}")
EXT_TYPE=$(grep -oP '<extension[^>]+type="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "component")
EXT_ELEMENT=$(grep -oP '<element>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "")
EXT_CLIENT=$(grep -oP '<extension[^>]+client="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "")
EXT_FOLDER=$(grep -oP '<extension[^>]+group="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "")
TARGET_PLATFORM=$(grep -oP '<targetplatform[^/]*/>' "$MANIFEST" 2>/dev/null | head -1 || echo "")
PHP_MINIMUM=$(grep -oP '<php_minimum>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "")
# Extract fields using sed (portable — no grep -P)
EXT_NAME=$(sed -n 's/.*<name>\([^<]*\)<\/name>.*/\1/p' "$MANIFEST" | head -1)
EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
EXT_ELEMENT=$(sed -n 's/.*<element>\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" | head -1)
EXT_CLIENT=$(sed -n 's/.*<extension[^>]*client="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
EXT_FOLDER=$(sed -n 's/.*<extension[^>]*group="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
TARGET_PLATFORM=$(sed -n 's/.*\(<targetplatform[^/]*\/>\).*/\1/p' "$MANIFEST" | head -1)
PHP_MINIMUM=$(sed -n 's/.*<php_minimum>\([^<]*\)<\/php_minimum>.*/\1/p' "$MANIFEST" | head -1)
# Derive element from manifest filename if not in XML
# Fallbacks
[ -z "$EXT_NAME" ] && EXT_NAME="${{ github.event.repository.name }}"
[ -z "$EXT_TYPE" ] && EXT_TYPE="component"
# Templates/modules don't have <element> — derive from <name> (lowercased)
if [ -z "$EXT_ELEMENT" ]; then
EXT_ELEMENT=$(basename "$MANIFEST" .xml)
EXT_ELEMENT=$(echo "$EXT_NAME" | tr '[:upper:]' '[:lower:]' | tr -d ' ')
fi
# Build client tag: plugins and frontend modules need <client>site</client>
@@ -341,24 +346,28 @@ jobs:
} > /tmp/stable_entry.xml
# -- Write updates.xml preserving dev/rc entries ──────────────
RC_ENTRY=""
DEV_ENTRY=""
# Extract existing entries for other stability levels
# Order reflects release workflow: development → alpha → beta → rc → stable
if [ -f "updates.xml" ]; then
printf 'import re\n' > /tmp/extract.py
printf 'import re, sys\n' > /tmp/extract.py
printf 'with open("updates.xml") as f: c = f.read()\n' >> /tmp/extract.py
printf 'import sys; tag = sys.argv[1]\n' >> /tmp/extract.py
printf 'tag = sys.argv[1]\n' >> /tmp/extract.py
printf 'm = re.search(r"( <update>.*?<tag>" + re.escape(tag) + r"</tag>.*?</update>)", c, re.DOTALL)\n' >> /tmp/extract.py
printf 'if m: print(m.group(1))\n' >> /tmp/extract.py
RC_ENTRY=$(python3 /tmp/extract.py rc 2>/dev/null || true)
DEV_ENTRY=$(python3 /tmp/extract.py development 2>/dev/null || true)
fi
DEV_ENTRY=$(python3 /tmp/extract.py development 2>/dev/null || true)
ALPHA_ENTRY=$(python3 /tmp/extract.py alpha 2>/dev/null || true)
BETA_ENTRY=$(python3 /tmp/extract.py beta 2>/dev/null || true)
RC_ENTRY=$(python3 /tmp/extract.py rc 2>/dev/null || true)
{
printf '%s\n' '<?xml version="1.0" encoding="utf-8"?>'
printf '%s\n' '<updates>'
cat /tmp/stable_entry.xml
[ -n "$RC_ENTRY" ] && echo "$RC_ENTRY"
[ -n "$DEV_ENTRY" ] && echo "$DEV_ENTRY"
[ -n "$ALPHA_ENTRY" ] && echo "$ALPHA_ENTRY"
[ -n "$BETA_ENTRY" ] && echo "$BETA_ENTRY"
[ -n "$RC_ENTRY" ] && echo "$RC_ENTRY"
cat /tmp/stable_entry.xml
printf '%s\n' '</updates>'
} > updates.xml
@@ -468,55 +477,64 @@ jobs:
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1 || true)
[ -z "$MANIFEST" ] && exit 0
EXT_ELEMENT=$(grep -oP '<element>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || basename "$MANIFEST" .xml)
PACKAGE_NAME="${EXT_ELEMENT}-${VERSION}.zip"
EXT_ELEMENT=$(sed -n 's/.*<element>\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" 2>/dev/null | head -1 || basename "$MANIFEST" .xml)
ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip"
TAR_NAME="${EXT_ELEMENT}-${VERSION}.tar.gz"
# -- Build install-ready ZIP from src/ ----------------------------
# -- Build install packages from src/ ----------------------------
SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
[ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ — skipping package"; exit 0; }
EXCLUDES=".ftpignore sftp-config* *.ppk *.pem *.key .env*"
# ZIP package
cd "$SOURCE_DIR"
zip -r "/tmp/${PACKAGE_NAME}" . -x '.ftpignore'
zip -r "/tmp/${ZIP_NAME}" . -x $EXCLUDES
cd ..
FILESIZE=$(stat -c%s "/tmp/${PACKAGE_NAME}" 2>/dev/null || stat -f%z "/tmp/${PACKAGE_NAME}" 2>/dev/null || echo "unknown")
# tar.gz package
tar -czf "/tmp/${TAR_NAME}" -C "$SOURCE_DIR" \
--exclude='.ftpignore' --exclude='sftp-config*' \
--exclude='*.ppk' --exclude='*.pem' --exclude='*.key' --exclude='.env*' .
# -- Calculate SHA-256 -------------------------------------------
SHA256=$(sha256sum "/tmp/${PACKAGE_NAME}" | cut -d' ' -f1)
ZIP_SIZE=$(stat -c%s "/tmp/${ZIP_NAME}" 2>/dev/null || stat -f%z "/tmp/${ZIP_NAME}" 2>/dev/null || echo "unknown")
TAR_SIZE=$(stat -c%s "/tmp/${TAR_NAME}" 2>/dev/null || stat -f%z "/tmp/${TAR_NAME}" 2>/dev/null || echo "unknown")
# -- Upload ZIP to the minor release tag -------------------------
gh release upload "$RELEASE_TAG" "/tmp/${PACKAGE_NAME}" --clobber 2>/dev/null || {
echo "Could not upload with --clobber, retrying..."
gh release upload "$RELEASE_TAG" "/tmp/${PACKAGE_NAME}" 2>/dev/null || true
}
# -- Calculate SHA-256 for both ----------------------------------
SHA256_ZIP=$(sha256sum "/tmp/${ZIP_NAME}" | cut -d' ' -f1)
SHA256_TAR=$(sha256sum "/tmp/${TAR_NAME}" | cut -d' ' -f1)
# -- Update updates.xml with SHA-256 for latest patch -------------
# -- Upload both to release tag ----------------------------------
gh release upload "$RELEASE_TAG" "/tmp/${ZIP_NAME}" --clobber 2>/dev/null || true
gh release upload "$RELEASE_TAG" "/tmp/${TAR_NAME}" --clobber 2>/dev/null || true
# -- Update updates.xml with both download formats ---------------
if [ -f "updates.xml" ]; then
ZIP_URL="https://github.com/${REPO}/releases/download/${RELEASE_TAG}/${ZIP_NAME}"
TAR_URL="https://github.com/${REPO}/releases/download/${RELEASE_TAG}/${TAR_NAME}"
# Replace downloads block with both formats + SHA
sed -i "s|<downloads>.*</downloads>|<downloads>\n <downloadurl type=\"full\" format=\"zip\">${ZIP_URL}</downloadurl>\n <downloadurl type=\"full\" format=\"tar.gz\">${TAR_URL}</downloadurl>\n </downloads>|" updates.xml 2>/dev/null || true
if grep -q '<sha256>' updates.xml; then
sed -i "s|<sha256>.*</sha256>|<sha256>sha256:${SHA256}</sha256>|" updates.xml
sed -i "s|<sha256>.*</sha256>|<sha256>sha256:${SHA256_ZIP}</sha256>|" updates.xml
else
sed -i "s|</downloads>|</downloads>\n <sha256>sha256:${SHA256}</sha256>|" updates.xml
sed -i "s|</downloads>|</downloads>\n <sha256>sha256:${SHA256_ZIP}</sha256>|" updates.xml
fi
# Also update the download URL to point to this patch's ZIP
DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}"
sed -i "s|<downloadurl[^>]*>[^<]*</downloadurl>|<downloadurl type=\"full\" format=\"zip\">${DOWNLOAD_URL}</downloadurl>|" updates.xml
git add updates.xml
git commit -m "chore(release): SHA-256 + download URL for ${VERSION} [skip ci]" \
git commit -m "chore(release): ZIP + tar.gz for ${VERSION} [skip ci]" \
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>" || true
git push || true
fi
echo "### Joomla Package" >> $GITHUB_STEP_SUMMARY
echo "### Joomla Packages" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Package | \`${PACKAGE_NAME}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Size | ${FILESIZE} bytes |" >> $GITHUB_STEP_SUMMARY
echo "| SHA-256 | \`${SHA256}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Release | \`${RELEASE_TAG}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Package | Size | SHA-256 |" >> $GITHUB_STEP_SUMMARY
echo "|---------|------|---------|" >> $GITHUB_STEP_SUMMARY
echo "| \`${ZIP_NAME}\` | ${ZIP_SIZE} | \`${SHA256_ZIP}\` |" >> $GITHUB_STEP_SUMMARY
echo "| \`${TAR_NAME}\` | ${TAR_SIZE} | \`${SHA256_TAR}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Release | \`${RELEASE_TAG}\` | |" >> $GITHUB_STEP_SUMMARY
echo "| Download | [${PACKAGE_NAME}](https://github.com/${REPO}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}) |" >> $GITHUB_STEP_SUMMARY
# -- Summary --------------------------------------------------------------

114
.github/workflows/branch-freeze.yml vendored Normal file
View File

@@ -0,0 +1,114 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: GitHub.Workflow
# INGROUP: MokoStandards.Automation
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /templates/workflows/shared/branch-freeze.yml.template
# VERSION: 04.06.00
# BRIEF: Freeze or unfreeze any branch via ruleset — manual workflow_dispatch
name: Branch Freeze
on:
workflow_dispatch:
inputs:
branch:
description: 'Branch to freeze/unfreeze (e.g., version/04, dev/feature)'
required: true
type: string
action:
description: 'Action to perform'
required: true
type: choice
options:
- freeze
- unfreeze
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
permissions:
contents: read
jobs:
manage-freeze:
name: "${{ inputs.action }} branch: ${{ inputs.branch }}"
runs-on: ubuntu-latest
steps:
- name: Check permissions
env:
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
run: |
ACTOR="${{ github.actor }}"
REPO="${{ github.repository }}"
PERMISSION=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" \
--jq '.permission' 2>/dev/null || echo "read")
if [ "$PERMISSION" != "admin" ]; then
echo "Denied: only admins can freeze/unfreeze branches (${ACTOR} has ${PERMISSION})"
exit 1
fi
- name: "${{ inputs.action }} branch"
env:
GH_TOKEN: ${{ secrets.GH_TOKEN || github.token }}
run: |
BRANCH="${{ inputs.branch }}"
ACTION="${{ inputs.action }}"
REPO="${{ github.repository }}"
RULESET_NAME="FROZEN: ${BRANCH}"
echo "## Branch Freeze" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ "$ACTION" = "freeze" ]; then
# Check if ruleset already exists
EXISTING=$(gh api "repos/${REPO}/rulesets" \
--jq ".[] | select(.name == \"${RULESET_NAME}\") | .id" 2>/dev/null || true)
if [ -n "$EXISTING" ]; then
echo "Branch \`${BRANCH}\` is already frozen (ruleset #${EXISTING})" >> $GITHUB_STEP_SUMMARY
exit 0
fi
# Create freeze ruleset — blocks all updates except admin bypass
printf '{"name":"%s","target":"branch","enforcement":"active",' "${RULESET_NAME}" > /tmp/ruleset.json
printf '"bypass_actors":[{"actor_id":5,"actor_type":"RepositoryRole","bypass_mode":"always"}],' >> /tmp/ruleset.json
printf '"conditions":{"ref_name":{"include":["refs/heads/%s"],"exclude":[]}},' "${BRANCH}" >> /tmp/ruleset.json
printf '"rules":[{"type":"update"},{"type":"deletion"},{"type":"non_fast_forward"}]}' >> /tmp/ruleset.json
RESULT=$(gh api "repos/${REPO}/rulesets" -X POST --input /tmp/ruleset.json --jq '.id' 2>&1) || true
if echo "$RESULT" | grep -qE '^[0-9]+$'; then
echo "Frozen \`${BRANCH}\` — ruleset #${RESULT}" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Branch | \`${BRANCH}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Ruleset | #${RESULT} |" >> $GITHUB_STEP_SUMMARY
echo "| Rules | No updates, no deletion, no force push |" >> $GITHUB_STEP_SUMMARY
echo "| Bypass | Repository admins only |" >> $GITHUB_STEP_SUMMARY
else
echo "Failed to freeze: ${RESULT}" >> $GITHUB_STEP_SUMMARY
exit 1
fi
elif [ "$ACTION" = "unfreeze" ]; then
# Find and delete the freeze ruleset
RULESET_ID=$(gh api "repos/${REPO}/rulesets" \
--jq ".[] | select(.name == \"${RULESET_NAME}\") | .id" 2>/dev/null || true)
if [ -z "$RULESET_ID" ]; then
echo "Branch \`${BRANCH}\` is not frozen (no ruleset found)" >> $GITHUB_STEP_SUMMARY
exit 0
fi
gh api "repos/${REPO}/rulesets/${RULESET_ID}" -X DELETE --silent 2>/dev/null
echo "Unfrozen \`${BRANCH}\` — ruleset #${RULESET_ID} deleted" >> $GITHUB_STEP_SUMMARY
fi
rm -f /tmp/ruleset.json

View File

@@ -9,19 +9,17 @@
# INGROUP: MokoStandards.CI
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /templates/workflows/shared/changelog-validation.yml.template
# VERSION: 03.09.03
# VERSION: 04.06.00
# BRIEF: Validates CHANGELOG.md format and version consistency
# NOTE: Deployed to .github/workflows/changelog-validation.yml in governed repos.
name: Changelog Validation
on:
push:
branches:
- main
pull_request:
branches:
- main
- 'dev/**'
workflow_dispatch:
permissions:

View File

@@ -9,24 +9,17 @@
# INGROUP: MokoStandards.CI
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /templates/workflows/joomla/ci-joomla.yml.template
# VERSION: 03.09.03
# VERSION: 04.06.00
# BRIEF: CI workflow for Joomla extensions — lint, validate, test
# NOTE: Deployed to .github/workflows/ci-joomla.yml in governed Joomla extension repos.
name: Joomla Extension CI
on:
push:
branches:
- main
- dev/**
- rc/**
- version/**
pull_request:
branches:
- main
- dev/**
- rc/**
- 'dev/**'
workflow_dispatch:
permissions:

View File

@@ -21,14 +21,7 @@ on:
push:
branches:
- main
- dev/**
- rc/**
- version/**
pull_request:
branches:
- main
- dev/**
- rc/**
- version/*
schedule:
# Weekly on Monday at 06:00 UTC
- cron: '0 6 * * 1'

View File

@@ -7,7 +7,7 @@
# INGROUP: MokoStandards.Deploy
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /templates/workflows/joomla/deploy-manual.yml.template
# VERSION: 03.09.03
# VERSION: 04.06.00
# BRIEF: Manual SFTP deploy to dev server for Joomla repos
# NOTE: Joomla repos use update.xml for distribution. This is for manual
# dev server testing only — triggered via workflow_dispatch.

View File

@@ -22,7 +22,7 @@
# INGROUP: MokoStandards.Firewall
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /templates/workflows/shared/enterprise-firewall-setup.yml.template
# VERSION: 03.09.03
# VERSION: 04.06.00
# BRIEF: Enterprise firewall configuration — generates outbound allow-rules including SFTP deployment server
# NOTE: Reads DEV_FTP_HOST / DEV_FTP_PORT variables to include SFTP egress rules alongside HTTPS rules.
@@ -90,7 +90,7 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Set up Python
uses: actions/setup-python@v6

View File

@@ -10,7 +10,7 @@
# INGROUP: MokoStandards.Validation
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /.github/workflows/repo_health.yml
# VERSION: 03.09.03
# VERSION: 04.06.00
# BRIEF: Enforces repository guardrails by validating release configuration, scripts governance, tooling availability, and core repository health artifacts.
# NOTE: Field is user-managed.
# ============================================================================

View File

@@ -9,7 +9,7 @@
# INGROUP: MokoStandards.Maintenance
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /templates/workflows/shared/repository-cleanup.yml.template
# VERSION: 03.09.03
# VERSION: 04.06.00
# BRIEF: Recurring repository maintenance — labels, branches, workflows, logs, doc indexes
# NOTE: Synced via bulk-repo-sync to .github/workflows/repository-cleanup.yml in all governed repos.
# Runs on the 1st and 15th of each month at 6:00 AM UTC, and on manual dispatch.
@@ -154,6 +154,10 @@ jobs:
".github/workflows/auto-version-branch.yml"
".github/workflows/publish-to-mokodolibarr.yml"
".github/workflows/ci.yml"
".github/workflows/deploy-rs.yml"
"sftp-config.json"
"sftp-config.json.template"
"scripts/sftp-config"
)
DELETED=0

View File

@@ -5,7 +5,7 @@
# INGROUP: MokoStandards.Compliance
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /.github/workflows/standards-compliance.yml
# VERSION: 03.09.03
# VERSION: 04.06.00
# BRIEF: MokoStandards compliance validation workflow
# NOTE: Validates repository structure, documentation, and coding standards

View File

@@ -9,7 +9,7 @@
# INGROUP: MokoStandards.Automation
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /templates/workflows/shared/sync-version-on-merge.yml.template
# VERSION: 03.09.03
# VERSION: 04.06.00
# BRIEF: Auto-bump patch version on every push to main and propagate to all file headers
# NOTE: Synced via bulk-repo-sync to .github/workflows/sync-version-on-merge.yml in all governed repos.
# README.md is the single source of truth for the repository version.
@@ -17,10 +17,10 @@
name: Sync Version from README
on:
push:
pull_request:
types: [closed]
branches:
- main
- master
workflow_dispatch:
inputs:
dry_run:
@@ -39,6 +39,8 @@ jobs:
sync-version:
name: Propagate README version
runs-on: ubuntu-latest
if: >-
github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
steps:
- name: Checkout repository
@@ -65,7 +67,7 @@ jobs:
composer install --no-dev --no-interaction --quiet
- name: Auto-bump patch version
if: ${{ github.event_name == 'push' && github.actor != 'github-actions[bot]' }}
if: ${{ github.event_name != 'workflow_dispatch' && github.actor != 'github-actions[bot]' }}
run: |
if git diff --name-only HEAD~1 HEAD 2>/dev/null | grep -q '^README\.md$'; then
echo "README.md changed in this push — skipping auto-bump"

View File

@@ -7,7 +7,7 @@
# INGROUP: MokoStandards.Joomla
# REPO: https://github.com/mokoconsulting-tech/MokoStandards
# PATH: /templates/workflows/joomla/update-server.yml.template
# VERSION: 03.09.03
# VERSION: 04.06.00
# BRIEF: Update Joomla update server XML feed with stable/rc/dev entries
#
# Writes updates.xml with multiple <update> entries:
@@ -20,7 +20,8 @@
name: Update Joomla Update Server XML Feed
on:
push:
pull_request:
types: [closed]
branches:
- 'dev/**'
- 'alpha/**'
@@ -53,6 +54,8 @@ jobs:
update-xml:
name: Update updates.xml
runs-on: ubuntu-latest
if: >-
github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
steps:
- name: Checkout repository
@@ -108,22 +111,35 @@ jobs:
STABILITY="stable"
fi
# Parse manifest
# Parse manifest (portable — no grep -P)
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
if [ -z "$MANIFEST" ]; then
echo "No Joomla manifest found — skipping"
exit 0
fi
EXT_NAME=$(grep -oP '<name>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "${{ github.event.repository.name }}")
EXT_TYPE=$(grep -oP '<extension[^>]+type="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "component")
EXT_ELEMENT=$(grep -oP '<element>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || basename "$MANIFEST" .xml)
EXT_CLIENT=$(grep -oP '<extension[^>]+client="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "")
EXT_FOLDER=$(grep -oP '<extension[^>]+group="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "")
TARGET_PLATFORM=$(grep -oP '<targetplatform[^/]*/>' "$MANIFEST" 2>/dev/null | head -1 || echo "")
PHP_MINIMUM=$(grep -oP '<php_minimum>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "")
# Extract fields using sed (works on all runners)
EXT_NAME=$(sed -n 's/.*<name>\([^<]*\)<\/name>.*/\1/p' "$MANIFEST" | head -1)
EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
EXT_ELEMENT=$(sed -n 's/.*<element>\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" | head -1)
EXT_CLIENT=$(sed -n 's/.*<extension[^>]*client="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
EXT_FOLDER=$(sed -n 's/.*<extension[^>]*group="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
EXT_VERSION=$(sed -n 's/.*<version>\([^<]*\)<\/version>.*/\1/p' "$MANIFEST" | head -1)
TARGET_PLATFORM=$(sed -n 's/.*\(<targetplatform[^/]*\/>\).*/\1/p' "$MANIFEST" | head -1)
PHP_MINIMUM=$(sed -n 's/.*<php_minimum>\([^<]*\)<\/php_minimum>.*/\1/p' "$MANIFEST" | head -1)
# Fallbacks
[ -z "$EXT_NAME" ] && EXT_NAME="${{ github.event.repository.name }}"
[ -z "$EXT_TYPE" ] && EXT_TYPE="component"
# Templates and modules don't have <element> — derive from <name>
if [ -z "$EXT_ELEMENT" ]; then
EXT_ELEMENT=$(echo "$EXT_NAME" | tr '[:upper:]' '[:lower:]' | tr -d ' ')
fi
# Use manifest version if README version is empty
[ "$VERSION" = "0.0.0" ] && [ -n "$EXT_VERSION" ] && VERSION="$EXT_VERSION"
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(basename "$MANIFEST" .xml)
[ -z "$TARGET_PLATFORM" ] && TARGET_PLATFORM=$(printf '<targetplatform name="joomla" version="5.*" %s>' "/")
CLIENT_TAG=""
@@ -160,24 +176,31 @@ jobs:
DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}"
INFO_URL="https://github.com/${REPO}"
# ── Build install-ready ZIP ─────────────────────────────────
# ── Build install packages (ZIP + tar.gz) ───────────────────
SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
if [ -d "$SOURCE_DIR" ]; then
EXCLUDES=".ftpignore sftp-config* *.ppk *.pem *.key .env*"
TAR_NAME="${EXT_ELEMENT}-${DISPLAY_VERSION}.tar.gz"
cd "$SOURCE_DIR"
zip -r "/tmp/${PACKAGE_NAME}" . -x '.ftpignore'
zip -r "/tmp/${PACKAGE_NAME}" . -x $EXCLUDES
cd ..
tar -czf "/tmp/${TAR_NAME}" -C "$SOURCE_DIR" \
--exclude='.ftpignore' --exclude='sftp-config*' \
--exclude='*.ppk' --exclude='*.pem' --exclude='*.key' --exclude='.env*' .
SHA256=$(sha256sum "/tmp/${PACKAGE_NAME}" | cut -d' ' -f1)
# Ensure draft release exists for this major
# Ensure release exists
gh release view "$RELEASE_TAG" --json tagName > /dev/null 2>&1 || \
gh release create "$RELEASE_TAG" --title "${RELEASE_TAG} (${DISPLAY_VERSION})" --notes "${STABILITY} release" --prerelease --target main 2>/dev/null || true
# Upload ZIP to the major release
# Upload both formats
gh release upload "$RELEASE_TAG" "/tmp/${PACKAGE_NAME}" --clobber 2>/dev/null || true
gh release upload "$RELEASE_TAG" "/tmp/${TAR_NAME}" --clobber 2>/dev/null || true
echo "Package: ${PACKAGE_NAME} (SHA: ${SHA256})" >> $GITHUB_STEP_SUMMARY
echo "Packages: ${PACKAGE_NAME} + ${TAR_NAME} (SHA: ${SHA256})" >> $GITHUB_STEP_SUMMARY
else
SHA256=""
fi
@@ -197,7 +220,9 @@ jobs:
NEW_ENTRY="${NEW_ENTRY} </tags>\n"
NEW_ENTRY="${NEW_ENTRY} <infourl title=\"${EXT_NAME}\">${INFO_URL}</infourl>\n"
NEW_ENTRY="${NEW_ENTRY} <downloads>\n"
TAR_URL="https://github.com/${REPO}/releases/download/${RELEASE_TAG}/${EXT_ELEMENT}-${DISPLAY_VERSION}.tar.gz"
NEW_ENTRY="${NEW_ENTRY} <downloadurl type=\"full\" format=\"zip\">${DOWNLOAD_URL}</downloadurl>\n"
NEW_ENTRY="${NEW_ENTRY} <downloadurl type=\"full\" format=\"tar.gz\">${TAR_URL}</downloadurl>\n"
NEW_ENTRY="${NEW_ENTRY} </downloads>\n"
[ -n "$SHA256" ] && NEW_ENTRY="${NEW_ENTRY} <sha256>sha256:${SHA256}</sha256>\n"
NEW_ENTRY="${NEW_ENTRY} ${TARGET_PLATFORM}\n"

View File

@@ -1,184 +1,21 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
This file is part of a Moko Consulting project.
SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION
DEFGROUP: Joomla.Template.Update
INGROUP: MokoCassiopeia
REPO: https://github.com/mokoconsulting-tech/MokoCassiopeia
PATH: ./updates.xml
VERSION: 03.09.05
BRIEF: Update manifest XML — stable, rc, and development channels
-->
<?xml version="1.0" encoding="utf-8"?>
<updates>
<!-- ══════════════════════════════════════════════════════════════
STABLE — production releases from main branch
══════════════════════════════════════════════════════════════ -->
<update>
<name>MokoCassiopeia</name>
<description>Moko Consulting's site template based on Cassiopeia.</description>
<element>mokocassiopeia</element>
<type>template</type>
<client>site</client>
<version>03.09.07</version>
<creationDate>2026-04-07</creationDate>
<author>Jonathan Miller || Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<copyright>(C)GNU General Public License Version 3 - 2026 Moko Consulting</copyright>
<infourl title='MokoCassiopeia'>https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/tag/v03</infourl>
<downloads>
<downloadurl type='full' format='zip'>https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/v03/mokocassiopeia-03.09.07.zip</downloadurl>
</downloads>
<sha256>16a7ac98dd6e26144618a4ba534ea8fae0d115ec8373712743ab6daff0960916</sha256>
<name></name>
<description> update</description>
<element></element>
<type>component</type>
<version></version>
<tags>
<tag>stable</tag>
</tags>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name='joomla' version='(5|6)\..*'/>
</update>
<!-- ══════════════════════════════════════════════════════════════
RC — release candidates for testing and validation
══════════════════════════════════════════════════════════════ -->
<update>
<name>MokoCassiopeia</name>
<description>MokoCassiopeia release candidate — testing only.</description>
<element>mokocassiopeia</element>
<type>template</type>
<client>site</client>
<version>03.09.07</version>
<creationDate>2026-04-07</creationDate>
<author>Jonathan Miller || Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<copyright>(C)GNU General Public License Version 3 - 2026 Moko Consulting</copyright>
<infourl title='MokoCassiopeia RC'>https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/tag/release-candidate</infourl>
<infourl title="">https://github.com/mokoconsulting-tech/MokoCassiopeia</infourl>
<downloads>
<downloadurl type='full' format='zip'>https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/release-candidate/mokocassiopeia-rc.zip</downloadurl>
<downloadurl type="full" format="zip">https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/v/-.zip</downloadurl>
<downloadurl type="full" format="tar.gz">https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/v/-.tar.gz</downloadurl>
</downloads>
<sha256>31e660078e728e8c9177b5a2d75efd89fea1fd4e9320d77444ab8fe28d3b354d</sha256>
<tags>
<tag>rc</tag>
</tags>
<targetplatform name="joomla" version="5.*" />
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name='joomla' version='(5|6)\..*'/>
</update>
<!-- ══════════════════════════════════════════════════════════════
DEVELOPMENT — latest dev branch builds, not for production
══════════════════════════════════════════════════════════════ -->
<update>
<name>MokoCassiopeia</name>
<description>MokoCassiopeia development build — unstable.</description>
<element>mokocassiopeia</element>
<type>template</type>
<client>site</client>
<version>03.09.08</version>
<creationDate>2026-04-07</creationDate>
<author>Jonathan Miller || Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<copyright>(C)GNU General Public License Version 3 - 2026 Moko Consulting</copyright>
<infourl title='MokoCassiopeia Dev'>https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/tag/development</infourl>
<downloads>
<downloadurl type='full' format='zip'>https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/development/mokocassiopeia-03.09.08-dev.zip</downloadurl>
</downloads>
<sha256>ecff187531e65a40ae958ae91fff74da0c8856d1cc13e17a6e3d6905806b189e</sha256>
<tags>
<tag>development</tag>
</tags>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name='joomla' version='(5|6)\..*'/>
</update>
<!-- ══════════════════════════════════════════════════════════════
ALPHA — early testing, expect breaking changes
══════════════════════════════════════════════════════════════ -->
<update>
<name>MokoCassiopeia</name>
<description>MokoCassiopeia alpha build — early testing.</description>
<element>mokocassiopeia</element>
<type>template</type>
<client>site</client>
<version>03.09.07</version>
<creationDate>2026-04-07</creationDate>
<author>Jonathan Miller || Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<copyright>(C)GNU General Public License Version 3 - 2026 Moko Consulting</copyright>
<infourl title='MokoCassiopeia Alpha'>https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/tag/alpha</infourl>
<downloads>
<downloadurl type='full' format='zip'>https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/alpha/mokocassiopeia-03.09.07-alpha.zip</downloadurl>
</downloads>
<sha256>1a32180f8b26749bf5daf0602262e33464bcb3a042a8ff51ec2844cdeef2f9e5</sha256>
<tags>
<tag>alpha</tag>
</tags>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name='joomla' version='(5|6)\..*'/>
</update>
<!-- ══════════════════════════════════════════════════════════════
BETA — feature complete, testing for stability
══════════════════════════════════════════════════════════════ -->
<update>
<name>MokoCassiopeia</name>
<description>MokoCassiopeia beta build — feature complete, stability testing.</description>
<element>mokocassiopeia</element>
<type>template</type>
<client>site</client>
<version>03.09.07</version>
<creationDate>2026-04-07</creationDate>
<author>Jonathan Miller || Moko Consulting</author>
<authorEmail>hello@mokoconsulting.tech</authorEmail>
<copyright>(C)GNU General Public License Version 3 - 2026 Moko Consulting</copyright>
<infourl title='MokoCassiopeia Beta'>https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/tag/beta</infourl>
<downloads>
<downloadurl type='full' format='zip'>https://github.com/mokoconsulting-tech/MokoCassiopeia/releases/download/beta/mokocassiopeia-03.09.07-beta.zip</downloadurl>
</downloads>
<sha256>1a32180f8b26749bf5daf0602262e33464bcb3a042a8ff51ec2844cdeef2f9e5</sha256>
<tags>
<tag>beta</tag>
</tags>
<maintainer>Moko Consulting</maintainer>
<maintainerurl>https://mokoconsulting.tech</maintainerurl>
<targetplatform name='joomla' version='(5|6)\..*'/>
</update>
</updates>