diff --git a/.gitea/workflows/ci-joomla.yml b/.gitea/workflows/ci-joomla.yml deleted file mode 100644 index 28cee48..0000000 --- a/.gitea/workflows/ci-joomla.yml +++ /dev/null @@ -1,450 +0,0 @@ -# Copyright (C) 2026 Moko Consulting -# -# This file is part of a Moko Consulting project. -# -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: Gitea.Workflow.Template -# INGROUP: MokoStandards.CI -# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API -# PATH: /templates/workflows/joomla/ci-joomla.yml.template -# VERSION: 04.06.00 -# BRIEF: CI workflow for Joomla extensions — lint, validate, test - -name: Joomla Extension CI - -on: - pull_request: - branches: - - main - - 'dev/**' - workflow_dispatch: - -permissions: - contents: read - pull-requests: write - -env: - FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true - -jobs: - lint-and-validate: - name: Lint & Validate - runs-on: ubuntu-latest - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Setup PHP - run: | - php -v && composer --version - - - name: Clone MokoStandards - 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' }} - run: | - git clone --depth 1 --branch main --quiet \ - "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \ - /tmp/mokostandards-api - - - name: Install dependencies - env: - COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}' - run: | - if [ -f "composer.json" ]; then - composer install \ - --no-interaction \ - --prefer-dist \ - --optimize-autoloader - else - echo "No composer.json found — skipping dependency install" - fi - - - name: PHP syntax check - run: | - ERRORS=0 - for DIR in src/ htdocs/; do - if [ -d "$DIR" ]; then - FOUND=1 - while IFS= read -r -d '' FILE; do - OUTPUT=$(php -l "$FILE" 2>&1) - if echo "$OUTPUT" | grep -q "Parse error"; then - echo "::error file=${FILE}::${OUTPUT}" - ERRORS=$((ERRORS + 1)) - fi - done < <(find "$DIR" -name "*.php" -print0) - fi - done - echo "### PHP Syntax Check" >> $GITHUB_STEP_SUMMARY - if [ "${ERRORS}" -gt 0 ]; then - echo "**${ERRORS} syntax error(s) found.**" >> $GITHUB_STEP_SUMMARY - exit 1 - else - echo "All PHP files passed syntax check." >> $GITHUB_STEP_SUMMARY - fi - - - name: XML manifest validation - run: | - echo "### XML Manifest Validation" >> $GITHUB_STEP_SUMMARY - ERRORS=0 - - # Find the extension manifest (XML with /dev/null; then - MANIFEST="$XML_FILE" - break - fi - done - - if [ -z "$MANIFEST" ]; then - echo "No Joomla extension manifest found (XML file with \`> $GITHUB_STEP_SUMMARY - ERRORS=$((ERRORS + 1)) - else - echo "Manifest found: \`${MANIFEST}\`" >> $GITHUB_STEP_SUMMARY - - # Validate well-formed XML - php -r " - \$xml = @simplexml_load_file('$MANIFEST'); - if (\$xml === false) { - echo 'INVALID'; - exit(1); - } - echo 'VALID'; - " > /tmp/xml_result 2>&1 - XML_RESULT=$(cat /tmp/xml_result) - if [ "$XML_RESULT" != "VALID" ]; then - echo "Manifest is not well-formed XML." >> $GITHUB_STEP_SUMMARY - ERRORS=$((ERRORS + 1)) - else - echo "Manifest is well-formed XML." >> $GITHUB_STEP_SUMMARY - fi - - # Check required tags: name, version, author, namespace (Joomla 5+) - for TAG in name version author namespace; do - if ! grep -q "<${TAG}>" "$MANIFEST" 2>/dev/null; then - echo "Missing required tag: \`<${TAG}>\`" >> $GITHUB_STEP_SUMMARY - ERRORS=$((ERRORS + 1)) - else - echo "Found required tag: \`<${TAG}>\`" >> $GITHUB_STEP_SUMMARY - fi - done - fi - - if [ "${ERRORS}" -gt 0 ]; then - echo "" >> $GITHUB_STEP_SUMMARY - echo "**${ERRORS} manifest issue(s) found.**" >> $GITHUB_STEP_SUMMARY - exit 1 - else - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Manifest validation passed.**" >> $GITHUB_STEP_SUMMARY - fi - - - name: Check language files referenced in manifest - run: | - echo "### Language File Check" >> $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 "/dev/null; then - MANIFEST="$XML_FILE" - break - fi - done - - if [ -n "$MANIFEST" ]; then - # Extract language file references from manifest - LANG_FILES=$(grep -oP 'language\s+tag="[^"]*"[^>]*>\K[^<]+' "$MANIFEST" 2>/dev/null || true) - if [ -z "$LANG_FILES" ]; then - echo "No language file references found in manifest — skipping." >> $GITHUB_STEP_SUMMARY - else - while IFS= read -r LANG_FILE; do - LANG_FILE=$(echo "$LANG_FILE" | xargs) - if [ -z "$LANG_FILE" ]; then - continue - fi - # Check in common locations - FOUND=0 - for BASE in "." "src" "htdocs"; do - if [ -f "${BASE}/${LANG_FILE}" ]; then - FOUND=1 - break - fi - done - if [ "$FOUND" -eq 0 ]; then - echo "Missing language file: \`${LANG_FILE}\`" >> $GITHUB_STEP_SUMMARY - ERRORS=$((ERRORS + 1)) - else - echo "Language file present: \`${LANG_FILE}\`" >> $GITHUB_STEP_SUMMARY - fi - done <<< "$LANG_FILES" - fi - else - echo "No manifest found — skipping language check." >> $GITHUB_STEP_SUMMARY - fi - - if [ "${ERRORS}" -gt 0 ]; then - echo "" >> $GITHUB_STEP_SUMMARY - echo "**${ERRORS} missing language file(s).**" >> $GITHUB_STEP_SUMMARY - exit 1 - else - echo "" >> $GITHUB_STEP_SUMMARY - echo "**Language file check passed.**" >> $GITHUB_STEP_SUMMARY - fi - - - name: Check index.html files in directories - run: | - echo "### Index.html Check" >> $GITHUB_STEP_SUMMARY - MISSING=0 - CHECKED=0 - - for DIR in src/ htdocs/; do - if [ -d "$DIR" ]; then - while IFS= read -r -d '' SUBDIR; do - CHECKED=$((CHECKED + 1)) - if [ ! -f "${SUBDIR}/index.html" ]; then - echo "Missing index.html in: \`${SUBDIR}\`" >> $GITHUB_STEP_SUMMARY - MISSING=$((MISSING + 1)) - fi - done < <(find "$DIR" -type d -print0) - fi - done - - if [ "${CHECKED}" -eq 0 ]; then - echo "No src/ or htdocs/ directories found — skipping." >> $GITHUB_STEP_SUMMARY - elif [ "${MISSING}" -gt 0 ]; then - echo "" >> $GITHUB_STEP_SUMMARY - echo "**${MISSING} director(ies) missing index.html out of ${CHECKED} checked.**" >> $GITHUB_STEP_SUMMARY - exit 1 - else - echo "All ${CHECKED} directories contain index.html." >> $GITHUB_STEP_SUMMARY - fi - - release-readiness: - name: Release Readiness Check - runs-on: ubuntu-latest - if: github.event_name == 'pull_request' && github.base_ref == 'main' - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Validate release readiness - run: | - echo "## Release Readiness" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - ERRORS=0 - - # Extract version from README.md - README_VERSION=$(grep -oP '^\s*VERSION:\s*\K[0-9]{2}\.[0-9]{2}\.[0-9]{2}' README.md | head -1) - if [ -z "$README_VERSION" ]; then - echo "No VERSION found in README.md FILE INFORMATION block." >> $GITHUB_STEP_SUMMARY - ERRORS=$((ERRORS + 1)) - else - echo "README version: \`${README_VERSION}\`" >> $GITHUB_STEP_SUMMARY - fi - - # Find the extension manifest - MANIFEST="" - for XML_FILE in $(find . -maxdepth 2 -name "*.xml" -not -path "./.git/*" -not -path "./vendor/*"); do - if grep -q "/dev/null; then - MANIFEST="$XML_FILE" - break - fi - done - - if [ -z "$MANIFEST" ]; then - echo "No Joomla extension manifest found." >> $GITHUB_STEP_SUMMARY - ERRORS=$((ERRORS + 1)) - else - echo "Manifest: \`${MANIFEST}\`" >> $GITHUB_STEP_SUMMARY - - # Check matches README VERSION - MANIFEST_VERSION=$(grep -oP '\K[^<]+' "$MANIFEST" | head -1) - if [ -z "$MANIFEST_VERSION" ]; then - echo "No \`\` tag in manifest." >> $GITHUB_STEP_SUMMARY - ERRORS=$((ERRORS + 1)) - elif [ -n "$README_VERSION" ] && [ "$MANIFEST_VERSION" != "$README_VERSION" ]; then - echo "Manifest version \`${MANIFEST_VERSION}\` does not match README \`${README_VERSION}\`." >> $GITHUB_STEP_SUMMARY - ERRORS=$((ERRORS + 1)) - else - echo "Manifest version: \`${MANIFEST_VERSION}\`" >> $GITHUB_STEP_SUMMARY - fi - - # Check extension type, element, client attributes - EXT_TYPE=$(grep -oP ']*\btype="\K[^"]+' "$MANIFEST" | head -1) - if [ -z "$EXT_TYPE" ]; then - echo "Missing \`type\` attribute on \`\` tag." >> $GITHUB_STEP_SUMMARY - ERRORS=$((ERRORS + 1)) - else - echo "Extension type: \`${EXT_TYPE}\`" >> $GITHUB_STEP_SUMMARY - fi - - # Element check (component/module/plugin name) - HAS_ELEMENT=$(grep -cP '<(element|name)>' "$MANIFEST" 2>/dev/null || echo "0") - if [ "$HAS_ELEMENT" -eq 0 ]; then - echo "Missing \`\` or \`\` in manifest." >> $GITHUB_STEP_SUMMARY - ERRORS=$((ERRORS + 1)) - fi - - # Client attribute for site/admin modules and plugins - if echo "$EXT_TYPE" | grep -qP "^(module|plugin)$"; then - HAS_CLIENT=$(grep -cP ']*\bclient=' "$MANIFEST" 2>/dev/null || echo "0") - if [ "$HAS_CLIENT" -eq 0 ]; then - echo "Missing \`client\` attribute for ${EXT_TYPE} extension." >> $GITHUB_STEP_SUMMARY - ERRORS=$((ERRORS + 1)) - fi - fi - fi - - # Check updates.xml exists - if [ -f "updates.xml" ] || [ -f "updates.xml" ]; then - echo "Update XML present." >> $GITHUB_STEP_SUMMARY - else - echo "No updates.xml found." >> $GITHUB_STEP_SUMMARY - ERRORS=$((ERRORS + 1)) - fi - - # Check CHANGELOG.md exists - if [ -f "CHANGELOG.md" ]; then - echo "CHANGELOG.md present." >> $GITHUB_STEP_SUMMARY - else - echo "No CHANGELOG.md found." >> $GITHUB_STEP_SUMMARY - ERRORS=$((ERRORS + 1)) - fi - - echo "" >> $GITHUB_STEP_SUMMARY - if [ $ERRORS -gt 0 ]; then - echo "**${ERRORS} issue(s) must be resolved before release.**" >> $GITHUB_STEP_SUMMARY - exit 1 - else - echo "**Extension is ready for release.**" >> $GITHUB_STEP_SUMMARY - fi - - test: - name: Tests (PHP ${{ matrix.php }}) - runs-on: ubuntu-latest - needs: lint-and-validate - - strategy: - fail-fast: false - matrix: - php: ['8.2', '8.3'] - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Setup PHP ${{ matrix.php }} - run: | - php -v && composer --version - - - name: Install dependencies - env: - COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}' - run: | - if [ -f "composer.json" ]; then - composer install \ - --no-interaction \ - --prefer-dist \ - --optimize-autoloader - else - echo "No composer.json found — skipping dependency install" - fi - - - name: Run tests - run: | - echo "### Test Results (PHP ${{ matrix.php }})" >> $GITHUB_STEP_SUMMARY - if [ -f "phpunit.xml" ] || [ -f "phpunit.xml.dist" ]; then - vendor/bin/phpunit --testdox 2>&1 | tee /tmp/test-output.log - EXIT=${PIPESTATUS[0]} - if [ $EXIT -eq 0 ]; then - echo "All tests passed." >> $GITHUB_STEP_SUMMARY - else - echo "Test failures detected — see log." >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - cat /tmp/test-output.log >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - fi - exit $EXIT - else - echo "No phpunit.xml found — skipping tests." >> $GITHUB_STEP_SUMMARY - fi - - static-analysis: - name: PHPStan Analysis - runs-on: ubuntu-latest - needs: lint-and-validate - continue-on-error: true - - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - - name: Setup PHP - run: php -v && composer --version - - - name: Install dependencies - env: - COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}' - run: | - if [ -f "composer.json" ]; then - composer install --no-interaction --prefer-dist --optimize-autoloader - fi - - - name: Install PHPStan - run: | - if ! command -v vendor/bin/phpstan &> /dev/null; then - composer require --dev phpstan/phpstan --no-interaction 2>/dev/null || \ - composer global require phpstan/phpstan --no-interaction - fi - - - name: Run PHPStan - run: | - echo "### PHPStan Static Analysis" >> $GITHUB_STEP_SUMMARY - PHPSTAN="vendor/bin/phpstan" - if [ ! -f "$PHPSTAN" ]; then - PHPSTAN=$(composer global config bin-dir --absolute 2>/dev/null)/phpstan - fi - - # Determine source directory - SRC_DIR="" - for DIR in src/ htdocs/ lib/; do - if [ -d "$DIR" ]; then - SRC_DIR="$DIR" - break - fi - done - - if [ -z "$SRC_DIR" ]; then - echo "No source directory found (src/, htdocs/, lib/) — skipping." >> $GITHUB_STEP_SUMMARY - exit 0 - fi - - # Use repo phpstan.neon if present, otherwise use baseline config - ARGS="analyse ${SRC_DIR} --memory-limit=512M --no-progress --error-format=table" - if [ -f "phpstan.neon" ] || [ -f "phpstan.neon.dist" ]; then - echo "Using project PHPStan config." >> $GITHUB_STEP_SUMMARY - else - ARGS="$ARGS --level=3" - echo "No phpstan.neon found — using level 3 (type inference)." >> $GITHUB_STEP_SUMMARY - fi - - $PHPSTAN $ARGS 2>&1 | tee /tmp/phpstan-output.txt - EXIT=${PIPESTATUS[0]} - - if [ $EXIT -eq 0 ]; then - echo "**No errors found.**" >> $GITHUB_STEP_SUMMARY - else - ERRORS=$(grep -c "ERROR" /tmp/phpstan-output.txt 2>/dev/null || echo "some") - echo "**${ERRORS} error(s) found.** Review output above." >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - tail -30 /tmp/phpstan-output.txt >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - fi - exit $EXIT