diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index b657b98..141fdcc 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -17,7 +17,7 @@ # | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. | # | | # | Platform-specific: | -# | joomla: XML manifest, type-prefixed packages | +# | joomla: XML manifest, updates.xml, type-prefixed packages | # | dolibarr: mod*.class.php, update.txt, dev version reset | # | generic: README-only, no update stream | # | | @@ -71,25 +71,20 @@ jobs: MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting run: | - if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then - echo Using pre-installed /opt/moko-platform - echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV - else - echo Falling back to fresh clone - if ! command -v composer > /dev/null 2>&1; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1 - fi - rm -rf /tmp/moko-platform-api - CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git - git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api - cd /tmp/moko-platform-api - composer install --no-dev --no-interaction --quiet - echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV + if ! command -v composer &> /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 + # Always fetch latest CLI tools — never use stale cache from previous runs + rm -rf /tmp/moko-platform-api + git clone --depth 1 --branch main --quiet \ + "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ + /tmp/moko-platform-api + cd /tmp/moko-platform-api + composer install --no-dev --no-interaction --quiet - name: Rename branch to rc run: | - php ${MOKO_CLI}/branch_rename.php \ + php /tmp/moko-platform-api/cli/branch_rename.php \ --from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \ --token "${{ secrets.MOKOGITEA_TOKEN }}" \ --api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \ @@ -105,15 +100,16 @@ jobs: - name: Publish RC release run: | - php ${MOKO_CLI}/release_publish.php \ + php /tmp/moko-platform-api/cli/release_publish.php \ --path . --stability rc --bump minor --branch rc \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --skip-update-stream - name: Summary if: always() run: | echo "## Promoted to Release Candidate" >> $GITHUB_STEP_SUMMARY - echo "Branch renamed to rc, minor bump, RC release built" >> $GITHUB_STEP_SUMMARY + echo "Branch renamed to rc, minor bump, RC release built (updates.xml managed by Gitea Pages)" >> $GITHUB_STEP_SUMMARY # ── Merged PR → Build & Release (or promote RC to stable) ──────────────────── release: @@ -155,60 +151,25 @@ jobs: MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}' run: | - if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then - echo Using pre-installed /opt/moko-platform - echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV - else - echo Falling back to fresh clone - if ! command -v composer > /dev/null 2>&1; then - sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer > /dev/null 2>&1 - fi - rm -rf /tmp/moko-platform-api - CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git - git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api - cd /tmp/moko-platform-api - composer install --no-dev --no-interaction --quiet - echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV + # Ensure PHP + Composer are available + if ! command -v composer &> /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 + # Always fetch latest CLI tools — never use stale cache from previous runs + rm -rf /tmp/moko-platform-api + git clone --depth 1 --branch main --quiet \ + "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ + /tmp/moko-platform-api + cd /tmp/moko-platform-api + composer install --no-dev --no-interaction --quiet + - name: "Publish stable release" run: | - php ${MOKO_CLI}/release_publish.php \ + php /tmp/moko-platform-api/cli/release_publish.php \ --path . --stability stable --bump minor --branch main \ - --token "${{ secrets.MOKOGITEA_TOKEN }}" - - - name: Update release notes from CHANGELOG.md - run: | - API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - - # Extract [Unreleased] section from changelog - if [ -f "CHANGELOG.md" ]; then - NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md) - [ -z "$NOTES" ] && NOTES="Stable release" - else - NOTES="Stable release" - fi - - # Update release body via API - RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ - "${API_BASE}/releases/tags/stable" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) - - if [ -n "$RELEASE_ID" ]; then - python3 -c " - import json, urllib.request - body = open('/dev/stdin').read() - payload = json.dumps({'body': body}).encode() - req = urllib.request.Request( - '${API_BASE}/releases/${RELEASE_ID}', - data=payload, method='PATCH', - headers={ - 'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}', - 'Content-Type': 'application/json' - }) - urllib.request.urlopen(req) - " <<< "$NOTES" - echo "Release notes updated from CHANGELOG.md" - fi + --token "${{ secrets.MOKOGITEA_TOKEN }}" \ + --skip-update-stream # -- STEP 9: Mirror to GitHub (stable only) -------------------------------- - name: "Step 9: Mirror release to GitHub" @@ -221,7 +182,7 @@ jobs: RELEASE_TAG="${{ steps.version.outputs.release_tag }}" GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php ${MOKO_CLI}/release_mirror.php \ + php /tmp/moko-platform-api/cli/release_mirror.php \ --version "$VERSION" --tag "$RELEASE_TAG" \ --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ --gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \ @@ -295,7 +256,7 @@ jobs: continue-on-error: true run: | API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" - php ${MOKO_CLI}/version_reset_dev.php \ + php /tmp/moko-platform-api/cli/version_reset_dev.php \ --token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \ --branch dev --path . 2>&1 || true diff --git a/.mokogitea/workflows/pr-check.yml b/.mokogitea/workflows/pr-check.yml index 7d086e2..6625857 100644 --- a/.mokogitea/workflows/pr-check.yml +++ b/.mokogitea/workflows/pr-check.yml @@ -159,11 +159,11 @@ jobs: echo "::error file=${file}::Missing JEXEC guard: ${file}" ERRORS=$((ERRORS + 1)) fi - done < <(find . -name "*.php" \( -path "*/source/*" -o -path "*/src/*" \) -not -path "./.git/*" -not -path "./vendor/*" -print0) + done < <(find . -name "*.php" -path "*/src/*" -not -path "./.git/*" -not -path "./vendor/*" -print0) if [ "$ERRORS" -gt 0 ]; then echo "::error::${ERRORS} PHP file(s) missing defined('_JEXEC') or die guard" echo "## JEXEC Guard Check: Failed" >> $GITHUB_STEP_SUMMARY - echo "${ERRORS} file(s) in source/ are missing the Joomla execution guard." >> $GITHUB_STEP_SUMMARY + echo "${ERRORS} file(s) in src/ are missing the Joomla execution guard." >> $GITHUB_STEP_SUMMARY exit 1 fi echo "JEXEC guard: OK" @@ -451,11 +451,10 @@ jobs: - name: Verify package source run: | - SOURCE_DIR="source" - [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="src" + SOURCE_DIR="src" [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" if [ ! -d "$SOURCE_DIR" ]; then - echo "::warning::No source/, src/, or htdocs/ directory" + echo "::warning::No src/ or htdocs/ directory" exit 0 fi FILE_COUNT=$(find "$SOURCE_DIR" -type f | wc -l) diff --git a/.mokogitea/workflows/repo-health.yml b/.mokogitea/workflows/repo-health.yml index 6dcdcf3..d0538d5 100644 --- a/.mokogitea/workflows/repo-health.yml +++ b/.mokogitea/workflows/repo-health.yml @@ -11,7 +11,7 @@ # REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform # PATH: /templates/workflows/joomla/repo_health.yml.template # VERSION: 09.23.00 -# BRIEF: Enforces repository guardrails by validating release configuration, scripts governance, tooling availability, and core repository health artifacts. +# BRIEF: Enforces repository guardrails by validating scripts governance, tooling availability, and core repository health artifacts. # ============================================================================ name: "Generic: Repo Health" @@ -24,13 +24,12 @@ on: workflow_dispatch: inputs: profile: - description: 'Validation profile: all, release, scripts, or repo' + description: 'Validation profile: all, scripts, or repo' required: true default: all type: choice options: - all - - release - scripts - repo pull_request: @@ -40,10 +39,6 @@ permissions: contents: read env: - # Release policy - Repository Variables Only - RELEASE_REQUIRED_REPO_VARS: RS_FTP_PATH_SUFFIX - RELEASE_OPTIONAL_REPO_VARS: DEV_FTP_SUFFIX - # Scripts governance policy SCRIPTS_REQUIRED_DIRS: SCRIPTS_ALLOWED_DIRS: scripts,scripts/fix,scripts/lib,scripts/release,scripts/run,scripts/validate @@ -138,101 +133,6 @@ jobs: printf '%s\n' 'ERROR: Access denied. Admin permission required.' >> "${GITHUB_STEP_SUMMARY}" exit 1 - release_config: - name: Release configuration - needs: access_check - if: ${{ needs.access_check.outputs.allowed == 'true' }} - runs-on: ubuntu-latest - timeout-minutes: 20 - permissions: - contents: read - - steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - with: - fetch-depth: 0 - - - name: Guardrails release vars - env: - PROFILE_RAW: ${{ github.event.inputs.profile }} - RS_FTP_PATH_SUFFIX: ${{ vars.RS_FTP_PATH_SUFFIX }} - DEV_FTP_SUFFIX: ${{ vars.DEV_FTP_SUFFIX }} - run: | - set -euo pipefail - - profile="${PROFILE_RAW:-all}" - case "${profile}" in - all|release|scripts|repo) ;; - *) - printf '%s\n' "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" - exit 1 - ;; - esac - - if [ "${profile}" = 'scripts' ] || [ "${profile}" = 'repo' ]; then - { - printf '%s\n' '### Release configuration (Repository Variables)' - printf '%s\n' "Profile: ${profile}" - printf '%s\n' 'Status: SKIPPED' - printf '%s\n' 'Reason: profile excludes release validation' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - exit 0 - fi - - IFS=',' read -r -a required <<< "${RELEASE_REQUIRED_REPO_VARS}" - IFS=',' read -r -a optional <<< "${RELEASE_OPTIONAL_REPO_VARS}" - - missing=() - missing_optional=() - - for k in "${required[@]}"; do - v="${!k:-}" - [ -z "${v}" ] && missing+=("${k}") - done - - for k in "${optional[@]}"; do - v="${!k:-}" - [ -z "${v}" ] && missing_optional+=("${k}") - done - - { - printf '%s\n' '### Release configuration (Repository Variables)' - printf '%s\n' "Profile: ${profile}" - printf '%s\n' '| Variable | Status |' - printf '%s\n' '|---|---|' - printf '%s\n' "| RS_FTP_PATH_SUFFIX | ${RS_FTP_PATH_SUFFIX:-NOT SET} |" - printf '%s\n' "| DEV_FTP_SUFFIX | ${DEV_FTP_SUFFIX:-NOT SET} |" - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - - if [ "${#missing_optional[@]}" -gt 0 ]; then - { - printf '%s\n' '### Missing optional repository variables' - for m in "${missing_optional[@]}"; do printf '%s\n' "- ${m}"; done - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - fi - - if [ "${#missing[@]}" -gt 0 ]; then - { - printf '%s\n' '### Missing required repository variables' - for m in "${missing[@]}"; do printf '%s\n' "- ${m}"; done - printf '%s\n' 'ERROR: Guardrails failed. Missing required repository variables.' - } >> "${GITHUB_STEP_SUMMARY}" - exit 1 - fi - - { - printf '%s\n' '### Repository variables validation result' - printf '%s\n' 'Status: OK' - printf '%s\n' 'All required repository variables present.' - printf '%s\n' '' - printf '%s\n' '**Note**: Organization secrets (RS_FTP_HOST, RS_FTP_USER, etc.) are validated at deployment time, not in repository health checks.' - printf '\n' - } >> "${GITHUB_STEP_SUMMARY}" - scripts_governance: name: Scripts governance needs: access_check @@ -256,14 +156,14 @@ jobs: profile="${PROFILE_RAW:-all}" case "${profile}" in - all|release|scripts|repo) ;; + all|scripts|repo) ;; *) printf '%s\n' "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" exit 1 ;; esac - if [ "${profile}" = 'release' ] || [ "${profile}" = 'repo' ]; then + if [ "${profile}" = 'repo' ]; then { printf '%s\n' '### Scripts governance' printf '%s\n' "Profile: ${profile}" @@ -370,14 +270,14 @@ jobs: profile="${PROFILE_RAW:-all}" case "${profile}" in - all|release|scripts|repo) ;; + all|scripts|repo) ;; *) printf '%s\n' "ERROR: Unknown profile: ${profile}" >> "${GITHUB_STEP_SUMMARY}" exit 1 ;; esac - if [ "${profile}" = 'release' ] || [ "${profile}" = 'scripts' ]; then + if [ "${profile}" = 'scripts' ]; then { printf '%s\n' '### Repository health' printf '%s\n' "Profile: ${profile}" @@ -396,19 +296,17 @@ jobs: missing_required=() missing_optional=() - # Source directory: source/, src/, or htdocs/ (any is valid for extension repos) + # Source directory: src/ or htdocs/ (either is valid for extension repos) SOURCE_DIR="" - if [ -d "source" ]; then - SOURCE_DIR="source" - elif [ -d "src" ]; then + if [ -d "src" ]; then SOURCE_DIR="src" elif [ -d "htdocs" ]; then SOURCE_DIR="htdocs" elif [ -d "deploy" ] || [ -d "cli" ] || [ -d "monitoring" ]; then - # Platform/tooling repos don't need source/ + # Platform/tooling repos don't need src/ SOURCE_DIR="" else - missing_required+=("source/ or htdocs/ (source directory required)") + missing_required+=("src/ or htdocs/ (source directory required)") fi for item in "${required_artifacts[@]}"; do @@ -706,7 +604,7 @@ jobs: printf '%s\n' '| Domain | Status | Notes |' printf '%s\n' '|---|---|---|' printf '%s\n' '| Access control | OK | Admin-only execution gate |' - printf '%s\n' '| Release variables | OK | Repository variables validation |' + printf '%s\n' '| Release policy | N/A | Releases handled by MokoGitea |' printf '%s\n' '| Scripts governance | OK | Directory policy and advisory reporting |' printf '%s\n' '| Repo required artifacts | OK | Required, optional, disallowed enforcement |' printf '%s\n' '| Repo content heuristics | OK | Brand, license, changelog structure |' @@ -775,11 +673,10 @@ jobs: report-issues: name: "Report Issues" runs-on: ubuntu-latest - needs: [access_check, release_config, scripts_governance, repo_health] + needs: [access_check, scripts_governance, repo_health] if: >- always() && - (needs.release_config.result == 'failure' || - needs.scripts_governance.result == 'failure' || + (needs.scripts_governance.result == 'failure' || needs.repo_health.result == 'failure') steps: @@ -805,10 +702,6 @@ jobs: fi } - report_gate "Release Configuration" \ - "${{ needs.release_config.result }}" \ - "Required repository variables are missing (RS_FTP_PATH_SUFFIX). Check repository settings." - report_gate "Scripts Governance" \ "${{ needs.scripts_governance.result }}" \ "Scripts directory policy violations detected. Review required and allowed directories." @@ -816,4 +709,3 @@ jobs: report_gate "Repository Health" \ "${{ needs.repo_health.result }}" \ "Repository health checks failed — missing required artifacts, disallowed files, or content warnings. Check the CI run summary." -