From f2d1695ac3985603f2471eef68d4c8697725a74a Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 21 May 2026 16:05:54 -0500 Subject: [PATCH 1/4] fix(ci): pipefail and rsync issues in release workflows (#20, #21) - Add || true to all find|grep|head pipelines to prevent grep exit-code 1 from killing steps under bash -e -o pipefail - Replace rsync with cp -a in pre-release Build Package step since rsync is not always available in runner containers (exit 127) Fixes #20, Fixes #21 Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitea/workflows/auto-release.yml | 6 +++--- .gitea/workflows/pre-release.yml | 20 ++++++++------------ 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/.gitea/workflows/auto-release.yml b/.gitea/workflows/auto-release.yml index 84fc701..22c4e7e 100644 --- a/.gitea/workflows/auto-release.yml +++ b/.gitea/workflows/auto-release.yml @@ -85,8 +85,8 @@ jobs: [ -z "$PLATFORM" ] && PLATFORM="generic" echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT" echo "Platform detected: ${PLATFORM}" - MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1) - MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1) + MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1 || true) + MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1 || true) echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT" echo "mod_file=${MOD_FILE}" >> "$GITHUB_OUTPUT" @@ -362,7 +362,7 @@ jobs: REPO="${{ github.repository }}" # -- Parse extension metadata from XML manifest ---------------- - MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '/dev/null | head -1) + MANIFEST=$(find . -maxdepth 2 -name "*.xml" -exec grep -l '/dev/null | head -1 || true) if [ -z "$MANIFEST" ]; then echo "Warning: No Joomla XML manifest found — skipping updates.xml" >> $GITHUB_STEP_SUMMARY exit 0 diff --git a/.gitea/workflows/pre-release.yml b/.gitea/workflows/pre-release.yml index c70ea7d..a121900 100644 --- a/.gitea/workflows/pre-release.yml +++ b/.gitea/workflows/pre-release.yml @@ -60,8 +60,8 @@ jobs: [ -z "$PLATFORM" ] && PLATFORM=$(cat .mokogitea/manifest.xml 2>/dev/null | tr -d '[:space:]') [ -z "$PLATFORM" ] && PLATFORM="generic" echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT" - MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1) - MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1) + MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '/dev/null | head -1 || true) + MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1 || true) echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT" echo "mod_file=${MOD_FILE}" >> "$GITHUB_OUTPUT" @@ -190,16 +190,12 @@ jobs: fi mkdir -p build/package - rsync -a \ - --exclude='sftp-config*' \ - --exclude='.ftpignore' \ - --exclude='*.ppk' \ - --exclude='*.pem' \ - --exclude='*.key' \ - --exclude='.env*' \ - --exclude='*.local' \ - --exclude='.build-trigger' \ - "${SOURCE_DIR}/" build/package/ + # Use cp instead of rsync (not always available in runner containers) + cp -a "${SOURCE_DIR}/." build/package/ + # Remove excluded files + cd build/package + rm -f sftp-config* .ftpignore *.ppk *.pem *.key .env* *.local .build-trigger + cd "$OLDPWD" - name: Create ZIP id: zip -- 2.52.0 From eb3e2af1ff0041dd5add53c19f41a40b71a64d2a Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 21 May 2026 16:18:26 -0500 Subject: [PATCH 2/4] refactor: rename all MokoStandards-API references to moko-platform MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bulk rename across all workflows, issue templates, and configs: - Clone URL: MokoStandards-API.git → moko-platform.git - Local path: /tmp/mokostandards-api → /tmp/moko-platform-api - DEFGROUP/INGROUP: MokoStandards.* → moko-platform.* - Step names and comments updated - REPO URLs updated to MokoConsulting/moko-platform EXCLUDE lists retain old repo names for backward compatibility during migration. Co-Authored-By: Claude Opus 4.6 (1M context) --- .mokogitea/ISSUE_TEMPLATE/config.yml | 2 +- .mokogitea/ISSUE_TEMPLATE/feature_request.md | 2 +- .mokogitea/ISSUE_TEMPLATE/security.md | 2 +- .mokogitea/branch-protection.yml | 4 +- .mokogitea/bulk-repo-sync.yml | 2 +- .mokogitea/manifest.xml | 2 +- .mokogitea/pr-branch-check.yml | 194 +++++++++---------- .mokogitea/renovate.yml | 4 +- .mokogitea/sync-wikis.yml | 2 +- .mokogitea/workflows/auto-release.yml | 20 +- .mokogitea/workflows/cascade-dev.yml | 4 +- .mokogitea/workflows/ci-platform.yml | 10 +- .mokogitea/workflows/cleanup.yml | 4 +- .mokogitea/workflows/deploy-manual.yml | 22 +-- .mokogitea/workflows/gitleaks.yml | 4 +- .mokogitea/workflows/notify.yml | 4 +- .mokogitea/workflows/pr-check.yml | 4 +- .mokogitea/workflows/pre-release.yml | 4 +- .mokogitea/workflows/repo-health.yml | 4 +- .mokogitea/workflows/security-audit.yml | 4 +- 20 files changed, 149 insertions(+), 149 deletions(-) diff --git a/.mokogitea/ISSUE_TEMPLATE/config.yml b/.mokogitea/ISSUE_TEMPLATE/config.yml index d4d49ec..06221e2 100644 --- a/.mokogitea/ISSUE_TEMPLATE/config.yml +++ b/.mokogitea/ISSUE_TEMPLATE/config.yml @@ -7,7 +7,7 @@ contact_links: - name: 💬 Ask a Question url: https://mokoconsulting.tech/ about: Get help or ask questions through our website - - name: 📚 MokoStandards Documentation + - name: 📚 moko-platform Documentation url: https://git.mokoconsulting.tech/MokoConsulting/moko-platform about: View our coding standards and best practices - name: 🔒 Report a Security Vulnerability diff --git a/.mokogitea/ISSUE_TEMPLATE/feature_request.md b/.mokogitea/ISSUE_TEMPLATE/feature_request.md index 7b76dc9..20544f9 100644 --- a/.mokogitea/ISSUE_TEMPLATE/feature_request.md +++ b/.mokogitea/ISSUE_TEMPLATE/feature_request.md @@ -37,7 +37,7 @@ If you have ideas about how this could be implemented, share them here: Add any other context, mockups, or screenshots about the feature request here. ## Relevant Standards -Does this relate to any standards in [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/MokoStandards)? +Does this relate to any standards in [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform)? - [ ] Accessibility (WCAG 2.1 AA) - [ ] Localization (en_US/en_GB) - [ ] Security best practices diff --git a/.mokogitea/ISSUE_TEMPLATE/security.md b/.mokogitea/ISSUE_TEMPLATE/security.md index f57b284..6cdd84f 100644 --- a/.mokogitea/ISSUE_TEMPLATE/security.md +++ b/.mokogitea/ISSUE_TEMPLATE/security.md @@ -35,7 +35,7 @@ Use this template only for: ## Standards Reference -Does this relate to security standards in [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/MokoStandards)? +Does this relate to security standards in [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform)? - [ ] SPDX license identifiers - [ ] Secret management - [ ] Dependency security diff --git a/.mokogitea/branch-protection.yml b/.mokogitea/branch-protection.yml index 5372dda..6fef3e3 100644 --- a/.mokogitea/branch-protection.yml +++ b/.mokogitea/branch-protection.yml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later # FILE INFORMATION # DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards-API.Automation +# INGROUP: moko-platform.Automation # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # PATH: /.gitea/workflows/branch-protection.yml # BRIEF: Apply standardised branch protection rules to all governed repositories @@ -62,7 +62,7 @@ jobs: API="${GITEA_URL}/api/v1" # Platform/standards/infra repos to exclude - EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private MokoStandards MokoStandards-API MokoTesting" + EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private MokoStandards moko-platform MokoTesting" EXCLUDE="$EXCLUDE MokoStandards-Template-Client MokoStandards-Template-Dolibarr MokoStandards-Template-Generic MokoStandards-Template-Joomla MokoDoliProjTemplate" if [ -n "${{ inputs.repos }}" ]; then diff --git a/.mokogitea/bulk-repo-sync.yml b/.mokogitea/bulk-repo-sync.yml index c81f0ad..d0fdeb2 100644 --- a/.mokogitea/bulk-repo-sync.yml +++ b/.mokogitea/bulk-repo-sync.yml @@ -2,7 +2,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later # FILE INFORMATION # DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards-API.Automation +# INGROUP: moko-platform.Automation # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # PATH: /.gitea/workflows/bulk-repo-sync.yml # BRIEF: Bulk repo sync — runs from API repo, syncs standards to all governed repos diff --git a/.mokogitea/manifest.xml b/.mokogitea/manifest.xml index 9e74dff..21b9bd1 100644 --- a/.mokogitea/manifest.xml +++ b/.mokogitea/manifest.xml @@ -1,6 +1,6 @@ diff --git a/.mokogitea/pr-branch-check.yml b/.mokogitea/pr-branch-check.yml index 5f3010e..e8b1750 100644 --- a/.mokogitea/pr-branch-check.yml +++ b/.mokogitea/pr-branch-check.yml @@ -1,97 +1,97 @@ -# Copyright (C) 2026 Moko Consulting -# SPDX-License-Identifier: GPL-3.0-or-later -# -# FILE INFORMATION -# DEFGROUP: MokoStandards.CI -# INGROUP: MokoStandards -# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform -# PATH: /.gitea/workflows/pr-branch-check.yml -# BRIEF: PR branch merge policy enforcement -# -# Enforces branch merge policy: -# feature/* → dev only -# fix/* → dev only -# hotfix/* → dev or main (emergency) -# dev → main only -# alpha/* → dev only -# beta/* → dev only -# rc/* → main only - -name: Branch Policy Check - -on: - pull_request: - types: [opened, synchronize, reopened, edited] - -jobs: - check-target: - name: Verify merge target - runs-on: ubuntu-latest - steps: - - name: Check branch policy - run: | - HEAD="${{ github.head_ref }}" - BASE="${{ github.base_ref }}" - - echo "PR: ${HEAD} → ${BASE}" - - ALLOWED=true - REASON="" - - case "$HEAD" in - feature/*|feat/*) - if [ "$BASE" != "dev" ]; then - ALLOWED=false - REASON="Feature branches must target 'dev', not '${BASE}'" - fi - ;; - fix/*|bugfix/*) - if [ "$BASE" != "dev" ]; then - ALLOWED=false - REASON="Fix branches must target 'dev', not '${BASE}'" - fi - ;; - hotfix/*) - if [ "$BASE" != "dev" ] && [ "$BASE" != "main" ]; then - ALLOWED=false - REASON="Hotfix branches can only target 'dev' or 'main', not '${BASE}'" - fi - ;; - alpha/*|beta/*) - if [ "$BASE" != "dev" ]; then - ALLOWED=false - REASON="Pre-release branches must target 'dev', not '${BASE}'" - fi - ;; - rc/*) - if [ "$BASE" != "main" ]; then - ALLOWED=false - REASON="Release candidate branches must target 'main', not '${BASE}'" - fi - ;; - dev) - if [ "$BASE" != "main" ]; then - ALLOWED=false - REASON="Dev branch can only merge into 'main', not '${BASE}'" - fi - ;; - esac - - if [ "$ALLOWED" = false ]; then - echo "::error::${REASON}" - echo "" - echo "## Branch Policy Violation" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "${REASON}" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Allowed merge paths:" >> $GITHUB_STEP_SUMMARY - echo "- \`feature/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY - echo "- \`fix/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY - echo "- \`hotfix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY - echo "- \`dev\` → \`main\`" >> $GITHUB_STEP_SUMMARY - echo "- \`rc/*\` → \`main\`" >> $GITHUB_STEP_SUMMARY - exit 1 - fi - - echo "Branch policy: OK (${HEAD} → ${BASE})" - echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY +# Copyright (C) 2026 Moko Consulting +# SPDX-License-Identifier: GPL-3.0-or-later +# +# FILE INFORMATION +# DEFGROUP: moko-platform.CI +# INGROUP: moko-platform +# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform +# PATH: /.gitea/workflows/pr-branch-check.yml +# BRIEF: PR branch merge policy enforcement +# +# Enforces branch merge policy: +# feature/* → dev only +# fix/* → dev only +# hotfix/* → dev or main (emergency) +# dev → main only +# alpha/* → dev only +# beta/* → dev only +# rc/* → main only + +name: Branch Policy Check + +on: + pull_request: + types: [opened, synchronize, reopened, edited] + +jobs: + check-target: + name: Verify merge target + runs-on: ubuntu-latest + steps: + - name: Check branch policy + run: | + HEAD="${{ github.head_ref }}" + BASE="${{ github.base_ref }}" + + echo "PR: ${HEAD} → ${BASE}" + + ALLOWED=true + REASON="" + + case "$HEAD" in + feature/*|feat/*) + if [ "$BASE" != "dev" ]; then + ALLOWED=false + REASON="Feature branches must target 'dev', not '${BASE}'" + fi + ;; + fix/*|bugfix/*) + if [ "$BASE" != "dev" ]; then + ALLOWED=false + REASON="Fix branches must target 'dev', not '${BASE}'" + fi + ;; + hotfix/*) + if [ "$BASE" != "dev" ] && [ "$BASE" != "main" ]; then + ALLOWED=false + REASON="Hotfix branches can only target 'dev' or 'main', not '${BASE}'" + fi + ;; + alpha/*|beta/*) + if [ "$BASE" != "dev" ]; then + ALLOWED=false + REASON="Pre-release branches must target 'dev', not '${BASE}'" + fi + ;; + rc/*) + if [ "$BASE" != "main" ]; then + ALLOWED=false + REASON="Release candidate branches must target 'main', not '${BASE}'" + fi + ;; + dev) + if [ "$BASE" != "main" ]; then + ALLOWED=false + REASON="Dev branch can only merge into 'main', not '${BASE}'" + fi + ;; + esac + + if [ "$ALLOWED" = false ]; then + echo "::error::${REASON}" + echo "" + echo "## Branch Policy Violation" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "${REASON}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Allowed merge paths:" >> $GITHUB_STEP_SUMMARY + echo "- \`feature/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY + echo "- \`fix/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY + echo "- \`hotfix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY + echo "- \`dev\` → \`main\`" >> $GITHUB_STEP_SUMMARY + echo "- \`rc/*\` → \`main\`" >> $GITHUB_STEP_SUMMARY + exit 1 + fi + + echo "Branch policy: OK (${HEAD} → ${BASE})" + echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY diff --git a/.mokogitea/renovate.yml b/.mokogitea/renovate.yml index 5181ff6..dd05e74 100644 --- a/.mokogitea/renovate.yml +++ b/.mokogitea/renovate.yml @@ -4,7 +4,7 @@ # # FILE INFORMATION # DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards-API.Automation +# INGROUP: moko-platform.Automation # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # PATH: /.gitea/workflows/renovate.yml # BRIEF: Run Renovate Bot across all governed repos for dependency updates @@ -61,7 +61,7 @@ jobs: run: | API="${GITEA_URL}/api/v1" - EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private MokoStandards MokoStandards-API MokoTesting" + EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private MokoStandards moko-platform MokoTesting" EXCLUDE="$EXCLUDE MokoStandards-Template-Client MokoStandards-Template-Dolibarr MokoStandards-Template-Generic MokoStandards-Template-Joomla MokoDoliProjTemplate" if [ -n "${{ inputs.repos }}" ]; then diff --git a/.mokogitea/sync-wikis.yml b/.mokogitea/sync-wikis.yml index 6c88dbb..af71890 100644 --- a/.mokogitea/sync-wikis.yml +++ b/.mokogitea/sync-wikis.yml @@ -4,7 +4,7 @@ # # FILE INFORMATION # DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards.Maintenance +# INGROUP: moko-platform.Maintenance # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # PATH: /.gitea/workflows/sync-wikis.yml # BRIEF: Daily sync of all Gitea wikis to consolidated GitHub wiki repo diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index 22c4e7e..b7212a0 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -4,8 +4,8 @@ # # FILE INFORMATION # DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards.Release -# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API +# INGROUP: moko-platform.Release +# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform # PATH: /templates/workflows/universal/auto-release.yml.template # VERSION: 05.00.00 # BRIEF: Universal build & release � detects platform from manifest.xml @@ -58,7 +58,7 @@ jobs: token: ${{ secrets.GA_TOKEN }} fetch-depth: 0 - - name: Setup MokoStandards tools + - name: Setup moko-platform tools env: MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }} MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting @@ -69,9 +69,9 @@ jobs: 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 git clone --depth 1 --branch main --quiet \ - "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \ - /tmp/mokostandards-api - cd /tmp/mokostandards-api + "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 @@ -94,7 +94,7 @@ jobs: - name: "Step 1: Read version from README.md" id: version run: | - VERSION=$(php /tmp/mokostandards-api/cli/version_read.php --path . 2>/dev/null) + VERSION=$(php /tmp/moko-platform-api/cli/version_read.php --path . 2>/dev/null) if [ -z "$VERSION" ]; then echo "No VERSION in README.md — skipping release" echo "skip=true" >> "$GITHUB_OUTPUT" @@ -335,7 +335,7 @@ jobs: steps.check.outputs.already_released != 'true' run: | VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" - php /tmp/mokostandards-api/cli/version_set_platform.php \ + php /tmp/moko-platform-api/cli/version_set_platform.php \ --path . --version "$VERSION" --branch main # -- STEP 4: Update version badges ---------------------------------------- @@ -559,7 +559,7 @@ jobs: fi [ -z "$EXT_NAME" ] && EXT_NAME="${GITEA_REPO}" - NOTES=$(php /tmp/mokostandards-api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null) + NOTES=$(php /tmp/moko-platform-api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null) [ -z "$NOTES" ] && NOTES="Release ${VERSION}" # Build release name: "Pretty Name VERSION (type_element-VERSION)" @@ -866,7 +866,7 @@ jobs: BRANCH="${{ steps.version.outputs.branch }}" GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" - NOTES=$(php /tmp/mokostandards-api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null || true) + NOTES=$(php /tmp/moko-platform-api/cli/release_notes.php --path . --version "$VERSION" 2>/dev/null || true) [ -z "$NOTES" ] && NOTES="Release ${VERSION}" echo "$NOTES" > /tmp/release_notes.md diff --git a/.mokogitea/workflows/cascade-dev.yml b/.mokogitea/workflows/cascade-dev.yml index 4dbb135..23b11a2 100644 --- a/.mokogitea/workflows/cascade-dev.yml +++ b/.mokogitea/workflows/cascade-dev.yml @@ -4,8 +4,8 @@ # # FILE INFORMATION # DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards.Maintenance -# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API +# INGROUP: moko-platform.Maintenance +# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform # PATH: /templates/workflows/cascade-dev.yml.template # VERSION: 02.00.00 # BRIEF: Forward-merge main → all open branches after every push to main diff --git a/.mokogitea/workflows/ci-platform.yml b/.mokogitea/workflows/ci-platform.yml index 745b820..14bb85d 100644 --- a/.mokogitea/workflows/ci-platform.yml +++ b/.mokogitea/workflows/ci-platform.yml @@ -4,18 +4,18 @@ # # FILE INFORMATION # DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards.CI +# INGROUP: moko-platform.CI # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # PATH: /.gitea/workflows/ci-platform.yml # VERSION: 01.00.00 -# BRIEF: MokoStandards Platform CI — the standards engine validates itself +# BRIEF: moko-platform CI — the standards engine validates itself # # +========================================================================+ # | MOKOSTANDARDS PLATFORM CI | # +========================================================================+ # | | # | This is NOT a generic CI workflow. This is the self-validation | -# | pipeline for the central MokoStandards enterprise platform. | +# | pipeline for the central moko-platform enterprise engine. | # | | # | It dogfoods every tool the platform ships to governed repos: | # | | @@ -29,7 +29,7 @@ # | | # +========================================================================+ -name: "Platform: MokoStandards CI" +name: "Platform: moko-platform CI" on: push: @@ -407,7 +407,7 @@ jobs: - name: Check gate results run: | { - echo "# MokoStandards Platform CI" + echo "# moko-platform CI" echo "" echo "| Gate | Job | Status |" echo "|---|---|---|" diff --git a/.mokogitea/workflows/cleanup.yml b/.mokogitea/workflows/cleanup.yml index 3a81856..a890001 100644 --- a/.mokogitea/workflows/cleanup.yml +++ b/.mokogitea/workflows/cleanup.yml @@ -4,8 +4,8 @@ # # FILE INFORMATION # DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards.Maintenance -# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards +# INGROUP: moko-platform.Maintenance +# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # PATH: /.gitea/workflows/cleanup.yml # VERSION: 01.00.00 # BRIEF: Scheduled cleanup — delete merged branches and old workflow runs diff --git a/.mokogitea/workflows/deploy-manual.yml b/.mokogitea/workflows/deploy-manual.yml index 132f488..6429460 100644 --- a/.mokogitea/workflows/deploy-manual.yml +++ b/.mokogitea/workflows/deploy-manual.yml @@ -4,8 +4,8 @@ # # FILE INFORMATION # DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards.Deploy -# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API +# INGROUP: moko-platform.Deploy +# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # PATH: /templates/workflows/joomla/deploy-manual.yml.template # VERSION: 04.07.00 # BRIEF: Manual SFTP deploy to dev server for Joomla repos @@ -40,7 +40,7 @@ jobs: run: | php -v && composer --version - - name: Setup MokoStandards tools + - name: Setup moko-platform tools env: GA_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }} MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }} @@ -48,10 +48,10 @@ jobs: 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 + "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" \ + /tmp/moko-platform-api 2>/dev/null || true + if [ -d "/tmp/moko-platform-api" ] && [ -f "/tmp/moko-platform-api/composer.json" ]; then + cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet 2>/dev/null || true fi - name: Check FTP configuration @@ -101,11 +101,11 @@ jobs: 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[@]}" + PLATFORM=$(php /tmp/moko-platform-api/cli/platform_detect.php --path . 2>/dev/null || true) + if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/moko-platform-api/deploy/deploy-joomla.php" ]; then + php /tmp/moko-platform-api/deploy/deploy-joomla.php "${DEPLOY_ARGS[@]}" else - php /tmp/mokostandards-api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}" + php /tmp/moko-platform-api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}" fi rm -f /tmp/deploy_key /tmp/sftp-config.json diff --git a/.mokogitea/workflows/gitleaks.yml b/.mokogitea/workflows/gitleaks.yml index 0c07612..e0fdd1d 100644 --- a/.mokogitea/workflows/gitleaks.yml +++ b/.mokogitea/workflows/gitleaks.yml @@ -4,8 +4,8 @@ # # FILE INFORMATION # DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards.Security -# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API +# INGROUP: moko-platform.Security +# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform # PATH: /templates/workflows/gitleaks.yml.template # VERSION: 01.00.00 # BRIEF: Secret scanning — detect leaked credentials, API keys, and tokens diff --git a/.mokogitea/workflows/notify.yml b/.mokogitea/workflows/notify.yml index 51dfcb5..cde4541 100644 --- a/.mokogitea/workflows/notify.yml +++ b/.mokogitea/workflows/notify.yml @@ -4,8 +4,8 @@ # # FILE INFORMATION # DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards.Notifications -# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards +# INGROUP: moko-platform.Notifications +# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # PATH: /.gitea/workflows/notify.yml # VERSION: 01.00.00 # BRIEF: Push notifications via ntfy on release success or workflow failure diff --git a/.mokogitea/workflows/pr-check.yml b/.mokogitea/workflows/pr-check.yml index 99e063f..bc1a001 100644 --- a/.mokogitea/workflows/pr-check.yml +++ b/.mokogitea/workflows/pr-check.yml @@ -4,8 +4,8 @@ # # FILE INFORMATION # DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards.CI -# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API +# INGROUP: moko-platform.CI +# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform # PATH: /templates/workflows/universal/pr-check.yml.template # VERSION: 05.00.00 # BRIEF: PR gate — branch policy + code validation before merge diff --git a/.mokogitea/workflows/pre-release.yml b/.mokogitea/workflows/pre-release.yml index a121900..948cd3f 100644 --- a/.mokogitea/workflows/pre-release.yml +++ b/.mokogitea/workflows/pre-release.yml @@ -4,8 +4,8 @@ # # FILE INFORMATION # DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards.Release -# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards +# INGROUP: moko-platform.Release +# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # PATH: /templates/workflows/universal/pre-release.yml.template # VERSION: 05.00.00 # BRIEF: Manual pre-release — builds dev/alpha/beta/rc packages from any branch diff --git a/.mokogitea/workflows/repo-health.yml b/.mokogitea/workflows/repo-health.yml index 5392de3..d738ad7 100644 --- a/.mokogitea/workflows/repo-health.yml +++ b/.mokogitea/workflows/repo-health.yml @@ -7,8 +7,8 @@ # # FILE INFORMATION # DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards.Validation -# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API +# INGROUP: moko-platform.Validation +# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform # PATH: /templates/workflows/joomla/repo_health.yml.template # VERSION: 04.06.00 # BRIEF: Enforces repository guardrails by validating release configuration, scripts governance, tooling availability, and core repository health artifacts. diff --git a/.mokogitea/workflows/security-audit.yml b/.mokogitea/workflows/security-audit.yml index f316b90..714d407 100644 --- a/.mokogitea/workflows/security-audit.yml +++ b/.mokogitea/workflows/security-audit.yml @@ -4,8 +4,8 @@ # # FILE INFORMATION # DEFGROUP: Gitea.Workflow -# INGROUP: MokoStandards.Security -# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards +# INGROUP: moko-platform.Security +# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # PATH: /.gitea/workflows/security-audit.yml # VERSION: 01.00.00 # BRIEF: Dependency vulnerability scanning for composer and npm packages -- 2.52.0 From e19ca4d7a9c6b50a82264df9cd09a0acbbe0cd80 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 21 May 2026 16:43:07 -0500 Subject: [PATCH 3/4] feat(ci): type-aware Joomla build via PHP API (#20, #21) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add cli/joomla_build.php — standalone build tool that detects all Joomla extension types from the XML manifest and builds accordingly: - plugin, module, component, template, library, file: flat ZIP - package: nested ZIPs for each sub-extension in packages/ Update both workflows to call joomla_build.php via the moko-platform PHP API instead of inlining bash build logic. Also extends joomla_release.php with: - typePrefix() for correct naming (plg_, mod_, com_, tpl_, pkg_, lib_) - buildPackageZip() for multi-extension package assembly - copyDir() helper Co-Authored-By: Claude Opus 4.6 (1M context) --- .mokogitea/workflows/auto-release.yml | 21 +- .mokogitea/workflows/pre-release.yml | 7 + cli/joomla_build.php | 295 ++++++++++++++++++++++++++ cli/joomla_release.php | 101 ++++++++- 4 files changed, 410 insertions(+), 14 deletions(-) create mode 100644 cli/joomla_build.php diff --git a/.mokogitea/workflows/auto-release.yml b/.mokogitea/workflows/auto-release.yml index b7212a0..91f0b06 100644 --- a/.mokogitea/workflows/auto-release.yml +++ b/.mokogitea/workflows/auto-release.yml @@ -648,19 +648,18 @@ jobs: # -- Build install packages from src/ ---------------------------- SOURCE_DIR="src" [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" - [ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ — skipping package"; exit 0; } + [ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/"; exit 0; } - EXCLUDES=".ftpignore sftp-config* *.ppk *.pem *.key .env*" + # ZIP package (type-aware via moko-platform PHP API) + php /tmp/moko-platform-api/cli/joomla_build.php --path . --version "${VERSION}" --output /tmp + # Match the expected ZIP_NAME for upload + BUILT_ZIP=$(ls /tmp/${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION}.zip 2>/dev/null | head -1 || true) + if [ -n "$BUILT_ZIP" ] && [ "$BUILT_ZIP" != "/tmp/${ZIP_NAME}" ]; then + mv "$BUILT_ZIP" "/tmp/${ZIP_NAME}" + fi - # ZIP package - cd "$SOURCE_DIR" - zip -r "/tmp/${ZIP_NAME}" . -x $EXCLUDES - cd .. - - # tar.gz package - tar -czf "/tmp/${TAR_NAME}" -C "$SOURCE_DIR" \ - --exclude='.ftpignore' --exclude='sftp-config*' \ - --exclude='*.ppk' --exclude='*.pem' --exclude='*.key' --exclude='.env*' . + # tar.gz package (flat source archive) + tar -czf "/tmp/${TAR_NAME}" -C "$SOURCE_DIR" --exclude='.ftpignore' --exclude='sftp-config*' --exclude='*.ppk' --exclude='*.pem' --exclude='*.key' --exclude='.env*' . ZIP_SIZE=$(stat -c%s "/tmp/${ZIP_NAME}" 2>/dev/null || stat -f%z "/tmp/${ZIP_NAME}" 2>/dev/null || echo "unknown") TAR_SIZE=$(stat -c%s "/tmp/${TAR_NAME}" 2>/dev/null || stat -f%z "/tmp/${TAR_NAME}" 2>/dev/null || echo "unknown") diff --git a/.mokogitea/workflows/pre-release.yml b/.mokogitea/workflows/pre-release.yml index 948cd3f..1ec5d77 100644 --- a/.mokogitea/workflows/pre-release.yml +++ b/.mokogitea/workflows/pre-release.yml @@ -52,6 +52,13 @@ jobs: sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip >/dev/null 2>&1 fi + - name: Setup moko-platform tools + env: + MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN }} + MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting + run: | + git clone --depth 1 --branch main --quiet "https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/moko-platform.git" /tmp/moko-platform-api + - name: Detect platform id: platform run: | diff --git a/cli/joomla_build.php b/cli/joomla_build.php new file mode 100644 index 0000000..17cbd88 --- /dev/null +++ b/cli/joomla_build.php @@ -0,0 +1,295 @@ +#!/usr/bin/env php + + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + * FILE INFORMATION + * DEFGROUP: moko-platform.CLI + * INGROUP: moko-platform + * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform + * PATH: /cli/joomla_build.php + * VERSION: 05.00.01 + * BRIEF: Build a Joomla extension ZIP from manifest — all types supported + * NOTE: Called by pre-release and auto-release workflows. + * + * USAGE + * php joomla_build.php --path . --version 02.01.24 + * php joomla_build.php --path . --version 02.01.24 --suffix -dev + * php joomla_build.php --path . --version 02.01.24 --output build --github-output + * + * Supports: plugin, module, component, template, package, library, file + */ + +declare(strict_types=1); + +// ── Argument parsing ──────────────────────────────────────────────────── +$path = '.'; +$version = ''; +$suffix = ''; +$outputDir = 'build'; +$ghOutput = false; + +foreach ($argv as $i => $arg) { + if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1]; + if ($arg === '--version' && isset($argv[$i + 1])) $version = $argv[$i + 1]; + if ($arg === '--suffix' && isset($argv[$i + 1])) $suffix = $argv[$i + 1]; + if ($arg === '--output' && isset($argv[$i + 1])) $outputDir = $argv[$i + 1]; + if ($arg === '--github-output') $ghOutput = true; +} + +if ($version === '') { + fwrite(STDERR, "::error::--version is required\n"); + exit(1); +} + +$path = realpath($path) ?: $path; + +// ── Find source directory ────────────────────────────────────────────── +$srcDir = null; +foreach (['src', 'htdocs'] as $d) { + if (is_dir("{$path}/{$d}")) { $srcDir = "{$path}/{$d}"; break; } +} +if ($srcDir === null) { + fwrite(STDERR, "::error::No src/ or htdocs/ directory in {$path}\n"); + exit(1); +} + +// ── Find manifest ────────────────────────────────────────────────────── +$manifest = findManifest($srcDir); +if ($manifest === null) { + fwrite(STDERR, "::error::No Joomla manifest found in {$srcDir}\n"); + exit(1); +} + +fwrite(STDERR, "Manifest: {$manifest}\n"); + +// ── Parse manifest ───────────────────────────────────────────────────── +$meta = parseManifest($manifest); + +// Resolve language-key names (e.g. PLG_SYSTEM_MOKOWAAS -> "System - Moko WaaS") +if (preg_match('/^[A-Z_]+$/', $meta['name'])) { + $resolved = resolveLanguageKey($srcDir, $meta['name']); + if ($resolved !== null) { $meta['name'] = $resolved; } +} + +$prefix = typePrefix($meta); +$zipName = "{$prefix}{$meta['element']}-{$version}{$suffix}.zip"; +$zipPath = "{$outputDir}/{$zipName}"; + +fwrite(STDERR, "=== Joomla Build: {$meta['type']} — {$meta['element']} {$version}{$suffix} ===\n"); +fwrite(STDERR, " Type: {$meta['type']}\n"); +fwrite(STDERR, " Element: {$meta['element']}\n"); +fwrite(STDERR, " Group: " . ($meta['group'] ?: 'n/a') . "\n"); +fwrite(STDERR, " Name: {$meta['name']}\n"); +fwrite(STDERR, " Output: {$zipName}\n"); + +// ── Build ────────────────────────────────────────────────────────────── +if (!is_dir($outputDir)) { mkdir($outputDir, 0755, true); } + +if ($meta['type'] === 'package') { + buildPackageZip($srcDir, $zipPath); +} else { + buildZip($srcDir, $zipPath); +} + +$sha256 = hash_file('sha256', $zipPath); +$size = filesize($zipPath); + +fwrite(STDERR, "Package: {$zipPath} ({$size} bytes, SHA: " . substr($sha256, 0, 16) . "...)\n"); + +// ── Output variables ─────────────────────────────────────────────────── +$vars = [ + 'zip_name' => $zipName, + 'zip_path' => $zipPath, + 'sha256' => $sha256, + 'ext_type' => $meta['type'], + 'ext_element' => $meta['element'], + 'ext_name' => $meta['name'], + 'ext_group' => $meta['group'], + 'type_prefix' => $prefix, +]; + +if ($ghOutput && ($ghFile = getenv('GITHUB_OUTPUT')) !== false && $ghFile !== '') { + $fh = fopen($ghFile, 'a'); + foreach ($vars as $k => $v) { fwrite($fh, "{$k}={$v}\n"); } + fclose($fh); + fwrite(STDERR, "Wrote " . count($vars) . " outputs to GITHUB_OUTPUT\n"); +} else { + foreach ($vars as $k => $v) { echo "{$k}={$v}\n"; } +} + +exit(0); + +// ═══════════════════════════════════════════════════════════════════════ +// Functions +// ═══════════════════════════════════════════════════════════════════════ + +function findManifest(string $dir): ?string +{ + // Priority: pkg_*.xml (packages), then any *.xml with + foreach (glob("{$dir}/pkg_*.xml") ?: [] as $f) { return $f; } + foreach (glob("{$dir}/*.xml") ?: [] as $f) { + if (str_contains((string) file_get_contents($f), 'isFile() && $item->getExtension() === 'xml') { + if (str_contains((string) file_get_contents($item->getPathname()), 'getPathname(); + } + } + } + return null; +} + +function parseManifest(string $file): array +{ + $xml = simplexml_load_file($file); + $name = (string) ($xml->name ?? ''); + $type = (string) ($xml->attributes()->type ?? 'component'); + $element = (string) ($xml->element ?? ''); + $group = (string) ($xml->attributes()->group ?? ''); + + // Fallback element detection + if ($element === '') { $element = (string) ($xml->attributes()->plugin ?? ''); } + if ($element === '') { $element = (string) ($xml->attributes()->module ?? ''); } + if ($element === '') { + $element = strtolower(basename($file, '.xml')); + if (in_array($element, ['templatedetails', 'manifest'], true)) { + $element = strtolower(basename(dirname($file))); + } + } + if ($name === '') { $name = $element; } + + return compact('name', 'type', 'element', 'group'); +} + +function typePrefix(array $meta): string +{ + return match ($meta['type']) { + 'plugin' => "plg_{$meta['group']}_", + 'module' => 'mod_', + 'component' => 'com_', + 'template' => 'tpl_', + 'package' => 'pkg_', + 'library' => 'lib_', + default => '', + }; +} + +function resolveLanguageKey(string $srcDir, string $key): ?string +{ + $iter = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($srcDir, FilesystemIterator::SKIP_DOTS) + ); + foreach ($iter as $item) { + if ($item->isFile() && str_ends_with($item->getFilename(), '.sys.ini')) { + foreach (file($item->getPathname()) as $line) { + if (preg_match('/^' . preg_quote($key, '/') . '="(.+)"/', trim($line), $m)) { + return $m[1]; + } + } + } + } + return null; +} + +function isExcluded(string $name): bool +{ + if ($name === '.ftpignore') return true; + if (str_starts_with($name, 'sftp-config')) return true; + if (str_starts_with($name, '.env')) return true; + if (str_starts_with($name, '.build-trigger')) return true; + $ext = pathinfo($name, PATHINFO_EXTENSION); + return in_array($ext, ['ppk', 'pem', 'key', 'local'], true); +} + +function buildZip(string $srcDir, string $outPath): void +{ + $zip = new ZipArchive(); + if ($zip->open($outPath, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) { + fwrite(STDERR, "::error::Cannot create ZIP: {$outPath}\n"); + exit(1); + } + $iter = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($srcDir, FilesystemIterator::SKIP_DOTS), + RecursiveIteratorIterator::SELF_FIRST + ); + foreach ($iter as $file) { + $local = str_replace('\\', '/', substr($file->getPathname(), strlen($srcDir) + 1)); + if (isExcluded(basename($local))) continue; + $file->isDir() ? $zip->addEmptyDir($local) : $zip->addFile($file->getPathname(), $local); + } + $zip->close(); +} + +function buildPackageZip(string $srcDir, string $outPath): void +{ + fwrite(STDERR, "Building Joomla package (multi-extension)...\n"); + $staging = sys_get_temp_dir() . '/moko_pkg_' . uniqid(); + mkdir($staging, 0755, true); + + // 1. Zip each sub-extension in packages/ + $packagesDir = "{$srcDir}/packages"; + if (is_dir($packagesDir)) { + foreach (glob("{$packagesDir}/*", GLOB_ONLYDIR) as $extDir) { + $subManifest = findManifest($extDir); + if ($subManifest) { + $sub = parseManifest($subManifest); + $subPrefix = typePrefix($sub); + $subZipName = "{$subPrefix}{$sub['element']}.zip"; + } else { + $subZipName = basename($extDir) . '.zip'; + } + + fwrite(STDERR, " Sub-extension: {$subZipName}\n"); + buildZip($extDir, "{$staging}/{$subZipName}"); + } + } + + // 2. Copy package-level files (manifest, script, language) + foreach (glob("{$srcDir}/*.xml") ?: [] as $f) copy($f, "{$staging}/" . basename($f)); + foreach (glob("{$srcDir}/*.php") ?: [] as $f) copy($f, "{$staging}/" . basename($f)); + foreach (['language', 'administrator'] as $d) { + if (is_dir("{$srcDir}/{$d}")) { + copyTree("{$srcDir}/{$d}", "{$staging}/{$d}"); + } + } + + // 3. Create outer zip + buildZip($staging, $outPath); + + // Cleanup + rmTree($staging); +} + +function copyTree(string $src, string $dst): void +{ + if (!is_dir($dst)) mkdir($dst, 0755, true); + $iter = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($src, FilesystemIterator::SKIP_DOTS), + RecursiveIteratorIterator::SELF_FIRST + ); + foreach ($iter as $item) { + $target = "{$dst}/" . $iter->getSubPathname(); + $item->isDir() ? (is_dir($target) || mkdir($target, 0755, true)) : copy($item->getPathname(), $target); + } +} + +function rmTree(string $dir): void +{ + if (!is_dir($dir)) return; + $iter = new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS), + RecursiveIteratorIterator::CHILD_FIRST + ); + foreach ($iter as $item) { + $item->isDir() ? rmdir($item->getPathname()) : unlink($item->getPathname()); + } + rmdir($dir); +} diff --git a/cli/joomla_release.php b/cli/joomla_release.php index 721c221..d27f917 100644 --- a/cli/joomla_release.php +++ b/cli/joomla_release.php @@ -117,14 +117,21 @@ class JoomlaRelease extends CLIApp return 1; } - $zipName = "{$meta['element']}-{$displayVersion}.zip"; - $tarName = "{$meta['element']}-{$displayVersion}.tar.gz"; + $prefix = $this->typePrefix($meta); + $zipName = "{$prefix}{$meta['element']}-{$displayVersion}.zip"; + $tarName = "{$prefix}{$meta['element']}-{$displayVersion}.tar.gz"; $zipPath = sys_get_temp_dir() . "/{$zipName}"; $tarPath = sys_get_temp_dir() . "/{$tarName}"; + $this->log('INFO', "Type: {$meta['type']} | Element: {$meta['element']} | Group: {$meta['group']}"); + $sha256 = 'dry-run'; if (!$dryRun) { - $this->buildZip($srcDir, $zipPath); + if ($meta['type'] === 'package') { + $this->buildPackageZip($srcDir, $zipPath); + } else { + $this->buildZip($srcDir, $zipPath); + } $this->buildTarGz($srcDir, $tarPath); $sha256 = hash_file('sha256', $zipPath); $this->log('SUCCESS', "ZIP: {$zipName} (" . filesize($zipPath) . " bytes)"); @@ -227,6 +234,94 @@ class JoomlaRelease extends CLIApp // ── Package building ───────────────────────────────────────────── + + /** + * Get the Joomla type prefix for ZIP naming. + * + * @param array $meta Parsed manifest metadata + * @return string Prefix like "plg_system_", "mod_", "com_", etc. + */ + private function typePrefix(array $meta): string + { + return match ($meta['type']) { + 'plugin' => "plg_{$meta['group']}_", + 'module' => 'mod_', + 'component' => 'com_', + 'template' => 'tpl_', + 'package' => 'pkg_', + 'library' => 'lib_', + default => '', + }; + } + + /** + * Build a Joomla package ZIP (type="package") with nested sub-extension zips. + * + * @param string $srcDir Source directory containing pkg_*.xml and packages/ + * @param string $outPath Output ZIP path + */ + private function buildPackageZip(string $srcDir, string $outPath): void + { + $staging = sys_get_temp_dir() . '/moko_pkg_' . uniqid(); + mkdir($staging, 0755, true); + + // 1. Zip each sub-extension in packages/ + $packagesDir = $srcDir . '/packages'; + if (is_dir($packagesDir)) { + foreach (glob("{$packagesDir}/*", GLOB_ONLYDIR) as $extDir) { + $subManifest = null; + foreach (glob("{$extDir}/*.xml") as $xml) { + if (str_contains(file_get_contents($xml), 'parseManifest($subManifest); + $prefix = $this->typePrefix($sub); + $subZipName = "{$prefix}{$sub['element']}.zip"; + } else { + $subZipName = basename($extDir) . '.zip'; + } + + $this->log('INFO', " Sub-extension: {$subZipName}"); + $this->buildZip($extDir, "{$staging}/{$subZipName}"); + } + } + + // 2. Copy package-level files (manifest, script, language) + foreach (glob("{$srcDir}/*.xml") as $f) { copy($f, "{$staging}/" . basename($f)); } + foreach (glob("{$srcDir}/*.php") as $f) { copy($f, "{$staging}/" . basename($f)); } + foreach (['language', 'administrator'] as $d) { + if (is_dir("{$srcDir}/{$d}")) { + $this->copyDir("{$srcDir}/{$d}", "{$staging}/{$d}"); + } + } + + // 3. Create the outer zip + $this->buildZip($staging, $outPath); + + // Cleanup + $this->rmdir($staging); + } + + /** + * Recursively copy a directory. + */ + private function copyDir(string $src, string $dst): void + { + if (!is_dir($dst)) { mkdir($dst, 0755, true); } + $iter = new \RecursiveIteratorIterator( + new \RecursiveDirectoryIterator($src, \FilesystemIterator::SKIP_DOTS), + \RecursiveIteratorIterator::SELF_FIRST + ); + foreach ($iter as $item) { + $target = $dst . '/' . $iter->getSubPathname(); + $item->isDir() ? (is_dir($target) || mkdir($target, 0755, true)) : copy($item->getPathname(), $target); + } + } + private function buildZip(string $srcDir, string $outPath): void { $zip = new \ZipArchive(); -- 2.52.0 From a09d880c0ad4d5a906b6c8571d4f260a91b51a12 Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Thu, 21 May 2026 16:54:55 -0500 Subject: [PATCH 4/4] refactor: rename .gitea/ to .mokogitea/ in all PHP code and sync engine Update GiteaAdapter.getWorkflowDir() and getMetadataDir() to return .mokogitea paths. All 24 PHP files referencing .gitea/ updated. Bulk sync will now push workflows to .mokogitea/workflows/ in governed repos. Co-Authored-By: Claude Opus 4.6 (1M context) --- .mokogitea/workflows/ci-platform.yml | 2 +- automation/enrich_mokostandards_xml.php | 4 ++-- automation/push_mokostandards_xml.php | 6 +++--- automation/repo_cleanup.php | 2 +- cli/create_project.php | 2 +- cli/manifest_read.php | 11 +++++++---- cli/release.php | 4 ++-- lib/Enterprise/GitPlatformAdapter.php | 4 ++-- lib/Enterprise/GiteaAdapter.php | 6 +++--- lib/Enterprise/MokoStandardsParser.php | 2 +- lib/Enterprise/Plugins/ApiPlugin.php | 6 +++--- lib/Enterprise/Plugins/GenericPlugin.php | 10 +++++----- lib/Enterprise/Plugins/MobilePlugin.php | 6 +++--- lib/Enterprise/Plugins/NodeJsPlugin.php | 6 +++--- lib/Enterprise/Plugins/PythonPlugin.php | 6 +++--- lib/Enterprise/ProjectMetricsCollector.php | 6 +++--- lib/Enterprise/RepositoryHealthChecker.php | 4 ++-- lib/Enterprise/RepositorySynchronizer.php | 8 ++++---- lib/Enterprise/UnifiedValidation.php | 4 ++-- templates/scripts/validate/validate_structure.php | 10 +++++----- tests/Enterprise/GitPlatformAdapterTest.php | 2 +- validate/check_repo_health.php | 2 +- validate/check_structure.php | 2 +- validate/check_version_consistency.php | 2 +- validate/scan_drift.php | 2 +- 25 files changed, 61 insertions(+), 58 deletions(-) diff --git a/.mokogitea/workflows/ci-platform.yml b/.mokogitea/workflows/ci-platform.yml index 14bb85d..aa777f0 100644 --- a/.mokogitea/workflows/ci-platform.yml +++ b/.mokogitea/workflows/ci-platform.yml @@ -348,7 +348,7 @@ jobs: echo "::error file=${file}::Invalid YAML" ERRORS=$((ERRORS + 1)) fi - done < <(find .gitea/workflows/ -name "*.yml" -o -name "*.yaml" 2>/dev/null | tr '\n' '\0') + done < <(find .mokogitea/workflows/ -name "*.yml" -o -name "*.yaml" 2>/dev/null | tr '\n' '\0') { echo "### Template Integrity" diff --git a/automation/enrich_mokostandards_xml.php b/automation/enrich_mokostandards_xml.php index ac75e2d..38d477f 100644 --- a/automation/enrich_mokostandards_xml.php +++ b/automation/enrich_mokostandards_xml.php @@ -276,7 +276,7 @@ foreach ($repos as $repo) { [$ret] = safeExec('git clone --depth 1 --branch ' . escapeshellarg($defaultBranch) . ' ' . escapeshellarg($authedUrl) . ' ' . escapeshellarg($workDir)); if ($ret !== 0) { echo "FAIL (clone)\n"; $stats['failed']++; continue; } - $manifestPath = "{$workDir}/.gitea/.mokostandards"; + $manifestPath = "{$workDir}/.mokogitea/.mokostandards"; if (!file_exists($manifestPath) || !str_contains(file_get_contents($manifestPath), 'extractPlatform(file_get_contents($manifestPath)); @@ -270,7 +270,7 @@ foreach ($repos as $repo) { gitCmd($workDir, 'config', 'user.name', 'gitea-actions[bot]'); gitCmd($workDir, 'config', 'user.email', 'gitea-actions[bot]@git.mokoconsulting.tech'); - gitCmd($workDir, 'add', '.gitea/.mokostandards'); + gitCmd($workDir, 'add', '.mokogitea/.mokostandards'); foreach ($legacyDeleted as $lf) { gitCmd($workDir, 'add', $lf); } diff --git a/automation/repo_cleanup.php b/automation/repo_cleanup.php index e93f8ba..0a522f2 100644 --- a/automation/repo_cleanup.php +++ b/automation/repo_cleanup.php @@ -361,7 +361,7 @@ class RepoCleanup extends CLIApp } catch (\Exception $e) { /* fallback to main */ } // Check both workflow directories for retired workflows (supports dual-platform repos) - $wfDirs = array_unique(['.github/workflows', '.gitea/workflows', $this->adapter->getWorkflowDir()]); + $wfDirs = array_unique(['.github/workflows', '.mokogitea/workflows', $this->adapter->getWorkflowDir()]); foreach (self::RETIRED_WORKFLOWS as $wf) { foreach ($wfDirs as $wfDir) { $path = "{$wfDir}/{$wf}"; diff --git a/cli/create_project.php b/cli/create_project.php index bbe28db..75a5a69 100644 --- a/cli/create_project.php +++ b/cli/create_project.php @@ -162,7 +162,7 @@ function restGet(string $path, string $token, ?\MokoEnterprise\ApiClient $apiCli function detectPlatform(string $org, string $repo, string $token, ?\MokoEnterprise\ApiClient $apiClient = null): string { // Try platform metadata dir first, then root - foreach (['.github/.mokostandards', '.gitea/.mokostandards', '.mokostandards'] as $path) { + foreach (['.github/.mokostandards', '.mokogitea/.mokostandards', '.mokostandards'] as $path) { $data = restGet("repos/{$org}/{$repo}/contents/{$path}", $token, $apiClient); if (!empty($data['content'])) { $content = base64_decode($data['content']); diff --git a/cli/manifest_read.php b/cli/manifest_read.php index 4e8a8ca..19444c9 100644 --- a/cli/manifest_read.php +++ b/cli/manifest_read.php @@ -51,7 +51,7 @@ $manifestFile = null; $candidates = [ "{$root}/.mokogitea/.manifest.xml", "{$root}/.mokogitea/.moko-platform", - "{$root}/.gitea/.mokostandards", // legacy v4 + "{$root}/.mokogitea/.mokostandards", // legacy v4 ]; foreach ($candidates as $candidate) { @@ -76,15 +76,18 @@ if ($xml === false) { $fields = []; if (preg_match('/^platform:\s*(.+)/m', $content, $m)) { $fields['platform'] = trim($m[1], " - \"'"); + +\"'"); } if (preg_match('/^standards_version:\s*(.+)/m', $content, $m)) { $fields['standards-version'] = trim($m[1], " - \"'"); + +\"'"); } if (preg_match('/^governed_repo:\s*(.+)/m', $content, $m)) { $fields['name'] = trim($m[1], " - \"'"); + +\"'"); } } else { // Register namespace for XPath (optional, simple path works without) diff --git a/cli/release.php b/cli/release.php index f92d5bc..63bc23d 100644 --- a/cli/release.php +++ b/cli/release.php @@ -31,8 +31,8 @@ foreach ($argv as $i => $arg) { $repoRoot = dirname(__DIR__, 2); $syncFile = "{$repoRoot}/lib/Enterprise/RepositorySynchronizer.php"; // Check both workflow directories for the bulk-repo-sync workflow -$bulkSyncFile = file_exists("{$repoRoot}/.gitea/workflows/bulk-repo-sync.yml") - ? "{$repoRoot}/.gitea/workflows/bulk-repo-sync.yml" +$bulkSyncFile = file_exists("{$repoRoot}/.mokogitea/workflows/bulk-repo-sync.yml") + ? "{$repoRoot}/.mokogitea/workflows/bulk-repo-sync.yml" : "{$repoRoot}/.github/workflows/bulk-repo-sync.yml"; $cleanupFile = "{$repoRoot}/templates/workflows/shared/repository-cleanup.yml.template"; diff --git a/lib/Enterprise/GitPlatformAdapter.php b/lib/Enterprise/GitPlatformAdapter.php index ddb7f48..39cd340 100644 --- a/lib/Enterprise/GitPlatformAdapter.php +++ b/lib/Enterprise/GitPlatformAdapter.php @@ -50,14 +50,14 @@ interface GitPlatformAdapter /** * Get the workflow directory name for this platform. * - * @return string '.github/workflows' or '.gitea/workflows' + * @return string '.github/workflows' or '.mokogitea/workflows' */ public function getWorkflowDir(): string; /** * Get the platform-specific metadata directory. * - * @return string '.github' or '.gitea' + * @return string '.github' or '.mokogitea' */ public function getMetadataDir(): string; diff --git a/lib/Enterprise/GiteaAdapter.php b/lib/Enterprise/GiteaAdapter.php index a38cbcf..d7e6817 100644 --- a/lib/Enterprise/GiteaAdapter.php +++ b/lib/Enterprise/GiteaAdapter.php @@ -30,7 +30,7 @@ use RuntimeException; * - File ops: POST for create, PUT for update (check existence first) * - Topics: PUT with {"topics": [...]} * - Branch protection: flat API (not rulesets) - * - Workflow dir: .gitea/workflows + * - Workflow dir: .mokogitea/workflows * * @package MokoStandards\Enterprise * @version 04.06.10 @@ -62,12 +62,12 @@ class GiteaAdapter implements GitPlatformAdapter public function getWorkflowDir(): string { - return '.gitea/workflows'; + return '.mokogitea/workflows'; } public function getMetadataDir(): string { - return '.gitea'; + return '.mokogitea'; } public function getRepoWebUrl(string $org, string $repo): string diff --git a/lib/Enterprise/MokoStandardsParser.php b/lib/Enterprise/MokoStandardsParser.php index 9d6374b..4b45b76 100644 --- a/lib/Enterprise/MokoStandardsParser.php +++ b/lib/Enterprise/MokoStandardsParser.php @@ -25,7 +25,7 @@ use SimpleXMLElement; * MokoStandards Parser * * Reads, writes, and validates the .mokostandards repository manifest. - * The file uses XML format (no file extension) and lives at .gitea/.mokostandards. + * The file uses XML format (no file extension) and lives at .mokogitea/.mokostandards. * * @package MokoStandards\Enterprise * @version 04.07.00 diff --git a/lib/Enterprise/Plugins/ApiPlugin.php b/lib/Enterprise/Plugins/ApiPlugin.php index 2c6baf0..7ce74fa 100644 --- a/lib/Enterprise/Plugins/ApiPlugin.php +++ b/lib/Enterprise/Plugins/ApiPlugin.php @@ -287,7 +287,7 @@ class ApiPlugin extends AbstractProjectPlugin 'docker-compose.yml', 'kubernetes/*.yaml', 'tests/ or test/', - '.gitea/workflows/* or .gitea/workflows/* or .gitlab-ci.yml', + '.mokogitea/workflows/* or .gitea/workflows/* or .gitlab-ci.yml', 'middleware/ or middlewares/', ]; } @@ -673,8 +673,8 @@ class ApiPlugin extends AbstractProjectPlugin */ private function hasCICD(string $projectPath): bool { - return $this->fileExists($projectPath, '.gitea/workflows') || - $this->fileExists($projectPath, '.gitea/workflows') || + return $this->fileExists($projectPath, '.mokogitea/workflows') || + $this->fileExists($projectPath, '.mokogitea/workflows') || $this->fileExists($projectPath, '.gitlab-ci.yml') || $this->fileExists($projectPath, 'Jenkinsfile') || $this->fileExists($projectPath, '.circleci'); diff --git a/lib/Enterprise/Plugins/GenericPlugin.php b/lib/Enterprise/Plugins/GenericPlugin.php index 27297b0..aec9b91 100644 --- a/lib/Enterprise/Plugins/GenericPlugin.php +++ b/lib/Enterprise/Plugins/GenericPlugin.php @@ -73,8 +73,8 @@ class GenericPlugin extends AbstractProjectPlugin } // Check for CI/CD configuration - $hasCICD = $this->fileExists($projectPath, '.gitea/workflows') || - $this->fileExists($projectPath, '.gitea/workflows') || + $hasCICD = $this->fileExists($projectPath, '.mokogitea/workflows') || + $this->fileExists($projectPath, '.mokogitea/workflows') || $this->fileExists($projectPath, '.gitlab-ci.yml') || $this->fileExists($projectPath, '.travis.yml') || $this->fileExists($projectPath, 'Jenkinsfile') || @@ -275,7 +275,7 @@ class GenericPlugin extends AbstractProjectPlugin 'CONTRIBUTING.md', 'CODE_OF_CONDUCT.md', 'SECURITY.md', - '.gitea/workflows/* or .gitea/workflows/* or .gitlab-ci.yml', + '.mokogitea/workflows/* or .gitea/workflows/* or .gitlab-ci.yml', 'docs/ or documentation/', 'tests/ or test/', ]; @@ -359,8 +359,8 @@ class GenericPlugin extends AbstractProjectPlugin */ private function hasCICD(string $projectPath): bool { - return $this->fileExists($projectPath, '.gitea/workflows') || - $this->fileExists($projectPath, '.gitea/workflows') || + return $this->fileExists($projectPath, '.mokogitea/workflows') || + $this->fileExists($projectPath, '.mokogitea/workflows') || $this->fileExists($projectPath, '.gitlab-ci.yml') || $this->fileExists($projectPath, '.travis.yml') || $this->fileExists($projectPath, 'Jenkinsfile') || diff --git a/lib/Enterprise/Plugins/MobilePlugin.php b/lib/Enterprise/Plugins/MobilePlugin.php index 87048dd..348ec7e 100644 --- a/lib/Enterprise/Plugins/MobilePlugin.php +++ b/lib/Enterprise/Plugins/MobilePlugin.php @@ -337,7 +337,7 @@ class MobilePlugin extends AbstractProjectPlugin 'App icons for all required sizes', 'Splash screen assets', 'tests/ or __tests__/', - '.gitea/workflows/* or .gitea/workflows/* or fastlane/', + '.mokogitea/workflows/* or .gitea/workflows/* or fastlane/', 'React Native: metro.config.js', 'Flutter: analysis_options.yaml', 'iOS: Podfile', @@ -542,8 +542,8 @@ class MobilePlugin extends AbstractProjectPlugin */ private function hasCICD(string $projectPath): bool { - return $this->fileExists($projectPath, '.gitea/workflows') || - $this->fileExists($projectPath, '.gitea/workflows') || + return $this->fileExists($projectPath, '.mokogitea/workflows') || + $this->fileExists($projectPath, '.mokogitea/workflows') || $this->fileExists($projectPath, '.gitlab-ci.yml') || $this->fileExists($projectPath, 'fastlane') || $this->fileExists($projectPath, '.circleci'); diff --git a/lib/Enterprise/Plugins/NodeJsPlugin.php b/lib/Enterprise/Plugins/NodeJsPlugin.php index 2ee3488..0bd0c28 100644 --- a/lib/Enterprise/Plugins/NodeJsPlugin.php +++ b/lib/Enterprise/Plugins/NodeJsPlugin.php @@ -314,7 +314,7 @@ class NodeJsPlugin extends AbstractProjectPlugin '.nvmrc or .node-version', '.editorconfig', 'jest.config.js or vitest.config.js', - '.gitea/workflows/* or .gitea/workflows/* or .gitlab-ci.yml', + '.mokogitea/workflows/* or .gitea/workflows/* or .gitlab-ci.yml', ]; } @@ -511,8 +511,8 @@ class NodeJsPlugin extends AbstractProjectPlugin */ private function hasCICD(string $projectPath): bool { - return $this->fileExists($projectPath, '.gitea/workflows') || - $this->fileExists($projectPath, '.gitea/workflows') || + return $this->fileExists($projectPath, '.mokogitea/workflows') || + $this->fileExists($projectPath, '.mokogitea/workflows') || $this->fileExists($projectPath, '.gitlab-ci.yml') || $this->fileExists($projectPath, '.travis.yml') || $this->fileExists($projectPath, '.circleci/config.yml'); diff --git a/lib/Enterprise/Plugins/PythonPlugin.php b/lib/Enterprise/Plugins/PythonPlugin.php index c8f6048..e398e36 100644 --- a/lib/Enterprise/Plugins/PythonPlugin.php +++ b/lib/Enterprise/Plugins/PythonPlugin.php @@ -323,7 +323,7 @@ class PythonPlugin extends AbstractProjectPlugin 'pytest.ini or pyproject.toml', '.python-version or .tool-versions', 'Dockerfile', - '.gitea/workflows/* or .gitea/workflows/* or .gitlab-ci.yml', + '.mokogitea/workflows/* or .gitea/workflows/* or .gitlab-ci.yml', ]; } @@ -532,8 +532,8 @@ class PythonPlugin extends AbstractProjectPlugin */ private function hasCICD(string $projectPath): bool { - return $this->fileExists($projectPath, '.gitea/workflows') || - $this->fileExists($projectPath, '.gitea/workflows') || + return $this->fileExists($projectPath, '.mokogitea/workflows') || + $this->fileExists($projectPath, '.mokogitea/workflows') || $this->fileExists($projectPath, '.gitlab-ci.yml') || $this->fileExists($projectPath, '.travis.yml') || $this->fileExists($projectPath, 'tox.ini'); diff --git a/lib/Enterprise/ProjectMetricsCollector.php b/lib/Enterprise/ProjectMetricsCollector.php index fc9ed07..ab2866e 100644 --- a/lib/Enterprise/ProjectMetricsCollector.php +++ b/lib/Enterprise/ProjectMetricsCollector.php @@ -125,13 +125,13 @@ class ProjectMetricsCollector // CI/CD — check both .github/workflows and .gitea/workflows $hasGithubWf = is_dir("{$path}/.github/workflows"); - $hasGiteaWf = is_dir("{$path}/.gitea/workflows"); + $hasGiteaWf = is_dir("{$path}/.mokogitea/workflows"); $this->collectedMetrics['has_ci_workflows'] = ($hasGithubWf || $hasGiteaWf) ? 1 : 0; $this->collectedMetrics['workflow_count'] = $this->countFiles("{$path}/.github/workflows", '*.yml') + $this->countFiles("{$path}/.github/workflows", '*.yaml') + - $this->countFiles("{$path}/.gitea/workflows", '*.yml') + - $this->countFiles("{$path}/.gitea/workflows", '*.yaml'); + $this->countFiles("{$path}/.mokogitea/workflows", '*.yml') + + $this->countFiles("{$path}/.mokogitea/workflows", '*.yaml'); } private function collectNodeJSMetrics(string $path): void diff --git a/lib/Enterprise/RepositoryHealthChecker.php b/lib/Enterprise/RepositoryHealthChecker.php index a82771c..c437354 100644 --- a/lib/Enterprise/RepositoryHealthChecker.php +++ b/lib/Enterprise/RepositoryHealthChecker.php @@ -175,7 +175,7 @@ class RepositoryHealthChecker // Check both .github/workflows and .gitea/workflows $githubDir = "{$path}/.github/workflows"; - $giteaDir = "{$path}/.gitea/workflows"; + $giteaDir = "{$path}/.mokogitea/workflows"; $hasWorkflowDir = is_dir($githubDir) || is_dir($giteaDir); $workflowDir = is_dir($giteaDir) ? $giteaDir : $githubDir; @@ -212,7 +212,7 @@ class RepositoryHealthChecker // Check for security scanning workflow (CodeQL on GitHub, Trivy on Gitea) $githubWf = "{$path}/.github/workflows"; - $giteaWf = "{$path}/.gitea/workflows"; + $giteaWf = "{$path}/.mokogitea/workflows"; $hasSecurityScan = false; if (is_dir($githubWf)) { $hasSecurityScan = !empty(glob("{$githubWf}/*codeql*.yml")) || !empty(glob("{$githubWf}/*codeql*.yaml")); diff --git a/lib/Enterprise/RepositorySynchronizer.php b/lib/Enterprise/RepositorySynchronizer.php index 747fbb8..0bc8320 100644 --- a/lib/Enterprise/RepositorySynchronizer.php +++ b/lib/Enterprise/RepositorySynchronizer.php @@ -424,7 +424,7 @@ HCL; /** * Read the platform slug from the remote .mokostandards manifest. - * Checks .gitea/.mokostandards, .github/.mokostandards, and root .mokostandards. + * Checks .mokogitea/.mokostandards, .github/.mokostandards, and root .mokostandards. * * @return string|null Platform slug or null if not found/parseable */ @@ -435,7 +435,7 @@ HCL; "{$metaDir}/.mokostandards", '.mokostandards', ]; - if ($metaDir === '.gitea') { + if ($metaDir === '.mokogitea') { $paths[] = '.github/.mokostandards'; } @@ -769,7 +769,7 @@ HCL; * and convert legacy YAML-like format to the new XML manifest. * * Handles: - * 1. Location migration: root or .github/ → .gitea/.mokostandards + * 1. Location migration: root or .github/ → .mokogitea/.mokostandards * 2. Format migration: legacy "platform: xxx" → XML manifest * 3. Update existing XML: refresh timestamp */ @@ -786,7 +786,7 @@ HCL; // ── Collect existing files from all legacy locations ───────── $legacySources = ['.mokostandards']; - if ($metaDir === '.gitea') { + if ($metaDir === '.mokogitea') { $legacySources[] = '.github/.mokostandards'; } diff --git a/lib/Enterprise/UnifiedValidation.php b/lib/Enterprise/UnifiedValidation.php index fffe02d..4a60dd0 100644 --- a/lib/Enterprise/UnifiedValidation.php +++ b/lib/Enterprise/UnifiedValidation.php @@ -275,7 +275,7 @@ class WorkflowValidatorPlugin extends ValidationPlugin public function validate(array $context): ValidationResult { $workflowDir = $context['workflow_dir'] - ?? (is_dir('.gitea/workflows') ? '.gitea/workflows' : '.github/workflows'); + ?? (is_dir('.mokogitea/workflows') ? '.mokogitea/workflows' : '.github/workflows'); if (!is_dir($workflowDir)) { return new ValidationResult($this->name, true, 'No workflows directory'); @@ -286,7 +286,7 @@ class WorkflowValidatorPlugin extends ValidationPlugin glob($workflowDir . '/*.yml') ?: [], glob($workflowDir . '/*.yaml') ?: [] ); - $altDir = ($workflowDir === '.gitea/workflows') ? '.github/workflows' : '.gitea/workflows'; + $altDir = ($workflowDir === '.mokogitea/workflows') ? '.github/workflows' : '.mokogitea/workflows'; if (is_dir($altDir)) { $workflows = array_merge($workflows, glob($altDir . '/*.yml') ?: [], diff --git a/templates/scripts/validate/validate_structure.php b/templates/scripts/validate/validate_structure.php index d5da100..20d4627 100644 --- a/templates/scripts/validate/validate_structure.php +++ b/templates/scripts/validate/validate_structure.php @@ -31,7 +31,7 @@ use MokoEnterprise\CliFramework; * - Required root files present (README.md, CHANGELOG.md, LICENSE, CONTRIBUTING.md, * SECURITY.md, .gitignore, .editorconfig, composer.json) * - Required directories present (src/, docs/, tests/) - * - .gitea/.mokostandards XML governance manifest present + * - .mokogitea/.mokostandards XML governance manifest present * - SPDX-License-Identifier header present in all PHP source files * - No tab characters in YAML/JSON config files * - No Windows path separators in PHP source @@ -73,16 +73,16 @@ class ValidateStructure extends CliFramework // ── Governance attachment ───────────────────────────────────────── $this->section('MokoStandards governance'); - $mokoFile = file_exists("{$path}/.gitea/.mokostandards") + $mokoFile = file_exists("{$path}/.mokogitea/.mokostandards") || file_exists("{$path}/.github/.mokostandards") || file_exists("{$path}/.mokostandards"); - $this->status($mokoFile, '.gitea/.mokostandards (XML manifest)'); + $this->status($mokoFile, '.mokogitea/.mokostandards (XML manifest)'); $mokoFile ? $passed++ : $failed++; // Validate XML format if file exists if ($mokoFile) { - $manifestPath = file_exists("{$path}/.gitea/.mokostandards") - ? "{$path}/.gitea/.mokostandards" + $manifestPath = file_exists("{$path}/.mokogitea/.mokostandards") + ? "{$path}/.mokogitea/.mokostandards" : (file_exists("{$path}/.github/.mokostandards") ? "{$path}/.github/.mokostandards" : "{$path}/.mokostandards"); diff --git a/tests/Enterprise/GitPlatformAdapterTest.php b/tests/Enterprise/GitPlatformAdapterTest.php index 7533874..86fe6e0 100644 --- a/tests/Enterprise/GitPlatformAdapterTest.php +++ b/tests/Enterprise/GitPlatformAdapterTest.php @@ -72,7 +72,7 @@ $giteaAdapter = new GiteaAdapter($giteaClient); assert_true($giteaAdapter instanceof GitPlatformAdapter, 'GiteaAdapter implements GitPlatformAdapter'); assert_true($giteaAdapter->getPlatformName() === 'gitea', 'getPlatformName() returns "gitea"'); assert_true($giteaAdapter->getBaseUrl() === 'https://git.mokoconsulting.tech/api/v1', 'getBaseUrl() returns Gitea API URL'); -assert_true($giteaAdapter->getWorkflowDir() === '.gitea/workflows', 'getWorkflowDir() returns .gitea/workflows'); +assert_true($giteaAdapter->getWorkflowDir() === '.mokogitea/workflows', 'getWorkflowDir() returns .gitea/workflows'); assert_true($giteaAdapter->getApiClient() === $giteaClient, 'getApiClient() returns injected client'); echo "\n"; diff --git a/validate/check_repo_health.php b/validate/check_repo_health.php index c66b82d..c3e93ff 100755 --- a/validate/check_repo_health.php +++ b/validate/check_repo_health.php @@ -133,7 +133,7 @@ class RepoHealthChecker extends CliFramework $cat = 'manifest'; $this->initCategory($cat, 'Manifest & Config', 15); - $this->addCheck($cat, '.gitea/.moko-platform manifest', + $this->addCheck($cat, '.mokogitea/.moko-platform manifest', file_exists("{$p}/.gitea/.moko-platform"), 5); $this->addCheck($cat, 'Workflows directory', is_dir("{$p}/.gitea/workflows") || is_dir("{$p}/.github/workflows"), 5); diff --git a/validate/check_structure.php b/validate/check_structure.php index 6c3c171..d2d789f 100644 --- a/validate/check_structure.php +++ b/validate/check_structure.php @@ -30,7 +30,7 @@ class CheckStructure extends CliFramework private const REQUIRED_DIRS = ['docs', 'scripts']; /** @var list At least one of these workflow directories must exist. */ - private const WORKFLOW_DIRS = ['.github/workflows', '.gitea/workflows']; + private const WORKFLOW_DIRS = ['.github/workflows', '.mokogitea/workflows']; /** @var list Required file paths (relative to repo root). */ private const REQUIRED_FILES = ['README.md', 'LICENSE', 'CONTRIBUTING.md', 'SECURITY.md']; diff --git a/validate/check_version_consistency.php b/validate/check_version_consistency.php index bd167b6..5af06c1 100755 --- a/validate/check_version_consistency.php +++ b/validate/check_version_consistency.php @@ -115,7 +115,7 @@ class CheckVersionConsistency extends CliFramework // Check both .github/workflows and .gitea/workflows $workflowFiles = []; - foreach (['.github/workflows', '.gitea/workflows'] as $wfDir) { + foreach (['.github/workflows', '.mokogitea/workflows'] as $wfDir) { $dir = $path . '/' . $wfDir; if (is_dir($dir)) { $workflowFiles = array_merge($workflowFiles, glob($dir . '/*.yml') ?: []); diff --git a/validate/scan_drift.php b/validate/scan_drift.php index 83eb7ca..36f2f17 100755 --- a/validate/scan_drift.php +++ b/validate/scan_drift.php @@ -269,7 +269,7 @@ class DriftScanner extends CliFramework // Check workflows — scan both .github/workflows and .gitea/workflows $drift = $this->checkFileCategory($org, $repo, 'workflows', '.github/workflows', $drift, $protectedFiles, $syncExclusions); - $drift = $this->checkFileCategory($org, $repo, 'workflows_gitea', '.gitea/workflows', $drift, $protectedFiles, $syncExclusions); + $drift = $this->checkFileCategory($org, $repo, 'workflows_gitea', '.mokogitea/workflows', $drift, $protectedFiles, $syncExclusions); // Check GitHub configs $drift = $this->checkFileCategory($org, $repo, 'github', '.github', $drift, $protectedFiles, $syncExclusions); -- 2.52.0