Compare commits

..

10 Commits

Author SHA1 Message Date
gitea-actions[bot] 22d8c16d62 chore(version): pre-release bump to 09.25.06-dev [skip ci] 2026-06-19 09:08:56 +00:00
Jonathan Miller 34ce9598ad refactor: rename src/ to source/
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Site Health (push) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Successful in 6s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 7s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 14s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 53s
Universal: Build & Release / Promote to RC (pull_request) Failing after 4s
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || 'development' }}) (pull_request_target) Failing after 9s
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
2026-06-19 04:06:23 -05:00
gitea-actions[bot] e4de039cff chore(version): pre-release bump to 09.25.05-dev [skip ci] 2026-06-19 08:16:30 +00:00
jmiller a6338493aa Merge pull request 'fix: rename package_type to extension_type, remove display_name (#259)' (#261) from fix/259-metadata-rename into dev
Universal: Auto Version Bump / Version Bump (push) Has been skipped
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 9s
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 38s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-19 08:16:20 +00:00
gitea-actions[bot] 1b113af068 chore(version): pre-release bump to 09.25.04-dev [skip ci]
Branch Cleanup / Delete merged branch (pull_request) Failing after 1s
2026-06-19 08:14:01 +00:00
Jonathan Miller a51f0bfb2f fix: rename package_type to extension_type, remove display_name validation (#259)
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Site Health (push) Has been skipped
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 6s
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
- API endpoint updated from /manifest to /metadata
- Removed dead .mokogitea/manifest.xml local file fallback
- display_name is now server-computed, no longer validated
- package_type renamed to extension_type throughout
2026-06-19 03:13:31 -05:00
Jonathan Miller c7b6f98f93 feat: add joomla_metadata_validate CLI command (#257)
Validates MokoGitea repo metadata against the actual Joomla extension
manifest XML to catch update delivery mismatches before production.

Checks:
- package_type matches <extension type>
- Element name derived correctly (prefix + lowercase + clean)
- Display name matches <name> tag
- Version consistency (ignoring -dev/-rc suffixes)
- PHP minimum matches composer.json
- Description match (informational)

Supports:
- Local mode: reads .mokogitea/manifest.xml + Joomla XML from disk
- API mode: fetches metadata via Gitea API (--token)
- CI mode: --ci flag exits 1 on errors
- JSON output: --json for workflow integration

Handles all Joomla types: package, component, module, plugin,
template, library, file. Replicates Joomla's InputFilter::clean('cmd')
for element name derivation.

Refs mokoplatform #257
2026-06-19 03:08:03 -05:00
jmiller 2dc43de160 revert: re-enable auto-bump on dev push [skip ci] 2026-06-18 13:22:15 +00:00
gitea-actions[bot] ea760bb75b chore(version): pre-release bump to 09.25.03-dev [skip ci] 2026-06-11 20:23:12 +00:00
jmiller d065eaf0fd ci(pre-release): add chore/** branch trigger for pre-release builds
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: Auto Version Bump / Version Bump (push) Failing after 4s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 10s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 1m3s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Generic: Repo Health / Report Issues (push) Has been cancelled
2026-06-11 20:22:31 +00:00
684 changed files with 2287 additions and 76843 deletions
-12
View File
@@ -1,12 +0,0 @@
[submodule "templates/repos/Template-Client"]
path = templates/repos/Template-Client
url = https://git.mokoconsulting.tech/MokoConsulting/Template-Client.git
[submodule "templates/repos/Template-Generic"]
path = templates/repos/Template-Generic
url = https://git.mokoconsulting.tech/MokoConsulting/Template-Generic.git
[submodule "templates/repos/Template-Joomla"]
path = templates/repos/Template-Joomla
url = https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla.git
[submodule "templates/repos/Template-MCP"]
path = templates/repos/Template-MCP
url = https://git.mokoconsulting.tech/MokoConsulting/Template-MCP.git
+4 -4
View File
@@ -1,4 +1,4 @@
# mokoplatform # moko-platform
Enterprise automation, validation, sync, and governance engine for all Moko Consulting repositories. Enterprise automation, validation, sync, and governance engine for all Moko Consulting repositories.
@@ -9,7 +9,7 @@ Enterprise automation, validation, sync, and governance engine for all Moko Cons
| **Language** | PHP 8.1+ | | **Language** | PHP 8.1+ |
| **Version** | 09.01.00 | | **Version** | 09.01.00 |
| **Branch** | develop on `dev`, merge to `main` (protected) | | **Branch** | develop on `dev`, merge to `main` (protected) |
| **Wiki** | [mokoplatform Wiki](https://git.mokoconsulting.tech/MokoConsulting/mokoplatform/wiki) | | **Wiki** | [moko-platform Wiki](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki) |
## Commands ## Commands
@@ -44,7 +44,7 @@ composer check # Run all checks
### CLI Framework ### CLI Framework
All CLI tools extend `MokoCli\CliFramework` (`lib/Enterprise/CliFramework.php`). All CLI tools extend `MokoEnterprise\CliFramework` (`lib/Enterprise/CliFramework.php`).
Built-in flags: `--help`, `--verbose`, `--quiet`, `--dry-run`. Built-in flags: `--help`, `--verbose`, `--quiet`, `--dry-run`.
After adding a CLI tool, register it in `bin/moko` COMMAND_MAP. After adding a CLI tool, register it in `bin/moko` COMMAND_MAP.
@@ -73,4 +73,4 @@ PHPStan runs with `--memory-limit=512M`. CI enforces PHPCS errors; PHPStan is `c
- **Workflow directory**: `.mokogitea/` (not `.gitea/` or `.github/`) - **Workflow directory**: `.mokogitea/` (not `.gitea/` or `.github/`)
- **Wiki**: documentation lives in the Gitea wiki, not `docs/` files - **Wiki**: documentation lives in the Gitea wiki, not `docs/` files
- **New CLI tools**: extend `CliFramework`, not `CLIApp` (legacy) - **New CLI tools**: extend `CliFramework`, not `CLIApp` (legacy)
- **Standards**: [MokoCli](https://git.mokoconsulting.tech/MokoConsulting/mokoplatform/wiki/Home) - **Standards**: [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/moko-platform/wiki/Home)
+2 -2
View File
@@ -7,8 +7,8 @@ contact_links:
- name: 💬 Ask a Question - name: 💬 Ask a Question
url: https://mokoconsulting.tech/ url: https://mokoconsulting.tech/
about: Get help or ask questions through our website about: Get help or ask questions through our website
- name: 📚 mokoplatform Documentation - name: 📚 moko-platform Documentation
url: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform url: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
about: View our coding standards and best practices about: View our coding standards and best practices
- name: 🔒 Report a Security Vulnerability - name: 🔒 Report a Security Vulnerability
url: https://git.mokoconsulting.tech/mokoconsulting-tech/.github-private/security/advisories/new url: https://git.mokoconsulting.tech/mokoconsulting-tech/.github-private/security/advisories/new
+1 -1
View File
@@ -42,7 +42,7 @@ Suggested text here
<!-- Add any other context, screenshots, or references --> <!-- Add any other context, screenshots, or references -->
## Standards Alignment ## Standards Alignment
- [ ] Follows mokoplatform documentation guidelines - [ ] Follows moko-platform documentation guidelines
- [ ] Uses en_US/en_GB localization - [ ] Uses en_US/en_GB localization
- [ ] Includes proper SPDX headers where applicable - [ ] Includes proper SPDX headers where applicable
+1 -1
View File
@@ -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. Add any other context, mockups, or screenshots about the feature request here.
## Relevant Standards ## Relevant Standards
Does this relate to any standards in [mokoplatform](https://git.mokoconsulting.tech/MokoConsulting/mokoplatform)? Does this relate to any standards in [moko-platform](https://git.mokoconsulting.tech/MokoConsulting/moko-platform)?
- [ ] Accessibility (WCAG 2.1 AA) - [ ] Accessibility (WCAG 2.1 AA)
- [ ] Localization (en_US/en_GB) - [ ] Localization (en_US/en_GB)
- [ ] Security best practices - [ ] Security best practices
+1 -1
View File
@@ -35,7 +35,7 @@ Use this template only for:
<!-- Describe how this could be addressed --> <!-- Describe how this could be addressed -->
## Standards Reference ## Standards Reference
Does this relate to security standards in [mokoplatform](https://git.mokoconsulting.tech/MokoConsulting/mokoplatform)? Does this relate to security standards in [moko-platform](https://git.mokoconsulting.tech/MokoConsulting/moko-platform)?
- [ ] SPDX license identifiers - [ ] SPDX license identifiers
- [ ] Secret management - [ ] Secret management
- [ ] Dependency security - [ ] Dependency security
+6 -6
View File
@@ -2,8 +2,8 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: mokoplatform.Automation # INGROUP: moko-platform.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /.gitea/workflows/branch-protection.yml # PATH: /.gitea/workflows/branch-protection.yml
# BRIEF: Apply standardised branch protection rules to all governed repositories # BRIEF: Apply standardised branch protection rules to all governed repositories
# #
@@ -57,13 +57,13 @@ jobs:
- name: Determine target repos - name: Determine target repos
id: repos id: repos
env: env:
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} GA_TOKEN: ${{ secrets.GA_TOKEN }}
run: | run: |
API="${GITEA_URL}/api/v1" API="${GITEA_URL}/api/v1"
# Platform/standards/infra repos to exclude # Platform/standards/infra repos to exclude
EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private mokoplatform MokoTesting" EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private moko-platform MokoTesting"
EXCLUDE="$EXCLUDE MokoCli-Template-Client MokoCli-Template-Dolibarr MokoCli-Template-Generic MokoCli-Template-Joomla MokoDoliProjTemplate" EXCLUDE="$EXCLUDE MokoStandards-Template-Client MokoStandards-Template-Dolibarr MokoStandards-Template-Generic MokoStandards-Template-Joomla MokoDoliProjTemplate"
if [ -n "${{ inputs.repos }}" ]; then if [ -n "${{ inputs.repos }}" ]; then
# User-specified repos # User-specified repos
@@ -105,7 +105,7 @@ jobs:
- name: Apply protection rules - name: Apply protection rules
env: env:
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} GA_TOKEN: ${{ secrets.GA_TOKEN }}
DRY_RUN: ${{ inputs.dry_run || 'false' }} DRY_RUN: ${{ inputs.dry_run || 'false' }}
run: | run: |
API="${GITEA_URL}/api/v1" API="${GITEA_URL}/api/v1"
+5 -5
View File
@@ -2,8 +2,8 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: mokoplatform.Automation # INGROUP: moko-platform.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /.gitea/workflows/bulk-repo-sync.yml # PATH: /.gitea/workflows/bulk-repo-sync.yml
# BRIEF: Bulk repo sync — runs from API repo, syncs standards to all governed repos # BRIEF: Bulk repo sync — runs from API repo, syncs standards to all governed repos
@@ -84,8 +84,8 @@ jobs:
echo "Running: php automation/bulk_sync.php ${{ steps.args.outputs.args }}" echo "Running: php automation/bulk_sync.php ${{ steps.args.outputs.args }}"
php automation/bulk_sync.php ${{ steps.args.outputs.args }} 2>&1 | tee /tmp/bulk_sync.log php automation/bulk_sync.php ${{ steps.args.outputs.args }} 2>&1 | tee /tmp/bulk_sync.log
env: env:
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} GA_TOKEN: ${{ secrets.GA_TOKEN }}
GH_TOKEN: ${{ secrets.GH_PAT }} GH_TOKEN: ${{ secrets.GH_TOKEN }}
GIT_PLATFORM: gitea GIT_PLATFORM: gitea
GITEA_URL: https://git.mokoconsulting.tech GITEA_URL: https://git.mokoconsulting.tech
GITEA_ORG: MokoConsulting GITEA_ORG: MokoConsulting
@@ -112,7 +112,7 @@ jobs:
bash automation/enforce_tags.sh || echo "Tag enforcement had errors (non-fatal)" bash automation/enforce_tags.sh || echo "Tag enforcement had errors (non-fatal)"
fi fi
env: env:
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} GA_TOKEN: ${{ secrets.GA_TOKEN }}
GITEA_URL: https://git.mokoconsulting.tech GITEA_URL: https://git.mokoconsulting.tech
GITEA_ORG: MokoConsulting GITEA_ORG: MokoConsulting
+3 -3
View File
@@ -2,9 +2,9 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: mokoplatform.CI # DEFGROUP: moko-platform.CI
# INGROUP: mokoplatform # INGROUP: moko-platform
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /.gitea/workflows/pr-branch-check.yml # PATH: /.gitea/workflows/pr-branch-check.yml
# BRIEF: PR branch merge policy enforcement # BRIEF: PR branch merge policy enforcement
# #
+6 -6
View File
@@ -4,8 +4,8 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: mokoplatform.Automation # INGROUP: moko-platform.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /.gitea/workflows/renovate.yml # PATH: /.gitea/workflows/renovate.yml
# BRIEF: Run Renovate Bot across all governed repos for dependency updates # BRIEF: Run Renovate Bot across all governed repos for dependency updates
# #
@@ -57,12 +57,12 @@ jobs:
- name: Determine target repos - name: Determine target repos
id: repos id: repos
env: env:
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} GA_TOKEN: ${{ secrets.GA_TOKEN }}
run: | run: |
API="${GITEA_URL}/api/v1" API="${GITEA_URL}/api/v1"
EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private mokoplatform MokoTesting" EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private moko-platform MokoTesting"
EXCLUDE="$EXCLUDE MokoCli-Template-Client MokoCli-Template-Dolibarr MokoCli-Template-Generic MokoCli-Template-Joomla MokoDoliProjTemplate" EXCLUDE="$EXCLUDE MokoStandards-Template-Client MokoStandards-Template-Dolibarr MokoStandards-Template-Generic MokoStandards-Template-Joomla MokoDoliProjTemplate"
if [ -n "${{ inputs.repos }}" ]; then if [ -n "${{ inputs.repos }}" ]; then
REPOS=$(echo "${{ inputs.repos }}" | tr ',' ' ') REPOS=$(echo "${{ inputs.repos }}" | tr ',' ' ')
@@ -107,7 +107,7 @@ jobs:
- name: Run Renovate - name: Run Renovate
if: steps.repos.outputs.repo_list != '' if: steps.repos.outputs.repo_list != ''
env: env:
RENOVATE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} RENOVATE_TOKEN: ${{ secrets.GA_TOKEN }}
RENOVATE_PLATFORM: gitea RENOVATE_PLATFORM: gitea
RENOVATE_ENDPOINT: ${{ env.GITEA_URL }}/api/v1 RENOVATE_ENDPOINT: ${{ env.GITEA_URL }}/api/v1
RENOVATE_GIT_AUTHOR: 'Renovate Bot <renovate@mokoconsulting.tech>' RENOVATE_GIT_AUTHOR: 'Renovate Bot <renovate@mokoconsulting.tech>'
+3 -3
View File
@@ -4,8 +4,8 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: mokoplatform.Maintenance # INGROUP: moko-platform.Maintenance
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /.gitea/workflows/sync-wikis.yml # PATH: /.gitea/workflows/sync-wikis.yml
# BRIEF: Daily sync of all Gitea wikis to consolidated GitHub wiki repo # BRIEF: Daily sync of all Gitea wikis to consolidated GitHub wiki repo
@@ -31,7 +31,7 @@ jobs:
- name: Sync all wikis - name: Sync all wikis
env: env:
GH_TOKEN: ${{ secrets.GH_PAT }} GH_TOKEN: ${{ secrets.GH_TOKEN }}
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
run: | run: |
if [ -z "$GH_TOKEN" ]; then if [ -z "$GH_TOKEN" ]; then
+15 -13
View File
@@ -4,10 +4,10 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Release # INGROUP: mokoplatform.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
# PATH: /.mokogitea/workflows/auto-bump.yml # PATH: /.mokogitea/workflows/auto-bump.yml
# VERSION: 09.02.00 # VERSION: 09.23.00
# BRIEF: Auto patch-bump version on every push to dev (skips merge commits) # BRIEF: Auto patch-bump version on every push to dev (skips merge commits)
name: "Universal: Auto Version Bump" name: "Universal: Auto Version Bump"
@@ -43,19 +43,21 @@ jobs:
token: ${{ secrets.MOKOGITEA_TOKEN }} token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 1 fetch-depth: 1
- name: Setup mokocli tools - name: Setup mokoplatform tools
run: | run: |
if ! command -v composer &> /dev/null; then if [ -f "/opt/mokoplatform/cli/version_bump.php" ] && [ -f "/opt/mokoplatform/vendor/autoload.php" ]; 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 echo "Using pre-installed /opt/mokoplatform"
fi echo "MOKO_CLI=/opt/mokoplatform/cli" >> "$GITHUB_ENV"
if [ -d "/opt/mokocli/cli" ]; then
echo "MOKO_CLI=/opt/mokocli/cli" >> "$GITHUB_ENV"
else else
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
rm -rf /tmp/mokoplatform-api
git clone --depth 1 --branch main --quiet \ git clone --depth 1 --branch main --quiet \
"https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/mokocli.git" \ "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/mokoplatform.git" \
/tmp/mokocli /tmp/mokoplatform-api
cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet cd /tmp/mokoplatform-api && composer install --no-dev --no-interaction --quiet
echo "MOKO_CLI=/tmp/mokocli/cli" >> "$GITHUB_ENV" echo "MOKO_CLI=/tmp/mokoplatform-api/cli" >> "$GITHUB_ENV"
fi fi
- name: Bump version - name: Bump version
+29 -126
View File
@@ -4,8 +4,8 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Release # INGROUP: moko-platform.Release
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli # REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
# PATH: /templates/workflows/universal/auto-release.yml.template # PATH: /templates/workflows/universal/auto-release.yml.template
# VERSION: 05.00.00 # VERSION: 05.00.00
# BRIEF: Universal build & release detects platform from manifest.xml # BRIEF: Universal build & release detects platform from manifest.xml
@@ -66,25 +66,25 @@ jobs:
token: ${{ secrets.MOKOGITEA_TOKEN }} token: ${{ secrets.MOKOGITEA_TOKEN }}
fetch-depth: 1 fetch-depth: 1
- name: Setup mokocli tools - name: Setup moko-platform tools
env: env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
run: | run: |
if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then
echo Using pre-installed /opt/mokocli echo Using pre-installed /opt/moko-platform
echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV
else else
echo Falling back to fresh clone echo Falling back to fresh clone
if ! command -v composer > /dev/null 2>&1; then 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 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 fi
rm -rf /tmp/mokocli rm -rf /tmp/moko-platform-api
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git 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/mokocli git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api
cd /tmp/mokocli cd /tmp/moko-platform-api
composer install --no-dev --no-interaction --quiet composer install --no-dev --no-interaction --quiet
echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
fi fi
- name: Rename branch to rc - name: Rename branch to rc
@@ -109,40 +109,6 @@ jobs:
--path . --stability rc --bump minor --branch rc \ --path . --stability rc --bump minor --branch rc \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --token "${{ secrets.MOKOGITEA_TOKEN }}"
- name: Update RC release notes from CHANGELOG.md
run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
# Extract [Unreleased] section from changelog
NOTES=""
if [ -f "CHANGELOG.md" ]; then
NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
fi
[ -z "$NOTES" ] && NOTES="Release candidate"
# Find the RC release and update its body
RELEASE_ID=$(curl -sf -H "Authorization: token ${TOKEN}" \
"${API_BASE}/releases/tags/release-candidate" \
| 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 ${TOKEN}',
'Content-Type': 'application/json'
})
urllib.request.urlopen(req)
" <<< "$NOTES"
echo "RC release notes updated from CHANGELOG.md"
fi
- name: Summary - name: Summary
if: always() if: always()
run: | run: |
@@ -183,95 +149,50 @@ jobs:
fi fi
echo "No conflict markers found" echo "No conflict markers found"
- name: Setup mokocli tools - name: Setup moko-platform tools
env: env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}' COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}'
run: | run: |
if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then
echo Using pre-installed /opt/mokocli echo Using pre-installed /opt/moko-platform
echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV
else else
echo Falling back to fresh clone echo Falling back to fresh clone
if ! command -v composer > /dev/null 2>&1; then 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 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 fi
rm -rf /tmp/mokocli rm -rf /tmp/moko-platform-api
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git 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/mokocli git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api
cd /tmp/mokocli cd /tmp/moko-platform-api
composer install --no-dev --no-interaction --quiet composer install --no-dev --no-interaction --quiet
echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
fi fi
- name: "Detect platform"
id: platform
run: |
php ${MOKO_CLI}/platform_detect.php --path . --github-output 2>/dev/null || true
php ${MOKO_CLI}/manifest_read.php --path . --github-output 2>/dev/null || true
- name: "Determine version bump level"
id: bump
run: |
# Fix/patch branches: version was already bumped by pre-release, just strip suffix
# Feature/dev branches: bump minor for the new stable release
HEAD_REF="${{ github.event.pull_request.head.ref || 'dev' }}"
case "$HEAD_REF" in
fix/*|patch/*|hotfix/*|bugfix/*) BUMP="none" ;;
*) BUMP="minor" ;;
esac
echo "level=${BUMP}" >> "$GITHUB_OUTPUT"
echo "Bump level: ${BUMP} (from branch: ${HEAD_REF})"
- name: "Publish stable release" - name: "Publish stable release"
run: | run: |
BUMP_FLAG=""
if [ "${{ steps.bump.outputs.level }}" != "none" ]; then
BUMP_FLAG="--bump ${{ steps.bump.outputs.level }}"
fi
php ${MOKO_CLI}/release_publish.php \ php ${MOKO_CLI}/release_publish.php \
--path . --stability stable ${BUMP_FLAG} --branch main \ --path . --stability stable --bump minor --branch main \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --token "${{ secrets.MOKOGITEA_TOKEN }}"
- name: "Read published version" - name: Update release notes from CHANGELOG.md
id: version
run: |
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null || echo "")
VERSION=$(echo "$VERSION" | sed 's/-\(dev\|alpha\|beta\|rc\)$//')
[ -z "$VERSION" ] && VERSION="00.00.00" && echo "skip=true" >> "$GITHUB_OUTPUT"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "tag=stable" >> "$GITHUB_OUTPUT"
echo "release_tag=stable" >> "$GITHUB_OUTPUT"
echo "branch=main" >> "$GITHUB_OUTPUT"
echo "Published version: ${VERSION}"
- name: Update release notes and promote changelog
run: | run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
# Get the stable release info (version and ID)
RELEASE_JSON=$(curl -sf -H "Authorization: token ${TOKEN}" \
"${API_BASE}/releases/tags/stable" 2>/dev/null || echo '{}')
RELEASE_ID=$(python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" <<< "$RELEASE_JSON" 2>/dev/null || true)
# Extract version from release name (e.g. "06.17.00" or "v06.17.00")
VERSION=$(python3 -c "
import json, sys, re
r = json.load(sys.stdin)
name = r.get('name', '')
m = re.search(r'(\d+\.\d+\.\d+)', name)
print(m.group(1) if m else '')
" <<< "$RELEASE_JSON" 2>/dev/null || true)
# Extract [Unreleased] section from changelog # Extract [Unreleased] section from changelog
NOTES=""
if [ -f "CHANGELOG.md" ]; then if [ -f "CHANGELOG.md" ]; then
NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md) NOTES=$(awk '/^## \[Unreleased\]/{found=1; next} /^## \[/{if(found) exit} found{print}' CHANGELOG.md)
[ -z "$NOTES" ] && NOTES="Stable release"
else
NOTES="Stable release"
fi fi
[ -z "$NOTES" ] && NOTES="Stable release"
# Update release body via API # 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 if [ -n "$RELEASE_ID" ]; then
python3 -c " python3 -c "
import json, urllib.request import json, urllib.request
@@ -281,7 +202,7 @@ jobs:
'${API_BASE}/releases/${RELEASE_ID}', '${API_BASE}/releases/${RELEASE_ID}',
data=payload, method='PATCH', data=payload, method='PATCH',
headers={ headers={
'Authorization': 'token ${TOKEN}', 'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}',
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}) })
urllib.request.urlopen(req) urllib.request.urlopen(req)
@@ -289,24 +210,6 @@ jobs:
echo "Release notes updated from CHANGELOG.md" echo "Release notes updated from CHANGELOG.md"
fi fi
# Promote [Unreleased] → [version] in CHANGELOG.md and reset
if [ -n "$VERSION" ] && [ -f "CHANGELOG.md" ]; then
DATE=$(date +%Y-%m-%d)
python3 -c "
import sys
version, date = sys.argv[1], sys.argv[2]
content = open('CHANGELOG.md').read()
old = '## [Unreleased]'
new = f'## [Unreleased]\n\n## [{version}] --- {date}'
content = content.replace(old, new, 1)
open('CHANGELOG.md', 'w').write(content)
" "$VERSION" "$DATE"
git add CHANGELOG.md
git commit -m "chore: promote changelog [Unreleased] → [${VERSION}]" || true
git push origin main || true
echo "Changelog promoted: [Unreleased] → [${VERSION}]"
fi
# -- STEP 9: Mirror to GitHub (stable only) -------------------------------- # -- STEP 9: Mirror to GitHub (stable only) --------------------------------
- name: "Step 9: Mirror release to GitHub" - name: "Step 9: Mirror release to GitHub"
if: >- if: >-
+3 -3
View File
@@ -4,10 +4,10 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Universal # INGROUP: MokoPlatform.Universal
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /.mokogitea/workflows/branch-cleanup.yml # PATH: /.mokogitea/workflows/branch-cleanup.yml
# VERSION: 01.00.00 # VERSION: 09.23.00
# BRIEF: Delete feature branches after PR merge # BRIEF: Delete feature branches after PR merge
name: "Branch Cleanup" name: "Branch Cleanup"
-191
View File
@@ -1,191 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.CI
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
# PATH: /.gitea/workflows/ci-generic.yml
# VERSION: 01.00.00
# BRIEF: CI pipeline — lint, validate, and test for generic projects (PHP + Node.js)
name: "Generic: Project CI"
on:
workflow_dispatch:
permissions:
contents: read
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
# ── Lint & Validate ───────────────────────────────────────────────────
lint:
name: Lint & Validate
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Detect toolchain
id: detect
run: |
HAS_PHP=false
HAS_NODE=false
[ -f "composer.json" ] && HAS_PHP=true
[ -f "package.json" ] && HAS_NODE=true
echo "has_php=$HAS_PHP" >> "$GITHUB_OUTPUT"
echo "has_node=$HAS_NODE" >> "$GITHUB_OUTPUT"
echo "Toolchain: PHP=$HAS_PHP Node=$HAS_NODE"
- name: Setup PHP
if: steps.detect.outputs.has_php == 'true'
run: |
if ! command -v php &> /dev/null; then
sudo apt-get update -qq
sudo apt-get install -y -qq php-cli php-mbstring php-xml >/dev/null 2>&1
fi
php -v
- name: Setup Node.js
if: steps.detect.outputs.has_node == 'true'
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install PHP dependencies
if: steps.detect.outputs.has_php == 'true'
run: |
if [ -f "composer.json" ]; then
composer install --no-interaction --prefer-dist --quiet 2>/dev/null || true
fi
- name: Install Node.js dependencies
if: steps.detect.outputs.has_node == 'true'
run: |
if [ -f "package.json" ]; then
npm ci --quiet 2>/dev/null || npm install --quiet 2>/dev/null || true
fi
- name: PHP syntax check
if: steps.detect.outputs.has_php == 'true'
run: |
ERRORS=0
while IFS= read -r -d '' file; do
if ! php -l "$file" 2>&1 | grep -q "No syntax errors"; then
echo "::error file=${file}::PHP syntax error"
ERRORS=$((ERRORS + 1))
fi
done < <(find . -name "*.php" -not -path "./.git/*" -not -path "./vendor/*" -not -path "./node_modules/*" -print0)
echo "## PHP Lint" >> $GITHUB_STEP_SUMMARY
if [ "$ERRORS" -eq 0 ]; then
echo "All PHP files passed syntax check." >> $GITHUB_STEP_SUMMARY
else
echo "${ERRORS} file(s) with syntax errors." >> $GITHUB_STEP_SUMMARY
exit 1
fi
- name: TypeScript/JavaScript lint
if: steps.detect.outputs.has_node == 'true'
run: |
if [ -f "node_modules/.bin/eslint" ]; then
npx eslint src/ --quiet 2>&1 || { echo "::error::ESLint errors found"; exit 1; }
echo "## ESLint" >> $GITHUB_STEP_SUMMARY
echo "All files passed ESLint." >> $GITHUB_STEP_SUMMARY
elif [ -f ".eslintrc.json" ] || [ -f ".eslintrc.js" ] || [ -f "eslint.config.js" ]; then
echo "::warning::ESLint config found but eslint not installed"
else
echo "No ESLint configured — skipping"
fi
- name: TypeScript compile check
if: steps.detect.outputs.has_node == 'true'
run: |
if [ -f "tsconfig.json" ] && [ -f "node_modules/.bin/tsc" ]; then
npx tsc --noEmit 2>&1 || { echo "::error::TypeScript compilation errors"; exit 1; }
echo "## TypeScript" >> $GITHUB_STEP_SUMMARY
echo "TypeScript compilation passed." >> $GITHUB_STEP_SUMMARY
fi
- name: PHPStan static analysis
if: steps.detect.outputs.has_php == 'true'
run: |
if [ -f "phpstan.neon" ] && [ -f "vendor/bin/phpstan" ]; then
vendor/bin/phpstan analyse --no-progress 2>&1 || { echo "::warning::PHPStan found issues"; }
fi
# ── Tests ─────────────────────────────────────────────────────────────
test:
name: Tests
runs-on: ubuntu-latest
needs: lint
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Detect toolchain
id: detect
run: |
HAS_PHP=false
HAS_NODE=false
[ -f "composer.json" ] && HAS_PHP=true
[ -f "package.json" ] && HAS_NODE=true
echo "has_php=$HAS_PHP" >> "$GITHUB_OUTPUT"
echo "has_node=$HAS_NODE" >> "$GITHUB_OUTPUT"
- name: Setup PHP
if: steps.detect.outputs.has_php == 'true'
run: |
if ! command -v php &> /dev/null; then
sudo apt-get update -qq
sudo apt-get install -y -qq php-cli php-mbstring php-xml >/dev/null 2>&1
fi
- name: Setup Node.js
if: steps.detect.outputs.has_node == 'true'
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: |
[ -f "composer.json" ] && composer install --no-interaction --prefer-dist --quiet 2>/dev/null || true
[ -f "package.json" ] && { npm ci --quiet 2>/dev/null || npm install --quiet 2>/dev/null || true; }
- name: Run PHP tests
if: steps.detect.outputs.has_php == 'true'
run: |
if [ -f "vendor/bin/phpunit" ]; then
vendor/bin/phpunit --testdox 2>&1
echo "## PHPUnit" >> $GITHUB_STEP_SUMMARY
echo "Tests passed." >> $GITHUB_STEP_SUMMARY
elif [ -f "phpunit.xml" ] || [ -f "phpunit.xml.dist" ]; then
echo "::warning::PHPUnit config found but phpunit not installed"
else
echo "No PHPUnit configured — skipping"
fi
- name: Run Node.js tests
if: steps.detect.outputs.has_node == 'true'
run: |
if jq -e '.scripts.test' package.json > /dev/null 2>&1; then
npm test 2>&1
echo "## Node.js Tests" >> $GITHUB_STEP_SUMMARY
echo "Tests passed." >> $GITHUB_STEP_SUMMARY
else
echo "No test script in package.json — skipping"
fi
- name: Build check
run: |
if [ -f "Makefile" ]; then
make build 2>&1 || echo "::warning::Build failed or not configured"
elif [ -f "package.json" ] && jq -e '.scripts.build' package.json > /dev/null 2>&1; then
npm run build 2>&1 || echo "::warning::Build failed"
fi
+6 -6
View File
@@ -4,18 +4,18 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: mokoplatform.CI # INGROUP: moko-platform.CI
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /.mokogitea/workflows/ci-platform.yml # PATH: /.mokogitea/workflows/ci-platform.yml
# VERSION: 09.23.00 # VERSION: 09.23.00
# BRIEF: mokoplatform CI — the standards engine validates itself # BRIEF: moko-platform CI — the standards engine validates itself
# #
# +========================================================================+ # +========================================================================+
# | MOKO-PLATFORM CI | # | MOKO-PLATFORM CI |
# +========================================================================+ # +========================================================================+
# | | # | |
# | This is NOT a generic CI workflow. This is the self-validation | # | This is NOT a generic CI workflow. This is the self-validation |
# | pipeline for the central mokoplatform enterprise engine. | # | pipeline for the central moko-platform enterprise engine. |
# | | # | |
# | It dogfoods every tool the platform ships to governed repos: | # | It dogfoods every tool the platform ships to governed repos: |
# | | # | |
@@ -29,7 +29,7 @@
# | | # | |
# +========================================================================+ # +========================================================================+
name: "Platform: mokoplatform CI" name: "Platform: moko-platform CI"
on: on:
push: push:
@@ -421,7 +421,7 @@ jobs:
- name: Check gate results - name: Check gate results
run: | run: |
{ {
echo "# mokoplatform CI" echo "# moko-platform CI"
echo "" echo ""
echo "| Gate | Job | Status |" echo "| Gate | Job | Status |"
echo "|---|---|---|" echo "|---|---|---|"
+11 -11
View File
@@ -4,10 +4,10 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Maintenance # INGROUP: moko-platform.Maintenance
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /.gitea/workflows/cleanup.yml # PATH: /.mokogitea/workflows/cleanup.yml
# VERSION: 01.00.00 # VERSION: 09.23.00
# BRIEF: Scheduled cleanup — delete merged branches and old workflow runs # BRIEF: Scheduled cleanup — delete merged branches and old workflow runs
name: "Universal: Repository Cleanup" name: "Universal: Repository Cleanup"
@@ -33,17 +33,17 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
token: ${{ secrets.GA_TOKEN }} token: ${{ secrets.MOKOGITEA_TOKEN }}
- name: Delete merged branches - name: Delete merged branches
env: env:
GA_TOKEN: ${{ secrets.GA_TOKEN }} GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
run: | run: |
echo "=== Merged Branch Cleanup ===" echo "=== Merged Branch Cleanup ==="
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}" API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
# List branches via API # List branches via API
BRANCHES=$(curl -sS -H "Authorization: token ${GA_TOKEN}" \ BRANCHES=$(curl -sS -H "Authorization: token ${GITEA_TOKEN}" \
"${API}/branches?limit=50" | jq -r '.[].name') "${API}/branches?limit=50" | jq -r '.[].name')
DELETED=0 DELETED=0
@@ -56,7 +56,7 @@ jobs:
# Check if branch is merged into main # Check if branch is merged into main
if git merge-base --is-ancestor "origin/${BRANCH}" origin/main 2>/dev/null; then if git merge-base --is-ancestor "origin/${BRANCH}" origin/main 2>/dev/null; then
echo " Deleting merged branch: ${BRANCH}" echo " Deleting merged branch: ${BRANCH}"
curl -sS -X DELETE -H "Authorization: token ${GA_TOKEN}" \ curl -sS -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \
"${API}/branches/${BRANCH}" 2>/dev/null || true "${API}/branches/${BRANCH}" 2>/dev/null || true
DELETED=$((DELETED + 1)) DELETED=$((DELETED + 1))
fi fi
@@ -66,20 +66,20 @@ jobs:
- name: Clean old workflow runs - name: Clean old workflow runs
env: env:
GA_TOKEN: ${{ secrets.GA_TOKEN }} GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
run: | run: |
echo "=== Workflow Run Cleanup ===" echo "=== Workflow Run Cleanup ==="
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}" API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
CUTOFF=$(date -d "30 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -v-30d +%Y-%m-%dT%H:%M:%SZ) CUTOFF=$(date -d "30 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -v-30d +%Y-%m-%dT%H:%M:%SZ)
# Get old completed runs # Get old completed runs
RUNS=$(curl -sS -H "Authorization: token ${GA_TOKEN}" \ RUNS=$(curl -sS -H "Authorization: token ${GITEA_TOKEN}" \
"${API}/actions/runs?status=completed&limit=50" | \ "${API}/actions/runs?status=completed&limit=50" | \
jq -r ".workflow_runs[] | select(.created_at < \"${CUTOFF}\") | .id" 2>/dev/null) jq -r ".workflow_runs[] | select(.created_at < \"${CUTOFF}\") | .id" 2>/dev/null)
DELETED=0 DELETED=0
for RUN_ID in $RUNS; do for RUN_ID in $RUNS; do
curl -sS -X DELETE -H "Authorization: token ${GA_TOKEN}" \ curl -sS -X DELETE -H "Authorization: token ${GITEA_TOKEN}" \
"${API}/actions/runs/${RUN_ID}" 2>/dev/null || true "${API}/actions/runs/${RUN_ID}" 2>/dev/null || true
DELETED=$((DELETED + 1)) DELETED=$((DELETED + 1))
done done
-126
View File
@@ -1,126 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Deploy
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API
# PATH: /templates/workflows/joomla/deploy-manual.yml.template
# VERSION: 04.07.00
# BRIEF: Manual SFTP deploy to dev server for Joomla repos
name: "Universal: Deploy to Dev (Manual)"
on:
workflow_dispatch:
inputs:
clear_remote:
description: 'Delete all remote files before uploading'
required: false
default: 'false'
type: boolean
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
permissions:
contents: read
jobs:
deploy:
name: SFTP Deploy to Dev
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Setup PHP
run: |
php -v && composer --version
- name: Setup MokoStandards tools
env:
GA_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
MOKO_CLONE_HOST: ${{ secrets.GA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }}
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}'
run: |
git clone --depth 1 --branch main --quiet \
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \
/tmp/mokostandards-api 2>/dev/null || true
if [ -d "/tmp/mokostandards-api" ] && [ -f "/tmp/mokostandards-api/composer.json" ]; then
cd /tmp/mokostandards-api && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
fi
- name: Check FTP configuration
id: check
env:
HOST: ${{ vars.DEV_FTP_HOST }}
PATH_VAR: ${{ vars.DEV_FTP_PATH }}
PORT: ${{ vars.DEV_FTP_PORT }}
run: |
if [ -z "$HOST" ] || [ -z "$PATH_VAR" ]; then
echo "DEV_FTP_HOST or DEV_FTP_PATH not configured -- cannot deploy"
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "host=$HOST" >> "$GITHUB_OUTPUT"
REMOTE="${PATH_VAR%/}"
echo "remote=$REMOTE" >> "$GITHUB_OUTPUT"
[ -z "$PORT" ] && PORT="22"
echo "port=$PORT" >> "$GITHUB_OUTPUT"
- name: Deploy via SFTP
if: steps.check.outputs.skip != 'true'
env:
SFTP_KEY: ${{ secrets.DEV_FTP_KEY }}
SFTP_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
SFTP_USER: ${{ vars.DEV_FTP_USERNAME }}
run: |
SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
[ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ -- nothing to deploy"; exit 0; }
printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \
"${{ steps.check.outputs.host }}" "${{ steps.check.outputs.port }}" "$SFTP_USER" "${{ steps.check.outputs.remote }}" \
> /tmp/sftp-config.json
if [ -n "$SFTP_KEY" ]; then
echo "$SFTP_KEY" > /tmp/deploy_key
chmod 600 /tmp/deploy_key
printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json
else
printf ',"password":"%s"}' "$SFTP_PASS" >> /tmp/sftp-config.json
fi
DEPLOY_ARGS=(--path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json)
[ "${{ inputs.clear_remote }}" = "true" ] && DEPLOY_ARGS+=(--clear-remote)
PLATFORM=$(php /tmp/mokostandards-api/cli/platform_detect.php --path . 2>/dev/null || true)
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards-api/deploy/deploy-joomla.php" ]; then
php /tmp/mokostandards-api/deploy/deploy-joomla.php "${DEPLOY_ARGS[@]}"
else
php /tmp/mokostandards-api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
fi
rm -f /tmp/deploy_key /tmp/sftp-config.json
- name: Summary
if: always()
run: |
if [ "${{ steps.check.outputs.skip }}" = "true" ]; then
echo "### Deploy Skipped -- FTP not configured" >> $GITHUB_STEP_SUMMARY
else
echo "### Manual Dev Deploy Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Host | \`${{ steps.check.outputs.host }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Remote | \`${{ steps.check.outputs.remote }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Clear | ${{ inputs.clear_remote }} |" >> $GITHUB_STEP_SUMMARY
fi
+7 -3
View File
@@ -4,10 +4,10 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Security # INGROUP: moko-platform.Security
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API # REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
# PATH: /templates/workflows/gitleaks.yml.template # PATH: /templates/workflows/gitleaks.yml.template
# VERSION: 01.00.00 # VERSION: 09.23.00
# BRIEF: Secret scanning — detect leaked credentials, API keys, and tokens # BRIEF: Secret scanning — detect leaked credentials, API keys, and tokens
# #
# +========================================================================+ # +========================================================================+
@@ -25,6 +25,10 @@
name: "Universal: Secret Scanning" name: "Universal: Secret Scanning"
on: on:
pull_request:
branches:
- main
- 'dev/**'
schedule: schedule:
- cron: '0 5 * * 1' # Weekly Monday 05:00 UTC - cron: '0 5 * * 1' # Weekly Monday 05:00 UTC
workflow_dispatch: workflow_dispatch:
+3 -3
View File
@@ -4,8 +4,8 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Automation # INGROUP: moko-platform.Automation
# VERSION: 09.33.00 # VERSION: 09.25.06
# BRIEF: Auto-create feature branch when an issue is opened # BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch" name: "Universal: Issue Branch"
@@ -28,7 +28,7 @@ jobs:
steps: steps:
- name: Create branch and comment - name: Create branch and comment
run: | run: |
TOKEN="${{ secrets.GA_TOKEN }}" TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}" API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
ISSUE_NUM="${{ github.event.issue.number }}" ISSUE_NUM="${{ github.event.issue.number }}"
ISSUE_TITLE="${{ github.event.issue.title }}" ISSUE_TITLE="${{ github.event.issue.title }}"
+4 -4
View File
@@ -4,10 +4,10 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Notifications # INGROUP: moko-platform.Notifications
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /.gitea/workflows/notify.yml # PATH: /.mokogitea/workflows/notify.yml
# VERSION: 01.00.00 # VERSION: 09.23.00
# BRIEF: Push notifications via ntfy on release success or workflow failure # BRIEF: Push notifications via ntfy on release success or workflow failure
name: "Universal: Notifications" name: "Universal: Notifications"
+6 -30
View File
@@ -96,32 +96,6 @@ jobs:
echo "Branch policy: OK (${HEAD} → ${BASE})" echo "Branch policy: OK (${HEAD} → ${BASE})"
echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY
# ── Secret Scanning ──────────────────────────────────────────────────
gitleaks:
name: Secret Scan
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Gitleaks
run: |
GITLEAKS_VERSION="8.21.2"
curl -sSL "https://github.com/gitleaks/gitleaks/releases/download/v${GITLEAKS_VERSION}/gitleaks_${GITLEAKS_VERSION}_linux_x64.tar.gz" \
| tar -xz -C /usr/local/bin gitleaks
- name: Scan PR commits for secrets
run: |
if gitleaks detect --source . --verbose \
--log-opts=${{ github.event.pull_request.base.sha }}..${{ github.event.pull_request.head.sha }} 2>&1; then
echo "**No secrets detected.**" >> $GITHUB_STEP_SUMMARY
else
echo "::error::Potential secrets detected in PR commits"
exit 1
fi
# ── Code Validation ──────────────────────────────────────────────────── # ── Code Validation ────────────────────────────────────────────────────
validate: validate:
name: Validate PR name: Validate PR
@@ -198,7 +172,8 @@ jobs:
if: steps.platform.outputs.platform == 'joomla' if: steps.platform.outputs.platform == 'joomla'
run: | run: |
MISSING=0 MISSING=0
SOURCE_DIR="src" SOURCE_DIR="source"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && exit 0 [ ! -d "$SOURCE_DIR" ] && exit 0
while IFS= read -r dir; do while IFS= read -r dir; do
if [ ! -f "${dir}/index.html" ]; then if [ ! -f "${dir}/index.html" ]; then
@@ -246,7 +221,7 @@ jobs:
echo "joomla.asset.json: valid" echo "joomla.asset.json: valid"
fi fi
# Validate all XML files in src/ are well-formed # Validate all XML files in source/src/ are well-formed
XML_ERRORS=0 XML_ERRORS=0
if command -v php &> /dev/null; then if command -v php &> /dev/null; then
while IFS= read -r -d '' xmlfile; do while IFS= read -r -d '' xmlfile; do
@@ -477,10 +452,11 @@ jobs:
- name: Verify package source - name: Verify package source
run: | run: |
SOURCE_DIR="src" SOURCE_DIR="source"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
if [ ! -d "$SOURCE_DIR" ]; then if [ ! -d "$SOURCE_DIR" ]; then
echo "::warning::No src/ or htdocs/ directory" echo "::warning::No source/, src/, or htdocs/ directory"
exit 0 exit 0
fi fi
FILE_COUNT=$(find "$SOURCE_DIR" -type f | wc -l) FILE_COUNT=$(find "$SOURCE_DIR" -type f | wc -l)
+12 -12
View File
@@ -4,8 +4,8 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Release # INGROUP: moko-platform.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /templates/workflows/universal/pre-release.yml.template # PATH: /templates/workflows/universal/pre-release.yml.template
# VERSION: 05.01.00 # VERSION: 05.01.00
# BRIEF: Auto pre-release on push to dev/alpha/beta/rc branches # BRIEF: Auto pre-release on push to dev/alpha/beta/rc branches
@@ -60,25 +60,25 @@ jobs:
token: ${{ secrets.MOKOGITEA_TOKEN }} token: ${{ secrets.MOKOGITEA_TOKEN }}
ref: ${{ github.ref_name }} ref: ${{ github.ref_name }}
- name: Setup mokocli tools - name: Setup moko-platform tools
env: env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
run: | run: |
# Use pre-installed /opt/mokocli if available (updated by cron every 6h) # Use pre-installed /opt/moko-platform if available (updated by cron every 6h)
if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/cli/manifest_element.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then if [ -f /opt/moko-platform/cli/version_bump.php ] && [ -f /opt/moko-platform/cli/manifest_element.php ] && [ -f /opt/moko-platform/vendor/autoload.php ]; then
echo Using pre-installed /opt/mokocli echo Using pre-installed /opt/moko-platform
echo MOKO_CLI=/opt/mokocli/cli >> $GITHUB_ENV echo MOKO_CLI=/opt/moko-platform/cli >> $GITHUB_ENV
else else
echo Falling back to fresh clone echo Falling back to fresh clone
if ! command -v composer > /dev/null 2>&1; then 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 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 fi
rm -rf /tmp/mokocli rm -rf /tmp/moko-platform-api
CLONE_URL=https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/mokocli.git 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/mokocli git clone --depth 1 --branch main --quiet $CLONE_URL /tmp/moko-platform-api
cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet cd /tmp/moko-platform-api && composer install --no-dev --no-interaction --quiet
echo MOKO_CLI=/tmp/mokocli/cli >> $GITHUB_ENV echo MOKO_CLI=/tmp/moko-platform-api/cli >> $GITHUB_ENV
fi fi
- name: Detect platform - name: Detect platform
-66
View File
@@ -1,66 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Universal
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/rc-revert.yml
# VERSION: 09.23.00
# BRIEF: Rename rc/ branch back to dev/ when PR is closed without merge
name: "RC Revert"
on:
pull_request:
types: [closed]
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
revert:
name: Rename rc/ back to dev/
runs-on: ubuntu-latest
if: >-
github.event.pull_request.merged == false &&
startsWith(github.event.pull_request.head.ref, 'rc/')
steps:
- name: Rename branch
run: |
BRANCH="${{ github.event.pull_request.head.ref }}"
SUFFIX="${BRANCH#rc/}"
DEV_BRANCH="dev/${SUFFIX}"
API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
# Create dev/ branch from rc/ branch
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X POST \
-H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
-d "{\"new_branch_name\": \"${DEV_BRANCH}\", \"old_branch_name\": \"${BRANCH}\"}" \
"${API}" 2>/dev/null || true)
if [ "$STATUS" = "201" ]; then
echo "Created branch: ${DEV_BRANCH}" >> $GITHUB_STEP_SUMMARY
else
echo "::error::Failed to create ${DEV_BRANCH} from ${BRANCH} (HTTP ${STATUS})"
exit 1
fi
# Delete rc/ branch
ENCODED=$(php -r "echo rawurlencode('${BRANCH}');")
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
-H "Authorization: token ${TOKEN}" \
"${API}/${ENCODED}" 2>/dev/null || true)
if [ "$STATUS" = "204" ]; then
echo "Deleted branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY
else
echo "::warning::Failed to delete ${BRANCH} (HTTP ${STATUS})"
fi
echo "### RC Reverted" >> $GITHUB_STEP_SUMMARY
echo "${BRANCH} → ${DEV_BRANCH}" >> $GITHUB_STEP_SUMMARY
+9 -8
View File
@@ -7,8 +7,8 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Validation # INGROUP: moko-platform.Validation
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli # REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform
# PATH: /templates/workflows/joomla/repo_health.yml.template # PATH: /templates/workflows/joomla/repo_health.yml.template
# VERSION: 09.23.00 # VERSION: 09.23.00
# BRIEF: Enforces repository guardrails by validating scripts governance, tooling availability, and core repository health artifacts. # BRIEF: Enforces repository guardrails by validating scripts governance, tooling availability, and core repository health artifacts.
@@ -33,8 +33,7 @@ on:
- scripts - scripts
- repo - repo
pull_request: pull_request:
branches: push:
- main
permissions: permissions:
contents: read contents: read
@@ -297,17 +296,19 @@ jobs:
missing_required=() missing_required=()
missing_optional=() missing_optional=()
# Source directory: src/ or htdocs/ (either is valid for extension repos) # Source directory: source/, src/, or htdocs/ (any is valid for extension repos)
SOURCE_DIR="" SOURCE_DIR=""
if [ -d "src" ]; then if [ -d "source" ]; then
SOURCE_DIR="source"
elif [ -d "src" ]; then
SOURCE_DIR="src" SOURCE_DIR="src"
elif [ -d "htdocs" ]; then elif [ -d "htdocs" ]; then
SOURCE_DIR="htdocs" SOURCE_DIR="htdocs"
elif [ -d "deploy" ] || [ -d "cli" ] || [ -d "monitoring" ]; then elif [ -d "deploy" ] || [ -d "cli" ] || [ -d "monitoring" ]; then
# Platform/tooling repos don't need src/ # Platform/tooling repos don't need source/
SOURCE_DIR="" SOURCE_DIR=""
else else
missing_required+=("src/ or htdocs/ (source directory required)") missing_required+=("source/ or src/ or htdocs/ (source directory required)")
fi fi
for item in "${required_artifacts[@]}"; do for item in "${required_artifacts[@]}"; do
+20 -4
View File
@@ -4,10 +4,10 @@
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Security # INGROUP: moko-platform.Security
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /.gitea/workflows/security-audit.yml # PATH: /.mokogitea/workflows/security-audit.yml
# VERSION: 01.00.00 # VERSION: 09.23.00
# BRIEF: Dependency vulnerability scanning for composer and npm packages # BRIEF: Dependency vulnerability scanning for composer and npm packages
name: "Universal: Security Audit" name: "Universal: Security Audit"
@@ -80,3 +80,19 @@ jobs:
-H "Priority: high" \ -H "Priority: high" \
-d "Security audit found vulnerabilities. Review dependency updates." \ -d "Security audit found vulnerabilities. Review dependency updates." \
"${NTFY_URL}/${NTFY_TOPIC}" || true "${NTFY_URL}/${NTFY_TOPIC}" || true
- name: Joomla version audit
if: always()
run: |
if [ -f "monitoring/joomla-version-audit.php" ] && [ -n "$JOOMLA_SITES" ]; then
echo "$JOOMLA_SITES" > /tmp/sites.json
php monitoring/joomla-version-audit.php --sites /tmp/sites.json || true
echo "### Joomla Version Audit" >> $GITHUB_STEP_SUMMARY
rm -f /tmp/sites.json
else
echo "Joomla audit skipped (no script or JOOMLA_SITES_JSON not configured)"
fi
env:
JOOMLA_SITES: ${{ vars.JOOMLA_SITES_JSON }}
@@ -1,103 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/sync-feature-versions.yml
# VERSION: 01.00.00
# BRIEF: Merge dev into open feature branches after version bumps
name: "Universal: Sync Feature Branch Versions"
on:
push:
branches:
- dev
workflow_dispatch:
permissions:
contents: write
env:
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
jobs:
sync:
name: Sync feature branches with dev
runs-on: ubuntu-latest
if: >-
github.event_name == 'workflow_dispatch' ||
contains(github.event.head_commit.message, 'chore(version)')
steps:
- name: Checkout dev
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: dev
token: ${{ secrets.MOKOGITEA_TOKEN }}
- name: Configure git
run: |
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]"
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
- name: Merge dev into feature branches
run: |
echo "=== Syncing feature branches with dev ==="
# Fetch all remote branches
git fetch origin
# Find feature branches (feature/*, fix/*, patch/*, hotfix/*, bugfix/*, chore/*)
BRANCHES=$(git branch -r --list 'origin/feature/*' 'origin/fix/*' 'origin/patch/*' 'origin/hotfix/*' 'origin/bugfix/*' 'origin/chore/*' | sed 's|origin/||; s/^[[:space:]]*//')
if [ -z "$BRANCHES" ]; then
echo "No feature branches found — nothing to sync"
exit 0
fi
SYNCED=0
SKIPPED=0
FAILED=0
for BRANCH in $BRANCHES; do
echo ""
echo "--- ${BRANCH} ---"
# Skip branches that are already up to date with dev
if git merge-base --is-ancestor dev "origin/${BRANCH}" 2>/dev/null; then
echo "Already up to date"
SKIPPED=$((SKIPPED + 1))
continue
fi
# Try to merge dev into the branch
git checkout "origin/${BRANCH}" -B "$BRANCH" 2>/dev/null
if git merge dev --no-edit -m "chore: merge dev into ${BRANCH} (version sync) [skip ci]" 2>/dev/null; then
git push origin "$BRANCH" 2>/dev/null
echo "Synced successfully"
SYNCED=$((SYNCED + 1))
else
git merge --abort 2>/dev/null || true
echo "Merge conflict — skipping (manual rebase needed)"
FAILED=$((FAILED + 1))
fi
done
# Return to dev
git checkout dev 2>/dev/null || true
echo ""
echo "=== Summary ==="
echo "Synced: ${SYNCED}"
echo "Already current: ${SKIPPED}"
echo "Conflicts (skipped): ${FAILED}"
if [ "$FAILED" -gt 0 ]; then
echo "::warning::${FAILED} branch(es) had merge conflicts and need manual rebase"
fi
@@ -1,73 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Universal
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/workflow-sync-trigger.yml
# VERSION: 01.01.00
# BRIEF: Trigger workflow sync to live repos when a PR is merged to main
name: "Universal: Workflow Sync Trigger"
on:
pull_request:
types: [closed]
branches:
- main
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
sync:
name: Sync workflows to live repos
runs-on: ubuntu-latest
if: >-
github.event.pull_request.merged == true &&
!contains(github.event.pull_request.title, '[skip sync]')
steps:
- name: Determine platform from repo name
id: platform
run: |
REPO="${{ github.event.repository.name }}"
case "$REPO" in
Template-Joomla) PLATFORM="joomla" ;;
Template-Dolibarr) PLATFORM="dolibarr" ;;
Template-Go) PLATFORM="go" ;;
Template-MCP) PLATFORM="mcp" ;;
Template-Generic) PLATFORM="" ;;
*) PLATFORM="" ;;
esac
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
echo "Platform: ${PLATFORM:-all}"
- name: Clone mokocli
env:
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
run: |
GITEA_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
git clone --depth 1 "${GITEA_URL}/MokoConsulting/mokocli.git" /tmp/mokocli
- name: Install dependencies
run: |
cd /tmp/mokocli
composer install --no-dev --no-interaction --quiet 2>/dev/null || true
- name: Run workflow sync
env:
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
run: |
ARGS="--token ${MOKOGITEA_TOKEN}"
ARGS="${ARGS} --org ${{ vars.GITEA_ORG || github.repository_owner }}"
ARGS="${ARGS} --phase repos"
PLATFORM="${{ steps.platform.outputs.platform }}"
if [ -n "$PLATFORM" ]; then
ARGS="${ARGS} --platform-filter ${PLATFORM}"
fi
php /tmp/mokocli/cli/workflow_sync.php ${ARGS}
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"metadata": { "metadata": {
"generated_at": "2026-03-10T19:51:42.238134Z", "generated_at": "2026-03-10T19:51:42.238134Z",
"repository": "MokoConsulting/mokoplatform", "repository": "MokoConsulting/moko-platform",
"version": "1.0.0" "version": "1.0.0"
}, },
"scripts": [ "scripts": [
+30 -8
View File
@@ -2,9 +2,9 @@
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech> Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later SPDX-License-Identifier: GPL-3.0-or-later
FILE INFORMATION FILE INFORMATION
DEFGROUP: MokoCli.Root DEFGROUP: MokoStandards.Root
INGROUP: MokoCli INGROUP: MokoStandards
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
PATH: /CHANGELOG.md PATH: /CHANGELOG.md
BRIEF: Release changelog BRIEF: Release changelog
--> -->
@@ -12,12 +12,34 @@ BRIEF: Release changelog
# Changelog # Changelog
## [Unreleased] ## [Unreleased]
## [09.33.00] --- 2026-06-21 ### Added
- `workflow_sync.php` — cascading workflow sync from Generic → platform templates → live repos based on manifest.platform
- `platform_detect.php` — auto-detect repo platform type (joomla/dolibarr/go/mcp/platform/generic) from file structure, optionally update manifest
- Version prefix support in `version_read.php` and `version_bump.php` — repos with `<version_prefix>` in manifest (e.g. MokoGitea: `1.26.1+moko.`) get prefix-aware version scanning and bumping
- Platform types: joomla, dolibarr, go, mcp, platform, generic
- Template-Go and Template-MCP repos created
## [09.32.00] --- 2026-06-21 ### Changed
- `auto-release.yml` — patch branches (fix/*, patch/*, hotfix/*, bugfix/*) use `--bump none` (pre-release already bumped); feature/dev branches bump minor
- `pre-release.yml` — triggers on push to dev, fix/**, patch/**, hotfix/**, bugfix/**, alpha, beta, rc branches
- Version format standardized: `[prefix]XX.YY.ZZ` in source files, suffix (`-dev`, `-rc`) added by release system only
## [09.32.00] --- 2026-06-21 ## [09.25.00] --- 2026-06-04
## [09.31.00] --- 2026-06-21 ## [09.23] --- 2026-05-31
## [09.31.00] --- 2026-06-21 ## [09.22] --- 2026-05-31
### Changed
- **refactor(cli):** migrate 64 legacy scripts to CliFramework (#235) — all tools in cli/, automation/, maintenance/, deploy/, release/ now extend CliFramework with free --help, --verbose, --quiet, --dry-run, --json, banners, and coloured logging
### Fixed
- fix: auto-detect org/repo in updates_xml_build from manifest and git remote
- fix: restore hyphen in version suffixes
- fix: release names use standardized format
- fix: remove lesser stream copies, each stream updates independently
- fix: sort updates.xml entries dev first, stable last
## [09.21] --- 2026-05-30
## [09.20] --- 2026-05-30
+2 -2
View File
@@ -4,14 +4,14 @@ SPDX-License-Identifier: GPL-3.0-or-later
FILE INFORMATION FILE INFORMATION
DEFGROUP: MokoPlatform.Root DEFGROUP: MokoPlatform.Root
INGROUP: MokoPlatform INGROUP: MokoPlatform
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
PATH: /PLUGIN_SCRIPTS.md PATH: /PLUGIN_SCRIPTS.md
BRIEF: Plugin system CLI documentation BRIEF: Plugin system CLI documentation
--> -->
# Plugin System CLI Scripts # Plugin System CLI Scripts
Command-line scripts for validating, health checking, and managing projects using the mokoplatform plugin system. Command-line scripts for validating, health checking, and managing projects using the moko-platform plugin system.
## Available Scripts ## Available Scripts
+6 -6
View File
@@ -4,20 +4,20 @@ SPDX-License-Identifier: GPL-3.0-or-later
FILE INFORMATION FILE INFORMATION
DEFGROUP: MokoPlatform.Root DEFGROUP: MokoPlatform.Root
INGROUP: MokoPlatform INGROUP: MokoPlatform
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
PATH: /README.md PATH: /README.md
VERSION: 09.33.00 VERSION: 09.25.06
BRIEF: Project overview and documentation BRIEF: Project overview and documentation
--> -->
# mokoplatform Enterprise API # moko-platform Enterprise API
![Version](https://img.shields.io/badge/version-09.01.00-blue) ![PHP](https://img.shields.io/badge/PHP-8.1%2B-777BB4) ![License](https://img.shields.io/badge/license-GPL--3.0--or--later-green) ![Version](https://img.shields.io/badge/version-09.01.00-blue) ![PHP](https://img.shields.io/badge/PHP-8.1%2B-777BB4) ![License](https://img.shields.io/badge/license-GPL--3.0--or--later-green)
PHP implementation of mokoplatform — enterprise standards, automation framework, workflow templates, and bulk sync tooling. PHP implementation of moko-platform — enterprise standards, automation framework, workflow templates, and bulk sync tooling.
> **Primary platform**: [Gitea — git.mokoconsulting.tech](https://git.mokoconsulting.tech/MokoConsulting/MokoCli-API) > **Primary platform**: [Gitea — git.mokoconsulting.tech](https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API)
> **Backup mirror**: [GitHub](https://github.com/MokoConsulting/MokoCli-API) *(read-only mirror)* > **Backup mirror**: [GitHub](https://github.com/MokoConsulting/MokoStandards-API) *(read-only mirror)*
## What Lives Here ## What Lives Here
+1 -1
View File
@@ -4,7 +4,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
FILE INFORMATION FILE INFORMATION
DEFGROUP: MokoPlatform.Index DEFGROUP: MokoPlatform.Index
INGROUP: MokoPlatform.Analysis INGROUP: MokoPlatform.Analysis
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
PATH: /analysis/index.md PATH: /analysis/index.md
BRIEF: Analysis directory index BRIEF: Analysis directory index
--> -->
+4 -4
View File
@@ -11,7 +11,7 @@
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.Automation * DEFGROUP: MokoPlatform.Automation
* INGROUP: MokoPlatform.Scripts * INGROUP: MokoPlatform.Scripts
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /automation/bulk_joomla_template.php * PATH: /automation/bulk_joomla_template.php
* BRIEF: Bulk scaffold and sync Joomla template repositories * BRIEF: Bulk scaffold and sync Joomla template repositories
* *
@@ -28,7 +28,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{ use MokoEnterprise\{
AuditLogger, AuditLogger,
CliFramework, CliFramework,
Config, Config,
@@ -42,7 +42,7 @@ use MokoCli\{
* *
* Provides three operations for Joomla template projects: * Provides three operations for Joomla template projects:
* --scaffold: Create a new template repository with the full directory structure * --scaffold: Create a new template repository with the full directory structure
* --sync: Push mokoplatform files to existing template repositories * --sync: Push moko-platform files to existing template repositories
* --list: List all repositories tagged as joomla-template * --list: List all repositories tagged as joomla-template
* *
* Works with both GitHub and Gitea via the PlatformAdapterFactory. * Works with both GitHub and Gitea via the PlatformAdapterFactory.
@@ -318,7 +318,7 @@ class BulkJoomlaTemplate extends CliFramework
$name, $name,
$path, $path,
$content, $content,
"chore: update {$path} from mokoplatform", "chore: update {$path} from moko-platform",
$existingSha, $existingSha,
$branch $branch
); );
+41 -41
View File
@@ -11,7 +11,7 @@
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.Automation * DEFGROUP: MokoPlatform.Automation
* INGROUP: MokoPlatform.Scripts * INGROUP: MokoPlatform.Scripts
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /automation/bulk_sync.php * PATH: /automation/bulk_sync.php
* BRIEF: Enterprise-grade bulk repository synchronization * BRIEF: Enterprise-grade bulk repository synchronization
*/ */
@@ -21,7 +21,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{ use MokoEnterprise\{
ApiClient, ApiClient,
AuditLogger, AuditLogger,
CheckpointManager, CheckpointManager,
@@ -42,7 +42,7 @@ use MokoCli\{
/** /**
* Bulk Repository Synchronization Tool * Bulk Repository Synchronization Tool
* *
* Synchronizes mokoplatform files across multiple repositories using * Synchronizes moko-platform files across multiple repositories using
* the Enterprise library for robust, audited operations. * the Enterprise library for robust, audited operations.
*/ */
class BulkSync extends CliFramework class BulkSync extends CliFramework
@@ -95,7 +95,7 @@ class BulkSync extends CliFramework
*/ */
protected function run(): int protected function run(): int
{ {
$this->log("🚀 mokoplatform Bulk Synchronization v" . self::VERSION, 'INFO'); $this->log("🚀 moko-platform Bulk Synchronization v" . self::VERSION, 'INFO');
// Initialize enterprise components // Initialize enterprise components
if (!$this->initializeComponents()) { if (!$this->initializeComponents()) {
@@ -180,7 +180,7 @@ class BulkSync extends CliFramework
$results['health'] = $this->runHealthChecksAll($org, $repositories); $results['health'] = $this->runHealthChecksAll($org, $repositories);
} }
// Create/update tracking issue in mokoplatform // Create/update tracking issue in moko-platform
$this->createSyncIssue($org, $results); $this->createSyncIssue($org, $results);
// Create/update a failure issue when any repos failed // Create/update a failure issue when any repos failed
@@ -244,7 +244,7 @@ class BulkSync extends CliFramework
* Filter repositories based on include/exclude lists * Filter repositories based on include/exclude lists
*/ */
/** Repositories that are permanently excluded from bulk sync. */ /** Repositories that are permanently excluded from bulk sync. */
private const ALWAYS_EXCLUDE = ['mokoplatform', '.github-private']; private const ALWAYS_EXCLUDE = ['moko-platform', '.github-private'];
private function filterRepositories(array $repositories, array $include, array $exclude): array private function filterRepositories(array $repositories, array $include, array $exclude): array
{ {
@@ -426,7 +426,7 @@ class BulkSync extends CliFramework
$this->log("", 'ERROR'); $this->log("", 'ERROR');
$this->log("Required Implementation:", 'ERROR'); $this->log("Required Implementation:", 'ERROR');
$this->log(" 1. Clone/fetch target repository", 'ERROR'); $this->log(" 1. Clone/fetch target repository", 'ERROR');
$this->log(" 2. Apply file updates based on mokoplatform configuration", 'ERROR'); $this->log(" 2. Apply file updates based on moko-platform configuration", 'ERROR');
$this->log(" 3. Create pull request with changes", 'ERROR'); $this->log(" 3. Create pull request with changes", 'ERROR');
$this->log(" 4. Handle merge conflicts and validation", 'ERROR'); $this->log(" 4. Handle merge conflicts and validation", 'ERROR');
$this->log("", 'ERROR'); $this->log("", 'ERROR');
@@ -837,7 +837,7 @@ class BulkSync extends CliFramework
} }
/** /**
* Ensure all standard mokoplatform labels exist on a target repository. * Ensure all standard moko-platform labels exist on a target repository.
* *
* Fetches existing labels first (GET) and only POSTs the ones that are * Fetches existing labels first (GET) and only POSTs the ones that are
* missing. This avoids the 422 "already exists" responses that would * missing. This avoids the 422 "already exists" responses that would
@@ -872,7 +872,7 @@ class BulkSync extends CliFramework
// Workflow / Process // Workflow / Process
['automation', '8B4513', 'Automated processes or scripts'], ['automation', '8B4513', 'Automated processes or scripts'],
['mokoplatform', 'B60205', 'mokoplatform compliance'], ['moko-platform', 'B60205', 'moko-platform compliance'],
['needs-review', 'FBCA04', 'Awaiting code review'], ['needs-review', 'FBCA04', 'Awaiting code review'],
['work-in-progress', 'D93F0B', 'Work in progress, not ready for merge'], ['work-in-progress', 'D93F0B', 'Work in progress, not ready for merge'],
['breaking-change', 'D73A4A', 'Breaking API or functionality change'], ['breaking-change', 'D73A4A', 'Breaking API or functionality change'],
@@ -912,8 +912,8 @@ class BulkSync extends CliFramework
['health: poor', 'FF6B6B', 'Health score below 50'], ['health: poor', 'FF6B6B', 'Health score below 50'],
// Sync / Automation (used by bulk_sync, scan_drift, check_repo_health) // Sync / Automation (used by bulk_sync, scan_drift, check_repo_health)
['standards-update', 'B60205', 'mokoplatform sync update'], ['standards-update', 'B60205', 'moko-platform sync update'],
['standards-drift', 'FBCA04', 'Repository drifted from mokoplatform'], ['standards-drift', 'FBCA04', 'Repository drifted from moko-platform'],
['sync-report', '0075CA', 'Bulk sync run report'], ['sync-report', '0075CA', 'Bulk sync run report'],
['sync-failure', 'D73A4A', 'Bulk sync failure requiring attention'], ['sync-failure', 'D73A4A', 'Bulk sync failure requiring attention'],
['push-failure', 'D73A4A', 'File push failure requiring attention'], ['push-failure', 'D73A4A', 'File push failure requiring attention'],
@@ -925,10 +925,10 @@ class BulkSync extends CliFramework
['type: version', '0E8A16', 'Version-related change'], ['type: version', '0E8A16', 'Version-related change'],
]; ];
// Quick check: if the repo already has the 'mokoplatform' label, it was // Quick check: if the repo already has the 'moko-platform' label, it was
// provisioned previously — skip the expensive full label provisioning. // provisioned previously — skip the expensive full label provisioning.
try { try {
$probe = $this->api->get("/repos/{$org}/{$repo}/labels/mokoplatform"); $probe = $this->api->get("/repos/{$org}/{$repo}/labels/moko-platform");
if (!empty($probe['name'])) { if (!empty($probe['name'])) {
return; // already provisioned return; // already provisioned
} }
@@ -1024,7 +1024,7 @@ class BulkSync extends CliFramework
*/ */
private function updateOpenBranches(string $org, string $repo): void private function updateOpenBranches(string $org, string $repo): void
{ {
$syncBranchPrefix = 'chore/sync-mokoplatform-'; $syncBranchPrefix = 'chore/sync-moko-platform-';
try { try {
$defaultBranch = 'main'; $defaultBranch = 'main';
@@ -1055,7 +1055,7 @@ class BulkSync extends CliFramework
$this->api->post("/repos/{$org}/{$repo}/merges", [ $this->api->post("/repos/{$org}/{$repo}/merges", [
'base' => $branch, 'base' => $branch,
'head' => $defaultBranch, 'head' => $defaultBranch,
'commit_message' => "chore: merge {$defaultBranch} into {$branch} (mokoplatform sync)", 'commit_message' => "chore: merge {$defaultBranch} into {$branch} (moko-platform sync)",
]); ]);
$this->log(" 🔀 Merged {$defaultBranch}{$branch} (PR #{$prNum})", 'INFO'); $this->log(" 🔀 Merged {$defaultBranch}{$branch} (PR #{$prNum})", 'INFO');
} catch (\Exception $e) { } catch (\Exception $e) {
@@ -1076,7 +1076,7 @@ class BulkSync extends CliFramework
/** /**
* Records which sync run touched the repo, the PR number, and the * Records which sync run touched the repo, the PR number, and the
* mokoplatform version that was applied — giving each repo a clear audit * moko-platform version that was applied — giving each repo a clear audit
* trail of what was changed and why. * trail of what was changed and why.
*/ */
/** /**
@@ -1119,16 +1119,16 @@ class BulkSync extends CliFramework
$minor = self::VERSION_MINOR; $minor = self::VERSION_MINOR;
$force = isset($this->options['force']) ? ' *(--force)*' : ''; $force = isset($this->options['force']) ? ' *(--force)*' : '';
$prLink = $this->adapter->getPullRequestWebUrl($org, $repo, $prNumber); $prLink = $this->adapter->getPullRequestWebUrl($org, $repo, $prNumber);
$source = $this->adapter->getRepoWebUrl($org, 'mokoplatform'); $source = $this->adapter->getRepoWebUrl($org, 'moko-platform');
$branchName = 'chore/sync-mokoplatform-v' . $minor; $branchName = 'chore/sync-moko-platform-v' . $minor;
$branchLink = $this->adapter->getBranchWebUrl($org, $repo, $branchName); $branchLink = $this->adapter->getBranchWebUrl($org, $repo, $branchName);
$title = "chore: mokoplatform v{$minor} sync tracking"; $title = "chore: moko-platform v{$minor} sync tracking";
$body = <<<MD $body = <<<MD
## mokoplatform Sync Applied ## moko-platform Sync Applied
A mokoplatform bulk sync run has updated files in this repository. A moko-platform bulk sync run has updated files in this repository.
| Field | Value | | Field | Value |
|-------|-------| |-------|-------|
@@ -1144,13 +1144,13 @@ class BulkSync extends CliFramework
Protected files (README, CHANGELOG, GOVERNANCE, etc.) were not overwritten. Protected files (README, CHANGELOG, GOVERNANCE, etc.) were not overwritten.
--- ---
*Updated automatically by [mokoplatform]({$source}) `bulk_sync.php`* *Updated automatically by [moko-platform]({$source}) `bulk_sync.php`*
MD; MD;
// Dedent heredoc // Dedent heredoc
$body = preg_replace('/^ /m', '', $body); $body = preg_replace('/^ /m', '', $body);
$labelNames = ['standards-update', 'mokoplatform', 'type: chore', 'automation']; $labelNames = ['standards-update', 'moko-platform', 'type: chore', 'automation'];
$labels = $this->resolveLabelIds($org, $repo, $labelNames); $labels = $this->resolveLabelIds($org, $repo, $labelNames);
try { try {
@@ -1213,7 +1213,7 @@ class BulkSync extends CliFramework
} }
/** /**
* Create a tracking issue in mokoplatform for this sync run. * Create a tracking issue in moko-platform for this sync run.
*/ */
private function createSyncIssue(string $org, array $results): void private function createSyncIssue(string $org, array $results): void
{ {
@@ -1232,7 +1232,7 @@ class BulkSync extends CliFramework
$issues = $results['issues'] ?? []; $issues = $results['issues'] ?? [];
// Stable title — no timestamp so repeated runs update a single issue // Stable title — no timestamp so repeated runs update a single issue
$title = "sync: mokoplatform v" . self::VERSION_MINOR . " bulk sync report"; $title = "sync: moko-platform v" . self::VERSION_MINOR . " bulk sync report";
$protection = $results['protection'] ?? []; $protection = $results['protection'] ?? [];
$hasProtect = !empty($protection); $hasProtect = !empty($protection);
@@ -1281,7 +1281,7 @@ class BulkSync extends CliFramework
: "|---|---|---|---|"; : "|---|---|---|---|";
$body = <<<MD $body = <<<MD
## mokoplatform Bulk Sync Report ## moko-platform Bulk Sync Report
**Organisation:** `{$org}` **Organisation:** `{$org}`
**Triggered:** {$now}{$force} **Triggered:** {$now}{$force}
@@ -1301,7 +1301,7 @@ class BulkSync extends CliFramework
try { try {
// Search for existing issue by label — any state so we can reopen closed ones // Search for existing issue by label — any state so we can reopen closed ones
$existing = $this->api->get("/repos/{$org}/mokoplatform/issues", [ $existing = $this->api->get("/repos/{$org}/moko-platform/issues", [
'labels' => 'sync-report', 'labels' => 'sync-report',
'state' => 'all', 'state' => 'all',
'per_page' => 1, 'per_page' => 1,
@@ -1309,8 +1309,8 @@ class BulkSync extends CliFramework
'direction' => 'desc', 'direction' => 'desc',
]); ]);
$labelNames = ['sync-report', 'mokoplatform', 'type: chore', 'automation']; $labelNames = ['sync-report', 'moko-platform', 'type: chore', 'automation'];
$labels = $this->resolveLabelIds($org, 'mokoplatform', $labelNames); $labels = $this->resolveLabelIds($org, 'moko-platform', $labelNames);
$existing = array_values($existing); $existing = array_values($existing);
if (!empty($existing) && isset($existing[0]['number'])) { if (!empty($existing) && isset($existing[0]['number'])) {
@@ -1319,22 +1319,22 @@ class BulkSync extends CliFramework
if (($existing[0]['state'] ?? 'open') === 'closed') { if (($existing[0]['state'] ?? 'open') === 'closed') {
$patch['state'] = 'open'; $patch['state'] = 'open';
} }
$this->api->patch("/repos/{$org}/mokoplatform/issues/{$issueNumber}", $patch); $this->api->patch("/repos/{$org}/moko-platform/issues/{$issueNumber}", $patch);
try { try {
$this->api->post("/repos/{$org}/mokoplatform/issues/{$issueNumber}/labels", ['labels' => $labels]); $this->api->post("/repos/{$org}/moko-platform/issues/{$issueNumber}/labels", ['labels' => $labels]);
} catch (\Exception $le) { } catch (\Exception $le) {
/* non-fatal */ /* non-fatal */
} }
$this->log("📋 Sync report issue updated: {$org}/mokoplatform#{$issueNumber}", 'INFO'); $this->log("📋 Sync report issue updated: {$org}/moko-platform#{$issueNumber}", 'INFO');
} else { } else {
$issue = $this->api->post("/repos/{$org}/mokoplatform/issues", [ $issue = $this->api->post("/repos/{$org}/moko-platform/issues", [
'title' => $title, 'title' => $title,
'body' => $body, 'body' => $body,
'labels' => $labels, 'labels' => $labels,
'assignees' => ['jmiller'], 'assignees' => ['jmiller'],
]); ]);
$issueNumber = $issue['number'] ?? '?'; $issueNumber = $issue['number'] ?? '?';
$this->log("📋 Sync report issue created: {$org}/mokoplatform#{$issueNumber}", 'INFO'); $this->log("📋 Sync report issue created: {$org}/moko-platform#{$issueNumber}", 'INFO');
} }
} catch (\Exception $e) { } catch (\Exception $e) {
$this->log("⚠️ Failed to create/update sync report issue: " . $e->getMessage(), 'WARN'); $this->log("⚠️ Failed to create/update sync report issue: " . $e->getMessage(), 'WARN');
@@ -1342,7 +1342,7 @@ class BulkSync extends CliFramework
} }
/** /**
* Create or update a failure issue in mokoplatform when repos fail to sync. * Create or update a failure issue in moko-platform when repos fail to sync.
* Uses the 'sync-failure' label so it is distinct from the run-report issue. * Uses the 'sync-failure' label so it is distinct from the run-report issue.
* Reopens a closed issue rather than creating a duplicate. * Reopens a closed issue rather than creating a duplicate.
*/ */
@@ -1388,7 +1388,7 @@ class BulkSync extends CliFramework
$body = preg_replace('/^ /m', '', $body); $body = preg_replace('/^ /m', '', $body);
try { try {
$existing = $this->api->get("/repos/{$org}/mokoplatform/issues", [ $existing = $this->api->get("/repos/{$org}/moko-platform/issues", [
'labels' => 'sync-failure', 'labels' => 'sync-failure',
'state' => 'all', 'state' => 'all',
'per_page' => 1, 'per_page' => 1,
@@ -1403,17 +1403,17 @@ class BulkSync extends CliFramework
if (($existing[0]['state'] ?? 'open') === 'closed') { if (($existing[0]['state'] ?? 'open') === 'closed') {
$patch['state'] = 'open'; $patch['state'] = 'open';
} }
$this->api->patch("/repos/{$org}/mokoplatform/issues/{$num}", $patch); $this->api->patch("/repos/{$org}/moko-platform/issues/{$num}", $patch);
$this->log("🚨 Failure issue #{$num} updated: {$org}/mokoplatform#{$num}", 'WARN'); $this->log("🚨 Failure issue #{$num} updated: {$org}/moko-platform#{$num}", 'WARN');
} else { } else {
$issue = $this->api->post("/repos/{$org}/mokoplatform/issues", [ $issue = $this->api->post("/repos/{$org}/moko-platform/issues", [
'title' => $title, 'title' => $title,
'body' => $body, 'body' => $body,
'labels' => $this->resolveLabelIds($org, 'mokoplatform', ['sync-failure']), 'labels' => $this->resolveLabelIds($org, 'moko-platform', ['sync-failure']),
'assignees' => ['jmiller'], 'assignees' => ['jmiller'],
]); ]);
$num = $issue['number'] ?? '?'; $num = $issue['number'] ?? '?';
$this->log("🚨 Failure issue created: {$org}/mokoplatform#{$num}", 'WARN'); $this->log("🚨 Failure issue created: {$org}/moko-platform#{$num}", 'WARN');
} }
} catch (\Exception $e) { } catch (\Exception $e) {
$this->log("⚠️ Could not create/update failure issue: " . $e->getMessage(), 'WARN'); $this->log("⚠️ Could not create/update failure issue: " . $e->getMessage(), 'WARN');
+9 -9
View File
@@ -8,7 +8,7 @@
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.Automation * DEFGROUP: MokoPlatform.Automation
* INGROUP: MokoPlatform * INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /automation/enrich_manifest_xml.php * PATH: /automation/enrich_manifest_xml.php
* BRIEF: Enrich XML manifests with repo-specific build and deploy details * BRIEF: Enrich XML manifests with repo-specific build and deploy details
* *
@@ -21,8 +21,8 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
use MokoCli\ManifestParser; use MokoEnterprise\MokoStandardsParser;
class EnrichManifestXmlCli extends CliFramework class EnrichManifestXmlCli extends CliFramework
{ {
@@ -43,10 +43,10 @@ class EnrichManifestXmlCli extends CliFramework
$skipStr = $this->getArgument('--skip'); $skipStr = $this->getArgument('--skip');
$skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : []; $skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : [];
$parser = new ManifestParser(); $parser = new MokoStandardsParser();
$tmpBase = sys_get_temp_dir() . '/moko-enrich-' . getmypid(); $tmpBase = sys_get_temp_dir() . '/moko-enrich-' . getmypid();
echo "=== mokoplatform XML Manifest Enrichment ===\n"; echo "=== moko-platform XML Manifest Enrichment ===\n";
echo "Mode: " . ($this->dryRun ? "DRY RUN" : "LIVE") . "\n"; echo "Mode: " . ($this->dryRun ? "DRY RUN" : "LIVE") . "\n";
if (!empty($skipRepos)) { if (!empty($skipRepos)) {
echo "Skipping: " . implode(', ', $skipRepos) . "\n"; echo "Skipping: " . implode(', ', $skipRepos) . "\n";
@@ -97,7 +97,7 @@ class EnrichManifestXmlCli extends CliFramework
} }
$manifestPath = "{$workDir}/.mokogitea/manifest.xml"; $manifestPath = "{$workDir}/.mokogitea/manifest.xml";
if (!file_exists($manifestPath) || !str_contains(file_get_contents($manifestPath), '<mokoplatform')) { if (!file_exists($manifestPath) || !str_contains(file_get_contents($manifestPath), '<moko-platform')) {
echo "SKIP (no XML manifest)\n"; echo "SKIP (no XML manifest)\n";
$stats['skipped']++; $stats['skipped']++;
$this->rmTree($workDir); $this->rmTree($workDir);
@@ -113,8 +113,8 @@ class EnrichManifestXmlCli extends CliFramework
} }
$enrichment['build']['language'] = $enrichment['build']['language'] $enrichment['build']['language'] = $enrichment['build']['language']
?? $repo['language'] ?? $repo['language']
?? ManifestParser::platformLanguage($platform); ?? MokoStandardsParser::platformLanguage($platform);
$enrichment['build']['package_type'] = $enrichment['build']['package_type'] ?? ManifestParser::platformPackageType($platform); $enrichment['build']['package_type'] = $enrichment['build']['package_type'] ?? MokoStandardsParser::platformPackageType($platform);
$enrichedXml = $this->enrichManifestXml($existingXml, $enrichment); $enrichedXml = $this->enrichManifestXml($existingXml, $enrichment);
$dc = count($enrichment['deploy'] ?? []); $dc = count($enrichment['deploy'] ?? []);
@@ -312,7 +312,7 @@ class EnrichManifestXmlCli extends CliFramework
return $xml; return $xml;
} }
$ns = ManifestParser::NAMESPACE_URI; $ns = MokoStandardsParser::NAMESPACE_URI;
$root = $dom->documentElement; $root = $dom->documentElement;
foreach (['build', 'deploy', 'scripts'] as $tag) { foreach (['build', 'deploy', 'scripts'] as $tag) {
+9 -9
View File
@@ -8,7 +8,7 @@
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.Automation * DEFGROUP: MokoPlatform.Automation
* INGROUP: MokoPlatform * INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /automation/enrich_mokostandards_xml.php * PATH: /automation/enrich_mokostandards_xml.php
* BRIEF: Enrich XML manifests with repo-specific build and deploy details * BRIEF: Enrich XML manifests with repo-specific build and deploy details
* *
@@ -21,8 +21,8 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
use MokoCli\ManifestParser; use MokoEnterprise\MokoStandardsParser;
class EnrichMokostandardsXmlCli extends CliFramework class EnrichMokostandardsXmlCli extends CliFramework
{ {
@@ -43,10 +43,10 @@ class EnrichMokostandardsXmlCli extends CliFramework
$skipStr = $this->getArgument('--skip'); $skipStr = $this->getArgument('--skip');
$skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : []; $skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : [];
$parser = new ManifestParser(); $parser = new MokoStandardsParser();
$tmpBase = sys_get_temp_dir() . '/moko-enrich-' . getmypid(); $tmpBase = sys_get_temp_dir() . '/moko-enrich-' . getmypid();
echo "=== mokoplatform XML Manifest Enrichment ===\n"; echo "=== moko-platform XML Manifest Enrichment ===\n";
echo "Mode: " . ($this->dryRun ? "DRY RUN" : "LIVE") . "\n"; echo "Mode: " . ($this->dryRun ? "DRY RUN" : "LIVE") . "\n";
if (!empty($skipRepos)) { if (!empty($skipRepos)) {
echo "Skipping: " . implode(', ', $skipRepos) . "\n"; echo "Skipping: " . implode(', ', $skipRepos) . "\n";
@@ -97,7 +97,7 @@ class EnrichMokostandardsXmlCli extends CliFramework
} }
$manifestPath = "{$workDir}/.mokogitea/manifest.xml"; $manifestPath = "{$workDir}/.mokogitea/manifest.xml";
if (!file_exists($manifestPath) || !str_contains(file_get_contents($manifestPath), '<mokoplatform')) { if (!file_exists($manifestPath) || !str_contains(file_get_contents($manifestPath), '<moko-platform')) {
echo "SKIP (no XML manifest)\n"; echo "SKIP (no XML manifest)\n";
$stats['skipped']++; $stats['skipped']++;
$this->rmTree($workDir); $this->rmTree($workDir);
@@ -113,8 +113,8 @@ class EnrichMokostandardsXmlCli extends CliFramework
} }
$enrichment['build']['language'] = $enrichment['build']['language'] $enrichment['build']['language'] = $enrichment['build']['language']
?? $repo['language'] ?? $repo['language']
?? ManifestParser::platformLanguage($platform); ?? MokoStandardsParser::platformLanguage($platform);
$enrichment['build']['package_type'] = $enrichment['build']['package_type'] ?? ManifestParser::platformPackageType($platform); $enrichment['build']['package_type'] = $enrichment['build']['package_type'] ?? MokoStandardsParser::platformPackageType($platform);
$enrichedXml = $this->enrichManifestXml($existingXml, $enrichment); $enrichedXml = $this->enrichManifestXml($existingXml, $enrichment);
$dc = count($enrichment['deploy'] ?? []); $dc = count($enrichment['deploy'] ?? []);
@@ -315,7 +315,7 @@ class EnrichMokostandardsXmlCli extends CliFramework
return $xml; return $xml;
} }
$ns = ManifestParser::NAMESPACE_URI; $ns = MokoStandardsParser::NAMESPACE_URI;
$root = $dom->documentElement; $root = $dom->documentElement;
foreach (['build', 'deploy', 'scripts'] as $tag) { foreach (['build', 'deploy', 'scripts'] as $tag) {
+1 -1
View File
@@ -4,7 +4,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
FILE INFORMATION FILE INFORMATION
DEFGROUP: MokoPlatform.Index DEFGROUP: MokoPlatform.Index
INGROUP: MokoPlatform.Automation INGROUP: MokoPlatform.Automation
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
PATH: /automation/index.md PATH: /automation/index.md
BRIEF: Automation directory index BRIEF: Automation directory index
--> -->
+9 -9
View File
@@ -10,14 +10,14 @@
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.Automation * DEFGROUP: MokoPlatform.Automation
* INGROUP: MokoPlatform * INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /automation/migrate_to_gitea.php * PATH: /automation/migrate_to_gitea.php
* BRIEF: Migrate repositories from GitHub to self-hosted Gitea instance * BRIEF: Migrate repositories from GitHub to self-hosted Gitea instance
* *
* USAGE * USAGE
* php automation/migrate_to_gitea.php --dry-run * php automation/migrate_to_gitea.php --dry-run
* php automation/migrate_to_gitea.php --repos MokoCRM MokoDoliMods * php automation/migrate_to_gitea.php --repos MokoCRM MokoDoliMods
* php automation/migrate_to_gitea.php --exclude mokoplatform --skip-archived * php automation/migrate_to_gitea.php --exclude moko-platform --skip-archived
* php automation/migrate_to_gitea.php --resume * php automation/migrate_to_gitea.php --resume
*/ */
@@ -25,12 +25,12 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
use MokoCli\CheckpointManager; use MokoEnterprise\CheckpointManager;
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
use MokoCli\Config; use MokoEnterprise\Config;
use MokoCli\PlatformAdapterFactory; use MokoEnterprise\PlatformAdapterFactory;
use MokoCli\GitHubAdapter; use MokoEnterprise\GitHubAdapter;
use MokoCli\MokoGiteaAdapter; use MokoEnterprise\MokoGiteaAdapter;
/** /**
* Gitea Migration Script * Gitea Migration Script
@@ -278,7 +278,7 @@ class MigrateToGitea extends CliFramework
try { try {
$this->gitea->createIssue( $this->gitea->createIssue(
$giteaOrg, $giteaOrg,
'mokoplatform', 'moko-platform',
'chore: GitHub → Gitea migration report — ' . count($results['migrated']) . ' repos migrated', 'chore: GitHub → Gitea migration report — ' . count($results['migrated']) . ' repos migrated',
$report, $report,
['labels' => ['automation', 'type: chore']] ['labels' => ['automation', 'type: chore']]
+21 -21
View File
@@ -11,7 +11,7 @@
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.Automation * DEFGROUP: MokoPlatform.Automation
* INGROUP: MokoPlatform.Scripts * INGROUP: MokoPlatform.Scripts
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /automation/push_files.php * PATH: /automation/push_files.php
* BRIEF: Push one or more specific files to one or more remote repositories * BRIEF: Push one or more specific files to one or more remote repositories
*/ */
@@ -21,7 +21,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{ use MokoEnterprise\{
ApiClient, ApiClient,
AuditLogger, AuditLogger,
CliFramework, CliFramework,
@@ -35,7 +35,7 @@ use MokoCli\{
/** /**
* Targeted File Push Tool * Targeted File Push Tool
* *
* Pushes one or more specific files from mokoplatform templates to one or * Pushes one or more specific files from moko-platform templates to one or
* more remote repositories — without running a full sync. * more remote repositories — without running a full sync.
* *
* Files are specified by their destination path as they appear in the target * Files are specified by their destination path as they appear in the target
@@ -81,7 +81,7 @@ class PushFiles extends CliFramework
*/ */
protected function run(): int protected function run(): int
{ {
$this->log('📦 mokoplatform File Push v' . self::VERSION, 'INFO'); $this->log('📦 moko-platform File Push v' . self::VERSION, 'INFO');
if (!$this->initializeComponents()) { if (!$this->initializeComponents()) {
return 1; return 1;
@@ -337,7 +337,7 @@ class PushFiles extends CliFramework
$prNumber = null; $prNumber = null;
if (!$direct) { if (!$direct) {
$prTitle = "chore: push " . count($entries) . " file(s) from mokoplatform"; $prTitle = "chore: push " . count($entries) . " file(s) from moko-platform";
$prBody = $this->buildPRBody($entries); $prBody = $this->buildPRBody($entries);
$pr = $this->adapter->createPullRequest( $pr = $this->adapter->createPullRequest(
$org, $org,
@@ -414,7 +414,7 @@ class PushFiles extends CliFramework
$message = !empty($customMessage) $message = !empty($customMessage)
? $customMessage ? $customMessage
: "chore: update {$destPath} from mokoplatform"; : "chore: update {$destPath} from moko-platform";
// Fetch existing file SHA (needed for updates) // Fetch existing file SHA (needed for updates)
$existingSha = null; $existingSha = null;
@@ -457,9 +457,9 @@ class PushFiles extends CliFramework
): void { ): void {
$now = gmdate('Y-m-d H:i:s') . ' UTC'; $now = gmdate('Y-m-d H:i:s') . ' UTC';
$version = self::VERSION; $version = self::VERSION;
$source = $this->adapter->getRepoWebUrl($org, 'mokoplatform'); $source = $this->adapter->getRepoWebUrl($org, 'moko-platform');
$title = "chore: mokoplatform file push tracking"; $title = "chore: moko-platform file push tracking";
$deliveryLine = $prNumber !== null $deliveryLine = $prNumber !== null
? "| **Pull request** | [#{$prNumber}](" . $this->adapter->getPullRequestWebUrl($org, $repo, $prNumber) . ") |" ? "| **Pull request** | [#{$prNumber}](" . $this->adapter->getPullRequestWebUrl($org, $repo, $prNumber) . ") |"
@@ -471,9 +471,9 @@ class PushFiles extends CliFramework
)); ));
$body = <<<MD $body = <<<MD
## mokoplatform File Push ## moko-platform File Push
One or more files were pushed to this repository from mokoplatform. One or more files were pushed to this repository from moko-platform.
| Field | Value | | Field | Value |
|-------|-------| |-------|-------|
@@ -487,12 +487,12 @@ class PushFiles extends CliFramework
{$fileRows} {$fileRows}
--- ---
*Generated automatically by [mokoplatform]({$source}) `push_files.php`* *Generated automatically by [moko-platform]({$source}) `push_files.php`*
MD; MD;
$body = preg_replace('/^ /m', '', $body); $body = preg_replace('/^ /m', '', $body);
$labels = ['standards-update', 'mokoplatform', 'type: chore', 'automation']; $labels = ['standards-update', 'moko-platform', 'type: chore', 'automation'];
try { try {
$existing = $this->api->get("/repos/{$org}/{$repo}/issues", [ $existing = $this->api->get("/repos/{$org}/{$repo}/issues", [
@@ -550,7 +550,7 @@ class PushFiles extends CliFramework
} }
/** /**
* Create or update a failure issue in mokoplatform when repos fail to receive files. * Create or update a failure issue in moko-platform when repos fail to receive files.
* Uses the 'push-failure' label. Reopens a closed issue rather than creating a duplicate. * Uses the 'push-failure' label. Reopens a closed issue rather than creating a duplicate.
*/ */
private function createFailureIssue(string $org, array $results): void private function createFailureIssue(string $org, array $results): void
@@ -598,7 +598,7 @@ class PushFiles extends CliFramework
$body = preg_replace('/^ /m', '', $body); $body = preg_replace('/^ /m', '', $body);
try { try {
$existing = $this->api->get("/repos/{$org}/mokoplatform/issues", [ $existing = $this->api->get("/repos/{$org}/moko-platform/issues", [
'labels' => 'push-failure', 'labels' => 'push-failure',
'state' => 'all', 'state' => 'all',
'per_page' => 1, 'per_page' => 1,
@@ -613,17 +613,17 @@ class PushFiles extends CliFramework
if (($existing[0]['state'] ?? 'open') === 'closed') { if (($existing[0]['state'] ?? 'open') === 'closed') {
$patch['state'] = 'open'; $patch['state'] = 'open';
} }
$this->api->patch("/repos/{$org}/mokoplatform/issues/{$num}", $patch); $this->api->patch("/repos/{$org}/moko-platform/issues/{$num}", $patch);
$this->log("🚨 Failure issue #{$num} updated: {$org}/mokoplatform#{$num}", 'WARN'); $this->log("🚨 Failure issue #{$num} updated: {$org}/moko-platform#{$num}", 'WARN');
} else { } else {
$issue = $this->api->post("/repos/{$org}/mokoplatform/issues", [ $issue = $this->api->post("/repos/{$org}/moko-platform/issues", [
'title' => $title, 'title' => $title,
'body' => $body, 'body' => $body,
'labels' => ['push-failure'], 'labels' => ['push-failure'],
'assignees' => ['jmiller'], 'assignees' => ['jmiller'],
]); ]);
$num = $issue['number'] ?? '?'; $num = $issue['number'] ?? '?';
$this->log("🚨 Failure issue created: {$org}/mokoplatform#{$num}", 'WARN'); $this->log("🚨 Failure issue created: {$org}/moko-platform#{$num}", 'WARN');
} }
} catch (\Exception $e) { } catch (\Exception $e) {
$this->log("⚠️ Could not create/update failure issue: " . $e->getMessage(), 'WARN'); $this->log("⚠️ Could not create/update failure issue: " . $e->getMessage(), 'WARN');
@@ -638,14 +638,14 @@ class PushFiles extends CliFramework
private function buildPRBody(array $entries): string private function buildPRBody(array $entries): string
{ {
$now = gmdate('Y-m-d H:i:s') . ' UTC'; $now = gmdate('Y-m-d H:i:s') . ' UTC';
$lines = ["## mokoplatform File Push\n", "**Pushed:** {$now}\n", '### Files\n']; $lines = ["## moko-platform File Push\n", "**Pushed:** {$now}\n", '### Files\n'];
foreach ($entries as $entry) { foreach ($entries as $entry) {
$lines[] = "- `{$entry['destination']}`"; $lines[] = "- `{$entry['destination']}`";
} }
$sourceUrl = $this->adapter->getRepoWebUrl(self::DEFAULT_ORG, 'mokoplatform'); $sourceUrl = $this->adapter->getRepoWebUrl(self::DEFAULT_ORG, 'moko-platform');
$lines[] = "\n---\n*Generated by [mokoplatform]({$sourceUrl}) `push_files.php`*"; $lines[] = "\n---\n*Generated by [moko-platform]({$sourceUrl}) `push_files.php`*";
return implode("\n", $lines); return implode("\n", $lines);
} }
+8 -8
View File
@@ -8,7 +8,7 @@
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.Automation * DEFGROUP: MokoPlatform.Automation
* INGROUP: MokoPlatform * INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /automation/push_manifest_xml.php * PATH: /automation/push_manifest_xml.php
* BRIEF: Push XML manifests to all governed repositories * BRIEF: Push XML manifests to all governed repositories
*/ */
@@ -18,8 +18,8 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
use MokoCli\ManifestParser; use MokoEnterprise\MokoStandardsParser;
class PushManifestXmlCli extends CliFramework class PushManifestXmlCli extends CliFramework
{ {
@@ -44,10 +44,10 @@ class PushManifestXmlCli extends CliFramework
$skipStr = $this->getArgument('--skip'); $skipStr = $this->getArgument('--skip');
$skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : []; $skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : [];
$parser = new ManifestParser(); $parser = new MokoStandardsParser();
$tmpBase = sys_get_temp_dir() . '/moko-manifest-push-' . getmypid(); $tmpBase = sys_get_temp_dir() . '/moko-manifest-push-' . getmypid();
echo "=== mokoplatform XML Manifest Push ===\n"; echo "=== moko-platform XML Manifest Push ===\n";
echo "Org: {$giteaOrg}\n"; echo "Org: {$giteaOrg}\n";
echo "Mode: " . ($this->dryRun ? "DRY RUN" : "LIVE") . "\n"; echo "Mode: " . ($this->dryRun ? "DRY RUN" : "LIVE") . "\n";
if ($repoFilter) { if ($repoFilter) {
@@ -97,8 +97,8 @@ class PushManifestXmlCli extends CliFramework
'description' => $repo['description'] ?? '', 'description' => $repo['description'] ?? '',
'license' => 'GPL-3.0-or-later', 'license' => 'GPL-3.0-or-later',
'topics' => $repo['topics'] ?? [], 'topics' => $repo['topics'] ?? [],
'language' => $repo['language'] ?? ManifestParser::platformLanguage($platform), 'language' => $repo['language'] ?? MokoStandardsParser::platformLanguage($platform),
'package_type' => ManifestParser::platformPackageType($platform), 'package_type' => MokoStandardsParser::platformPackageType($platform),
'last_synced' => date('c'), 'last_synced' => date('c'),
]); ]);
@@ -125,7 +125,7 @@ class PushManifestXmlCli extends CliFramework
// Check if already XML and up-to-date // Check if already XML and up-to-date
$manifestPath = "{$workDir}/.mokogitea/manifest.xml"; $manifestPath = "{$workDir}/.mokogitea/manifest.xml";
$existingIsXml = file_exists($manifestPath) && str_contains(file_get_contents($manifestPath), '<mokoplatform'); $existingIsXml = file_exists($manifestPath) && str_contains(file_get_contents($manifestPath), '<moko-platform');
if ($existingIsXml && !$force) { if ($existingIsXml && !$force) {
$existingPlatform = $parser->extractPlatform(file_get_contents($manifestPath)); $existingPlatform = $parser->extractPlatform(file_get_contents($manifestPath));
if ($existingPlatform === $platform) { if ($existingPlatform === $platform) {
+8 -8
View File
@@ -8,7 +8,7 @@
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.Automation * DEFGROUP: MokoPlatform.Automation
* INGROUP: MokoPlatform * INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /automation/push_mokostandards_xml.php * PATH: /automation/push_mokostandards_xml.php
* BRIEF: Push XML manifests to all governed repositories * BRIEF: Push XML manifests to all governed repositories
*/ */
@@ -18,8 +18,8 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
use MokoCli\ManifestParser; use MokoEnterprise\MokoStandardsParser;
class PushMokostandardsXmlCli extends CliFramework class PushMokostandardsXmlCli extends CliFramework
{ {
@@ -44,10 +44,10 @@ class PushMokostandardsXmlCli extends CliFramework
$skipStr = $this->getArgument('--skip'); $skipStr = $this->getArgument('--skip');
$skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : []; $skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : [];
$parser = new ManifestParser(); $parser = new MokoStandardsParser();
$tmpBase = sys_get_temp_dir() . '/moko-manifest-push-' . getmypid(); $tmpBase = sys_get_temp_dir() . '/moko-manifest-push-' . getmypid();
echo "=== mokoplatform XML Manifest Push ===\n"; echo "=== moko-platform XML Manifest Push ===\n";
echo "Org: {$giteaOrg}\n"; echo "Org: {$giteaOrg}\n";
echo "Mode: " . ($this->dryRun ? "DRY RUN" : "LIVE") . "\n"; echo "Mode: " . ($this->dryRun ? "DRY RUN" : "LIVE") . "\n";
if ($repoFilter) { if ($repoFilter) {
@@ -97,8 +97,8 @@ class PushMokostandardsXmlCli extends CliFramework
'description' => $repo['description'] ?? '', 'description' => $repo['description'] ?? '',
'license' => 'GPL-3.0-or-later', 'license' => 'GPL-3.0-or-later',
'topics' => $repo['topics'] ?? [], 'topics' => $repo['topics'] ?? [],
'language' => $repo['language'] ?? ManifestParser::platformLanguage($platform), 'language' => $repo['language'] ?? MokoStandardsParser::platformLanguage($platform),
'package_type' => ManifestParser::platformPackageType($platform), 'package_type' => MokoStandardsParser::platformPackageType($platform),
'last_synced' => date('c'), 'last_synced' => date('c'),
]); ]);
@@ -125,7 +125,7 @@ class PushMokostandardsXmlCli extends CliFramework
// Check if already XML and up-to-date // Check if already XML and up-to-date
$manifestPath = "{$workDir}/.mokogitea/manifest.xml"; $manifestPath = "{$workDir}/.mokogitea/manifest.xml";
$existingIsXml = file_exists($manifestPath) && str_contains(file_get_contents($manifestPath), '<mokoplatform'); $existingIsXml = file_exists($manifestPath) && str_contains(file_get_contents($manifestPath), '<moko-platform');
if ($existingIsXml && !$force) { if ($existingIsXml && !$force) {
$existingPlatform = $parser->extractPlatform(file_get_contents($manifestPath)); $existingPlatform = $parser->extractPlatform(file_get_contents($manifestPath));
if ($existingPlatform === $platform) { if ($existingPlatform === $platform) {
+10 -10
View File
@@ -11,7 +11,7 @@
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.Automation * DEFGROUP: MokoPlatform.Automation
* INGROUP: MokoPlatform.Scripts * INGROUP: MokoPlatform.Scripts
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /automation/repo_cleanup.php * PATH: /automation/repo_cleanup.php
* BRIEF: Enterprise repository cleanup — branches, PRs, issues, workflows, labels, logs * BRIEF: Enterprise repository cleanup — branches, PRs, issues, workflows, labels, logs
*/ */
@@ -21,7 +21,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{ApiClient, AuditLogger, CliFramework, Config, GitPlatformAdapter, MetricsCollector, PlatformAdapterFactory}; use MokoEnterprise\{ApiClient, AuditLogger, CliFramework, Config, GitPlatformAdapter, MetricsCollector, PlatformAdapterFactory};
/** /**
* Enterprise Repository Cleanup * Enterprise Repository Cleanup
@@ -39,14 +39,14 @@ use MokoCli\{ApiClient, AuditLogger, CliFramework, Config, GitPlatformAdapter, M
class RepoCleanup extends CliFramework class RepoCleanup extends CliFramework
{ {
private const VERSION = '09.23.00'; private const VERSION = '09.23.00';
private const SYNC_PREFIX = 'chore/sync-mokoplatform-'; private const SYNC_PREFIX = 'chore/sync-moko-platform-';
private const CURRENT_BRANCH = 'chore/sync-mokoplatform-v04.02.00'; private const CURRENT_BRANCH = 'chore/sync-moko-platform-v04.02.00';
/** Workflow files that have been retired and should be deleted from governed repos. */ /** Workflow files that have been retired and should be deleted from governed repos. */
private const RETIRED_WORKFLOWS = [ private const RETIRED_WORKFLOWS = [
'build.yml', 'code-quality.yml', 'release-cycle.yml', 'release-pipeline.yml', 'build.yml', 'code-quality.yml', 'release-cycle.yml', 'release-pipeline.yml',
'branch-cleanup.yml', 'auto-update-changelog.yml', 'enterprise-issue-manager.yml', 'branch-cleanup.yml', 'auto-update-changelog.yml', 'enterprise-issue-manager.yml',
'flush-actions-cache.yml', 'mokoplatform-script-runner.yml', 'unified-ci.yml', 'flush-actions-cache.yml', 'moko-platform-script-runner.yml', 'unified-ci.yml',
'unified-platform-testing.yml', 'reusable-build.yml', 'reusable-ci-validation.yml', 'unified-platform-testing.yml', 'reusable-build.yml', 'reusable-ci-validation.yml',
'reusable-deploy.yml', 'reusable-php-quality.yml', 'reusable-platform-testing.yml', 'reusable-deploy.yml', 'reusable-php-quality.yml', 'reusable-platform-testing.yml',
'reusable-project-detector.yml', 'reusable-release.yml', 'reusable-script-executor.yml', 'reusable-project-detector.yml', 'reusable-release.yml', 'reusable-script-executor.yml',
@@ -98,7 +98,7 @@ class RepoCleanup extends CliFramework
} }
$this->logMsg("🧹 mokoplatform Repository Cleanup v" . self::VERSION); $this->logMsg("🧹 moko-platform Repository Cleanup v" . self::VERSION);
$this->logMsg("Organization: {$org}"); $this->logMsg("Organization: {$org}");
$this->logMsg("Current sync branch: " . self::CURRENT_BRANCH); $this->logMsg("Current sync branch: " . self::CURRENT_BRANCH);
if ($this->dryRun) { if ($this->dryRun) {
@@ -225,7 +225,7 @@ class RepoCleanup extends CliFramework
} }
$allRepos = $this->adapter->listOrgRepos($org, $skipArchived); $allRepos = $this->adapter->listOrgRepos($org, $skipArchived);
return array_filter($allRepos, fn($r) => !in_array($r['name'], ['mokoplatform', '.github-private'], true)); return array_filter($allRepos, fn($r) => !in_array($r['name'], ['moko-platform', '.github-private'], true));
} }
// ─── Cleanup operations ────────────────────────────────────────────── // ─── Cleanup operations ──────────────────────────────────────────────
@@ -463,9 +463,9 @@ class RepoCleanup extends CliFramework
private function checkLabels(string $org, string $repo, array &$results): void private function checkLabels(string $org, string $repo, array &$results): void
{ {
try { try {
$this->api->get("/repos/{$org}/{$repo}/labels/mokoplatform"); $this->api->get("/repos/{$org}/{$repo}/labels/moko-platform");
} catch (\Exception $e) { } catch (\Exception $e) {
$this->logMsg(" ⚠️ Missing 'mokoplatform' label"); $this->logMsg(" ⚠️ Missing 'moko-platform' label");
$results['labels_missing']++; $results['labels_missing']++;
$this->api->resetCircuitBreaker(); $this->api->resetCircuitBreaker();
} }
@@ -479,7 +479,7 @@ class RepoCleanup extends CliFramework
if (preg_match('/^\s*VERSION:\s*(\d{2}\.\d{2}\.\d{2})/m', $content, $m)) { if (preg_match('/^\s*VERSION:\s*(\d{2}\.\d{2}\.\d{2})/m', $content, $m)) {
$version = $m[1]; $version = $m[1];
// Check manifest.xml for the tracked mokoplatform version // Check manifest.xml for the tracked moko-platform version
try { try {
$mokoFile = $this->api->get("/repos/{$org}/{$repo}/contents/.mokogitea/manifest.xml"); $mokoFile = $this->api->get("/repos/{$org}/{$repo}/contents/.mokogitea/manifest.xml");
$mokoContent = base64_decode($mokoFile['content'] ?? ''); $mokoContent = base64_decode($mokoFile['content'] ?? '');
+6 -9
View File
@@ -9,11 +9,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoCli.CLI * DEFGROUP: MokoStandards.CLI
* INGROUP: MokoCli * INGROUP: MokoStandards
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /bin/moko * PATH: /bin/moko
* BRIEF: Unified CLI dispatcher — run any MokoCli script without needing GitHub Actions * BRIEF: Unified CLI dispatcher — run any MokoStandards script without needing GitHub Actions
* *
* USAGE * USAGE
* php bin/moko <command> [options] (all platforms) * php bin/moko <command> [options] (all platforms)
@@ -220,9 +220,6 @@ const COMMAND_MAP = [
// Licensing // Licensing
'license' => 'cli/license_manage.php', 'license' => 'cli/license_manage.php',
// Security
'security:advisories' => 'security/advisory_scan.php',
// Shell completion // Shell completion
'completion' => 'cli/completion.php', 'completion' => 'cli/completion.php',
@@ -295,10 +292,10 @@ function printHelp(): void
{ {
echo <<<'HELP' echo <<<'HELP'
╔══════════════════════════════════════════════════════════╗ ╔══════════════════════════════════════════════════════════╗
║ MokoCli CLI (bin/moko) ║ ║ MokoStandards CLI (bin/moko) ║
╚══════════════════════════════════════════════════════════╝ ╚══════════════════════════════════════════════════════════╝
Run any MokoCli script locally without GitHub Actions. Run any MokoStandards script locally without GitHub Actions.
USAGE USAGE
php bin/moko <command> [options] (all platforms) php bin/moko <command> [options] (all platforms)
@@ -400,7 +397,7 @@ function loadPluginCommands(): array
$commands = []; $commands = [];
foreach (glob("{$pluginDir}/*Plugin.php") as $file) { foreach (glob("{$pluginDir}/*Plugin.php") as $file) {
$className = 'MokoCli\\Plugins\\' $className = 'MokoEnterprise\\Plugins\\'
. pathinfo($file, PATHINFO_FILENAME); . pathinfo($file, PATHINFO_FILENAME);
if (!class_exists($className)) { if (!class_exists($className)) {
+8 -8
View File
@@ -8,9 +8,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/archive_repo.php * PATH: /cli/archive_repo.php
* BRIEF: Gracefully retire a governed repository — archive, close issues/PRs, remove sync def * BRIEF: Gracefully retire a governed repository — archive, close issues/PRs, remove sync def
*/ */
@@ -20,9 +20,9 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
use MokoCli\Config; use MokoEnterprise\Config;
use MokoCli\PlatformAdapterFactory; use MokoEnterprise\PlatformAdapterFactory;
class ArchiveRepoCli extends CliFramework class ArchiveRepoCli extends CliFramework
{ {
@@ -135,7 +135,7 @@ class ArchiveRepoCli extends CliFramework
try { try {
$issue = $adapter->createIssue( $issue = $adapter->createIssue(
$org, $org,
'mokoplatform', 'moko-platform',
"chore: archived repository {$repoName}", "chore: archived repository {$repoName}",
"## Repository Archived\n\n" "## Repository Archived\n\n"
. "**Repository:** `{$org}/{$repoName}`\n" . "**Repository:** `{$org}/{$repoName}`\n"
@@ -150,7 +150,7 @@ class ArchiveRepoCli extends CliFramework
] ]
); );
if (isset($issue['number'])) { if (isset($issue['number'])) {
echo " Archival record: mokoplatform#{$issue['number']}\n"; echo " Archival record: moko-platform#{$issue['number']}\n";
} }
} catch (\Exception $e) { } catch (\Exception $e) {
echo " Warning: could not create archival record: " . $e->getMessage() . "\n"; echo " Warning: could not create archival record: " . $e->getMessage() . "\n";
+2 -2
View File
@@ -16,7 +16,7 @@
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.Enterprise.CLI * DEFGROUP: MokoPlatform.Enterprise.CLI
* INGROUP: MokoPlatform.Enterprise * INGROUP: MokoPlatform.Enterprise
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/audit_query.php * PATH: /cli/audit_query.php
* BRIEF: Search, filter, and export audit logs * BRIEF: Search, filter, and export audit logs
*/ */
@@ -25,7 +25,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
/** /**
* CLI tool to search, filter, and export audit logs. * CLI tool to search, filter, and export audit logs.
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/badge_update.php * PATH: /cli/badge_update.php
* BRIEF: Update [VERSION: XX.XX.XX] badges in all markdown files * BRIEF: Update [VERSION: XX.XX.XX] badges in all markdown files
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class BadgeUpdateCli extends CliFramework class BadgeUpdateCli extends CliFramework
{ {
+5 -5
View File
@@ -6,11 +6,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/branch_rename.php * PATH: /cli/branch_rename.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Rename a git branch via Gitea API (create new, update PR, delete old) * BRIEF: Rename a git branch via Gitea API (create new, update PR, delete old)
*/ */
@@ -18,7 +18,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class BranchRenameCli extends CliFramework class BranchRenameCli extends CliFramework
{ {
+7 -7
View File
@@ -8,11 +8,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/bulk_workflow_push.php * PATH: /cli/bulk_workflow_push.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Push a workflow file to all governed repos via the Gitea Contents API * BRIEF: Push a workflow file to all governed repos via the Gitea Contents API
*/ */
@@ -20,7 +20,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class BulkWorkflowPushCli extends CliFramework class BulkWorkflowPushCli extends CliFramework
{ {
@@ -154,7 +154,7 @@ class BulkWorkflowPushCli extends CliFramework
'content' => $encodedContent, 'content' => $encodedContent,
'sha' => $remoteSha, 'sha' => $remoteSha,
'message' => "chore: sync {$destPath} " 'message' => "chore: sync {$destPath} "
. "from mokoplatform [skip ci]", . "from moko-platform [skip ci]",
'branch' => $branch, 'branch' => $branch,
]); ]);
@@ -184,7 +184,7 @@ class BulkWorkflowPushCli extends CliFramework
$payload = json_encode([ $payload = json_encode([
'content' => $encodedContent, 'content' => $encodedContent,
'message' => "chore: add {$destPath} " 'message' => "chore: add {$destPath} "
. "from mokoplatform [skip ci]", . "from moko-platform [skip ci]",
'branch' => $branch, 'branch' => $branch,
]); ]);
+5 -5
View File
@@ -8,11 +8,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/bulk_workflow_trigger.php * PATH: /cli/bulk_workflow_trigger.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Trigger a workflow across multiple repos at once * BRIEF: Trigger a workflow across multiple repos at once
*/ */
@@ -20,7 +20,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class BulkWorkflowTriggerCli extends CliFramework class BulkWorkflowTriggerCli extends CliFramework
{ {
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/changelog_promote.php * PATH: /cli/changelog_promote.php
* BRIEF: Promote [Unreleased] section in CHANGELOG.md to a versioned entry * BRIEF: Promote [Unreleased] section in CHANGELOG.md to a versioned entry
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class ChangelogPromoteCli extends CliFramework class ChangelogPromoteCli extends CliFramework
{ {
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/changelog_prune.php * PATH: /cli/changelog_prune.php
* BRIEF: Prune old CHANGELOG.md entries — keeps [Unreleased] + last N releases * BRIEF: Prune old CHANGELOG.md entries — keeps [Unreleased] + last N releases
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class ChangelogPruneCli extends CliFramework class ChangelogPruneCli extends CliFramework
{ {
+5 -5
View File
@@ -8,11 +8,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/client_dashboard.php * PATH: /cli/client_dashboard.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Generate unified client dashboard HTML * BRIEF: Generate unified client dashboard HTML
*/ */
@@ -20,7 +20,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class ClientDashboardCli extends CliFramework class ClientDashboardCli extends CliFramework
{ {
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/client_health_check.php * PATH: /cli/client_health_check.php
* BRIEF: Verify a client site's update server, installed version, and release availability * BRIEF: Verify a client site's update server, installed version, and release availability
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class ClientHealthCheckCli extends CliFramework class ClientHealthCheckCli extends CliFramework
{ {
+5 -5
View File
@@ -8,11 +8,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/client_inventory.php * PATH: /cli/client_inventory.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Discover and list all client-waas repos with their server configuration status * BRIEF: Discover and list all client-waas repos with their server configuration status
*/ */
@@ -20,7 +20,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class ClientInventoryCli extends CliFramework class ClientInventoryCli extends CliFramework
{ {
+5 -5
View File
@@ -8,11 +8,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/client_provision.php * PATH: /cli/client_provision.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Provision a new client environment end-to-end * BRIEF: Provision a new client environment end-to-end
*/ */
@@ -20,7 +20,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class ClientProvisionCli extends CliFramework class ClientProvisionCli extends CliFramework
{ {
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/completion.php * PATH: /cli/completion.php
* BRIEF: Generate bash/zsh tab completion scripts for bin/moko * BRIEF: Generate bash/zsh tab completion scripts for bin/moko
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class CompletionCli extends CliFramework class CompletionCli extends CliFramework
{ {
+12 -12
View File
@@ -8,9 +8,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/create_project.php * PATH: /cli/create_project.php
* BRIEF: Create baseline GitHub Projects for repositories with standard fields and views * BRIEF: Create baseline GitHub Projects for repositories with standard fields and views
*/ */
@@ -19,12 +19,12 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class CreateProjectCli extends CliFramework class CreateProjectCli extends CliFramework
{ {
/** @var string[] */ /** @var string[] */
private array $ALWAYS_EXCLUDE = ['mokoplatform', '.github-private']; private array $ALWAYS_EXCLUDE = ['moko-platform', '.github-private'];
/** @var array<string, string> */ /** @var array<string, string> */
private array $PLATFORM_TO_TYPE = [ private array $PLATFORM_TO_TYPE = [
@@ -80,10 +80,10 @@ class CreateProjectCli extends CliFramework
return 2; return 2;
} }
$config = \MokoCli\Config::load(); $config = \MokoEnterprise\Config::load();
$platformName = $config->getString('platform', 'gitea'); $platformName = $config->getString('platform', 'gitea');
try { try {
$adapter = \MokoCli\PlatformAdapterFactory::create($config); $adapter = \MokoEnterprise\PlatformAdapterFactory::create($config);
$api = $adapter->getApiClient(); $api = $adapter->getApiClient();
} catch (\Exception $e) { } catch (\Exception $e) {
$this->log('ERROR', "Platform initialization failed: " . $e->getMessage()); $this->log('ERROR', "Platform initialization failed: " . $e->getMessage());
@@ -183,7 +183,7 @@ class CreateProjectCli extends CliFramework
CURLOPT_HTTPHEADER => [ CURLOPT_HTTPHEADER => [
'Authorization: bearer ' . $token, 'Authorization: bearer ' . $token,
'Content-Type: application/json', 'Content-Type: application/json',
'User-Agent: mokoplatform-CreateProject', 'User-Agent: moko-platform-CreateProject',
], ],
]); ]);
$body = (string) curl_exec($ch); $body = (string) curl_exec($ch);
@@ -205,7 +205,7 @@ class CreateProjectCli extends CliFramework
return $data['data'] ?? []; return $data['data'] ?? [];
} }
private function restGet(string $path, string $token, ?\MokoCli\ApiClient $apiClient = null): array private function restGet(string $path, string $token, ?\MokoEnterprise\ApiClient $apiClient = null): array
{ {
if ($apiClient !== null) { if ($apiClient !== null) {
try { try {
@@ -217,7 +217,7 @@ class CreateProjectCli extends CliFramework
return []; return [];
} }
private function detectRepoPlatform(string $org, string $repo, string $token, ?\MokoCli\ApiClient $apiClient = null): string private function detectRepoPlatform(string $org, string $repo, string $token, ?\MokoEnterprise\ApiClient $apiClient = null): string
{ {
foreach (['.github/.mokostandards', '.mokogitea/.mokostandards', '.mokostandards'] as $path) { foreach (['.github/.mokostandards', '.mokogitea/.mokostandards', '.mokostandards'] as $path) {
$data = $this->restGet("repos/{$org}/{$repo}/contents/{$path}", $token, $apiClient); $data = $this->restGet("repos/{$org}/{$repo}/contents/{$path}", $token, $apiClient);
@@ -422,14 +422,14 @@ class CreateProjectCli extends CliFramework
updateProjectV2(input: { updateProjectV2(input: {
projectId: $projectId, projectId: $projectId,
shortDescription: $shortDescription, shortDescription: $shortDescription,
readme: "Managed by mokoplatform. Run `php cli/create_project.php` to regenerate." readme: "Managed by moko-platform. Run `php cli/create_project.php` to regenerate."
}) { }) {
projectV2 { id } projectV2 { id }
} }
}', }',
[ [
'projectId' => $projectId, 'projectId' => $projectId,
'shortDescription' => "Standard project board for {$repo}. Auto-created by mokoplatform.", 'shortDescription' => "Standard project board for {$repo}. Auto-created by moko-platform.",
], ],
$token $token
); );
+21 -21
View File
@@ -8,11 +8,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/create_repo.php * PATH: /cli/create_repo.php
* BRIEF: Scaffold a new governed repository with full mokoplatform baseline * BRIEF: Scaffold a new governed repository with full moko-platform baseline
*/ */
declare(strict_types=1); declare(strict_types=1);
@@ -20,15 +20,15 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
use MokoCli\Config; use MokoEnterprise\Config;
use MokoCli\PlatformAdapterFactory; use MokoEnterprise\PlatformAdapterFactory;
class CreateRepoCli extends CliFramework class CreateRepoCli extends CliFramework
{ {
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Scaffold a new governed repository with full mokoplatform baseline'); $this->setDescription('Scaffold a new governed repository with full moko-platform baseline');
$this->addArgument('--name', 'Repository name', null); $this->addArgument('--name', 'Repository name', null);
$this->addArgument('--type', 'Project type', null); $this->addArgument('--type', 'Project type', null);
$this->addArgument('--description', 'Repository description', ''); $this->addArgument('--description', 'Repository description', '');
@@ -60,16 +60,16 @@ class CreateRepoCli extends CliFramework
'generic' => 'generic', 'generic' => 'generic',
]; ];
$TYPE_TO_TOPICS = [ $TYPE_TO_TOPICS = [
'dolibarr' => ['dolibarr', 'erp', 'crm', 'php', 'mokoplatform'], 'dolibarr' => ['dolibarr', 'erp', 'crm', 'php', 'moko-platform'],
'joomla' => ['joomla', 'cms', 'php', 'mokoplatform'], 'joomla' => ['joomla', 'cms', 'php', 'moko-platform'],
'nodejs' => ['nodejs', 'javascript', 'typescript', 'mokoplatform'], 'nodejs' => ['nodejs', 'javascript', 'typescript', 'moko-platform'],
'terraform' => ['terraform', 'infrastructure', 'iac', 'mokoplatform'], 'terraform' => ['terraform', 'infrastructure', 'iac', 'moko-platform'],
'python' => ['python', 'mokoplatform'], 'python' => ['python', 'moko-platform'],
'wordpress' => ['wordpress', 'php', 'cms', 'mokoplatform'], 'wordpress' => ['wordpress', 'php', 'cms', 'moko-platform'],
'generic' => ['mokoplatform'], 'generic' => ['moko-platform'],
]; ];
$platform = $TYPE_TO_PLATFORM[$type] ?? 'generic'; $platform = $TYPE_TO_PLATFORM[$type] ?? 'generic';
$topics = $TYPE_TO_TOPICS[$type] ?? ['mokoplatform']; $topics = $TYPE_TO_TOPICS[$type] ?? ['moko-platform'];
$platformName = $adapter->getPlatformName(); $platformName = $adapter->getPlatformName();
$vis = $private ? 'private' : 'public'; $vis = $private ? 'private' : 'public';
echo "Scaffolding new repository: {$org}/{$name}" echo "Scaffolding new repository: {$org}/{$name}"
@@ -84,7 +84,7 @@ class CreateRepoCli extends CliFramework
if (!$this->dryRun) { if (!$this->dryRun) {
try { try {
$data = $adapter->createOrgRepo($org, $name, [ $data = $adapter->createOrgRepo($org, $name, [
'description' => $description ?: "Managed by mokoplatform ({$type})", 'description' => $description ?: "Managed by moko-platform ({$type})",
'private' => $private, 'private' => $private,
'has_issues' => true, 'has_issues' => true,
'has_projects' => true, 'has_projects' => true,
@@ -138,12 +138,12 @@ class CreateRepoCli extends CliFramework
echo "Step 4: Creating README.md...\n"; echo "Step 4: Creating README.md...\n";
$baseUrl = $platformName === 'gitea' ? $config->getString('gitea.url', 'https://git.mokoconsulting.tech') : 'https://github.com'; $baseUrl = $platformName === 'gitea' ? $config->getString('gitea.url', 'https://git.mokoconsulting.tech') : 'https://github.com';
$repoUrl = "{$baseUrl}/{$org}/{$name}"; $repoUrl = "{$baseUrl}/{$org}/{$name}";
$standardsUrl = "{$baseUrl}/{$org}/MokoCli"; $standardsUrl = "{$baseUrl}/{$org}/MokoStandards";
$readmeContent = "<!--\n" $readmeContent = "<!--\n"
. "Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>\n" . "Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>\n"
. "SPDX-License-Identifier: GPL-3.0-or-later\n" . "SPDX-License-Identifier: GPL-3.0-or-later\n"
. "DEFGROUP: {$name}\n" . "DEFGROUP: {$name}\n"
. "INGROUP: mokoplatform\n" . "INGROUP: moko-platform\n"
. "REPO: {$repoUrl}\n" . "REPO: {$repoUrl}\n"
. "PATH: /README.md\n" . "PATH: /README.md\n"
. "BRIEF: {$description}\n" . "BRIEF: {$description}\n"
@@ -152,7 +152,7 @@ class CreateRepoCli extends CliFramework
. "{$description}\n\n" . "{$description}\n\n"
. "## Getting Started\n\n" . "## Getting Started\n\n"
. "This repository is governed by" . "This repository is governed by"
. " [mokoplatform]({$standardsUrl}).\n\n" . " [moko-platform]({$standardsUrl}).\n\n"
. "## License\n\n" . "## License\n\n"
. "GPL-3.0-or-later. See [LICENSE](LICENSE)" . "GPL-3.0-or-later. See [LICENSE](LICENSE)"
. " for details.\n"; . " for details.\n";
@@ -169,7 +169,7 @@ class CreateRepoCli extends CliFramework
$name, $name,
'README.md', 'README.md',
$readmeContent, $readmeContent,
'docs: initialize README with mokoplatform header [skip ci]', 'docs: initialize README with moko-platform header [skip ci]',
$sha $sha
); );
echo " README.md created\n"; echo " README.md created\n";
+2 -2
View File
@@ -10,7 +10,7 @@
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.CLI * DEFGROUP: MokoPlatform.CLI
* INGROUP: MokoPlatform * INGROUP: MokoPlatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/deploy_joomla.php * PATH: /cli/deploy_joomla.php
* BRIEF: Smart Joomla deploy — routes files to correct server directories by extension type * BRIEF: Smart Joomla deploy — routes files to correct server directories by extension type
* *
@@ -31,7 +31,7 @@ require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver}; use MokoEnterprise\{CliFramework, SourceResolver};
use phpseclib3\Net\SFTP; use phpseclib3\Net\SFTP;
use phpseclib3\Crypt\PublicKeyLoader; use phpseclib3\Crypt\PublicKeyLoader;
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/dev_branch_reset.php * PATH: /cli/dev_branch_reset.php
* BRIEF: Delete and recreate dev branch from main via Gitea API * BRIEF: Delete and recreate dev branch from main via Gitea API
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class DevBranchResetCli extends CliFramework class DevBranchResetCli extends CliFramework
{ {
+5 -5
View File
@@ -8,11 +8,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/grafana_dashboard.php * PATH: /cli/grafana_dashboard.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Manage Grafana dashboards via API * BRIEF: Manage Grafana dashboards via API
*/ */
@@ -20,7 +20,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class GrafanaDashboardCli extends CliFramework class GrafanaDashboardCli extends CliFramework
{ {
+5 -5
View File
@@ -6,11 +6,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/joomla_build.php * PATH: /cli/joomla_build.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Build a Joomla extension ZIP from manifest — all types supported * BRIEF: Build a Joomla extension ZIP from manifest — all types supported
* NOTE: Called by pre-release and auto-release workflows. * NOTE: Called by pre-release and auto-release workflows.
*/ */
@@ -19,7 +19,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver}; use MokoEnterprise\{CliFramework, SourceResolver};
class JoomlaBuildCli extends CliFramework class JoomlaBuildCli extends CliFramework
{ {
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/joomla_compat_check.php * PATH: /cli/joomla_compat_check.php
* BRIEF: Check if extension targetplatform regex matches the latest Joomla version * BRIEF: Check if extension targetplatform regex matches the latest Joomla version
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class JoomlaCompatCheckCli extends CliFramework class JoomlaCompatCheckCli extends CliFramework
{ {
+22 -57
View File
@@ -10,7 +10,7 @@
* INGROUP: mokoplatform * INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/joomla_metadata_validate.php * PATH: /cli/joomla_metadata_validate.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Validate MokoGitea repo metadata against Joomla extension manifest XML * BRIEF: Validate MokoGitea repo metadata against Joomla extension manifest XML
*/ */
@@ -18,7 +18,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class JoomlaMetadataValidateCli extends CliFramework class JoomlaMetadataValidateCli extends CliFramework
{ {
@@ -146,9 +146,6 @@ class JoomlaMetadataValidateCli extends CliFramework
if (preg_match('/<extension\s[^>]*type=["\']([^"\']+)["\']/', $content, $typeMatch)) { if (preg_match('/<extension\s[^>]*type=["\']([^"\']+)["\']/', $content, $typeMatch)) {
$xml = @simplexml_load_string($content); $xml = @simplexml_load_string($content);
if ($xml === false) { if ($xml === false) {
$relPath = str_replace($root . '/', '', $file);
$relPath = str_replace($root . '\\', '', $relPath);
$this->log('WARN', "Skipping {$relPath}: malformed XML");
continue; continue;
} }
@@ -173,57 +170,27 @@ class JoomlaMetadataValidateCli extends CliFramework
private function loadMetadata(string $root, string $org, string $repoName, string $token, string $apiBase): ?array private function loadMetadata(string $root, string $org, string $repoName, string $token, string $apiBase): ?array
{ {
if ($token === '') { if ($token !== '') {
$this->log('ERROR', 'No API token provided (use --token or set GITEA_TOKEN env var)'); $url = "{$apiBase}/repos/{$org}/{$repoName}/metadata";
return null; $ctx = stream_context_create([
'http' => [
'header' => "Authorization: token {$token}\r\nAccept: application/json\r\n",
'timeout' => 10,
],
]);
$body = @file_get_contents($url, false, $ctx);
if ($body !== false) {
$data = json_decode($body, true);
if (is_array($data)) {
$data['source'] = 'api';
return $data;
}
}
} }
$url = "{$apiBase}/repos/{$org}/{$repoName}/metadata"; return null;
$ctx = stream_context_create([
'http' => [
'header' => "Authorization: token {$token}\r\nAccept: application/json\r\n",
'timeout' => 10,
'ignore_errors' => true,
],
]);
$body = file_get_contents($url, false, $ctx);
// Extract HTTP status from response headers
$httpCode = 0;
if (isset($http_response_header[0]) && preg_match('/\d{3}/', $http_response_header[0], $m)) {
$httpCode = (int) $m[0];
}
if ($body === false) {
$this->log('ERROR', "Failed to connect to {$url} — check network or TLS configuration");
return null;
}
if ($httpCode === 404) {
$this->log('ERROR', "API endpoint not found: {$url}");
$this->log('ERROR', 'Server may need MokoGitea-Fork >= #650 (metadata endpoint rename)');
return null;
}
if ($httpCode === 401 || $httpCode === 403) {
$this->log('ERROR', "Authentication failed (HTTP {$httpCode}) — check your API token");
return null;
}
if ($httpCode >= 400) {
$this->log('ERROR', "API returned HTTP {$httpCode}: " . substr($body, 0, 200));
return null;
}
$data = json_decode($body, true);
if (!is_array($data)) {
$this->log('ERROR', "API returned invalid JSON from {$url}");
return null;
}
$data['source'] = 'api';
return $data;
} }
// ================================================================= // =================================================================
@@ -237,9 +204,7 @@ class JoomlaMetadataValidateCli extends CliFramework
$type = $joomlaXml['type']; $type = $joomlaXml['type'];
// 1. Extension type // 1. Extension type
$metaType = $this->normalizeExtensionType( $metaType = $this->normalizeExtensionType($metadata['extension_type'] ?? '');
$metadata['extension_type'] ?? $metadata['package_type'] ?? ''
);
$results[] = [ $results[] = [
'field' => 'extension_type', 'field' => 'extension_type',
'metadata' => $metaType, 'metadata' => $metaType,
+6 -6
View File
@@ -8,9 +8,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/joomla_release.php * PATH: /cli/joomla_release.php
* BRIEF: Joomla release pipeline — build ZIP+tar.gz, upload to GitHub Release, update updates.xml * BRIEF: Joomla release pipeline — build ZIP+tar.gz, upload to GitHub Release, update updates.xml
* *
@@ -25,7 +25,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
use MokoCli\{ApiClient, AuditLogger, CliFramework, Config, PlatformAdapterFactory, SourceResolver}; use MokoEnterprise\{ApiClient, AuditLogger, CliFramework, Config, PlatformAdapterFactory, SourceResolver};
/** /**
* Joomla Release Manager * Joomla Release Manager
@@ -57,7 +57,7 @@ class JoomlaRelease extends CliFramework
]; ];
private ApiClient $api; private ApiClient $api;
private \MokoCli\GitPlatformAdapter $adapter; private \MokoEnterprise\GitPlatformAdapter $adapter;
protected function configure(): void protected function configure(): void
{ {
@@ -407,7 +407,7 @@ class JoomlaRelease extends CliFramework
$this->api->post("/repos/{$repo}/releases", [ $this->api->post("/repos/{$repo}/releases", [
'tag_name' => $tag, 'tag_name' => $tag,
'name' => $releaseName, 'name' => $releaseName,
'body' => "## {$version}\n\nCreated by mokoplatform release pipeline.", 'body' => "## {$version}\n\nCreated by moko-platform release pipeline.",
'prerelease' => ($stability !== 'stable'), 'prerelease' => ($stability !== 'stable'),
]); ]);
} }
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/license_manage.php * PATH: /cli/license_manage.php
* BRIEF: Manage license packages and keys via MokoGitea licensing API * BRIEF: Manage license packages and keys via MokoGitea licensing API
* *
@@ -28,7 +28,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class LicenseManage extends CliFramework class LicenseManage extends CliFramework
{ {
-749
View File
@@ -1,749 +0,0 @@
#!/usr/bin/env php
<?php
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* FILE INFORMATION
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/manifest_detect.php
* VERSION: 09.33.00
* BRIEF: Auto-detect manifest fields from source files and optionally push to API
*/
declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver};
class ManifestDetectCli extends CliFramework
{
protected function configure(): void
{
$this->setDescription('Auto-detect manifest fields from source files');
$this->addArgument('--path', 'Repository root path', '.');
$this->addArgument('--json', 'Output as JSON', false);
$this->addArgument('--diff', 'Show diff against current manifest API values', false);
$this->addArgument('--update', 'Push detected fields to manifest API', false);
$this->addArgument('--token', 'Gitea API token (or GITEA_TOKEN env)', '');
$this->addArgument('--api-base', 'Gitea API base URL', 'https://git.mokoconsulting.tech/api/v1');
$this->addArgument('--org', 'Gitea org', 'MokoConsulting');
$this->addArgument('--repo', 'Gitea repo name (auto-detected from remote if empty)', '');
$this->addArgument('--github-output', 'Append fields to $GITHUB_OUTPUT', false);
}
protected function run(): int
{
$path = $this->getArgument('--path');
$jsonMode = (bool) $this->getArgument('--json');
$diffMode = (bool) $this->getArgument('--diff');
$updateMode = (bool) $this->getArgument('--update');
$ghOutput = (bool) $this->getArgument('--github-output');
$token = $this->getArgument('--token') ?: getenv('GITEA_TOKEN') ?: '';
$apiBase = rtrim($this->getArgument('--api-base'), '/');
$org = $this->getArgument('--org');
$repoName = $this->getArgument('--repo');
$root = realpath($path) ?: $path;
if (!is_dir($root)) {
$this->log('ERROR', "Path does not exist: {$path}");
return 1;
}
// Auto-detect repo name from git remote
if ($repoName === '') {
$repoName = $this->detectRepoName($root);
}
// ── Detect all fields ───────────────────────────────────────
$detected = $this->detectAll($root, $repoName);
// ── Warn about missing fields ────────────────────────────────
$expected = ['platform', 'name', 'version', 'package_type', 'language', 'entry_point'];
foreach ($expected as $field) {
if (!isset($detected[$field]) || $detected[$field] === '') {
$this->log('WARN', "Could not detect: {$field}");
}
}
// ── Output ──────────────────────────────────────────────────
if ($diffMode || $updateMode) {
if ($token === '') {
$this->log('ERROR', 'API token required for --diff/--update (use --token or GITEA_TOKEN env)');
return 1;
}
if ($repoName === '') {
$this->log('ERROR', 'Could not determine repo name (use --repo)');
return 1;
}
$current = $this->fetchManifest($apiBase, $org, $repoName, $token);
if ($current === null) {
$this->log('ERROR', 'Failed to fetch current manifest from API');
return 1;
}
$changes = $this->computeDiff($current, $detected);
if ($diffMode) {
if (empty($changes)) {
$this->log('INFO', 'No differences — manifest matches source');
} else {
$this->sectionHeader('Manifest Drift');
foreach ($changes as $field => $info) {
$this->log('WARN', sprintf(
'%-20s API: %-30s Detected: %s',
$field,
$info['current'] === '' ? '(empty)' : $info['current'],
$info['detected']
));
}
}
}
if ($updateMode) {
if (empty($changes)) {
$this->log('INFO', 'Nothing to update');
} else {
$update = array_map(fn($i) => $i['detected'], $changes);
$ok = $this->pushManifest($apiBase, $org, $repoName, $token, $current, $update);
if ($ok) {
$this->log('OK', 'Updated ' . count($update) . ' field(s): ' . implode(', ', array_keys($update)));
} else {
$this->log('ERROR', 'Failed to push manifest update');
return 1;
}
}
}
return 0;
}
if ($ghOutput) {
$outputFile = getenv('GITHUB_OUTPUT');
$lines = [];
foreach ($detected as $k => $v) {
$envKey = str_replace('-', '_', $k);
$lines[] = "{$envKey}={$v}";
}
if ($outputFile !== false && $outputFile !== '') {
file_put_contents($outputFile, implode("\n", $lines) . "\n", FILE_APPEND);
$this->log('INFO', 'Wrote ' . count($detected) . ' fields to GITHUB_OUTPUT');
} else {
$this->log('WARN', 'GITHUB_OUTPUT not set — printing to stdout instead');
echo implode("\n", $lines) . "\n";
}
return 0;
}
if ($jsonMode) {
echo json_encode($detected, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
} else {
foreach ($detected as $k => $v) {
echo "{$k}={$v}\n";
}
}
return 0;
}
// =====================================================================
// Detection engine
// =====================================================================
private function detectAll(string $root, string $repoName): array
{
$platform = $this->detectPlatform($root);
$fields = [
'platform' => $platform,
'name' => '',
'description' => '',
'version' => '',
'element_name' => '',
'package_type' => '',
'language' => '',
'entry_point' => '',
'license_spdx' => '',
'display_name' => '',
'target_version' => '',
'php_minimum' => '',
];
switch ($platform) {
case 'joomla':
$this->detectJoomla($root, $repoName, $fields);
break;
case 'dolibarr':
$this->detectDolibarr($root, $repoName, $fields);
break;
case 'go':
$this->detectGo($root, $repoName, $fields);
break;
case 'mcp':
$this->detectNode($root, $repoName, $fields);
break;
case 'node':
$this->detectNode($root, $repoName, $fields);
$fields['platform'] = 'node';
break;
default:
$this->detectGeneric($root, $repoName, $fields);
break;
}
// Fallbacks
if ($fields['name'] === '') {
$fields['name'] = $repoName ?: basename($root);
}
if ($fields['entry_point'] === '') {
$fields['entry_point'] = $this->detectEntryPoint($root);
}
if ($fields['license_spdx'] === '') {
$fields['license_spdx'] = $this->detectLicense($root);
}
// description: only from platform-specific source, never guessed
// Strip empty values
return array_filter($fields, fn($v) => $v !== '');
}
// ── Platform detection ──────────────────────────────────────────
private function detectPlatform(string $root): string
{
// Joomla: look for pkg_*.xml or extension XML in source dirs
$joomlaXmls = array_merge(
SourceResolver::globSource($root, 'pkg_*.xml'),
glob("{$root}/pkg_*.xml") ?: []
);
if (!empty($joomlaXmls)) {
return 'joomla';
}
// Check source dirs for any Joomla extension XML
foreach (SourceResolver::globSource($root, '*.xml') as $xmlFile) {
$content = file_get_contents($xmlFile);
if (strpos($content, '<extension') !== false) {
return 'joomla';
}
}
// Dolibarr: mod*.class.php with DolibarrModules
$modFiles = array_merge(
SourceResolver::globSource($root, 'core/modules/mod*.class.php'),
glob("{$root}/core/modules/mod*.class.php") ?: []
);
foreach ($modFiles as $file) {
if (strpos(file_get_contents($file), 'DolibarrModules') !== false) {
return 'dolibarr';
}
}
// Go
if (file_exists("{$root}/go.mod")) {
return 'go';
}
// MCP: package.json with mcp-related content
if (file_exists("{$root}/package.json")) {
$pkg = json_decode(file_get_contents("{$root}/package.json"), true) ?? [];
$deps = array_merge(
array_keys($pkg['dependencies'] ?? []),
array_keys($pkg['devDependencies'] ?? [])
);
foreach ($deps as $dep) {
if (strpos($dep, '@modelcontextprotocol/') === 0 || $dep === '@anthropic/mcp-sdk') {
return 'mcp';
}
}
return 'node';
}
// Python
if (file_exists("{$root}/pyproject.toml") || file_exists("{$root}/setup.py")) {
return 'python';
}
return 'generic';
}
// ── Joomla ──────────────────────────────────────────────────────
private function detectJoomla(string $root, string $repoName, array &$fields): void
{
$fields['language'] = 'PHP';
// Find the primary extension manifest XML
$extManifest = $this->findJoomlaManifest($root);
if ($extManifest === null) {
return;
}
$xml = file_get_contents($extManifest);
// Type
$extType = '';
if (preg_match('/type="([^"]*)"/', $xml, $m)) {
$extType = $m[1];
}
$fields['package_type'] = $extType;
// Element name
$element = '';
if (preg_match('/<element>([^<]+)<\/element>/', $xml, $m)) {
$element = $m[1];
}
if ($element === '' && preg_match('/module="([^"]*)"/', $xml, $m)) {
$element = $m[1];
}
if ($element === '' && preg_match('/plugin="([^"]*)"/', $xml, $m)) {
$element = $m[1];
}
if ($extType === 'package' && preg_match('/<packagename>([^<]+)<\/packagename>/', $xml, $m)) {
$element = $m[1];
}
if ($element === '') {
$element = strtolower(basename($extManifest, '.xml'));
}
// Ensure element has type prefix (API stores full element_name like pkg_mokosuite)
$prefixMap = [
'package' => 'pkg_', 'component' => 'com_', 'module' => 'mod_',
'template' => 'tpl_', 'library' => 'lib_', 'file' => 'file_',
];
if (isset($prefixMap[$extType])) {
$prefix = $prefixMap[$extType];
// Only add prefix if not already present (check all known prefixes)
$hasPrefix = false;
foreach ($prefixMap as $p) {
if (strpos($element, $p) === 0) { $hasPrefix = true; break; }
}
if (strpos($element, 'plg_') === 0) { $hasPrefix = true; }
if (!$hasPrefix) {
$element = $prefix . $element;
}
} elseif ($extType === 'plugin') {
$folder = '';
if (preg_match('/group="([^"]*)"/', $xml, $gm)) {
$folder = $gm[1];
}
if ($folder !== '' && strpos($element, 'plg_') !== 0) {
$element = "plg_{$folder}_" . $element;
}
}
$fields['element_name'] = $element;
// Name
if (preg_match('/<name>([^<]+)<\/name>/', $xml, $m)) {
$fields['name'] = trim($m[1]);
}
// Version
if (preg_match('/<version>([^<]+)<\/version>/', $xml, $m)) {
$fields['version'] = trim($m[1]);
}
// Description
if (preg_match('/<description>([^<]+)<\/description>/', $xml, $m)) {
$desc = trim($m[1]);
// Skip language string keys like COM_MOKOSUITE_DESCRIPTION
if (strpos($desc, '_') === false || strlen($desc) > 60) {
$fields['description'] = $desc;
}
}
// Display name for update feeds
if (!empty($fields['name'])) {
$name = $fields['name'];
// If name already has "Type - " prefix, use as-is
if (preg_match('/^(Package|Component|Module|Plugin|Template|Library)\s*-\s*/i', $name)) {
$fields['display_name'] = $name;
} elseif (!empty($extType)) {
$fields['display_name'] = ucfirst($extType) . ' - ' . $name;
}
}
// Target Joomla version
if (preg_match('/<targetplatform\s[^>]*version="([^"]+)"/', $xml, $m)) {
$fields['target_version'] = trim($m[1]);
} else {
// Default for Joomla 5/6
$fields['target_version'] = '(5|6)\..*';
}
// PHP minimum
if (preg_match('/<php_minimum>([^<]+)<\/php_minimum>/', $xml, $m)) {
$fields['php_minimum'] = trim($m[1]);
}
// License
if (preg_match('/<license>([^<]+)<\/license>/', $xml, $m)) {
$fields['license_spdx'] = $this->normalizeLicense(trim($m[1]));
}
}
private function findJoomlaManifest(string $root): ?string
{
// Priority: pkg_*.xml (package manifest)
$pkgXmls = array_merge(
SourceResolver::globSource($root, 'pkg_*.xml'),
glob("{$root}/pkg_*.xml") ?: []
);
if (!empty($pkgXmls)) {
return $pkgXmls[0];
}
// Any extension XML in source dir
foreach (SourceResolver::globSource($root, '*.xml') as $file) {
$content = file_get_contents($file);
if (strpos($content, '<extension') !== false) {
return $file;
}
}
// Root level
foreach (glob("{$root}/*.xml") ?: [] as $file) {
$content = file_get_contents($file);
if (strpos($content, '<extension') !== false) {
return $file;
}
}
return null;
}
// ── Dolibarr ────────────────────────────────────────────────────
private function detectDolibarr(string $root, string $repoName, array &$fields): void
{
$fields['language'] = 'PHP';
$fields['package_type'] = 'dolibarr-module';
$modFile = $this->findDolibarrModule($root);
if ($modFile === null) {
return;
}
$content = file_get_contents($modFile);
// Element name from class file
$modBasename = basename($modFile, '.class.php');
$fields['element_name'] = strtolower(preg_replace('/^mod/', '', $modBasename));
// Name
if (preg_match('/\$this->name\s*=\s*[\'"]([^\'"]+)[\'"]/', $content, $m)) {
$fields['name'] = $m[1];
}
// Version
if (preg_match('/\$this->version\s*=\s*[\'"]([^\'"]+)[\'"]/', $content, $m)) {
$fields['version'] = $m[1];
}
// Description
if (preg_match('/\$this->description\s*=\s*[\'"]([^\'"]+)[\'"]/', $content, $m)) {
$desc = $m[1];
if (strpos($desc, '$') === false) {
$fields['description'] = $desc;
}
}
// License
if (preg_match('/SPDX-License-Identifier:\s*(\S+)/', $content, $m)) {
$fields['license_spdx'] = $m[1];
}
}
private function findDolibarrModule(string $root): ?string
{
$candidates = array_merge(
SourceResolver::globSource($root, 'core/modules/mod*.class.php'),
glob("{$root}/core/modules/mod*.class.php") ?: []
);
foreach ($candidates as $file) {
if (strpos(file_get_contents($file), 'DolibarrModules') !== false) {
return $file;
}
}
return null;
}
// ── Go ──────────────────────────────────────────────────────────
private function detectGo(string $root, string $repoName, array &$fields): void
{
$fields['language'] = 'Go';
$fields['package_type'] = 'application';
$fields['entry_point'] = './';
$goMod = "{$root}/go.mod";
if (!file_exists($goMod)) {
return;
}
$content = file_get_contents($goMod);
// Module path → name
if (preg_match('/^module\s+(\S+)/m', $content, $m)) {
$modulePath = $m[1];
$parts = explode('/', $modulePath);
$fields['name'] = end($parts);
}
// Go version
if (preg_match('/^go\s+(\S+)/m', $content, $m)) {
// This is Go language version, not the project version
// Project version comes from git tags or source files
}
// License
$fields['license_spdx'] = $this->detectLicense($root);
}
// ── Node / MCP ──────────────────────────────────────────────────
private function detectNode(string $root, string $repoName, array &$fields): void
{
$pkgFile = "{$root}/package.json";
if (!file_exists($pkgFile)) {
return;
}
$pkg = json_decode(file_get_contents($pkgFile), true) ?? [];
$fields['name'] = $pkg['name'] ?? '';
// Strip npm scope
if (strpos($fields['name'], '/') !== false) {
$fields['name'] = explode('/', $fields['name'])[1];
}
$fields['version'] = $pkg['version'] ?? '';
$fields['description'] = $pkg['description'] ?? '';
$fields['license_spdx'] = $pkg['license'] ?? '';
// Language detection
if (file_exists("{$root}/tsconfig.json")) {
$fields['language'] = 'TypeScript';
} else {
$fields['language'] = 'JavaScript';
}
// Package type
$deps = array_merge(
array_keys($pkg['dependencies'] ?? []),
array_keys($pkg['devDependencies'] ?? [])
);
$isMcp = false;
foreach ($deps as $dep) {
if (strpos($dep, '@modelcontextprotocol/') === 0 || $dep === '@anthropic/mcp-sdk') {
$isMcp = true;
break;
}
}
$fields['package_type'] = $isMcp ? 'mcp-server' : 'application';
// Entry point
if (file_exists("{$root}/dist")) {
$fields['entry_point'] = 'dist/';
} elseif (file_exists("{$root}/src")) {
$fields['entry_point'] = 'src/';
} else {
$fields['entry_point'] = './';
}
}
// ── Generic ─────────────────────────────────────────────────────
private function detectGeneric(string $root, string $repoName, array &$fields): void
{
$fields['package_type'] = 'generic';
// Try to detect language from file extensions
$fields['language'] = $this->detectLanguageFromFiles($root);
$fields['license_spdx'] = $this->detectLicense($root);
}
// =====================================================================
// Shared detection helpers
// =====================================================================
private function detectEntryPoint(string $root): string
{
$abs = SourceResolver::resolveAbsolute($root);
if ($abs !== null) {
return basename($abs) . '/';
}
if (is_dir("{$root}/dist")) return 'dist/';
if (is_dir("{$root}/src")) return 'src/';
return './';
}
private function detectLicense(string $root): string
{
// Check LICENSE file
foreach (['LICENSE', 'LICENSE.md', 'LICENSE.txt', 'COPYING'] as $name) {
$file = "{$root}/{$name}";
if (!file_exists($file)) continue;
$content = file_get_contents($file);
// SPDX header
if (preg_match('/SPDX-License-Identifier:\s*(\S+)/', $content, $m)) {
return $m[1];
}
// Common license patterns
if (strpos($content, 'GNU GENERAL PUBLIC LICENSE') !== false) {
if (strpos($content, 'Version 3') !== false) return 'GPL-3.0-or-later';
if (strpos($content, 'Version 2') !== false) return 'GPL-2.0-or-later';
}
if (strpos($content, 'MIT License') !== false) return 'MIT';
if (strpos($content, 'Apache License') !== false && strpos($content, 'Version 2.0') !== false) return 'Apache-2.0';
}
return '';
}
private function detectLanguageFromFiles(string $root): string
{
$counts = ['PHP' => 0, 'Go' => 0, 'TypeScript' => 0, 'JavaScript' => 0, 'Python' => 0, 'Shell' => 0];
$extensions = [
'php' => 'PHP', 'go' => 'Go', 'ts' => 'TypeScript',
'js' => 'JavaScript', 'py' => 'Python', 'sh' => 'Shell',
];
// Quick scan: only check top two levels
foreach (glob("{$root}/*") ?: [] as $item) {
$ext = pathinfo($item, PATHINFO_EXTENSION);
if (isset($extensions[$ext])) {
$counts[$extensions[$ext]]++;
}
if (is_dir($item) && basename($item)[0] !== '.') {
foreach (glob("{$item}/*") ?: [] as $subItem) {
$ext = pathinfo($subItem, PATHINFO_EXTENSION);
if (isset($extensions[$ext])) {
$counts[$extensions[$ext]]++;
}
}
}
}
arsort($counts);
$top = key($counts);
return $counts[$top] > 0 ? $top : '';
}
private function normalizeLicense(string $license): string
{
$lower = strtolower($license);
$isGpl = strpos($lower, 'gpl') !== false || strpos($lower, 'general public license') !== false;
if ($isGpl && strpos($lower, '3') !== false) return 'GPL-3.0-or-later';
if ($isGpl && strpos($lower, '2') !== false) return 'GPL-2.0-or-later';
if ($lower === 'mit' || strpos($lower, 'mit license') !== false) return 'MIT';
if (strpos($lower, 'apache') !== false) return 'Apache-2.0';
return $license;
}
private function detectRepoName(string $root): string
{
$gitConfig = "{$root}/.git/config";
if (!file_exists($gitConfig)) {
return basename($root);
}
$content = file_get_contents($gitConfig);
if (preg_match('/url\s*=\s*.*\/([^\/\s]+?)(?:\.git)?\s*$/m', $content, $m)) {
return $m[1];
}
return basename($root);
}
// =====================================================================
// API interaction
// =====================================================================
private function fetchManifest(string $apiBase, string $org, string $repo, string $token): ?array
{
$url = "{$apiBase}/repos/{$org}/{$repo}/manifest";
$ctx = stream_context_create([
'http' => [
'header' => "Authorization: token {$token}\r\nAccept: application/json\r\n",
'timeout' => 10,
],
]);
$body = @file_get_contents($url, false, $ctx);
if ($body === false) return null;
return json_decode($body, true);
}
private function computeDiff(array $current, array $detected): array
{
// Map detected keys to API keys (underscores match)
$changes = [];
foreach ($detected as $key => $value) {
$apiKey = $key;
$currentVal = $current[$apiKey] ?? '';
// Only flag as changed if detected value is non-empty and differs
if ($value !== '' && $value !== $currentVal) {
// Don't overwrite a non-empty API value with a detected value
// unless the API value is actually empty
if ($currentVal === '' || $this->shouldOverride($key, $currentVal, $value)) {
$changes[$key] = [
'current' => $currentVal,
'detected' => $value,
];
}
}
}
return $changes;
}
private function shouldOverride(string $field, string $current, string $detected): bool
{
// Version: detected from source is authoritative
if ($field === 'version') return true;
// These fields: source files are authoritative
if (in_array($field, ['element_name', 'package_type', 'language', 'entry_point'], true)) {
return true;
}
// For other fields, only fill empty — don't overwrite manual edits
return false;
}
private function pushManifest(string $apiBase, string $org, string $repo, string $token, array $current, array $update): bool
{
$merged = array_merge($current, $update);
$url = "{$apiBase}/repos/{$org}/{$repo}/manifest";
$payload = json_encode($merged);
$ctx = stream_context_create([
'http' => [
'method' => 'PUT',
'header' => "Authorization: token {$token}\r\nContent-Type: application/json\r\nAccept: application/json\r\n",
'content' => $payload,
'timeout' => 10,
],
]);
$body = @file_get_contents($url, false, $ctx);
return $body !== false;
}
}
$app = new ManifestDetectCli();
exit($app->execute());
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/manifest_element.php * PATH: /cli/manifest_element.php
* BRIEF: Extract element name, type, type prefix, and ZIP name from manifest * BRIEF: Extract element name, type, type prefix, and ZIP name from manifest
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver}; use MokoEnterprise\{CliFramework, SourceResolver};
class ManifestElementCli extends CliFramework class ManifestElementCli extends CliFramework
{ {
-564
View File
@@ -1,564 +0,0 @@
#!/usr/bin/env php
<?php
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* FILE INFORMATION
* DEFGROUP: mokoplatform.CLI
* INGROUP: mokoplatform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
* PATH: /cli/manifest_integrity.php
* VERSION: 09.33.00
* BRIEF: Cross-check manifest API fields against repo contents across the org
*/
declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework;
class ManifestIntegrityCli extends CliFramework
{
protected function configure(): void
{
$this->setDescription('Cross-check manifest fields against repo contents across the org');
$this->addArgument('--path', 'Single repo path (local mode)', '');
$this->addArgument('--org', 'Gitea org (bulk mode)', 'MokoConsulting');
$this->addArgument('--repo', 'Single repo name (remote mode)', '');
$this->addArgument('--token', 'Gitea API token (or GITEA_TOKEN env)', '');
$this->addArgument('--api-base', 'Gitea API base URL', 'https://git.mokoconsulting.tech/api/v1');
$this->addArgument('--fix', 'Push fixes for detected drift', false);
$this->addArgument('--json', 'Output as JSON', false);
$this->addArgument('--quiet', 'Only show repos with issues', false);
}
protected function run(): int
{
$path = $this->getArgument('--path');
$org = $this->getArgument('--org');
$repoName = $this->getArgument('--repo');
$token = $this->getArgument('--token') ?: getenv('GITEA_TOKEN') ?: '';
$apiBase = rtrim($this->getArgument('--api-base'), '/');
$fixMode = (bool) $this->getArgument('--fix');
$jsonMode = (bool) $this->getArgument('--json');
$quiet = (bool) $this->getArgument('--quiet');
if ($token === '') {
$this->log('ERROR', 'API token required (use --token or GITEA_TOKEN env)');
return 1;
}
// ── Mode selection ──────────────────────────────────────────
if ($path !== '') {
// Local mode: detect from source + compare to API
return $this->checkLocal($path, $org, $repoName, $token, $apiBase, $fixMode, $jsonMode);
}
if ($repoName !== '') {
// Single remote repo
return $this->checkRemoteRepo($org, $repoName, $token, $apiBase, $fixMode, $jsonMode);
}
// Bulk mode: all repos in org
return $this->checkOrg($org, $token, $apiBase, $fixMode, $jsonMode, $quiet);
}
// =====================================================================
// Local mode — detect from source, compare to API
// =====================================================================
private function checkLocal(string $path, string $org, string $repoName, string $token, string $apiBase, bool $fix, bool $json): int
{
$root = realpath($path) ?: $path;
if (!is_dir($root)) {
$this->log('ERROR', "Path does not exist: {$path}");
return 1;
}
if ($repoName === '') {
$repoName = $this->detectRepoName($root);
}
// Run manifest_detect logic
$detected = $this->runDetect($root, $repoName);
$current = $this->fetchManifest($apiBase, $org, $repoName, $token);
if ($current === null) {
$this->log('ERROR', "Failed to fetch manifest for {$org}/{$repoName}");
return 1;
}
$issues = $this->validate($current, $detected, $repoName);
if ($json) {
echo json_encode(['repo' => $repoName, 'issues' => $issues], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
} else {
$this->printIssues($repoName, $issues);
}
if ($fix && !empty($issues)) {
return $this->applyFixes($apiBase, $org, $repoName, $token, $current, $issues);
}
return empty($issues) ? 0 : 1;
}
// =====================================================================
// Remote single repo mode — fetch source files via API
// =====================================================================
private function checkRemoteRepo(string $org, string $repoName, string $token, string $apiBase, bool $fix, bool $json): int
{
$current = $this->fetchManifest($apiBase, $org, $repoName, $token);
if ($current === null) {
$this->log('ERROR', "Failed to fetch manifest for {$org}/{$repoName}");
return 1;
}
$issues = $this->validateManifestOnly($current, $repoName);
if ($json) {
echo json_encode(['repo' => $repoName, 'issues' => $issues], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
} else {
$this->printIssues($repoName, $issues);
}
if ($fix && !empty($issues)) {
return $this->applyFixes($apiBase, $org, $repoName, $token, $current, $issues);
}
return empty($issues) ? 0 : 1;
}
// =====================================================================
// Bulk org mode — check all repos
// =====================================================================
private function checkOrg(string $org, string $token, string $apiBase, bool $fix, bool $json, bool $quiet): int
{
$repos = $this->fetchOrgRepos($apiBase, $org, $token);
if ($repos === null) {
$this->log('ERROR', "Failed to fetch repos for org {$org}");
return 1;
}
$this->log('INFO', "Manifest Integrity Check — {$org} (" . count($repos) . " repos)");
$allResults = [];
$totalIssues = 0;
$reposWithIssues = 0;
foreach ($repos as $repo) {
$name = $repo['name'];
$manifest = $this->fetchManifest($apiBase, $org, $name, $token);
if ($manifest === null) {
if (!$quiet) {
$this->log('WARN', "{$name}: no manifest");
}
continue;
}
$issues = $this->validateManifestOnly($manifest, $name);
if (!empty($issues)) {
$reposWithIssues++;
$totalIssues += count($issues);
if ($json) {
$allResults[] = ['repo' => $name, 'issues' => $issues];
} else {
$this->printIssues($name, $issues);
}
if ($fix) {
$this->applyFixes($apiBase, $org, $name, $token, $manifest, $issues);
}
} elseif (!$quiet && !$json) {
$this->log('OK', "{$name}: clean");
}
}
if ($json) {
echo json_encode($allResults, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . "\n";
} else {
echo "\n";
$level = $reposWithIssues > 0 ? 'WARN' : 'OK';
$this->log($level, sprintf(
'Summary: %d repos checked, %d with issues (%d total issues)',
count($repos),
$reposWithIssues,
$totalIssues
));
}
return $reposWithIssues > 0 ? 1 : 0;
}
// =====================================================================
// Validation rules
// =====================================================================
/**
* Full validation: compare API manifest against locally-detected fields.
*/
private function validate(array $current, array $detected, string $repoName): array
{
$issues = [];
// Required fields that should never be empty
$required = ['platform', 'name', 'version', 'package_type', 'language', 'entry_point'];
foreach ($required as $field) {
if (empty($current[$field])) {
$fix = $detected[$field] ?? null;
$issues[] = [
'field' => $field,
'severity' => 'error',
'message' => 'Missing required field',
'current' => '',
'fix' => $fix,
];
}
}
// Drift detection: detected value differs from API
foreach ($detected as $field => $detectedValue) {
$currentValue = $current[$field] ?? '';
if ($detectedValue !== '' && $currentValue !== '' && $detectedValue !== $currentValue) {
// Version drift is expected on dev branches (suffix)
if ($field === 'version' && strpos($detectedValue, $currentValue) === 0) {
continue; // e.g., detected "02.34.50-dev" vs API "02.34.50"
}
if ($field === 'version' && strpos($currentValue, $detectedValue) === 0) {
continue;
}
$issues[] = [
'field' => $field,
'severity' => 'warn',
'message' => 'Drift: source differs from manifest',
'current' => $currentValue,
'fix' => $detectedValue,
];
}
}
// Platform-specific structure validation
$platform = $current['platform'] ?? '';
$issues = array_merge($issues, $this->validatePlatformStructure($platform, $current, $repoName));
return $issues;
}
/**
* API-only validation: check manifest fields for completeness and consistency
* without access to source files.
*/
private function validateManifestOnly(array $manifest, string $repoName): array
{
$issues = [];
// Required fields
$required = ['platform', 'name', 'version', 'language'];
foreach ($required as $field) {
if (empty($manifest[$field])) {
$issues[] = [
'field' => $field,
'severity' => 'error',
'message' => 'Missing required field',
'current' => '',
'fix' => null,
];
}
}
// Recommended fields
$recommended = ['package_type', 'entry_point', 'license_spdx', 'description'];
foreach ($recommended as $field) {
if (empty($manifest[$field])) {
$issues[] = [
'field' => $field,
'severity' => 'info',
'message' => 'Recommended field is empty',
'current' => '',
'fix' => null,
];
}
}
// Platform-specific checks
$platform = $manifest['platform'] ?? '';
$issues = array_merge($issues, $this->validatePlatformStructure($platform, $manifest, $repoName));
return $issues;
}
/**
* Platform-specific validation rules.
*/
private function validatePlatformStructure(string $platform, array $manifest, string $repoName): array
{
$issues = [];
switch ($platform) {
case 'joomla':
case 'waas-component':
// Joomla repos must have element_name
if (empty($manifest['element_name'])) {
$issues[] = [
'field' => 'element_name',
'severity' => 'error',
'message' => 'Joomla repos require element_name',
'current' => '',
'fix' => null,
];
}
// Language should be PHP
if (!empty($manifest['language']) && $manifest['language'] !== 'PHP') {
$issues[] = [
'field' => 'language',
'severity' => 'warn',
'message' => 'Joomla repos should have language=PHP',
'current' => $manifest['language'],
'fix' => 'PHP',
];
}
break;
case 'dolibarr':
case 'crm-module':
if (!empty($manifest['language']) && $manifest['language'] !== 'PHP') {
$issues[] = [
'field' => 'language',
'severity' => 'warn',
'message' => 'Dolibarr repos should have language=PHP',
'current' => $manifest['language'],
'fix' => 'PHP',
];
}
break;
case 'go':
if (!empty($manifest['language']) && $manifest['language'] !== 'Go') {
$issues[] = [
'field' => 'language',
'severity' => 'warn',
'message' => 'Go repos should have language=Go',
'current' => $manifest['language'],
'fix' => 'Go',
];
}
break;
case 'mcp':
if (!empty($manifest['language']) && !in_array($manifest['language'], ['TypeScript', 'JavaScript'], true)) {
$issues[] = [
'field' => 'language',
'severity' => 'warn',
'message' => 'MCP repos should have language=TypeScript or JavaScript',
'current' => $manifest['language'],
'fix' => null,
];
}
break;
}
// Version format check: should be XX.YY.ZZ
$version = $manifest['version'] ?? '';
if ($version !== '' && !preg_match('/^\d{2}\.\d{2}\.\d{2}/', $version)) {
// Allow semver for node/go repos
if (!in_array($platform, ['mcp', 'node', 'go'], true)) {
$issues[] = [
'field' => 'version',
'severity' => 'info',
'message' => 'Version does not match XX.YY.ZZ format',
'current' => $version,
'fix' => null,
];
}
}
return $issues;
}
// =====================================================================
// Output
// =====================================================================
private function printIssues(string $repoName, array $issues): void
{
if (empty($issues)) {
return;
}
$errors = count(array_filter($issues, fn($i) => $i['severity'] === 'error'));
$warns = count(array_filter($issues, fn($i) => $i['severity'] === 'warn'));
$infos = count($issues) - $errors - $warns;
echo "\n";
$summary = [];
if ($errors > 0) $summary[] = "{$errors} error(s)";
if ($warns > 0) $summary[] = "{$warns} warning(s)";
if ($infos > 0) $summary[] = "{$infos} info";
$this->log($errors > 0 ? 'ERROR' : 'WARN', "{$repoName}" . implode(', ', $summary));
foreach ($issues as $issue) {
$icon = match ($issue['severity']) {
'error' => 'ERROR',
'warn' => 'WARN',
default => 'INFO',
};
$msg = sprintf(' %-18s %s', $issue['field'], $issue['message']);
if ($issue['current'] !== '') {
$msg .= " (current: {$issue['current']})";
}
if ($issue['fix'] !== null) {
$msg .= " → fix: {$issue['fix']}";
}
$this->log($icon, $msg);
}
}
// =====================================================================
// Fix application
// =====================================================================
private function applyFixes(string $apiBase, string $org, string $repo, string $token, array $current, array $issues): int
{
$fixes = [];
foreach ($issues as $issue) {
if ($issue['fix'] !== null && $issue['fix'] !== '') {
$fixes[$issue['field']] = $issue['fix'];
}
}
if (empty($fixes)) {
$this->log('INFO', "{$repo}: no auto-fixable issues");
return 0;
}
$merged = array_merge($current, $fixes);
$url = "{$apiBase}/repos/{$org}/{$repo}/manifest";
$payload = json_encode($merged);
$ctx = stream_context_create([
'http' => [
'method' => 'PUT',
'header' => "Authorization: token {$token}\r\nContent-Type: application/json\r\nAccept: application/json\r\n",
'content' => $payload,
'timeout' => 10,
],
]);
$body = @file_get_contents($url, false, $ctx);
if ($body === false) {
$this->log('ERROR', "{$repo}: failed to push fixes");
return 1;
}
$this->log('OK', "{$repo}: fixed " . implode(', ', array_keys($fixes)));
return 0;
}
// =====================================================================
// API helpers
// =====================================================================
private function fetchManifest(string $apiBase, string $org, string $repo, string $token): ?array
{
$url = "{$apiBase}/repos/{$org}/{$repo}/manifest";
$ctx = stream_context_create([
'http' => [
'header' => "Authorization: token {$token}\r\nAccept: application/json\r\n",
'timeout' => 10,
],
]);
$body = @file_get_contents($url, false, $ctx);
if ($body === false) return null;
$data = json_decode($body, true);
return is_array($data) ? $data : null;
}
private function fetchOrgRepos(string $apiBase, string $org, string $token): ?array
{
$allRepos = [];
$page = 1;
$limit = 50;
while (true) {
$url = "{$apiBase}/orgs/{$org}/repos?page={$page}&limit={$limit}";
$ctx = stream_context_create([
'http' => [
'header' => "Authorization: token {$token}\r\nAccept: application/json\r\n",
'timeout' => 15,
],
]);
$body = @file_get_contents($url, false, $ctx);
if ($body === false) return null;
$repos = json_decode($body, true);
if (!is_array($repos) || empty($repos)) break;
$allRepos = array_merge($allRepos, $repos);
if (count($repos) < $limit) break;
$page++;
}
// Filter out archived and empty repos
return array_filter($allRepos, fn($r) => !($r['archived'] ?? false) && !($r['empty'] ?? false));
}
// =====================================================================
// Detection (delegates to manifest_detect logic)
// =====================================================================
private function runDetect(string $root, string $repoName): array
{
$script = __DIR__ . '/manifest_detect.php';
$redirect = PHP_OS_FAMILY === 'Windows' ? '2>NUL' : '2>/dev/null';
$cmd = sprintf(
'php %s --path %s --repo %s --json --quiet %s',
escapeshellarg($script),
escapeshellarg($root),
escapeshellarg($repoName),
$redirect
);
$output = shell_exec($cmd) ?? '';
// Extract JSON object from output (skip banner/log lines)
if (preg_match('/\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/s', $output, $m)) {
$data = json_decode($m[0], true);
if (is_array($data)) {
return $data;
}
}
return [];
}
private function detectRepoName(string $root): string
{
$gitConfig = "{$root}/.git/config";
if (!file_exists($gitConfig)) {
return basename($root);
}
$content = file_get_contents($gitConfig);
if (preg_match('/url\s*=\s*.*\/([^\/\s]+?)(?:\.git)?\s*$/m', $content, $m)) {
return $m[1];
}
return basename($root);
}
}
$app = new ManifestIntegrityCli();
exit($app->execute());
+5 -5
View File
@@ -6,11 +6,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/manifest_licensing.php * PATH: /cli/manifest_licensing.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Ensure licensing tags (updateservers, dlid) in Joomla extension manifests * BRIEF: Ensure licensing tags (updateservers, dlid) in Joomla extension manifests
*/ */
@@ -18,7 +18,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver}; use MokoEnterprise\{CliFramework, SourceResolver};
/** /**
* Reads the <licensing> block from .mokogitea/manifest.xml and ensures that the * Reads the <licensing> block from .mokogitea/manifest.xml and ensures that the
+94 -398
View File
@@ -6,431 +6,126 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokocli.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokocli * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/manifest_read.php * PATH: /cli/manifest_read.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Read repo metadata from Gitea manifest API, auto-detect the rest * BRIEF: Parse .manifest.xml and output requested field(s) for CI consumption
*/ */
declare(strict_types=1); declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class ManifestReadCli extends CliFramework class ManifestReadCli extends CliFramework
{ {
/** Joomla extension XML element names searched in root and source/ dirs. */
private const JOOMLA_XML_ROOTS = ['extension', 'install'];
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Read repo metadata from Gitea API with auto-detection fallback'); $this->setDescription('Parse manifest.xml and output requested field(s) for CI consumption');
$this->addArgument('--path', 'Repository root path', '.'); $this->addArgument('--path', 'Repository root path', '.');
$this->addArgument('--field', 'Single field name to output', ''); $this->addArgument('--field', 'Single field name to output', '');
$this->addArgument('--all', 'Print all fields as KEY=VALUE lines', false); $this->addArgument('--all', 'Print all fields as KEY=VALUE lines', false);
$this->addArgument('--github-output', 'Append all fields to $GITHUB_OUTPUT', false); $this->addArgument('--github-output', 'Append all fields to $GITHUB_OUTPUT', false);
$this->addArgument('--json', 'Output all fields as JSON', false); $this->addArgument('--json', 'Output all fields as JSON', false);
} }
protected function run(): int protected function run(): int
{ {
$path = $this->getArgument('--path'); $path = $this->getArgument('--path');
$field = $this->getArgument('--field'); $field = $this->getArgument('--field');
$showAll = $this->getArgument('--all'); $showAll = $this->getArgument('--all');
$ghOut = $this->getArgument('--github-output'); $ghOutput = $this->getArgument('--github-output');
$jsonMode = $this->getArgument('--json'); $jsonMode = $this->getArgument('--json');
$mode = match (true) { // Determine mode
(bool) $ghOut => 'github-output', if ($ghOutput) {
(bool) $showAll => 'all', $mode = 'github-output';
(bool) $jsonMode => 'json', } elseif ($showAll) {
default => 'field', $mode = 'all';
}; } elseif ($jsonMode) {
$mode = 'json';
} else {
$mode = 'field';
}
// -- Locate manifest --
$root = realpath($path) ?: $path; $root = realpath($path) ?: $path;
$manifestFile = null;
// ── 1. Resolve org/repo ────────────────────────────────────────── // Priority: manifest.xml (current standard)
[$org, $repo] = $this->resolveOrgRepo($root); $candidates = [
"{$root}/.mokogitea/manifest.xml",
// ── 2. Primary: Gitea manifest API ─────────────────────────────── "{$root}/.mokogitea/.manifest.xml", // legacy (dot-prefixed)
$fields = null; "{$root}/.mokogitea/.moko-platform", // legacy v4
if ($org !== '' && $repo !== '') {
$fields = $this->fetchFromApi($org, $repo);
}
// ── 3. Fallback: auto-detect from source tree ────────────────────
if ($fields === null) {
$this->log('INFO', 'API unavailable — falling back to source-tree detection');
$fields = $this->autoDetect($root, $repo);
}
if (empty($fields)) {
$this->log('ERROR', "Could not resolve metadata for {$root}");
return 1;
}
// Provide backward-compatible aliases (hyphenated → underscore)
$fields = $this->addAliases($fields);
// Strip empty values
$fields = array_filter($fields, fn($v) => $v !== '' && $v !== null);
// ── 4. Output ────────────────────────────────────────────────────
return $this->outputFields($fields, $mode, $field);
}
// ── Gitea manifest API ───────────────────────────────────────────────
private function fetchFromApi(string $org, string $repo): ?array
{
$token = getenv('GA_TOKEN') ?: getenv('GITEA_TOKEN') ?: '';
$baseUrl = getenv('GITEA_URL') ?: 'https://git.mokoconsulting.tech';
$baseUrl = rtrim($baseUrl, '/');
if ($token === '') {
return null;
}
$url = "{$baseUrl}/api/v1/repos/{$org}/{$repo}/manifest";
$ctx = stream_context_create([
'http' => [
'header' => "Authorization: token {$token}\r\nAccept: application/json\r\n",
'timeout' => 10,
'ignore_errors' => true,
],
]);
$body = @file_get_contents($url, false, $ctx);
if ($body === false) {
return null;
}
// Check HTTP status from response headers
$status = 0;
if (isset($http_response_header[0])) {
preg_match('/\d{3}/', $http_response_header[0], $m);
$status = (int) ($m[0] ?? 0);
}
if ($status < 200 || $status >= 300) {
return null;
}
$data = json_decode($body, true);
if (!is_array($data) || empty($data)) {
return null;
}
$this->log('INFO', "Loaded metadata from Gitea manifest API ({$org}/{$repo})");
return $data;
}
// ── Auto-detection fallback ──────────────────────────────────────────
private function autoDetect(string $root, string $repoName): array
{
$fields = [
'name' => $repoName ?: basename($root),
'org' => 'MokoConsulting',
]; ];
// Resolve source directory (source/ or src/) foreach ($candidates as $candidate) {
$srcDir = null; if (file_exists($candidate)) {
foreach (['source', 'src'] as $candidate) { $manifestFile = $candidate;
if (is_dir("{$root}/{$candidate}")) {
$srcDir = $candidate;
break; break;
} }
} }
// ── Try Joomla detection ───────────────────────────────────── if ($manifestFile === null) {
$joomlaResult = $this->detectJoomla($root, $srcDir); $this->log('ERROR', "No manifest found in {$root}");
if ($joomlaResult !== null) { return 1;
$fields = array_merge($fields, $joomlaResult);
$this->log('INFO', "Auto-detected platform: joomla ({$fields['extension_type']}{$fields['element_name']})");
return $fields;
} }
// ── Try Dolibarr detection ─────────────────────────────────── // -- Parse XML --
$dolibarrResult = $this->detectDolibarr($root); $xml = @simplexml_load_file($manifestFile);
if ($dolibarrResult !== null) {
$fields = array_merge($fields, $dolibarrResult);
$this->log('INFO', "Auto-detected platform: dolibarr");
return $fields;
}
// ── Generic fallback ───────────────────────────────────────── if ($xml === false) {
$fields['platform'] = $this->detectGenericPlatform($root); // Fallback: try YAML format (.mokostandards legacy)
$fields['element_name'] = strtolower($fields['name']); $content = file_get_contents($manifestFile);
$fields['extension_type'] = 'application'; $fields = [];
$fields['language'] = $this->detectLanguage($root); if (preg_match('/^platform:\s*(.+)/m', $content, $m)) {
if ($srcDir !== null) { $fields['platform'] = trim($m[1], " \t\n\r\"'");
$fields['entry_point'] = "{$srcDir}/";
}
$this->log('INFO', "Auto-detected platform: {$fields['platform']}");
return $fields;
}
/**
* Detect Joomla platform by scanning for extension XML manifests.
*
* Searches root and source/ dirs for XML files containing <extension type="...">.
* Extracts element name from the filename (pkg_*, com_*, mod_*, plg_*, tpl_*) or
* from the <element> tag inside the manifest.
*/
private function detectJoomla(string $root, ?string $srcDir): ?array
{
$searchDirs = [$root];
if ($srcDir !== null) {
$searchDirs[] = "{$root}/{$srcDir}";
}
foreach ($searchDirs as $dir) {
$xmlFiles = glob("{$dir}/*.xml") ?: [];
foreach ($xmlFiles as $xmlFile) {
$content = @file_get_contents($xmlFile);
if ($content === false) {
continue;
}
// Match <extension type="component|module|plugin|package|template|file|library">
if (!preg_match('/<extension\s+[^>]*type="([^"]+)"/', $content, $typeMatch)) {
// Also try legacy <install type="...">
if (!preg_match('/<install\s+[^>]*type="([^"]+)"/', $content, $typeMatch)) {
continue;
}
}
$extType = strtolower($typeMatch[1]);
$basename = pathinfo($xmlFile, PATHINFO_FILENAME);
// Try to extract element name from XML <element> tag
$xml = @simplexml_load_string($content);
$element = '';
if ($xml !== false) {
// Package manifests have <files><file ...>element</file></files>
// Component/module manifests have <element> or use filename
$element = (string) ($xml->element ?? '');
if ($element === '') {
$element = strtolower($basename);
}
} else {
$element = strtolower($basename);
}
// Derive display name
$displayName = (string) ($xml->name ?? ucfirst(str_replace('_', ' ', $basename)));
return [
'platform' => 'joomla',
'extension_type' => $extType,
'element_name' => $element,
'display_name' => $displayName,
'language' => 'PHP',
'entry_point' => ($srcDir ?? '.') . '/',
];
} }
if (preg_match('/^standards_version:\s*(.+)/m', $content, $m)) {
// Also check for pkg_*.xml pattern specifically $fields['standards-version'] = trim($m[1], " \t\n\r\"'");
$pkgFiles = glob("{$dir}/pkg_*.xml") ?: [];
if (!empty($pkgFiles)) {
$basename = pathinfo($pkgFiles[0], PATHINFO_FILENAME);
return [
'platform' => 'joomla',
'extension_type' => 'package',
'element_name' => strtolower($basename),
'display_name' => ucfirst(str_replace('_', ' ', $basename)),
'language' => 'PHP',
'entry_point' => ($srcDir ?? '.') . '/',
];
} }
} if (preg_match('/^governed_repo:\s*(.+)/m', $content, $m)) {
$fields['name'] = trim($m[1], " \t\n\r\"'");
// Check for com_*/manifest.xml pattern (component subdirectory)
$comDirs = glob("{$root}/com_*", GLOB_ONLYDIR) ?: [];
foreach ($comDirs as $comDir) {
$comManifest = glob("{$comDir}/*.xml") ?: [];
foreach ($comManifest as $xmlFile) {
$content = @file_get_contents($xmlFile);
if ($content && preg_match('/<extension\s+[^>]*type="component"/', $content)) {
return [
'platform' => 'joomla',
'extension_type' => 'component',
'element_name' => strtolower(basename($comDir)),
'display_name' => ucfirst(str_replace('com_', '', basename($comDir))),
'language' => 'PHP',
'entry_point' => ($srcDir ?? '.') . '/',
];
}
} }
} } else {
// Register namespace for XPath (optional, simple path works without)
return null; $fields = [
} 'name' => (string)($xml->identity->name ?? ''),
'display-name' => (string)($xml->identity->{"display-name"} ?? ''),
/** 'org' => (string)($xml->identity->org ?? ''),
* Detect Dolibarr platform by scanning for module descriptor files. 'description' => (string)($xml->identity->description ?? ''),
*/ 'license' => (string)($xml->identity->license ?? ''),
private function detectDolibarr(string $root): ?array 'license-spdx' => (string)($xml->identity->license['spdx'] ?? ''),
{ 'platform' => (string)($xml->governance->platform ?? ''),
// Look for mod*.class.php containing DolibarrModules 'standards-version' => (string)($xml->governance->{"standards-version"} ?? ''),
$searchPaths = [ 'standards-source' => (string)($xml->governance->{"standards-source"} ?? ''),
"{$root}/core/modules/mod*.class.php", 'language' => (string)($xml->build->language ?? ''),
"{$root}/*/core/modules/mod*.class.php", 'package-type' => (string)($xml->build->{"package-type"} ?? ''),
]; 'entry-point' => (string)($xml->build->{"entry-point"} ?? ''),
'version' => (string)($xml->identity->version ?? ''),
foreach ($searchPaths as $pattern) { 'source-dir' => (string)($xml->deploy->{"source-dir"} ?? ''),
$files = glob($pattern) ?: []; 'remote-subdir' => (string)($xml->deploy->{"remote-subdir"} ?? ''),
foreach ($files as $file) { 'excludes' => (string)($xml->deploy->excludes ?? ''),
$content = @file_get_contents($file); 'dev-host' => (string)($xml->deploy->{"dev-host"} ?? ''),
if ($content && str_contains($content, 'DolibarrModules')) { 'demo-host' => (string)($xml->deploy->{"demo-host"} ?? ''),
$modName = pathinfo($file, PATHINFO_FILENAME); 'manifest-file' => $manifestFile,
// modMyModule.class → mymodule
$element = strtolower(preg_replace('/^mod/', '', str_replace('.class', '', $modName)));
return [
'platform' => 'dolibarr',
'extension_type' => 'module',
'element_name' => $element,
'display_name' => ucfirst($element),
'language' => 'PHP',
'entry_point' => './',
];
}
}
}
// Secondary: check for update.txt (Dolibarr marker)
if (file_exists("{$root}/update.txt")) {
return [
'platform' => 'dolibarr',
'extension_type' => 'module',
'element_name' => strtolower(basename($root)),
'display_name' => basename($root),
'language' => 'PHP',
'entry_point' => './',
]; ];
} }
return null; // Strip empty values for cleaner output
} $fields = array_filter($fields, fn($v) => $v !== '');
/** // -- Output --
* Detect generic platform type (php, nodejs, python, etc.) from project files.
*/
private function detectGenericPlatform(string $root): string
{
if (file_exists("{$root}/composer.json")) {
return 'php';
}
if (file_exists("{$root}/package.json")) {
return 'nodejs';
}
if (file_exists("{$root}/pyproject.toml") || file_exists("{$root}/setup.py")) {
return 'python';
}
if (file_exists("{$root}/go.mod")) {
return 'go';
}
if (file_exists("{$root}/Cargo.toml")) {
return 'rust';
}
return 'generic';
}
/**
* Detect primary language from project files.
*/
private function detectLanguage(string $root): string
{
if (file_exists("{$root}/composer.json")) {
return 'PHP';
}
if (file_exists("{$root}/tsconfig.json")) {
return 'TypeScript';
}
if (file_exists("{$root}/package.json")) {
return 'JavaScript';
}
if (file_exists("{$root}/pyproject.toml") || file_exists("{$root}/setup.py")) {
return 'Python';
}
return '';
}
// ── Org/repo resolution ──────────────────────────────────────────────
/**
* Resolve org and repo name from environment or git remote.
*
* @return array{0: string, 1: string} [org, repo]
*/
private function resolveOrgRepo(string $root): array
{
// 1. GITHUB_REPOSITORY env (set in Gitea Actions / GitHub Actions)
$envRepo = getenv('GITHUB_REPOSITORY') ?: '';
if ($envRepo !== '' && str_contains($envRepo, '/')) {
return explode('/', $envRepo, 2);
}
// 2. Parse git remote origin URL
$remoteUrl = trim((string) shell_exec(
'git -C ' . escapeshellarg($root) . ' remote get-url origin 2>/dev/null'
));
if ($remoteUrl !== '') {
// SSH: git@host:Org/Repo.git or HTTPS: https://host/Org/Repo.git
if (preg_match('#[/:]([^/]+)/([^/]+?)(?:\.git)?$#', $remoteUrl, $m)) {
return [$m[1], $m[2]];
}
}
return ['', basename($root)];
}
// ── Backward-compatible aliases ──────────────────────────────────────
/**
* Add hyphenated aliases for underscore fields (backward compat with old manifest.xml consumers).
* Also map old field names to new ones.
*/
private function addAliases(array $fields): array
{
// Map API field names → old manifest.xml hyphenated names
$aliases = [
'display_name' => 'display-name',
'license_spdx' => 'license-spdx',
'license_name' => 'license',
'standards_version' => 'standards-version',
'standards_source' => 'standards-source',
'extension_type' => 'package-type',
'entry_point' => 'entry-point',
'element_name' => 'name',
];
foreach ($aliases as $newKey => $oldKey) {
if (isset($fields[$newKey]) && !isset($fields[$oldKey])) {
$fields[$oldKey] = $fields[$newKey];
}
}
return $fields;
}
// ── Output ───────────────────────────────────────────────────────────
private function outputFields(array $fields, string $mode, string $field): int
{
switch ($mode) { switch ($mode) {
case 'field': case 'field':
if ($field === '') { if ($field === '') {
$this->log('ERROR', "Usage: manifest:read --path <dir> --field <name>"); $this->log('ERROR', "Usage: manifest_read.php --path <dir> --field <name>");
$this->log('ERROR', " manifest:read --path <dir> --all"); $this->log('ERROR', " manifest_read.php --path <dir> --all");
$this->log('ERROR', " manifest:read --path <dir> --json"); $this->log('ERROR', " manifest_read.php --path <dir> --json");
$this->log('ERROR', " manifest:read --path <dir> --github-output"); $this->log('ERROR', " manifest_read.php --path <dir> --github-output");
return 2; return 2;
} }
echo ($fields[$field] ?? '') . "\n"; echo ($fields[$field] ?? '') . "\n";
@@ -447,21 +142,22 @@ class ManifestReadCli extends CliFramework
break; break;
case 'github-output': case 'github-output':
$outputFile = getenv('GITHUB_OUTPUT') ?: getenv('GITEA_OUTPUT') ?: ''; $outputFile = getenv('GITHUB_OUTPUT');
$lines = []; if ($outputFile === false || $outputFile === '') {
foreach ($fields as $k => $v) { $this->log('ERROR', 'GITHUB_OUTPUT not set — printing to stdout instead');
$envKey = str_replace('-', '_', $k); foreach ($fields as $k => $v) {
$lines[$envKey] = "{$envKey}={$v}\n"; // Convert field-name to FIELD_NAME for env var style
} $envKey = str_replace('-', '_', $k);
// Deduplicate (aliases may collide after underscore conversion) echo "{$envKey}={$v}\n";
$output = implode('', $lines); }
if ($outputFile === '') {
$this->log('WARNING', 'GITHUB_OUTPUT not set — printing to stdout');
echo $output;
} else { } else {
file_put_contents($outputFile, $output, FILE_APPEND); $fh = fopen($outputFile, 'a');
$this->log('INFO', "Wrote " . count($lines) . " fields to GITHUB_OUTPUT"); foreach ($fields as $k => $v) {
$envKey = str_replace('-', '_', $k);
fwrite($fh, "{$envKey}={$v}\n");
}
fclose($fh);
$this->log('INFO', "Wrote " . count($fields) . " fields to GITHUB_OUTPUT");
} }
break; break;
} }
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/package_build.php * PATH: /cli/package_build.php
* BRIEF: Build ZIP and tar.gz install packages for Joomla/Dolibarr/generic projects * BRIEF: Build ZIP and tar.gz install packages for Joomla/Dolibarr/generic projects
* *
@@ -19,7 +19,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver}; use MokoEnterprise\{CliFramework, SourceResolver};
class PackageBuildCli extends CliFramework class PackageBuildCli extends CliFramework
{ {
+6 -6
View File
@@ -6,11 +6,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/platform_detect.php * PATH: /cli/platform_detect.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Auto-detect repository platform type and optionally update manifest * BRIEF: Auto-detect repository platform type and optionally update manifest
*/ */
@@ -18,7 +18,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class PlatformDetectCli extends CliFramework class PlatformDetectCli extends CliFramework
{ {
@@ -82,7 +82,7 @@ class PlatformDetectCli extends CliFramework
$giteaUrl, $giteaUrl,
$token, $token,
'PATCH', 'PATCH',
"/api/v1/repos/{$owner}/{$repo}/metadata", "/api/v1/repos/{$owner}/{$repo}/manifest",
json_encode(['platform' => $platform]) json_encode(['platform' => $platform])
); );
+6 -6
View File
@@ -6,24 +6,24 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/release.php * PATH: /cli/release.php
* BRIEF: Automate the mokoplatform version branch release flow * BRIEF: Automate the moko-platform version branch release flow
*/ */
declare(strict_types=1); declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class ReleaseCli extends CliFramework class ReleaseCli extends CliFramework
{ {
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Automate the mokoplatform version branch release flow'); $this->setDescription('Automate the moko-platform version branch release flow');
$this->addArgument('--bump', 'Bump type: patch, minor, or major', ''); $this->addArgument('--bump', 'Bump type: patch, minor, or major', '');
} }
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/release_body_update.php * PATH: /cli/release_body_update.php
* BRIEF: Update Gitea release body with changelog extract and checksums * BRIEF: Update Gitea release body with changelog extract and checksums
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class ReleaseBodyUpdateCli extends CliFramework class ReleaseBodyUpdateCli extends CliFramework
{ {
+9 -314
View File
@@ -6,336 +6,31 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokocli.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokocli * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/release_cascade.php * PATH: /cli/release_cascade.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Cascade release zip to all lower stability channels * BRIEF: DEPRECATED — cascade behavior removed. Each release stream is independent.
*/ */
declare(strict_types=1); declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class ReleaseCascadeCli extends CliFramework class ReleaseCascadeCli extends CliFramework
{ {
/** Channel hierarchy: highest stability first. */
private const CHANNELS = ['stable', 'release-candidate', 'beta', 'alpha', 'development'];
/** Map stability input names to canonical tag names. */
private const TAG_MAP = [
'stable' => 'stable',
'release-candidate' => 'release-candidate',
'rc' => 'release-candidate',
'beta' => 'beta',
'alpha' => 'alpha',
'development' => 'development',
'dev' => 'development',
];
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Cascade release zip to all lower stability channels'); $this->setDescription('DEPRECATED — cascade behavior removed');
$this->addArgument('--stability', 'Source stability channel (required)', '');
$this->addArgument('--token', 'Gitea API token (required)', '');
$this->addArgument('--api-base', 'Gitea API base URL for the repo (required)', '');
} }
protected function run(): int protected function run(): int
{ {
$stability = strtolower($this->getArgument('--stability')); $this->log('INFO', 'No-op (cascade behavior removed — each stream is independent)');
$token = $this->getArgument('--token'); return 0;
$apiBase = rtrim($this->getArgument('--api-base'), '/');
if ($token === '') {
$envToken = getenv('MOKOGITEA_TOKEN');
if ($envToken === false || $envToken === '') {
$envToken = getenv('GITEA_TOKEN');
}
if ($envToken !== false && $envToken !== '') {
$token = $envToken;
}
}
if ($stability === '' || $token === '' || $apiBase === '') {
$this->log('ERROR', 'Usage: release_cascade.php --stability CHANNEL --token TOKEN --api-base URL');
return 1;
}
$sourceTag = self::TAG_MAP[$stability] ?? null;
if ($sourceTag === null) {
$this->log('ERROR', "Unknown stability: {$stability}");
return 1;
}
// Find lower channels to cascade to
$lowerChannels = $this->getLowerChannels($sourceTag);
if (count($lowerChannels) === 0) {
$this->log('INFO', "No lower channels for '{$stability}' — nothing to cascade.");
return 0;
}
$this->log('INFO', "Cascading from '{$sourceTag}' to: " . implode(', ', $lowerChannels));
if ($this->dryRun) {
$this->log('INFO', '[DRY RUN] No changes will be made.');
}
// 1. Get source release
$sourceRelease = $this->giteaApi("{$apiBase}/releases/tags/{$sourceTag}", $token);
if ($sourceRelease === null) {
$this->log('WARN', "No release found at tag '{$sourceTag}' — nothing to cascade.");
return 0;
}
$sourceVersion = $sourceRelease['name'] ?? $sourceTag;
$sourceBody = $sourceRelease['body'] ?? '';
$sourceAssets = $sourceRelease['assets'] ?? [];
// Find zip assets (exclude .sha256 sidecars)
$zipAssets = array_filter($sourceAssets, function (array $asset): bool {
$name = strtolower($asset['name'] ?? '');
return str_ends_with($name, '.zip') && !str_ends_with($name, '.sha256');
});
// Also grab sha256 sidecars
$sha256Assets = array_filter($sourceAssets, function (array $asset): bool {
return str_ends_with(strtolower($asset['name'] ?? ''), '.zip.sha256');
});
if (count($zipAssets) === 0) {
$this->log('WARN', "Source release '{$sourceTag}' has no zip assets — nothing to cascade.");
return 0;
}
$this->log('INFO', "Source: {$sourceVersion}" . count($zipAssets) . " zip(s)");
echo "\n";
// 2. Download source assets to temp files
$downloads = [];
foreach (array_merge($zipAssets, $sha256Assets) as $asset) {
$url = $asset['browser_download_url'] ?? '';
if ($url === '') {
continue;
}
$tmpFile = tempnam(sys_get_temp_dir(), 'cascade_');
if ($this->downloadFile($url, $token, $tmpFile)) {
$downloads[] = ['name' => $asset['name'], 'path' => $tmpFile];
$this->log('INFO', "Downloaded: {$asset['name']}");
} else {
$this->log('ERROR', "Failed to download: {$asset['name']}");
}
}
if (count($downloads) === 0) {
$this->log('ERROR', 'Could not download any source assets.');
return 1;
}
// 3. Cascade to each lower channel
$errors = 0;
foreach ($lowerChannels as $targetTag) {
echo "\n";
$result = $this->cascadeToChannel(
$apiBase, $token, $targetTag,
$sourceVersion, $sourceBody, $downloads
);
if (!$result) {
$errors++;
}
}
// 4. Cleanup temp files
foreach ($downloads as $dl) {
@unlink($dl['path']);
}
echo "\n";
$this->log('INFO', "Cascade complete. " . (count($lowerChannels) - $errors)
. "/" . count($lowerChannels) . " channels updated.");
return $errors > 0 ? 1 : 0;
}
/**
* Cascade assets to a single target channel.
*/
private function cascadeToChannel(
string $apiBase,
string $token,
string $targetTag,
string $sourceVersion,
string $sourceBody,
array $downloads
): bool {
$this->log('INFO', "{$targetTag}");
if ($this->dryRun) {
$this->log('INFO', " [DRY RUN] Would cascade to {$targetTag}");
return true;
}
// Find existing release at target tag
$existing = $this->giteaApi("{$apiBase}/releases/tags/{$targetTag}", $token);
if ($existing !== null && !empty($existing['id'])) {
$releaseId = (int) $existing['id'];
// Delete existing assets
$existingAssets = $existing['assets'] ?? [];
foreach ($existingAssets as $asset) {
$assetId = $asset['id'] ?? 0;
if ($assetId > 0) {
$this->giteaApi(
"{$apiBase}/releases/{$releaseId}/assets/{$assetId}",
$token, 'DELETE'
);
}
}
// Update release metadata
$updatePayload = json_encode([
'name' => $sourceVersion,
'body' => $sourceBody,
]);
$this->giteaApi(
"{$apiBase}/releases/{$releaseId}",
$token, 'PATCH', $updatePayload
);
$this->log('INFO', " Updated release metadata (id: {$releaseId})");
} else {
// Create new release at target tag
// Use the source release's target commitish so the tag points to the same commit
$createPayload = json_encode([
'tag_name' => $targetTag,
'target_commitish' => 'main',
'name' => $sourceVersion,
'body' => $sourceBody,
'prerelease' => ($targetTag !== 'stable'),
]);
$newRelease = $this->giteaApi("{$apiBase}/releases", $token, 'POST', $createPayload);
if ($newRelease === null || empty($newRelease['id'])) {
$this->log('ERROR', " Failed to create release at tag '{$targetTag}'");
return false;
}
$releaseId = (int) $newRelease['id'];
$this->log('INFO', " Created release (id: {$releaseId})");
}
// Upload assets
foreach ($downloads as $dl) {
$uploadUrl = "{$apiBase}/releases/{$releaseId}/assets?name=" . rawurlencode($dl['name']);
$success = $this->uploadAsset($uploadUrl, $token, $dl['path'], $dl['name']);
if ($success) {
$this->log('INFO', " Uploaded: {$dl['name']}");
} else {
$this->log('ERROR', " Failed to upload: {$dl['name']}");
}
}
return true;
}
/**
* Get all channels below the given source channel.
*/
private function getLowerChannels(string $sourceTag): array
{
$idx = array_search($sourceTag, self::CHANNELS, true);
if ($idx === false) {
return [];
}
return array_slice(self::CHANNELS, $idx + 1);
}
/**
* Download a file via HTTP.
*/
private function downloadFile(string $url, string $token, string $destPath): bool
{
$ch = curl_init($url);
if ($ch === false) {
return false;
}
$fp = fopen($destPath, 'wb');
if ($fp === false) {
return false;
}
curl_setopt_array($ch, [
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_FILE => $fp,
CURLOPT_HTTPHEADER => ["Authorization: token {$token}"],
CURLOPT_TIMEOUT => 120,
]);
curl_exec($ch);
$code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
fclose($fp);
return $code >= 200 && $code < 300;
}
/**
* Upload a file as a release asset via multipart form.
*/
private function uploadAsset(string $url, string $token, string $filePath, string $fileName): bool
{
$ch = curl_init($url);
if ($ch === false) {
return false;
}
$cfile = new CURLFile($filePath, 'application/octet-stream', $fileName);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => ['attachment' => $cfile],
CURLOPT_HTTPHEADER => ["Authorization: token {$token}"],
CURLOPT_TIMEOUT => 120,
]);
$response = curl_exec($ch);
$code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return $code >= 200 && $code < 300;
}
/**
* Make an HTTP request to the Gitea API.
*/
private function giteaApi(
string $url,
string $token,
string $method = 'GET',
?string $body = null
): ?array {
$ch = curl_init($url);
if ($ch === false) {
return null;
}
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
"Authorization: token {$token}",
'Content-Type: application/json',
],
CURLOPT_TIMEOUT => 30,
CURLOPT_CUSTOMREQUEST => $method,
]);
if ($body !== null) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
}
$response = curl_exec($ch);
$httpCode = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode < 200 || $httpCode >= 300 || empty($response) || !is_string($response)) {
return null;
}
$decoded = json_decode($response, true);
return is_array($decoded) ? $decoded : null;
} }
} }
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/release_create.php * PATH: /cli/release_create.php
* BRIEF: Create or overwrite a Gitea release with proper naming * BRIEF: Create or overwrite a Gitea release with proper naming
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver}; use MokoEnterprise\{CliFramework, SourceResolver};
class ReleaseCreateCli extends CliFramework class ReleaseCreateCli extends CliFramework
{ {
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/release_manage.php * PATH: /cli/release_manage.php
* BRIEF: Create/update Gitea releases, upload assets, update release body * BRIEF: Create/update Gitea releases, upload assets, update release body
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class ReleaseManageCli extends CliFramework class ReleaseManageCli extends CliFramework
{ {
+6 -6
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/release_mirror.php * PATH: /cli/release_mirror.php
* BRIEF: Mirror a Gitea release (with assets) to a GitHub repository * BRIEF: Mirror a Gitea release (with assets) to a GitHub repository
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class ReleaseMirrorCli extends CliFramework class ReleaseMirrorCli extends CliFramework
{ {
@@ -201,7 +201,7 @@ class ReleaseMirrorCli extends CliFramework
CURLOPT_HTTPHEADER => [ CURLOPT_HTTPHEADER => [
"Authorization: token {$token}", "Authorization: token {$token}",
'Accept: application/vnd.github+json', 'Accept: application/vnd.github+json',
'User-Agent: mokoplatform', 'User-Agent: moko-platform',
'Content-Type: application/json', 'Content-Type: application/json',
], ],
CURLOPT_TIMEOUT => 30, CURLOPT_TIMEOUT => 30,
@@ -229,7 +229,7 @@ class ReleaseMirrorCli extends CliFramework
CURLOPT_HTTPHEADER => [ CURLOPT_HTTPHEADER => [
"Authorization: token {$token}", "Authorization: token {$token}",
'Accept: application/vnd.github+json', 'Accept: application/vnd.github+json',
'User-Agent: mokoplatform', 'User-Agent: moko-platform',
'Content-Type: application/octet-stream', 'Content-Type: application/octet-stream',
], ],
CURLOPT_POSTFIELDS => file_get_contents($filePath), CURLOPT_POSTFIELDS => file_get_contents($filePath),
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/release_notes.php * PATH: /cli/release_notes.php
* BRIEF: Extract release notes from CHANGELOG.md for a given version * BRIEF: Extract release notes from CHANGELOG.md for a given version
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class ReleaseNotesCli extends CliFramework class ReleaseNotesCli extends CliFramework
{ {
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/release_package.php * PATH: /cli/release_package.php
* BRIEF: Build packages (ZIP + tar.gz) with SHA-256 and upload to Gitea release * BRIEF: Build packages (ZIP + tar.gz) with SHA-256 and upload to Gitea release
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver}; use MokoEnterprise\{CliFramework, SourceResolver};
class ReleasePackageCli extends CliFramework class ReleasePackageCli extends CliFramework
{ {
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/release_promote.php * PATH: /cli/release_promote.php
* BRIEF: Promote a Gitea release from one channel to another (rename release, tag, assets) * BRIEF: Promote a Gitea release from one channel to another (rename release, tag, assets)
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver}; use MokoEnterprise\{CliFramework, SourceResolver};
class ReleasePromoteCli extends CliFramework class ReleasePromoteCli extends CliFramework
{ {
+5 -5
View File
@@ -6,11 +6,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/release_publish.php * PATH: /cli/release_publish.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Publish a release and create copies for all lesser stability streams. * BRIEF: Publish a release and create copies for all lesser stability streams.
*/ */
@@ -18,7 +18,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class ReleasePublishCli extends CliFramework class ReleasePublishCli extends CliFramework
{ {
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/release_validate.php * PATH: /cli/release_validate.php
* BRIEF: Pre-release validation -- version consistency, required files, manifest checks * BRIEF: Pre-release validation -- version consistency, required files, manifest checks
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver}; use MokoEnterprise\{CliFramework, SourceResolver};
class ReleaseValidateCli extends CliFramework class ReleaseValidateCli extends CliFramework
{ {
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/release_verify.php * PATH: /cli/release_verify.php
* BRIEF: Verify a built release artifact — version, SHA256, disallowed files * BRIEF: Verify a built release artifact — version, SHA256, disallowed files
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class ReleaseVerifyCli extends CliFramework class ReleaseVerifyCli extends CliFramework
{ {
+5 -5
View File
@@ -8,11 +8,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/scaffold_client.php * PATH: /cli/scaffold_client.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Scaffold a new client-waas repo from Template-Client-WaaS with pre-configured settings * BRIEF: Scaffold a new client-waas repo from Template-Client-WaaS with pre-configured settings
*/ */
@@ -20,7 +20,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class ScaffoldClientCli extends CliFramework class ScaffoldClientCli extends CliFramework
{ {
+7 -7
View File
@@ -8,9 +8,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/sync_rulesets.php * PATH: /cli/sync_rulesets.php
* BRIEF: Apply branch protection rules to all repos via platform adapter * BRIEF: Apply branch protection rules to all repos via platform adapter
*/ */
@@ -20,9 +20,9 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php'; require_once __DIR__ . '/../vendor/autoload.php';
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
use MokoCli\Config; use MokoEnterprise\Config;
use MokoCli\PlatformAdapterFactory; use MokoEnterprise\PlatformAdapterFactory;
class SyncRulesetsCli extends CliFramework class SyncRulesetsCli extends CliFramework
{ {
@@ -46,7 +46,7 @@ class SyncRulesetsCli extends CliFramework
); );
$platformName = $adapter->getPlatformName(); $platformName = $adapter->getPlatformName();
$ALWAYS_EXCLUDE = ['mokoplatform', '.github-private']; $ALWAYS_EXCLUDE = ['moko-platform', '.github-private'];
// -- Protection rules (platform-agnostic format) -- // -- Protection rules (platform-agnostic format) --
$PROTECTIONS = [ $PROTECTIONS = [
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/theme_lint.php * PATH: /cli/theme_lint.php
* BRIEF: Lint theme files -- CSS syntax, image sizes, hardcoded URLs * BRIEF: Lint theme files -- CSS syntax, image sizes, hardcoded URLs
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver}; use MokoEnterprise\{CliFramework, SourceResolver};
class ThemeLintCli extends CliFramework class ThemeLintCli extends CliFramework
{ {
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/updates_xml_build.php * PATH: /cli/updates_xml_build.php
* BRIEF: Generate Joomla updates.xml from extension manifest metadata * BRIEF: Generate Joomla updates.xml from extension manifest metadata
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver}; use MokoEnterprise\{CliFramework, SourceResolver};
class UpdatesXmlBuildCli extends CliFramework class UpdatesXmlBuildCli extends CliFramework
{ {
+5 -5
View File
@@ -6,11 +6,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/updates_xml_sync.php * PATH: /cli/updates_xml_sync.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Sync updates.xml to target branches via Gitea API * BRIEF: Sync updates.xml to target branches via Gitea API
* NOTE: Called by pre-release and auto-release workflows after updates.xml * NOTE: Called by pre-release and auto-release workflows after updates.xml
* is modified on the current branch. Pushes the file to other branches * is modified on the current branch. Pushes the file to other branches
@@ -21,7 +21,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class UpdatesXmlSyncCli extends CliFramework class UpdatesXmlSyncCli extends CliFramework
{ {
+5 -5
View File
@@ -6,11 +6,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/version_auto_bump.php * PATH: /cli/version_auto_bump.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Auto patch-bump, set stability suffix, and commit — single CLI replacing inline workflow bash * BRIEF: Auto patch-bump, set stability suffix, and commit — single CLI replacing inline workflow bash
*/ */
@@ -18,7 +18,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class VersionAutoBumpCli extends CliFramework class VersionAutoBumpCli extends CliFramework
{ {
+5 -21
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/version_bump.php * PATH: /cli/version_bump.php
* BRIEF: Auto-increment version -- manifest.xml is canonical, cascades to all XML and MD files * BRIEF: Auto-increment version -- manifest.xml is canonical, cascades to all XML and MD files
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver}; use MokoEnterprise\{CliFramework, SourceResolver};
class VersionBumpCli extends CliFramework class VersionBumpCli extends CliFramework
{ {
@@ -232,25 +232,9 @@ class VersionBumpCli extends CliFramework
$pkgContent $pkgContent
); );
} }
if ($updatedPkg !== $pkgContent && $updatedPkg !== null) { if ($updatedPkg !== $pkgContent) {
file_put_contents($packageJsonFile, $updatedPkg); file_put_contents($packageJsonFile, $updatedPkg);
fwrite(STDERR, "Updated package.json\n"); fwrite(STDERR, "Updated package.json\n");
} elseif (preg_match('/("version"\s*:\s*")(\d+)\.(\d+)\.(\d+)(")/m', $pkgContent, $semM)) {
// Semver fallback: bump standard x.y.z version when XX.YY.ZZ pattern didn't match
$sMajor = (int)$semM[2];
$sMinor = (int)$semM[3];
$sPatch = (int)$semM[4];
switch ($type) {
case 'major': $sMajor++; $sMinor = 0; $sPatch = 0; break;
case 'minor': $sMinor++; $sPatch = 0; break;
default: $sPatch++; break;
}
$semNew = "{$sMajor}.{$sMinor}.{$sPatch}";
$semUpdated = preg_replace('/("version"\s*:\s*")\d+\.\d+\.\d+(")/m', '${1}' . $semNew . '${2}', $pkgContent);
if ($semUpdated !== $pkgContent) {
file_put_contents($packageJsonFile, $semUpdated);
fwrite(STDERR, "Updated package.json (semver: {$semM[2]}.{$semM[3]}.{$semM[4]} -> $semNew)\n");
}
} }
} }
$pyprojectFile = "{$root}/pyproject.toml"; $pyprojectFile = "{$root}/pyproject.toml";
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/version_bump_remote.php * PATH: /cli/version_bump_remote.php
* BRIEF: Bump version in manifest XML and CHANGELOG.md on a remote branch via Gitea API * BRIEF: Bump version in manifest XML and CHANGELOG.md on a remote branch via Gitea API
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver}; use MokoEnterprise\{CliFramework, SourceResolver};
class VersionBumpRemoteCli extends CliFramework class VersionBumpRemoteCli extends CliFramework
{ {
+5 -5
View File
@@ -6,11 +6,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/version_check.php * PATH: /cli/version_check.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Validate version consistency across README, manifests, and sub-packages * BRIEF: Validate version consistency across README, manifests, and sub-packages
*/ */
@@ -18,7 +18,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver}; use MokoEnterprise\{CliFramework, SourceResolver};
class VersionCheckCli extends CliFramework class VersionCheckCli extends CliFramework
{ {
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/version_read.php * PATH: /cli/version_read.php
* BRIEF: Read version — manifest.xml is canonical, falls back to README.md and Joomla XML * BRIEF: Read version — manifest.xml is canonical, falls back to README.md and Joomla XML
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver}; use MokoEnterprise\{CliFramework, SourceResolver};
class VersionReadCli extends CliFramework class VersionReadCli extends CliFramework
{ {
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/version_reset_dev.php * PATH: /cli/version_reset_dev.php
* BRIEF: Reset platform version to 'development' on a branch via Gitea API * BRIEF: Reset platform version to 'development' on a branch via Gitea API
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class VersionResetDevCli extends CliFramework class VersionResetDevCli extends CliFramework
{ {
+4 -4
View File
@@ -6,9 +6,9 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/version_set_platform.php * PATH: /cli/version_set_platform.php
* BRIEF: Set version in platform-specific files (Dolibarr $this->version, Joomla <version>) * BRIEF: Set version in platform-specific files (Dolibarr $this->version, Joomla <version>)
*/ */
@@ -17,7 +17,7 @@ declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver}; use MokoEnterprise\{CliFramework, SourceResolver};
class VersionSetPlatformCli extends CliFramework class VersionSetPlatformCli extends CliFramework
{ {
+9 -9
View File
@@ -6,26 +6,26 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: mokoplatform.CLI * DEFGROUP: moko-platform.CLI
* INGROUP: mokoplatform * INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform * REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/wiki_sync.php * PATH: /cli/wiki_sync.php
* VERSION: 09.33.00 * VERSION: 09.25.06
* BRIEF: Sync select wiki pages from mokoplatform to all template repos * BRIEF: Sync select wiki pages from moko-platform to all template repos
*/ */
declare(strict_types=1); declare(strict_types=1);
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php'; require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\CliFramework; use MokoEnterprise\CliFramework;
class WikiSyncCli extends CliFramework class WikiSyncCli extends CliFramework
{ {
private string $giteaUrl = 'https://git.mokoconsulting.tech'; private string $giteaUrl = 'https://git.mokoconsulting.tech';
private string $token = ''; private string $token = '';
private string $org = 'MokoConsulting'; private string $org = 'MokoConsulting';
private string $sourceRepo = 'mokoplatform'; private string $sourceRepo = 'moko-platform';
private array $targetRepos = []; private array $targetRepos = [];
private array $pages = []; private array $pages = [];
private bool $allTemplates = false; private bool $allTemplates = false;
@@ -38,10 +38,10 @@ class WikiSyncCli extends CliFramework
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Sync wiki pages from mokoplatform to template repos'); $this->setDescription('Sync wiki pages from moko-platform to template repos');
$this->addArgument('--token', 'Gitea API token (required)', ''); $this->addArgument('--token', 'Gitea API token (required)', '');
$this->addArgument('--org', 'Organization (default: MokoConsulting)', 'MokoConsulting'); $this->addArgument('--org', 'Organization (default: MokoConsulting)', 'MokoConsulting');
$this->addArgument('--source', 'Source repo (default: mokoplatform)', 'mokoplatform'); $this->addArgument('--source', 'Source repo (default: moko-platform)', 'moko-platform');
$this->addArgument('--target', 'Target repo (can repeat)', ''); $this->addArgument('--target', 'Target repo (can repeat)', '');
$this->addArgument('--page', 'Page to sync (can repeat)', ''); $this->addArgument('--page', 'Page to sync (can repeat)', '');
$this->addArgument('--all-standards', 'Sync all UPPERCASE standards pages', false); $this->addArgument('--all-standards', 'Sync all UPPERCASE standards pages', false);

Some files were not shown because too many files have changed in this diff Show More