Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5461bccd49 | |||
| 167ae2dc06 | |||
| 20250db9e0 |
@@ -164,6 +164,75 @@ jobs:
|
||||
echo "**Manifest validation passed.**" >> $GITHUB_STEP_SUMMARY
|
||||
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
|
||||
run: |
|
||||
echo "### Language File Check" >> $GITHUB_STEP_SUMMARY
|
||||
@@ -647,6 +716,268 @@ jobs:
|
||||
echo "**Service provider check passed.**" >> $GITHUB_STEP_SUMMARY
|
||||
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:
|
||||
name: Release Readiness Check
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
@@ -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
|
||||
@@ -5,7 +5,7 @@
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: mokocli.Automation
|
||||
# VERSION: 01.00.00
|
||||
# VERSION: 01.00.01
|
||||
# BRIEF: Auto-create feature branch when an issue is opened
|
||||
|
||||
name: "Universal: Issue Branch"
|
||||
|
||||
@@ -88,20 +88,8 @@ jobs:
|
||||
php ${MOKO_CLI}/platform_detect.php --path . --github-output 2>/dev/null || true
|
||||
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
|
||||
id: meta
|
||||
if: steps.eligibility.outputs.proceed == 'true'
|
||||
run: |
|
||||
# Auto-detect stability from branch name on push, or use input on dispatch
|
||||
if [ "${{ github.event_name }}" = "push" ]; then
|
||||
@@ -178,7 +166,6 @@ jobs:
|
||||
|
||||
- name: Create release
|
||||
id: release
|
||||
if: steps.eligibility.outputs.proceed == 'true'
|
||||
run: |
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
@@ -189,7 +176,6 @@ jobs:
|
||||
--repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease
|
||||
|
||||
- name: Update release notes from CHANGELOG.md
|
||||
if: steps.eligibility.outputs.proceed == 'true'
|
||||
run: |
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
@@ -226,7 +212,6 @@ jobs:
|
||||
|
||||
- name: Build package and upload
|
||||
id: package
|
||||
if: steps.eligibility.outputs.proceed == 'true'
|
||||
run: |
|
||||
VERSION="${{ steps.meta.outputs.version }}"
|
||||
TAG="${{ steps.meta.outputs.tag }}"
|
||||
@@ -240,7 +225,6 @@ jobs:
|
||||
# No need to build, commit, or sync updates.xml from workflows
|
||||
|
||||
- name: "Delete lesser pre-release channels (cascade)"
|
||||
if: steps.eligibility.outputs.proceed == 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,130 @@
|
||||
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow.Template
|
||||
# INGROUP: MokoStandards.CI
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla
|
||||
# PATH: /.mokogitea/workflows/version-set.yml
|
||||
# VERSION: 01.00.00
|
||||
# BRIEF: Set or reset the extension version across all version-bearing files
|
||||
|
||||
name: "Joomla: Set Version"
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: "Version number (e.g. 01.00.00)"
|
||||
required: true
|
||||
type: string
|
||||
branch:
|
||||
description: "Branch to update (default: current)"
|
||||
required: false
|
||||
type: string
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
||||
|
||||
jobs:
|
||||
set-version:
|
||||
name: Set Version to ${{ inputs.version }}
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Validate version format
|
||||
run: |
|
||||
VERSION="${{ inputs.version }}"
|
||||
if ! echo "$VERSION" | grep -qP '^\d{2}\.\d{2}\.\d{2}$'; then
|
||||
echo "::error::Invalid version format '${VERSION}' — expected XX.YY.ZZ (e.g. 01.00.00)"
|
||||
exit 1
|
||||
fi
|
||||
echo "VERSION=${VERSION}" >> "$GITHUB_ENV"
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.MOKOGITEA_TOKEN || secrets.GA_TOKEN || github.token }}
|
||||
ref: ${{ inputs.branch || github.ref }}
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Update manifest version
|
||||
run: |
|
||||
MANIFEST=""
|
||||
for XML_FILE in $(find . -maxdepth 3 -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 "::warning::No Joomla extension manifest found — skipping manifest update"
|
||||
else
|
||||
OLD_VER=$(grep -oP '<version>\K[^<]+' "$MANIFEST" | head -1)
|
||||
sed -i "s|<version>${OLD_VER}</version>|<version>${VERSION}</version>|" "$MANIFEST"
|
||||
echo "Manifest: ${OLD_VER} → ${VERSION} (${MANIFEST})"
|
||||
fi
|
||||
|
||||
- name: Update README.md version
|
||||
run: |
|
||||
if [ -f "README.md" ]; then
|
||||
if grep -qP '^\s*VERSION:\s*\d' README.md; then
|
||||
sed -i -E "s/(VERSION:\s*)[0-9]{2}\.[0-9]{2}\.[0-9]{2}/\1${VERSION}/" README.md
|
||||
echo "README.md version updated to ${VERSION}"
|
||||
else
|
||||
echo "::warning::No VERSION line found in README.md — skipping"
|
||||
fi
|
||||
fi
|
||||
|
||||
- name: Update CHANGELOG.md
|
||||
run: |
|
||||
if [ -f "CHANGELOG.md" ]; then
|
||||
DATE=$(date +%Y-%m-%d)
|
||||
# Check if this version already has an entry
|
||||
if grep -q "^\#\# \[${VERSION}\]" CHANGELOG.md; then
|
||||
echo "CHANGELOG.md already has entry for ${VERSION} — skipping"
|
||||
else
|
||||
# Insert new version entry after [Unreleased] or at the top after header
|
||||
if grep -q '^\#\# \[Unreleased\]' CHANGELOG.md; then
|
||||
sed -i "/^\#\# \[Unreleased\]/a\\\\n## [${VERSION}] --- ${DATE}" CHANGELOG.md
|
||||
else
|
||||
sed -i "/^\# Changelog/a\\\\n## [Unreleased]\n\n## [${VERSION}] --- ${DATE}" CHANGELOG.md
|
||||
fi
|
||||
echo "CHANGELOG.md: added entry for ${VERSION}"
|
||||
fi
|
||||
else
|
||||
echo "::warning::No CHANGELOG.md found — skipping"
|
||||
fi
|
||||
|
||||
- name: Update FILE INFORMATION blocks
|
||||
run: |
|
||||
# Update VERSION in file header blocks (# VERSION: XX.YY.ZZ)
|
||||
find . -maxdepth 1 -type f \( -name "*.yml" -o -name "*.yaml" -o -name "*.php" -o -name "*.md" \) \
|
||||
-not -path "./.git/*" -not -path "./vendor/*" -print0 2>/dev/null | \
|
||||
while IFS= read -r -d '' FILE; do
|
||||
if head -20 "$FILE" | grep -qP '^\s*#?\s*VERSION:\s*\d{2}\.\d{2}\.\d{2}'; then
|
||||
sed -i -E "s/(#?\s*VERSION:\s*)[0-9]{2}\.[0-9]{2}\.[0-9]{2}/\1${VERSION}/" "$FILE"
|
||||
echo "Updated FILE INFORMATION VERSION in ${FILE}"
|
||||
fi
|
||||
done
|
||||
|
||||
- name: Commit and push
|
||||
run: |
|
||||
git config user.name "Moko Consulting [bot]"
|
||||
git config user.email "hello@mokoconsulting.tech"
|
||||
git add -A
|
||||
if git diff --cached --quiet; then
|
||||
echo "No version changes detected — nothing to commit"
|
||||
else
|
||||
git commit -m "chore: set version to ${VERSION} [skip bump]
|
||||
|
||||
Authored-by: Moko Consulting"
|
||||
git push
|
||||
echo "### Version Set" >> $GITHUB_STEP_SUMMARY
|
||||
echo "Version updated to \`${VERSION}\` on branch \`${GITHUB_REF_NAME}\`" >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
@@ -45,6 +45,13 @@ jobs:
|
||||
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
|
||||
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
|
||||
env:
|
||||
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
|
||||
|
||||
+1
-1
@@ -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
|
||||
- Language strings for directions, geocoding feedback, and import UI
|
||||
|
||||
## [1.0.0] - 2026-06-23
|
||||
## [01.00.00] - 2026-06-23
|
||||
|
||||
### Added
|
||||
- Admin `LocationController` (FormController) for single-record save/cancel/apply
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
DEFGROUP:
|
||||
INGROUP: Project.Documentation
|
||||
REPO:
|
||||
VERSION: 04.04.01
|
||||
VERSION: 01.00.01
|
||||
PATH: ./CODE_OF_CONDUCT.md
|
||||
BRIEF: Reference + packaging repo for Moko Consulting Developer GPT Other Default
|
||||
-->
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ DEFGROUP: [PROJECT_NAME]
|
||||
INGROUP: [PROJECT_NAME].Documentation
|
||||
REPO: [REPOSITORY_URL]
|
||||
PATH: /SECURITY.md
|
||||
VERSION: 04.04.01
|
||||
VERSION: 01.00.01
|
||||
BRIEF: Security vulnerability reporting and handling policy
|
||||
-->
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
-->
|
||||
<extension type="component" method="upgrade">
|
||||
<name>com_mokosuitestorelocator</name>
|
||||
<version>1.0.0</version>
|
||||
<version>01.00.01</version>
|
||||
<creationDate>2026-06-23</creationDate>
|
||||
<author>Moko Consulting</author>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
-->
|
||||
<extension type="module" client="site" method="upgrade">
|
||||
<name>mod_mokosuitestorelocator_map</name>
|
||||
<version>1.0.0</version>
|
||||
<version>01.00.01</version>
|
||||
<creationDate>2026-06-23</creationDate>
|
||||
<author>Moko Consulting</author>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@
|
||||
-->
|
||||
<extension type="module" client="site" method="upgrade">
|
||||
<name>mod_mokosuitestorelocator_search</name>
|
||||
<version>1.0.0</version>
|
||||
<version>01.00.01</version>
|
||||
<creationDate>2026-06-23</creationDate>
|
||||
<author>Moko Consulting</author>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
<extension type="package" method="upgrade">
|
||||
<name>pkg_mokosuitestorelocator</name>
|
||||
<packagename>mokosuitestorelocator</packagename>
|
||||
<version>1.0.0</version>
|
||||
<version>01.00.01</version>
|
||||
<creationDate>2026-06-23</creationDate>
|
||||
<author>Moko Consulting</author>
|
||||
<authorEmail>hello@mokoconsulting.tech</authorEmail>
|
||||
|
||||
Reference in New Issue
Block a user