chore(deps)(deps): bump the github-actions group with 6 updates #119
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -27,6 +27,7 @@
|
|||||||
/.github/workflows/ci-dolibarr.yml @jmiller-moko
|
/.github/workflows/ci-dolibarr.yml @jmiller-moko
|
||||||
/.github/workflows/publish-to-mokodolimods.yml @jmiller-moko
|
/.github/workflows/publish-to-mokodolimods.yml @jmiller-moko
|
||||||
/.github/workflows/changelog-validation.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.
|
# Custom workflows in .github/workflows/ not listed above are repo-owned.
|
||||||
|
|
||||||
# ── GitHub configuration ─────────────────────────────────────────────────
|
# ── GitHub configuration ─────────────────────────────────────────────────
|
||||||
|
|||||||
124
.github/workflows/auto-release.yml
vendored
124
.github/workflows/auto-release.yml
vendored
@@ -35,13 +35,14 @@
|
|||||||
name: Build & Release
|
name: Build & Release
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
pull_request:
|
||||||
|
types: [closed]
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- master
|
|
||||||
paths:
|
paths:
|
||||||
- 'src/**'
|
- 'src/**'
|
||||||
- 'htdocs/**'
|
- 'htdocs/**'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||||
@@ -54,8 +55,7 @@ jobs:
|
|||||||
name: Build & Release Pipeline
|
name: Build & Release Pipeline
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: >-
|
if: >-
|
||||||
!contains(github.event.head_commit.message, '[skip ci]') &&
|
github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
|
||||||
github.actor != 'github-actions[bot]'
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -147,7 +147,7 @@ jobs:
|
|||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
# -- Version drift check (must pass before release) --------
|
# -- 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
|
if [ "$README_VER" != "$VERSION" ]; then
|
||||||
echo "- Version drift: README says \`${README_VER}\` but releasing \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
echo "- Version drift: README says \`${README_VER}\` but releasing \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
ERRORS=$((ERRORS+1))
|
ERRORS=$((ERRORS+1))
|
||||||
@@ -156,7 +156,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Check CHANGELOG version matches
|
# 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
|
if [ -n "$CL_VER" ] && [ "$CL_VER" != "$VERSION" ]; then
|
||||||
echo "- CHANGELOG drift: \`${CL_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
echo "- CHANGELOG drift: \`${CL_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
ERRORS=$((ERRORS+1))
|
ERRORS=$((ERRORS+1))
|
||||||
@@ -164,7 +164,7 @@ jobs:
|
|||||||
|
|
||||||
# Check composer.json version if present
|
# Check composer.json version if present
|
||||||
if [ -f "composer.json" ]; then
|
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
|
if [ -n "$COMP_VER" ] && [ "$COMP_VER" != "$VERSION" ]; then
|
||||||
echo "- composer.json drift: \`${COMP_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
echo "- composer.json drift: \`${COMP_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
ERRORS=$((ERRORS+1))
|
ERRORS=$((ERRORS+1))
|
||||||
@@ -188,7 +188,7 @@ jobs:
|
|||||||
# -- Joomla: manifest version drift --------
|
# -- Joomla: manifest version drift --------
|
||||||
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||||
if [ -n "$MANIFEST" ]; then
|
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
|
if [ -n "$XML_VER" ] && [ "$XML_VER" != "$VERSION" ]; then
|
||||||
echo "- Manifest drift: \`${XML_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
echo "- Manifest drift: \`${XML_VER}\` != \`${VERSION}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
ERRORS=$((ERRORS+1))
|
ERRORS=$((ERRORS+1))
|
||||||
@@ -205,7 +205,7 @@ jobs:
|
|||||||
echo "- Manifest: \`${MANIFEST}\`" >> $GITHUB_STEP_SUMMARY
|
echo "- Manifest: \`${MANIFEST}\`" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
# -- Joomla: extension type check --------
|
# -- 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
|
echo "- Extension type: ${TYPE:-unknown}" >> $GITHUB_STEP_SUMMARY
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -275,17 +275,22 @@ jobs:
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
EXT_NAME=$(grep -oP '<name>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "${{ github.event.repository.name }}")
|
# Extract fields using sed (portable — no grep -P)
|
||||||
EXT_TYPE=$(grep -oP '<extension[^>]+type="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "component")
|
EXT_NAME=$(sed -n 's/.*<name>\([^<]*\)<\/name>.*/\1/p' "$MANIFEST" | head -1)
|
||||||
EXT_ELEMENT=$(grep -oP '<element>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "")
|
EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||||
EXT_CLIENT=$(grep -oP '<extension[^>]+client="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "")
|
EXT_ELEMENT=$(sed -n 's/.*<element>\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" | head -1)
|
||||||
EXT_FOLDER=$(grep -oP '<extension[^>]+group="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "")
|
EXT_CLIENT=$(sed -n 's/.*<extension[^>]*client="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||||
TARGET_PLATFORM=$(grep -oP '<targetplatform[^/]*/>' "$MANIFEST" 2>/dev/null | head -1 || echo "")
|
EXT_FOLDER=$(sed -n 's/.*<extension[^>]*group="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||||
PHP_MINIMUM=$(grep -oP '<php_minimum>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "")
|
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
|
if [ -z "$EXT_ELEMENT" ]; then
|
||||||
EXT_ELEMENT=$(basename "$MANIFEST" .xml)
|
EXT_ELEMENT=$(echo "$EXT_NAME" | tr '[:upper:]' '[:lower:]' | tr -d ' ')
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Build client tag: plugins and frontend modules need <client>site</client>
|
# Build client tag: plugins and frontend modules need <client>site</client>
|
||||||
@@ -341,24 +346,28 @@ jobs:
|
|||||||
} > /tmp/stable_entry.xml
|
} > /tmp/stable_entry.xml
|
||||||
|
|
||||||
# -- Write updates.xml preserving dev/rc entries ──────────────
|
# -- Write updates.xml preserving dev/rc entries ──────────────
|
||||||
RC_ENTRY=""
|
# Extract existing entries for other stability levels
|
||||||
DEV_ENTRY=""
|
# Order reflects release workflow: development → alpha → beta → rc → stable
|
||||||
if [ -f "updates.xml" ]; then
|
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 '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 '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
|
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
|
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' '<?xml version="1.0" encoding="utf-8"?>'
|
||||||
printf '%s\n' '<updates>'
|
printf '%s\n' '<updates>'
|
||||||
cat /tmp/stable_entry.xml
|
|
||||||
[ -n "$RC_ENTRY" ] && echo "$RC_ENTRY"
|
|
||||||
[ -n "$DEV_ENTRY" ] && echo "$DEV_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>'
|
printf '%s\n' '</updates>'
|
||||||
} > updates.xml
|
} > updates.xml
|
||||||
|
|
||||||
@@ -468,55 +477,64 @@ jobs:
|
|||||||
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1 || true)
|
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1 || true)
|
||||||
[ -z "$MANIFEST" ] && exit 0
|
[ -z "$MANIFEST" ] && exit 0
|
||||||
|
|
||||||
EXT_ELEMENT=$(grep -oP '<element>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || basename "$MANIFEST" .xml)
|
EXT_ELEMENT=$(sed -n 's/.*<element>\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" 2>/dev/null | head -1 || basename "$MANIFEST" .xml)
|
||||||
PACKAGE_NAME="${EXT_ELEMENT}-${VERSION}.zip"
|
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"
|
SOURCE_DIR="src"
|
||||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||||
[ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ — skipping package"; exit 0; }
|
[ ! -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"
|
cd "$SOURCE_DIR"
|
||||||
zip -r "/tmp/${PACKAGE_NAME}" . -x '.ftpignore' 'sftp-config*' '*.ppk' '*.pem' '*.key' '.env*'
|
zip -r "/tmp/${ZIP_NAME}" . -x $EXCLUDES
|
||||||
cd ..
|
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 -------------------------------------------
|
ZIP_SIZE=$(stat -c%s "/tmp/${ZIP_NAME}" 2>/dev/null || stat -f%z "/tmp/${ZIP_NAME}" 2>/dev/null || echo "unknown")
|
||||||
SHA256=$(sha256sum "/tmp/${PACKAGE_NAME}" | cut -d' ' -f1)
|
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 -------------------------
|
# -- Calculate SHA-256 for both ----------------------------------
|
||||||
gh release upload "$RELEASE_TAG" "/tmp/${PACKAGE_NAME}" --clobber 2>/dev/null || {
|
SHA256_ZIP=$(sha256sum "/tmp/${ZIP_NAME}" | cut -d' ' -f1)
|
||||||
echo "Could not upload with --clobber, retrying..."
|
SHA256_TAR=$(sha256sum "/tmp/${TAR_NAME}" | cut -d' ' -f1)
|
||||||
gh release upload "$RELEASE_TAG" "/tmp/${PACKAGE_NAME}" 2>/dev/null || true
|
|
||||||
}
|
|
||||||
|
|
||||||
# -- 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
|
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
|
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
|
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
|
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 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
|
--author="github-actions[bot] <github-actions[bot]@users.noreply.github.com>" || true
|
||||||
git push || true
|
git push || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "### Joomla Package" >> $GITHUB_STEP_SUMMARY
|
echo "### Joomla Packages" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "" >> $GITHUB_STEP_SUMMARY
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
echo "| Package | Size | SHA-256 |" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
echo "|---------|------|---------|" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "| Package | \`${PACKAGE_NAME}\` |" >> $GITHUB_STEP_SUMMARY
|
echo "| \`${ZIP_NAME}\` | ${ZIP_SIZE} | \`${SHA256_ZIP}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "| Size | ${FILESIZE} bytes |" >> $GITHUB_STEP_SUMMARY
|
echo "| \`${TAR_NAME}\` | ${TAR_SIZE} | \`${SHA256_TAR}\` |" >> $GITHUB_STEP_SUMMARY
|
||||||
echo "| SHA-256 | \`${SHA256}\` |" >> $GITHUB_STEP_SUMMARY
|
echo "| Release | \`${RELEASE_TAG}\` | |" >> $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
|
echo "| Download | [${PACKAGE_NAME}](https://github.com/${REPO}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}) |" >> $GITHUB_STEP_SUMMARY
|
||||||
|
|
||||||
# -- Summary --------------------------------------------------------------
|
# -- Summary --------------------------------------------------------------
|
||||||
|
|||||||
114
.github/workflows/branch-freeze.yml
vendored
Normal file
114
.github/workflows/branch-freeze.yml
vendored
Normal 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
|
||||||
4
.github/workflows/changelog-validation.yml
vendored
4
.github/workflows/changelog-validation.yml
vendored
@@ -16,12 +16,10 @@
|
|||||||
name: Changelog Validation
|
name: Changelog Validation
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
|
- 'dev/**'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
|||||||
9
.github/workflows/ci-joomla.yml
vendored
9
.github/workflows/ci-joomla.yml
vendored
@@ -16,17 +16,10 @@
|
|||||||
name: Joomla Extension CI
|
name: Joomla Extension CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- dev/**
|
|
||||||
- rc/**
|
|
||||||
- version/**
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- dev/**
|
- 'dev/**'
|
||||||
- rc/**
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
|
|||||||
9
.github/workflows/codeql-analysis.yml
vendored
9
.github/workflows/codeql-analysis.yml
vendored
@@ -21,14 +21,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- dev/**
|
- version/*
|
||||||
- rc/**
|
|
||||||
- version/**
|
|
||||||
pull_request:
|
|
||||||
branches:
|
|
||||||
- main
|
|
||||||
- dev/**
|
|
||||||
- rc/**
|
|
||||||
schedule:
|
schedule:
|
||||||
# Weekly on Monday at 06:00 UTC
|
# Weekly on Monday at 06:00 UTC
|
||||||
- cron: '0 6 * * 1'
|
- cron: '0 6 * * 1'
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
|
||||||
|
|
||||||
- name: Set up Python
|
- name: Set up Python
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-python@v6
|
||||||
|
|||||||
8
.github/workflows/sync-version-on-merge.yml
vendored
8
.github/workflows/sync-version-on-merge.yml
vendored
@@ -17,10 +17,10 @@
|
|||||||
name: Sync Version from README
|
name: Sync Version from README
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
pull_request:
|
||||||
|
types: [closed]
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
- master
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
dry_run:
|
dry_run:
|
||||||
@@ -39,6 +39,8 @@ jobs:
|
|||||||
sync-version:
|
sync-version:
|
||||||
name: Propagate README version
|
name: Propagate README version
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: >-
|
||||||
|
github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -65,7 +67,7 @@ jobs:
|
|||||||
composer install --no-dev --no-interaction --quiet
|
composer install --no-dev --no-interaction --quiet
|
||||||
|
|
||||||
- name: Auto-bump patch version
|
- 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: |
|
run: |
|
||||||
if git diff --name-only HEAD~1 HEAD 2>/dev/null | grep -q '^README\.md$'; then
|
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"
|
echo "README.md changed in this push — skipping auto-bump"
|
||||||
|
|||||||
55
.github/workflows/update-server.yml
vendored
55
.github/workflows/update-server.yml
vendored
@@ -20,7 +20,8 @@
|
|||||||
name: Update Joomla Update Server XML Feed
|
name: Update Joomla Update Server XML Feed
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
pull_request:
|
||||||
|
types: [closed]
|
||||||
branches:
|
branches:
|
||||||
- 'dev/**'
|
- 'dev/**'
|
||||||
- 'alpha/**'
|
- 'alpha/**'
|
||||||
@@ -53,6 +54,8 @@ jobs:
|
|||||||
update-xml:
|
update-xml:
|
||||||
name: Update updates.xml
|
name: Update updates.xml
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
if: >-
|
||||||
|
github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@@ -108,22 +111,35 @@ jobs:
|
|||||||
STABILITY="stable"
|
STABILITY="stable"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Parse manifest
|
# Parse manifest (portable — no grep -P)
|
||||||
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
|
||||||
if [ -z "$MANIFEST" ]; then
|
if [ -z "$MANIFEST" ]; then
|
||||||
echo "No Joomla manifest found — skipping"
|
echo "No Joomla manifest found — skipping"
|
||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
EXT_NAME=$(grep -oP '<name>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "${{ github.event.repository.name }}")
|
# Extract fields using sed (works on all runners)
|
||||||
EXT_TYPE=$(grep -oP '<extension[^>]+type="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "component")
|
EXT_NAME=$(sed -n 's/.*<name>\([^<]*\)<\/name>.*/\1/p' "$MANIFEST" | head -1)
|
||||||
EXT_ELEMENT=$(grep -oP '<element>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || basename "$MANIFEST" .xml)
|
EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||||
EXT_CLIENT=$(grep -oP '<extension[^>]+client="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "")
|
EXT_ELEMENT=$(sed -n 's/.*<element>\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" | head -1)
|
||||||
EXT_FOLDER=$(grep -oP '<extension[^>]+group="\K[^"]+' "$MANIFEST" 2>/dev/null || echo "")
|
EXT_CLIENT=$(sed -n 's/.*<extension[^>]*client="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||||
TARGET_PLATFORM=$(grep -oP '<targetplatform[^/]*/>' "$MANIFEST" 2>/dev/null | head -1 || echo "")
|
EXT_FOLDER=$(sed -n 's/.*<extension[^>]*group="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
|
||||||
PHP_MINIMUM=$(grep -oP '<php_minimum>\K[^<]+' "$MANIFEST" 2>/dev/null | head -1 || echo "")
|
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>' "/")
|
[ -z "$TARGET_PLATFORM" ] && TARGET_PLATFORM=$(printf '<targetplatform name="joomla" version="5.*" %s>' "/")
|
||||||
|
|
||||||
CLIENT_TAG=""
|
CLIENT_TAG=""
|
||||||
@@ -160,24 +176,31 @@ jobs:
|
|||||||
DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}"
|
DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}"
|
||||||
INFO_URL="https://github.com/${REPO}"
|
INFO_URL="https://github.com/${REPO}"
|
||||||
|
|
||||||
# ── Build install-ready ZIP ─────────────────────────────────
|
# ── Build install packages (ZIP + tar.gz) ───────────────────
|
||||||
SOURCE_DIR="src"
|
SOURCE_DIR="src"
|
||||||
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
|
||||||
if [ -d "$SOURCE_DIR" ]; then
|
if [ -d "$SOURCE_DIR" ]; then
|
||||||
|
EXCLUDES=".ftpignore sftp-config* *.ppk *.pem *.key .env*"
|
||||||
|
TAR_NAME="${EXT_ELEMENT}-${DISPLAY_VERSION}.tar.gz"
|
||||||
|
|
||||||
cd "$SOURCE_DIR"
|
cd "$SOURCE_DIR"
|
||||||
zip -r "/tmp/${PACKAGE_NAME}" . -x '.ftpignore' 'sftp-config*' '*.ppk' '*.pem' '*.key' '.env*'
|
zip -r "/tmp/${PACKAGE_NAME}" . -x $EXCLUDES
|
||||||
cd ..
|
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)
|
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 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
|
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/${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
|
else
|
||||||
SHA256=""
|
SHA256=""
|
||||||
fi
|
fi
|
||||||
@@ -197,7 +220,9 @@ jobs:
|
|||||||
NEW_ENTRY="${NEW_ENTRY} </tags>\n"
|
NEW_ENTRY="${NEW_ENTRY} </tags>\n"
|
||||||
NEW_ENTRY="${NEW_ENTRY} <infourl title=\"${EXT_NAME}\">${INFO_URL}</infourl>\n"
|
NEW_ENTRY="${NEW_ENTRY} <infourl title=\"${EXT_NAME}\">${INFO_URL}</infourl>\n"
|
||||||
NEW_ENTRY="${NEW_ENTRY} <downloads>\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=\"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"
|
NEW_ENTRY="${NEW_ENTRY} </downloads>\n"
|
||||||
[ -n "$SHA256" ] && NEW_ENTRY="${NEW_ENTRY} <sha256>sha256:${SHA256}</sha256>\n"
|
[ -n "$SHA256" ] && NEW_ENTRY="${NEW_ENTRY} <sha256>sha256:${SHA256}</sha256>\n"
|
||||||
NEW_ENTRY="${NEW_ENTRY} ${TARGET_PLATFORM}\n"
|
NEW_ENTRY="${NEW_ENTRY} ${TARGET_PLATFORM}\n"
|
||||||
|
|||||||
Reference in New Issue
Block a user