Public Access
feat: security advisory aggregator, manifest API rewrite, namespace rename (#150, #283)
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Universal: Security Audit / Dependency Audit (pull_request) Successful in 7s
Universal: Auto Version Bump / Version Bump (push) Successful in 14s
Platform: mokoplatform CI / Gate 1: Code Quality (pull_request) Failing after 1m8s
Generic: Repo Health / Scripts governance (push) Blocked by required conditions
Generic: Repo Health / Repository health (push) Blocked by required conditions
Generic: Repo Health / Report Issues (push) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.1) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.2) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 2: Unit Tests (8.3) (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 3: Self-Health Check (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 4: Governance (pull_request) Blocked by required conditions
Platform: mokoplatform CI / Gate 5: Template Integrity (pull_request) Blocked by required conditions
Platform: mokoplatform CI / CI Summary (pull_request) Blocked by required conditions
Universal: PR Check / Build RC Package (pull_request) Blocked by required conditions
Universal: PR Check / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Scripts governance (pull_request) Blocked by required conditions
Generic: Repo Health / Repository health (pull_request) Blocked by required conditions
Generic: Repo Health / Report Issues (pull_request) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Universal: Security Audit / Dependency Audit (pull_request) Successful in 7s
Universal: Auto Version Bump / Version Bump (push) Successful in 14s
Platform: mokoplatform CI / Gate 1: Code Quality (pull_request) Failing after 1m8s
- Add `security:advisories` command — cross-repo CVE scanner via composer audit with checkpoint resumability, severity filtering, and auto-issue creation - Rewrite `manifest:read` to use Gitea manifest API as primary source with auto-detection fallback from source tree (no more manifest.xml dependency) - Rename MokoStandards namespace → MokoCli across all files - Rename MokoEnterprise namespace → MokoCli across all files - Rename MokoStandardsParser class → ManifestParser - Fix composer.json autoload paths: src/ → source/
This commit is contained in:
@@ -44,7 +44,7 @@ composer check # Run all checks
|
||||
|
||||
### CLI Framework
|
||||
|
||||
All CLI tools extend `MokoEnterprise\CliFramework` (`lib/Enterprise/CliFramework.php`).
|
||||
All CLI tools extend `MokoCli\CliFramework` (`lib/Enterprise/CliFramework.php`).
|
||||
Built-in flags: `--help`, `--verbose`, `--quiet`, `--dry-run`.
|
||||
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/`)
|
||||
- **Wiki**: documentation lives in the Gitea wiki, not `docs/` files
|
||||
- **New CLI tools**: extend `CliFramework`, not `CLIApp` (legacy)
|
||||
- **Standards**: [MokoStandards](https://git.mokoconsulting.tech/MokoConsulting/mokoplatform/wiki/Home)
|
||||
- **Standards**: [MokoCli](https://git.mokoconsulting.tech/MokoConsulting/mokoplatform/wiki/Home)
|
||||
|
||||
@@ -63,7 +63,7 @@ jobs:
|
||||
|
||||
# Platform/standards/infra repos to exclude
|
||||
EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private mokoplatform MokoTesting"
|
||||
EXCLUDE="$EXCLUDE MokoStandards-Template-Client MokoStandards-Template-Dolibarr MokoStandards-Template-Generic MokoStandards-Template-Joomla MokoDoliProjTemplate"
|
||||
EXCLUDE="$EXCLUDE MokoCli-Template-Client MokoCli-Template-Dolibarr MokoCli-Template-Generic MokoCli-Template-Joomla MokoDoliProjTemplate"
|
||||
|
||||
if [ -n "${{ inputs.repos }}" ]; then
|
||||
# User-specified repos
|
||||
|
||||
@@ -62,7 +62,7 @@ jobs:
|
||||
API="${GITEA_URL}/api/v1"
|
||||
|
||||
EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private mokoplatform MokoTesting"
|
||||
EXCLUDE="$EXCLUDE MokoStandards-Template-Client MokoStandards-Template-Dolibarr MokoStandards-Template-Generic MokoStandards-Template-Joomla MokoDoliProjTemplate"
|
||||
EXCLUDE="$EXCLUDE MokoCli-Template-Client MokoCli-Template-Dolibarr MokoCli-Template-Generic MokoCli-Template-Joomla MokoDoliProjTemplate"
|
||||
|
||||
if [ -n "${{ inputs.repos }}" ]; then
|
||||
REPOS=$(echo "${{ inputs.repos }}" | tr ',' ' ')
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.CI
|
||||
# INGROUP: MokoCli.CI
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
|
||||
# PATH: /.gitea/workflows/ci-generic.yml
|
||||
# VERSION: 01.00.00
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
#
|
||||
# FILE INFORMATION
|
||||
# DEFGROUP: Gitea.Workflow
|
||||
# INGROUP: MokoStandards.Deploy
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API
|
||||
# INGROUP: MokoCli.Deploy
|
||||
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoCli-API
|
||||
# PATH: /templates/workflows/joomla/deploy-manual.yml.template
|
||||
# VERSION: 04.07.00
|
||||
# BRIEF: Manual SFTP deploy to dev server for Joomla repos
|
||||
@@ -40,7 +40,7 @@ jobs:
|
||||
run: |
|
||||
php -v && composer --version
|
||||
|
||||
- name: Setup MokoStandards tools
|
||||
- name: Setup MokoCli tools
|
||||
env:
|
||||
GA_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
|
||||
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
|
||||
@@ -48,7 +48,7 @@ jobs:
|
||||
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}'
|
||||
run: |
|
||||
git clone --depth 1 --branch main --quiet \
|
||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \
|
||||
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoCli-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
|
||||
|
||||
+508
-534
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+20
-2
@@ -2,8 +2,8 @@
|
||||
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
FILE INFORMATION
|
||||
DEFGROUP: MokoStandards.Root
|
||||
INGROUP: MokoStandards
|
||||
DEFGROUP: MokoCli.Root
|
||||
INGROUP: MokoCli
|
||||
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
|
||||
PATH: /CHANGELOG.md
|
||||
BRIEF: Release changelog
|
||||
@@ -12,6 +12,24 @@ BRIEF: Release changelog
|
||||
# Changelog
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- `security:advisories` command — cross-repo security advisory aggregator (#150)
|
||||
- Scans org repos for known CVEs via `composer audit`
|
||||
- Aggregates results into a single report with severity breakdown
|
||||
- Auto-creates tracking issues for critical/high vulnerabilities (`--create-issues`)
|
||||
- Checkpoint-based resumability with `--resume`
|
||||
- Export to JSON/CSV with `--export`
|
||||
|
||||
### Changed
|
||||
- `manifest:read` rewritten to use Gitea manifest API as primary source (#283)
|
||||
- Falls back to auto-detection from source tree (Joomla, Dolibarr, generic)
|
||||
- No longer requires `.mokogitea/manifest.xml` file
|
||||
- Backward-compatible field aliases for existing CI consumers
|
||||
- Renamed `MokoStandards` namespace → `MokoCli` across all files
|
||||
- Renamed `MokoEnterprise` namespace → `MokoCli` across all files
|
||||
- Renamed `MokoStandardsParser` class → `ManifestParser`
|
||||
- Fixed `composer.json` autoload paths: `src/` → `source/`
|
||||
|
||||
## [09.29.00] --- 2026-06-09
|
||||
|
||||
## [09.28.00] --- 2026-06-07
|
||||
|
||||
+161
-161
@@ -1,161 +1,161 @@
|
||||
# Contributing to Moko Consulting Projects
|
||||
|
||||
Thank you for your interest in contributing. All Moko Consulting repositories follow this universal workflow and version policy.
|
||||
|
||||
## Branching Workflow
|
||||
|
||||
```
|
||||
feature/* ──PR──> dev ──draft PR──> (renamed to rc) ──merge──> main
|
||||
```
|
||||
|
||||
### Step by step
|
||||
|
||||
1. **Create a feature branch** from `dev`:
|
||||
```bash
|
||||
git checkout dev && git pull
|
||||
git checkout -b feature/my-change
|
||||
```
|
||||
|
||||
2. **Work and commit** on your feature branch. Push to origin.
|
||||
|
||||
3. **Open a PR**: `feature/my-change` → `dev`. After review and checks, merge it.
|
||||
|
||||
4. **When ready for release**, open a **draft PR**: `dev` → `main`.
|
||||
- This automatically renames the source branch to `rc` (release candidate)
|
||||
- An RC pre-release is built and uploaded
|
||||
|
||||
5. **Alpha and beta branches** are created by manually renaming the branch before the RC stage:
|
||||
- Rename `dev` to `alpha` for early testing → alpha pre-release is built
|
||||
- Rename `alpha` to `beta` for feature-complete testing → beta pre-release is built
|
||||
- When the draft PR is created, the branch is renamed to `rc`
|
||||
|
||||
6. **Once PR checks pass** on the `rc` branch, mark the PR as ready and merge to `main`.
|
||||
|
||||
7. **Merging to main** triggers the stable release pipeline:
|
||||
- Minor version bump (e.g., `02.09.xx` → `02.10.00`)
|
||||
- Stability suffix stripped (clean version)
|
||||
- Gitea release created with ZIP/tar.gz packages
|
||||
- `updates.xml` updated (Joomla extensions)
|
||||
- `dev` branch recreated from `main`
|
||||
|
||||
### Branch summary
|
||||
|
||||
| Branch | Purpose | Created by |
|
||||
|--------|---------|-----------|
|
||||
| `feature/*` | New features and fixes | Developer |
|
||||
| `dev` | Integration branch | Auto-recreated after release |
|
||||
| `alpha` | Alpha pre-release testing | Manual rename from `dev` |
|
||||
| `beta` | Beta pre-release testing | Manual rename from `alpha` |
|
||||
| `rc` | Release candidate | Auto-renamed on draft PR to main |
|
||||
| `main` | Stable releases | Protected, merge only |
|
||||
| `version/XX.YY.ZZ` | Archived release snapshots | Auto-created by CI |
|
||||
|
||||
### Protected branches
|
||||
|
||||
| Branch | Direct push | Merge via |
|
||||
|--------|------------|-----------|
|
||||
| `main` | Blocked (CI bot whitelisted) | PR merge only |
|
||||
| `dev` | Blocked (CI bot whitelisted) | PR merge from feature/* |
|
||||
| `rc` | Blocked (CI bot whitelisted) | Auto-created on draft PR |
|
||||
| `alpha` | Blocked (CI bot whitelisted) | Manual rename |
|
||||
| `beta` | Blocked (CI bot whitelisted) | Manual rename |
|
||||
| `feature/*` | Open | N/A (source branch) |
|
||||
|
||||
## Version Policy
|
||||
|
||||
### Format
|
||||
|
||||
All versions use `XX.YY.ZZ` — three two-digit segments, zero-padded:
|
||||
|
||||
- **XX** — Major version (breaking changes)
|
||||
- **YY** — Minor version (new features, bumped on release to main)
|
||||
- **ZZ** — Patch version (auto-incremented on every push to dev/feature branches)
|
||||
|
||||
Rollover: patch `99` → `00` increments minor; minor `99` → `00` increments major.
|
||||
|
||||
### Stability suffixes
|
||||
|
||||
Each branch appends a suffix to indicate stability:
|
||||
|
||||
| Branch | Suffix | Example |
|
||||
|--------|--------|---------|
|
||||
| `main` | (none) | `02.09.00` |
|
||||
| `dev` | `-dev` | `02.09.01-dev` |
|
||||
| `feature/*` | `-dev` | `02.09.01-dev` |
|
||||
| `alpha` | `-alpha` | `02.09.01-alpha` |
|
||||
| `beta` | `-beta` | `02.09.01-beta` |
|
||||
| `rc` | `-rc` | `02.09.01-rc` |
|
||||
|
||||
### Auto version bump
|
||||
|
||||
On every push to `dev`, `feature/*`, or `patch/*`:
|
||||
|
||||
1. Patch version incremented
|
||||
2. Stability suffix `-dev` applied
|
||||
3. All version-bearing files updated (manifests, CHANGELOG, PHP headers, etc.)
|
||||
4. Commit created with `[skip ci]` to avoid loops
|
||||
|
||||
### Release version flow
|
||||
|
||||
Version bumps happen at specific release events:
|
||||
|
||||
| Event | Bump | Example |
|
||||
|-------|------|---------|
|
||||
| Feature merged to dev | Patch bump after dev release | `02.09.01-dev` → release → `02.09.02-dev` |
|
||||
| Dev promoted to RC | Minor bump | `02.09.02-dev` → `02.10.00-rc` |
|
||||
| RC merged to main | Minor bump | `02.10.00-rc` → `02.11.00` (stable) |
|
||||
| Dev recreated from main | Patch bump | `02.11.00` → `02.11.01-dev` |
|
||||
|
||||
### Release stream copies
|
||||
|
||||
When a higher-stability release is published, copies are created for all lesser streams with the same base version:
|
||||
|
||||
- **RC `02.10.00-rc`** also creates: `02.10.00-dev`, `02.10.00-alpha`, `02.10.00-beta`
|
||||
- **Stable `02.11.00`** also creates: `02.11.00-dev`, `02.11.00-alpha`, `02.11.00-beta`, `02.11.00-rc`
|
||||
|
||||
This ensures Joomla sites on ANY stability channel see the update (Joomla only shows versions higher than what's installed).
|
||||
|
||||
### Version files
|
||||
|
||||
The version tools update all files containing version stamps:
|
||||
|
||||
- `.mokogitea/manifest.xml` (canonical source)
|
||||
- Joomla XML manifests (`<version>` tag)
|
||||
- `README.md`, `CHANGELOG.md` (`VERSION:` pattern)
|
||||
- `package.json`, `pyproject.toml`
|
||||
- Any text file with a `VERSION: XX.YY.ZZ` label
|
||||
|
||||
Files synced from other repos (with a `# REPO:` header) are not touched.
|
||||
|
||||
## Code Standards
|
||||
|
||||
- **PHP**: PSR-12, tabs for indentation
|
||||
- **Copyright**: all files must include the Moko Consulting copyright header
|
||||
- **License**: SPDX identifier `GPL-3.0-or-later` (or as specified per repo)
|
||||
- **Attribution**: use `Authored-by: Moko Consulting` in commits, not individual names
|
||||
|
||||
## Commit Messages
|
||||
|
||||
Use conventional commit format:
|
||||
|
||||
```
|
||||
type(scope): short description
|
||||
|
||||
Optional body with context.
|
||||
|
||||
Authored-by: Moko Consulting
|
||||
```
|
||||
|
||||
Types: `feat`, `fix`, `chore`, `docs`, `style`, `refactor`, `test`, `ci`
|
||||
|
||||
Special flags in commit messages:
|
||||
- `[skip ci]` — skip all CI workflows
|
||||
- `[skip bump]` — skip auto version bump only
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
Use the repository's issue tracker with the appropriate template.
|
||||
|
||||
---
|
||||
|
||||
*Moko Consulting <hello@mokoconsulting.tech>*
|
||||
# Contributing to Moko Consulting Projects
|
||||
|
||||
Thank you for your interest in contributing. All Moko Consulting repositories follow this universal workflow and version policy.
|
||||
|
||||
## Branching Workflow
|
||||
|
||||
```
|
||||
feature/* ──PR──> dev ──draft PR──> (renamed to rc) ──merge──> main
|
||||
```
|
||||
|
||||
### Step by step
|
||||
|
||||
1. **Create a feature branch** from `dev`:
|
||||
```bash
|
||||
git checkout dev && git pull
|
||||
git checkout -b feature/my-change
|
||||
```
|
||||
|
||||
2. **Work and commit** on your feature branch. Push to origin.
|
||||
|
||||
3. **Open a PR**: `feature/my-change` → `dev`. After review and checks, merge it.
|
||||
|
||||
4. **When ready for release**, open a **draft PR**: `dev` → `main`.
|
||||
- This automatically renames the source branch to `rc` (release candidate)
|
||||
- An RC pre-release is built and uploaded
|
||||
|
||||
5. **Alpha and beta branches** are created by manually renaming the branch before the RC stage:
|
||||
- Rename `dev` to `alpha` for early testing → alpha pre-release is built
|
||||
- Rename `alpha` to `beta` for feature-complete testing → beta pre-release is built
|
||||
- When the draft PR is created, the branch is renamed to `rc`
|
||||
|
||||
6. **Once PR checks pass** on the `rc` branch, mark the PR as ready and merge to `main`.
|
||||
|
||||
7. **Merging to main** triggers the stable release pipeline:
|
||||
- Minor version bump (e.g., `02.09.xx` → `02.10.00`)
|
||||
- Stability suffix stripped (clean version)
|
||||
- Gitea release created with ZIP/tar.gz packages
|
||||
- `updates.xml` updated (Joomla extensions)
|
||||
- `dev` branch recreated from `main`
|
||||
|
||||
### Branch summary
|
||||
|
||||
| Branch | Purpose | Created by |
|
||||
|--------|---------|-----------|
|
||||
| `feature/*` | New features and fixes | Developer |
|
||||
| `dev` | Integration branch | Auto-recreated after release |
|
||||
| `alpha` | Alpha pre-release testing | Manual rename from `dev` |
|
||||
| `beta` | Beta pre-release testing | Manual rename from `alpha` |
|
||||
| `rc` | Release candidate | Auto-renamed on draft PR to main |
|
||||
| `main` | Stable releases | Protected, merge only |
|
||||
| `version/XX.YY.ZZ` | Archived release snapshots | Auto-created by CI |
|
||||
|
||||
### Protected branches
|
||||
|
||||
| Branch | Direct push | Merge via |
|
||||
|--------|------------|-----------|
|
||||
| `main` | Blocked (CI bot whitelisted) | PR merge only |
|
||||
| `dev` | Blocked (CI bot whitelisted) | PR merge from feature/* |
|
||||
| `rc` | Blocked (CI bot whitelisted) | Auto-created on draft PR |
|
||||
| `alpha` | Blocked (CI bot whitelisted) | Manual rename |
|
||||
| `beta` | Blocked (CI bot whitelisted) | Manual rename |
|
||||
| `feature/*` | Open | N/A (source branch) |
|
||||
|
||||
## Version Policy
|
||||
|
||||
### Format
|
||||
|
||||
All versions use `XX.YY.ZZ` — three two-digit segments, zero-padded:
|
||||
|
||||
- **XX** — Major version (breaking changes)
|
||||
- **YY** — Minor version (new features, bumped on release to main)
|
||||
- **ZZ** — Patch version (auto-incremented on every push to dev/feature branches)
|
||||
|
||||
Rollover: patch `99` → `00` increments minor; minor `99` → `00` increments major.
|
||||
|
||||
### Stability suffixes
|
||||
|
||||
Each branch appends a suffix to indicate stability:
|
||||
|
||||
| Branch | Suffix | Example |
|
||||
|--------|--------|---------|
|
||||
| `main` | (none) | `02.09.00` |
|
||||
| `dev` | `-dev` | `02.09.01-dev` |
|
||||
| `feature/*` | `-dev` | `02.09.01-dev` |
|
||||
| `alpha` | `-alpha` | `02.09.01-alpha` |
|
||||
| `beta` | `-beta` | `02.09.01-beta` |
|
||||
| `rc` | `-rc` | `02.09.01-rc` |
|
||||
|
||||
### Auto version bump
|
||||
|
||||
On every push to `dev`, `feature/*`, or `patch/*`:
|
||||
|
||||
1. Patch version incremented
|
||||
2. Stability suffix `-dev` applied
|
||||
3. All version-bearing files updated (manifests, CHANGELOG, PHP headers, etc.)
|
||||
4. Commit created with `[skip ci]` to avoid loops
|
||||
|
||||
### Release version flow
|
||||
|
||||
Version bumps happen at specific release events:
|
||||
|
||||
| Event | Bump | Example |
|
||||
|-------|------|---------|
|
||||
| Feature merged to dev | Patch bump after dev release | `02.09.01-dev` → release → `02.09.02-dev` |
|
||||
| Dev promoted to RC | Minor bump | `02.09.02-dev` → `02.10.00-rc` |
|
||||
| RC merged to main | Minor bump | `02.10.00-rc` → `02.11.00` (stable) |
|
||||
| Dev recreated from main | Patch bump | `02.11.00` → `02.11.01-dev` |
|
||||
|
||||
### Release stream copies
|
||||
|
||||
When a higher-stability release is published, copies are created for all lesser streams with the same base version:
|
||||
|
||||
- **RC `02.10.00-rc`** also creates: `02.10.00-dev`, `02.10.00-alpha`, `02.10.00-beta`
|
||||
- **Stable `02.11.00`** also creates: `02.11.00-dev`, `02.11.00-alpha`, `02.11.00-beta`, `02.11.00-rc`
|
||||
|
||||
This ensures Joomla sites on ANY stability channel see the update (Joomla only shows versions higher than what's installed).
|
||||
|
||||
### Version files
|
||||
|
||||
The version tools update all files containing version stamps:
|
||||
|
||||
- `.mokogitea/manifest.xml` (canonical source)
|
||||
- Joomla XML manifests (`<version>` tag)
|
||||
- `README.md`, `CHANGELOG.md` (`VERSION:` pattern)
|
||||
- `package.json`, `pyproject.toml`
|
||||
- Any text file with a `VERSION: XX.YY.ZZ` label
|
||||
|
||||
Files synced from other repos (with a `# REPO:` header) are not touched.
|
||||
|
||||
## Code Standards
|
||||
|
||||
- **PHP**: PSR-12, tabs for indentation
|
||||
- **Copyright**: all files must include the Moko Consulting copyright header
|
||||
- **License**: SPDX identifier `GPL-3.0-or-later` (or as specified per repo)
|
||||
- **Attribution**: use `Authored-by: Moko Consulting` in commits, not individual names
|
||||
|
||||
## Commit Messages
|
||||
|
||||
Use conventional commit format:
|
||||
|
||||
```
|
||||
type(scope): short description
|
||||
|
||||
Optional body with context.
|
||||
|
||||
Authored-by: Moko Consulting
|
||||
```
|
||||
|
||||
Types: `feat`, `fix`, `chore`, `docs`, `style`, `refactor`, `test`, `ci`
|
||||
|
||||
Special flags in commit messages:
|
||||
- `[skip ci]` — skip all CI workflows
|
||||
- `[skip bump]` — skip auto version bump only
|
||||
|
||||
## Reporting Issues
|
||||
|
||||
Use the repository's issue tracker with the appropriate template.
|
||||
|
||||
---
|
||||
|
||||
*Moko Consulting <hello@mokoconsulting.tech>*
|
||||
|
||||
@@ -16,8 +16,8 @@ BRIEF: Project overview and documentation
|
||||
|
||||
PHP implementation of mokoplatform — enterprise standards, automation framework, workflow templates, and bulk sync tooling.
|
||||
|
||||
> **Primary platform**: [Gitea — git.mokoconsulting.tech](https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API)
|
||||
> **Backup mirror**: [GitHub](https://github.com/MokoConsulting/MokoStandards-API) *(read-only mirror)*
|
||||
> **Primary platform**: [Gitea — git.mokoconsulting.tech](https://git.mokoconsulting.tech/MokoConsulting/MokoCli-API)
|
||||
> **Backup mirror**: [GitHub](https://github.com/MokoConsulting/MokoCli-API) *(read-only mirror)*
|
||||
|
||||
## What Lives Here
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{
|
||||
use MokoCli\{
|
||||
AuditLogger,
|
||||
CliFramework,
|
||||
Config,
|
||||
|
||||
@@ -21,7 +21,7 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{
|
||||
use MokoCli\{
|
||||
ApiClient,
|
||||
AuditLogger,
|
||||
CheckpointManager,
|
||||
|
||||
@@ -21,8 +21,8 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoEnterprise\MokoStandardsParser;
|
||||
use MokoCli\CliFramework;
|
||||
use MokoCli\ManifestParser;
|
||||
|
||||
class EnrichManifestXmlCli extends CliFramework
|
||||
{
|
||||
@@ -43,7 +43,7 @@ class EnrichManifestXmlCli extends CliFramework
|
||||
$skipStr = $this->getArgument('--skip');
|
||||
$skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : [];
|
||||
|
||||
$parser = new MokoStandardsParser();
|
||||
$parser = new ManifestParser();
|
||||
$tmpBase = sys_get_temp_dir() . '/moko-enrich-' . getmypid();
|
||||
|
||||
echo "=== mokoplatform XML Manifest Enrichment ===\n";
|
||||
@@ -113,8 +113,8 @@ class EnrichManifestXmlCli extends CliFramework
|
||||
}
|
||||
$enrichment['build']['language'] = $enrichment['build']['language']
|
||||
?? $repo['language']
|
||||
?? MokoStandardsParser::platformLanguage($platform);
|
||||
$enrichment['build']['package_type'] = $enrichment['build']['package_type'] ?? MokoStandardsParser::platformPackageType($platform);
|
||||
?? ManifestParser::platformLanguage($platform);
|
||||
$enrichment['build']['package_type'] = $enrichment['build']['package_type'] ?? ManifestParser::platformPackageType($platform);
|
||||
|
||||
$enrichedXml = $this->enrichManifestXml($existingXml, $enrichment);
|
||||
$dc = count($enrichment['deploy'] ?? []);
|
||||
@@ -312,7 +312,7 @@ class EnrichManifestXmlCli extends CliFramework
|
||||
return $xml;
|
||||
}
|
||||
|
||||
$ns = MokoStandardsParser::NAMESPACE_URI;
|
||||
$ns = ManifestParser::NAMESPACE_URI;
|
||||
$root = $dom->documentElement;
|
||||
|
||||
foreach (['build', 'deploy', 'scripts'] as $tag) {
|
||||
|
||||
@@ -21,8 +21,8 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoEnterprise\MokoStandardsParser;
|
||||
use MokoCli\CliFramework;
|
||||
use MokoCli\ManifestParser;
|
||||
|
||||
class EnrichMokostandardsXmlCli extends CliFramework
|
||||
{
|
||||
@@ -43,7 +43,7 @@ class EnrichMokostandardsXmlCli extends CliFramework
|
||||
$skipStr = $this->getArgument('--skip');
|
||||
$skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : [];
|
||||
|
||||
$parser = new MokoStandardsParser();
|
||||
$parser = new ManifestParser();
|
||||
$tmpBase = sys_get_temp_dir() . '/moko-enrich-' . getmypid();
|
||||
|
||||
echo "=== mokoplatform XML Manifest Enrichment ===\n";
|
||||
@@ -113,8 +113,8 @@ class EnrichMokostandardsXmlCli extends CliFramework
|
||||
}
|
||||
$enrichment['build']['language'] = $enrichment['build']['language']
|
||||
?? $repo['language']
|
||||
?? MokoStandardsParser::platformLanguage($platform);
|
||||
$enrichment['build']['package_type'] = $enrichment['build']['package_type'] ?? MokoStandardsParser::platformPackageType($platform);
|
||||
?? ManifestParser::platformLanguage($platform);
|
||||
$enrichment['build']['package_type'] = $enrichment['build']['package_type'] ?? ManifestParser::platformPackageType($platform);
|
||||
|
||||
$enrichedXml = $this->enrichManifestXml($existingXml, $enrichment);
|
||||
$dc = count($enrichment['deploy'] ?? []);
|
||||
@@ -315,7 +315,7 @@ class EnrichMokostandardsXmlCli extends CliFramework
|
||||
return $xml;
|
||||
}
|
||||
|
||||
$ns = MokoStandardsParser::NAMESPACE_URI;
|
||||
$ns = ManifestParser::NAMESPACE_URI;
|
||||
$root = $dom->documentElement;
|
||||
|
||||
foreach (['build', 'deploy', 'scripts'] as $tag) {
|
||||
|
||||
@@ -25,12 +25,12 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use MokoEnterprise\CheckpointManager;
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoEnterprise\Config;
|
||||
use MokoEnterprise\PlatformAdapterFactory;
|
||||
use MokoEnterprise\GitHubAdapter;
|
||||
use MokoEnterprise\MokoGiteaAdapter;
|
||||
use MokoCli\CheckpointManager;
|
||||
use MokoCli\CliFramework;
|
||||
use MokoCli\Config;
|
||||
use MokoCli\PlatformAdapterFactory;
|
||||
use MokoCli\GitHubAdapter;
|
||||
use MokoCli\MokoGiteaAdapter;
|
||||
|
||||
/**
|
||||
* Gitea Migration Script
|
||||
|
||||
@@ -21,7 +21,7 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{
|
||||
use MokoCli\{
|
||||
ApiClient,
|
||||
AuditLogger,
|
||||
CliFramework,
|
||||
|
||||
@@ -18,8 +18,8 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoEnterprise\MokoStandardsParser;
|
||||
use MokoCli\CliFramework;
|
||||
use MokoCli\ManifestParser;
|
||||
|
||||
class PushManifestXmlCli extends CliFramework
|
||||
{
|
||||
@@ -44,7 +44,7 @@ class PushManifestXmlCli extends CliFramework
|
||||
$skipStr = $this->getArgument('--skip');
|
||||
$skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : [];
|
||||
|
||||
$parser = new MokoStandardsParser();
|
||||
$parser = new ManifestParser();
|
||||
$tmpBase = sys_get_temp_dir() . '/moko-manifest-push-' . getmypid();
|
||||
|
||||
echo "=== mokoplatform XML Manifest Push ===\n";
|
||||
@@ -97,8 +97,8 @@ class PushManifestXmlCli extends CliFramework
|
||||
'description' => $repo['description'] ?? '',
|
||||
'license' => 'GPL-3.0-or-later',
|
||||
'topics' => $repo['topics'] ?? [],
|
||||
'language' => $repo['language'] ?? MokoStandardsParser::platformLanguage($platform),
|
||||
'package_type' => MokoStandardsParser::platformPackageType($platform),
|
||||
'language' => $repo['language'] ?? ManifestParser::platformLanguage($platform),
|
||||
'package_type' => ManifestParser::platformPackageType($platform),
|
||||
'last_synced' => date('c'),
|
||||
]);
|
||||
|
||||
|
||||
@@ -18,8 +18,8 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoEnterprise\MokoStandardsParser;
|
||||
use MokoCli\CliFramework;
|
||||
use MokoCli\ManifestParser;
|
||||
|
||||
class PushMokostandardsXmlCli extends CliFramework
|
||||
{
|
||||
@@ -44,7 +44,7 @@ class PushMokostandardsXmlCli extends CliFramework
|
||||
$skipStr = $this->getArgument('--skip');
|
||||
$skipRepos = $skipStr !== '' ? array_map('trim', explode(',', $skipStr)) : [];
|
||||
|
||||
$parser = new MokoStandardsParser();
|
||||
$parser = new ManifestParser();
|
||||
$tmpBase = sys_get_temp_dir() . '/moko-manifest-push-' . getmypid();
|
||||
|
||||
echo "=== mokoplatform XML Manifest Push ===\n";
|
||||
@@ -97,8 +97,8 @@ class PushMokostandardsXmlCli extends CliFramework
|
||||
'description' => $repo['description'] ?? '',
|
||||
'license' => 'GPL-3.0-or-later',
|
||||
'topics' => $repo['topics'] ?? [],
|
||||
'language' => $repo['language'] ?? MokoStandardsParser::platformLanguage($platform),
|
||||
'package_type' => MokoStandardsParser::platformPackageType($platform),
|
||||
'language' => $repo['language'] ?? ManifestParser::platformLanguage($platform),
|
||||
'package_type' => ManifestParser::platformPackageType($platform),
|
||||
'last_synced' => date('c'),
|
||||
]);
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{ApiClient, AuditLogger, CliFramework, Config, GitPlatformAdapter, MetricsCollector, PlatformAdapterFactory};
|
||||
use MokoCli\{ApiClient, AuditLogger, CliFramework, Config, GitPlatformAdapter, MetricsCollector, PlatformAdapterFactory};
|
||||
|
||||
/**
|
||||
* Enterprise Repository Cleanup
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: MokoStandards.CLI
|
||||
* INGROUP: MokoStandards
|
||||
* DEFGROUP: MokoCli.CLI
|
||||
* INGROUP: MokoCli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
|
||||
* PATH: /bin/moko
|
||||
* BRIEF: Unified CLI dispatcher — run any MokoStandards script without needing GitHub Actions
|
||||
* BRIEF: Unified CLI dispatcher — run any MokoCli script without needing GitHub Actions
|
||||
*
|
||||
* USAGE
|
||||
* php bin/moko <command> [options] (all platforms)
|
||||
@@ -220,6 +220,9 @@ const COMMAND_MAP = [
|
||||
// Licensing
|
||||
'license' => 'cli/license_manage.php',
|
||||
|
||||
// Security
|
||||
'security:advisories' => 'security/advisory_scan.php',
|
||||
|
||||
// Shell completion
|
||||
'completion' => 'cli/completion.php',
|
||||
|
||||
@@ -292,10 +295,10 @@ function printHelp(): void
|
||||
{
|
||||
echo <<<'HELP'
|
||||
╔══════════════════════════════════════════════════════════╗
|
||||
║ MokoStandards CLI (bin/moko) ║
|
||||
║ MokoCli CLI (bin/moko) ║
|
||||
╚══════════════════════════════════════════════════════════╝
|
||||
|
||||
Run any MokoStandards script locally without GitHub Actions.
|
||||
Run any MokoCli script locally without GitHub Actions.
|
||||
|
||||
USAGE
|
||||
php bin/moko <command> [options] (all platforms)
|
||||
@@ -397,7 +400,7 @@ function loadPluginCommands(): array
|
||||
$commands = [];
|
||||
|
||||
foreach (glob("{$pluginDir}/*Plugin.php") as $file) {
|
||||
$className = 'MokoEnterprise\\Plugins\\'
|
||||
$className = 'MokoCli\\Plugins\\'
|
||||
. pathinfo($file, PATHINFO_FILENAME);
|
||||
|
||||
if (!class_exists($className)) {
|
||||
|
||||
@@ -20,9 +20,9 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoEnterprise\Config;
|
||||
use MokoEnterprise\PlatformAdapterFactory;
|
||||
use MokoCli\CliFramework;
|
||||
use MokoCli\Config;
|
||||
use MokoCli\PlatformAdapterFactory;
|
||||
|
||||
class ArchiveRepoCli extends CliFramework
|
||||
{
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
/**
|
||||
* CLI tool to search, filter, and export audit logs.
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class BadgeUpdateCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class BranchRenameCli extends CliFramework
|
||||
{
|
||||
|
||||
+309
-407
@@ -1,407 +1,309 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* This file is part of a Moko Consulting project.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: mokocli.CLI
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/bulk_workflow_push.php
|
||||
* VERSION: 09.29.01
|
||||
* BRIEF: Push a workflow file to all governed repos via the Gitea Contents API
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
|
||||
class BulkWorkflowPushCli extends CliFramework
|
||||
{
|
||||
private int $updated = 0;
|
||||
private int $created = 0;
|
||||
private int $skipped = 0;
|
||||
private int $errors = 0;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->setDescription('Push a workflow file to all governed repos via the Gitea Contents API');
|
||||
$this->addArgument('--gitea-url', 'Gitea URL (default: https://git.mokoconsulting.tech)', 'https://git.mokoconsulting.tech');
|
||||
$this->addArgument('--token', 'Gitea API token', '');
|
||||
$this->addArgument('--org', 'Target organization', '');
|
||||
$this->addArgument('--file', 'Local workflow file to push', '');
|
||||
$this->addArgument('--dest', 'Destination path in repos (default: .mokogitea/workflows/<filename>)', '');
|
||||
$this->addArgument('--branch', 'Target branch (default: main)', 'main');
|
||||
}
|
||||
|
||||
protected function run(): int
|
||||
{
|
||||
$giteaUrl = rtrim($this->getArgument('--gitea-url'), '/');
|
||||
$token = $this->getArgument('--token');
|
||||
$org = $this->getArgument('--org');
|
||||
$workflowFile = $this->getArgument('--file');
|
||||
$destPath = $this->getArgument('--dest');
|
||||
$branch = $this->getArgument('--branch');
|
||||
|
||||
if ($token === '') {
|
||||
$this->log('ERROR', '--token is required.');
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($workflowFile === '') {
|
||||
$this->log('ERROR', '--file is required.');
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!file_exists($workflowFile)) {
|
||||
$this->log('ERROR', "File not found: {$workflowFile}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($org === '') {
|
||||
$this->log('ERROR', '--org is required.');
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($destPath === '') {
|
||||
$destPath = '.mokogitea/workflows/' . basename($workflowFile);
|
||||
}
|
||||
|
||||
$localContent = file_get_contents($workflowFile);
|
||||
|
||||
if ($localContent === false) {
|
||||
$this->log('ERROR', "Could not read file: {$workflowFile}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->log('INFO', "Pushing: {$workflowFile}");
|
||||
$this->log('INFO', " -> {$destPath} (branch: {$branch})");
|
||||
$this->log('INFO', " -> Org: {$org} @ {$giteaUrl}");
|
||||
|
||||
if ($this->dryRun) {
|
||||
$this->log('INFO', '[DRY RUN] No changes will be made.');
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
|
||||
$repos = $this->fetchOrgRepos($giteaUrl, $token, $org);
|
||||
|
||||
if ($repos === null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->log('INFO', "Found " . count($repos) . " repo(s) in \"{$org}\".");
|
||||
echo "\n";
|
||||
fprintf(STDERR, "%-45s | %s\n", 'Repo', 'Status');
|
||||
fprintf(STDERR, "%s\n", str_repeat('-', 70));
|
||||
|
||||
$encodedContent = base64_encode($localContent);
|
||||
|
||||
foreach ($repos as $repo) {
|
||||
$this->pushToRepo($giteaUrl, $token, $repo, $encodedContent, $localContent, $destPath, $branch);
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
$this->log('INFO', "Done: {$this->created} created, {$this->updated} updated, "
|
||||
. "{$this->skipped} skipped, {$this->errors} error(s).");
|
||||
|
||||
return $this->errors > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
private function pushToRepo(
|
||||
string $giteaUrl,
|
||||
string $token,
|
||||
string $repoFullName,
|
||||
string $encodedContent,
|
||||
string $localContent,
|
||||
string $destPath,
|
||||
string $branch
|
||||
): void {
|
||||
[$owner, $repoName] = explode('/', $repoFullName, 2);
|
||||
|
||||
$existing = $this->apiRequest(
|
||||
$giteaUrl,
|
||||
$token,
|
||||
'GET',
|
||||
"/api/v1/repos/{$owner}/{$repoName}/contents/"
|
||||
. "{$destPath}?ref={$branch}"
|
||||
);
|
||||
|
||||
if ($existing['code'] === 200) {
|
||||
$data = json_decode($existing['body'], true);
|
||||
$remoteSha = $data['sha'] ?? '';
|
||||
$remoteContent = base64_decode($data['content'] ?? '');
|
||||
|
||||
if ($remoteContent === $localContent) {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, 'IDENTICAL (skipped)');
|
||||
$this->skipped++;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->dryRun) {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, 'WOULD UPDATE');
|
||||
$this->updated++;
|
||||
return;
|
||||
}
|
||||
|
||||
$payload = json_encode([
|
||||
'content' => $encodedContent,
|
||||
'sha' => $remoteSha,
|
||||
'message' => "chore: sync {$destPath} "
|
||||
. "from mokocli [skip ci]",
|
||||
'branch' => $branch,
|
||||
]);
|
||||
|
||||
$response = $this->apiRequest(
|
||||
$giteaUrl,
|
||||
$token,
|
||||
'PUT',
|
||||
"/api/v1/repos/{$owner}/{$repoName}/contents/"
|
||||
. $destPath,
|
||||
$payload
|
||||
);
|
||||
|
||||
if ($response['code'] === 200) {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, 'UPDATED');
|
||||
$this->updated++;
|
||||
} elseif ($response['code'] === 403) {
|
||||
// Branch protection — fall back to chore branch + PR
|
||||
$this->pushViaPR($giteaUrl, $token, $owner, $repoName, $encodedContent, $remoteSha, $destPath, $branch);
|
||||
} else {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, "ERROR (HTTP {$response['code']})");
|
||||
$this->errors++;
|
||||
}
|
||||
} elseif ($existing['code'] === 404) {
|
||||
if ($this->dryRun) {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, 'WOULD CREATE');
|
||||
$this->created++;
|
||||
return;
|
||||
}
|
||||
|
||||
$payload = json_encode([
|
||||
'content' => $encodedContent,
|
||||
'message' => "chore: add {$destPath} "
|
||||
. "from mokocli [skip ci]",
|
||||
'branch' => $branch,
|
||||
]);
|
||||
|
||||
$response = $this->apiRequest(
|
||||
$giteaUrl,
|
||||
$token,
|
||||
'POST',
|
||||
"/api/v1/repos/{$owner}/{$repoName}/contents/"
|
||||
. $destPath,
|
||||
$payload
|
||||
);
|
||||
|
||||
if ($response['code'] === 201) {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, 'CREATED');
|
||||
$this->created++;
|
||||
} elseif ($response['code'] === 403) {
|
||||
$this->pushViaPR($giteaUrl, $token, $owner, $repoName, $encodedContent, '', $destPath, $branch);
|
||||
} else {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, "ERROR (HTTP {$response['code']})");
|
||||
$this->errors++;
|
||||
}
|
||||
} else {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, "ERROR (HTTP {$existing['code']})");
|
||||
$this->errors++;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fallback: push via chore branch + PR when direct push is blocked (403).
|
||||
*/
|
||||
private function pushViaPR(
|
||||
string $giteaUrl,
|
||||
string $token,
|
||||
string $owner,
|
||||
string $repoName,
|
||||
string $encodedContent,
|
||||
string $remoteSha,
|
||||
string $destPath,
|
||||
string $targetBranch
|
||||
): void {
|
||||
$repoFullName = "{$owner}/{$repoName}";
|
||||
$choreBranch = 'chore/workflow-sync';
|
||||
$commitMsg = "chore: sync {$destPath} from mokocli [skip ci]";
|
||||
$apiBase = "/api/v1/repos/{$owner}/{$repoName}";
|
||||
|
||||
// 1. Create chore branch from target
|
||||
$branchPayload = json_encode([
|
||||
'new_branch_name' => $choreBranch,
|
||||
'old_branch_name' => $targetBranch,
|
||||
]);
|
||||
$branchResp = $this->apiRequest($giteaUrl, $token, 'POST', "{$apiBase}/branches", $branchPayload);
|
||||
if ($branchResp['code'] !== 201 && $branchResp['code'] !== 409) {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, "ERROR (branch create HTTP {$branchResp['code']})");
|
||||
$this->errors++;
|
||||
return;
|
||||
}
|
||||
|
||||
// If branch already exists (409), get the current SHA of the file on that branch
|
||||
if ($branchResp['code'] === 409 || $remoteSha === '') {
|
||||
$existing = $this->apiRequest($giteaUrl, $token, 'GET',
|
||||
"{$apiBase}/contents/{$destPath}?ref={$choreBranch}");
|
||||
if ($existing['code'] === 200) {
|
||||
$data = json_decode($existing['body'], true);
|
||||
$remoteSha = $data['sha'] ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Push file to chore branch
|
||||
$filePayload = ['content' => $encodedContent, 'message' => $commitMsg, 'branch' => $choreBranch];
|
||||
if ($remoteSha !== '') {
|
||||
$filePayload['sha'] = $remoteSha;
|
||||
$method = 'PUT';
|
||||
} else {
|
||||
$method = 'POST';
|
||||
}
|
||||
$fileResp = $this->apiRequest($giteaUrl, $token, $method,
|
||||
"{$apiBase}/contents/{$destPath}", json_encode($filePayload));
|
||||
if ($fileResp['code'] !== 200 && $fileResp['code'] !== 201) {
|
||||
// 422 = file unchanged, still create PR if branch is new
|
||||
if ($fileResp['code'] !== 422) {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, "ERROR (file push HTTP {$fileResp['code']})");
|
||||
$this->errors++;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Create PR
|
||||
$prPayload = json_encode([
|
||||
'title' => "chore: sync workflows from mokocli",
|
||||
'body' => "Automated workflow sync via bulk_workflow_push.",
|
||||
'head' => $choreBranch,
|
||||
'base' => $targetBranch,
|
||||
]);
|
||||
$prResp = $this->apiRequest($giteaUrl, $token, 'POST', "{$apiBase}/pulls", $prPayload);
|
||||
|
||||
if ($prResp['code'] === 201) {
|
||||
$prData = json_decode($prResp['body'], true);
|
||||
$prNumber = $prData['number'] ?? '?';
|
||||
|
||||
// 4. Auto-merge the PR
|
||||
$mergePayload = json_encode(['Do' => 'merge', 'merge_message_field' => $commitMsg]);
|
||||
$mergeResp = $this->apiRequest($giteaUrl, $token, 'POST',
|
||||
"{$apiBase}/pulls/{$prNumber}/merge", $mergePayload);
|
||||
|
||||
if ($mergeResp['code'] === 200 || $mergeResp['code'] === 204) {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, "UPDATED (via PR #{$prNumber}, merged)");
|
||||
$this->updated++;
|
||||
} else {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, "PR #{$prNumber} created (merge HTTP {$mergeResp['code']})");
|
||||
$this->updated++;
|
||||
}
|
||||
} elseif ($prResp['code'] === 409 || $prResp['code'] === 422) {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, 'PR already exists');
|
||||
$this->skipped++;
|
||||
} else {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, "ERROR (PR create HTTP {$prResp['code']})");
|
||||
$this->errors++;
|
||||
}
|
||||
}
|
||||
|
||||
private function fetchOrgRepos(string $giteaUrl, string $token, string $org): ?array
|
||||
{
|
||||
$this->log('INFO', "Fetching repos from org: {$org}");
|
||||
|
||||
$page = 1;
|
||||
$repos = [];
|
||||
|
||||
while (true) {
|
||||
$response = $this->apiRequest(
|
||||
$giteaUrl,
|
||||
$token,
|
||||
'GET',
|
||||
"/api/v1/orgs/{$org}/repos?"
|
||||
. "limit=50&page={$page}"
|
||||
);
|
||||
|
||||
if ($response['code'] < 200 || $response['code'] >= 300) {
|
||||
if ($page === 1) {
|
||||
$this->log('ERROR', "Could not fetch repos "
|
||||
. "(HTTP {$response['code']}).");
|
||||
return null;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$data = json_decode($response['body'], true);
|
||||
|
||||
if (!is_array($data) || count($data) === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($data as $repo) {
|
||||
if (!empty($repo['archived'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fullName = $repo['full_name'] ?? '';
|
||||
|
||||
if ($fullName !== '') {
|
||||
$repos[] = $fullName;
|
||||
}
|
||||
}
|
||||
|
||||
$page++;
|
||||
}
|
||||
|
||||
return $repos;
|
||||
}
|
||||
|
||||
private function apiRequest(
|
||||
string $giteaUrl,
|
||||
string $token,
|
||||
string $method,
|
||||
string $endpoint,
|
||||
?string $body = null
|
||||
): array {
|
||||
$url = $giteaUrl . $endpoint;
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'Accept: application/json',
|
||||
"Authorization: token {$token}",
|
||||
]);
|
||||
|
||||
if ($body !== null) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
}
|
||||
|
||||
$responseBody = curl_exec($ch);
|
||||
$httpCode = (int) curl_getinfo(
|
||||
$ch,
|
||||
CURLINFO_HTTP_CODE
|
||||
);
|
||||
|
||||
if (curl_errno($ch)) {
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return [
|
||||
'code' => 0,
|
||||
'body' => "cURL error: {$error}",
|
||||
];
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
return ['code' => $httpCode, 'body' => $responseBody];
|
||||
}
|
||||
}
|
||||
|
||||
$app = new BulkWorkflowPushCli();
|
||||
exit($app->execute());
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
|
||||
*
|
||||
* This file is part of a Moko Consulting project.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: mokoplatform.CLI
|
||||
* INGROUP: mokoplatform
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
|
||||
* PATH: /cli/bulk_workflow_push.php
|
||||
* VERSION: 09.29.01
|
||||
* BRIEF: Push a workflow file to all governed repos via the Gitea Contents API
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class BulkWorkflowPushCli extends CliFramework
|
||||
{
|
||||
private int $updated = 0;
|
||||
private int $created = 0;
|
||||
private int $skipped = 0;
|
||||
private int $errors = 0;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
$this->setDescription('Push a workflow file to all governed repos via the Gitea Contents API');
|
||||
$this->addArgument('--gitea-url', 'Gitea URL (default: https://git.mokoconsulting.tech)', 'https://git.mokoconsulting.tech');
|
||||
$this->addArgument('--token', 'Gitea API token', '');
|
||||
$this->addArgument('--org', 'Target organization', '');
|
||||
$this->addArgument('--file', 'Local workflow file to push', '');
|
||||
$this->addArgument('--dest', 'Destination path in repos (default: .mokogitea/workflows/<filename>)', '');
|
||||
$this->addArgument('--branch', 'Target branch (default: main)', 'main');
|
||||
}
|
||||
|
||||
protected function run(): int
|
||||
{
|
||||
$giteaUrl = rtrim($this->getArgument('--gitea-url'), '/');
|
||||
$token = $this->getArgument('--token');
|
||||
$org = $this->getArgument('--org');
|
||||
$workflowFile = $this->getArgument('--file');
|
||||
$destPath = $this->getArgument('--dest');
|
||||
$branch = $this->getArgument('--branch');
|
||||
|
||||
if ($token === '') {
|
||||
$this->log('ERROR', '--token is required.');
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($workflowFile === '') {
|
||||
$this->log('ERROR', '--file is required.');
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!file_exists($workflowFile)) {
|
||||
$this->log('ERROR', "File not found: {$workflowFile}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($org === '') {
|
||||
$this->log('ERROR', '--org is required.');
|
||||
return 1;
|
||||
}
|
||||
|
||||
if ($destPath === '') {
|
||||
$destPath = '.mokogitea/workflows/' . basename($workflowFile);
|
||||
}
|
||||
|
||||
$localContent = file_get_contents($workflowFile);
|
||||
|
||||
if ($localContent === false) {
|
||||
$this->log('ERROR', "Could not read file: {$workflowFile}");
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->log('INFO', "Pushing: {$workflowFile}");
|
||||
$this->log('INFO', " -> {$destPath} (branch: {$branch})");
|
||||
$this->log('INFO', " -> Org: {$org} @ {$giteaUrl}");
|
||||
|
||||
if ($this->dryRun) {
|
||||
$this->log('INFO', '[DRY RUN] No changes will be made.');
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
|
||||
$repos = $this->fetchOrgRepos($giteaUrl, $token, $org);
|
||||
|
||||
if ($repos === null) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
$this->log('INFO', "Found " . count($repos) . " repo(s) in \"{$org}\".");
|
||||
echo "\n";
|
||||
fprintf(STDERR, "%-45s | %s\n", 'Repo', 'Status');
|
||||
fprintf(STDERR, "%s\n", str_repeat('-', 70));
|
||||
|
||||
$encodedContent = base64_encode($localContent);
|
||||
|
||||
foreach ($repos as $repo) {
|
||||
$this->pushToRepo($giteaUrl, $token, $repo, $encodedContent, $localContent, $destPath, $branch);
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
$this->log('INFO', "Done: {$this->created} created, {$this->updated} updated, "
|
||||
. "{$this->skipped} skipped, {$this->errors} error(s).");
|
||||
|
||||
return $this->errors > 0 ? 1 : 0;
|
||||
}
|
||||
|
||||
private function pushToRepo(
|
||||
string $giteaUrl,
|
||||
string $token,
|
||||
string $repoFullName,
|
||||
string $encodedContent,
|
||||
string $localContent,
|
||||
string $destPath,
|
||||
string $branch
|
||||
): void {
|
||||
[$owner, $repoName] = explode('/', $repoFullName, 2);
|
||||
|
||||
$existing = $this->apiRequest(
|
||||
$giteaUrl,
|
||||
$token,
|
||||
'GET',
|
||||
"/api/v1/repos/{$owner}/{$repoName}/contents/"
|
||||
. "{$destPath}?ref={$branch}"
|
||||
);
|
||||
|
||||
if ($existing['code'] === 200) {
|
||||
$data = json_decode($existing['body'], true);
|
||||
$remoteSha = $data['sha'] ?? '';
|
||||
$remoteContent = base64_decode($data['content'] ?? '');
|
||||
|
||||
if ($remoteContent === $localContent) {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, 'IDENTICAL (skipped)');
|
||||
$this->skipped++;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->dryRun) {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, 'WOULD UPDATE');
|
||||
$this->updated++;
|
||||
return;
|
||||
}
|
||||
|
||||
$payload = json_encode([
|
||||
'content' => $encodedContent,
|
||||
'sha' => $remoteSha,
|
||||
'message' => "chore: sync {$destPath} "
|
||||
. "from mokoplatform [skip ci]",
|
||||
'branch' => $branch,
|
||||
]);
|
||||
|
||||
$response = $this->apiRequest(
|
||||
$giteaUrl,
|
||||
$token,
|
||||
'PUT',
|
||||
"/api/v1/repos/{$owner}/{$repoName}/contents/"
|
||||
. $destPath,
|
||||
$payload
|
||||
);
|
||||
|
||||
if ($response['code'] === 200) {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, 'UPDATED');
|
||||
$this->updated++;
|
||||
} else {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, "ERROR (HTTP {$response['code']})");
|
||||
$this->errors++;
|
||||
}
|
||||
} elseif ($existing['code'] === 404) {
|
||||
if ($this->dryRun) {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, 'WOULD CREATE');
|
||||
$this->created++;
|
||||
return;
|
||||
}
|
||||
|
||||
$payload = json_encode([
|
||||
'content' => $encodedContent,
|
||||
'message' => "chore: add {$destPath} "
|
||||
. "from mokoplatform [skip ci]",
|
||||
'branch' => $branch,
|
||||
]);
|
||||
|
||||
$response = $this->apiRequest(
|
||||
$giteaUrl,
|
||||
$token,
|
||||
'POST',
|
||||
"/api/v1/repos/{$owner}/{$repoName}/contents/"
|
||||
. $destPath,
|
||||
$payload
|
||||
);
|
||||
|
||||
if ($response['code'] === 201) {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, 'CREATED');
|
||||
$this->created++;
|
||||
} else {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, "ERROR (HTTP {$response['code']})");
|
||||
$this->errors++;
|
||||
}
|
||||
} else {
|
||||
fprintf(STDERR, "%-45s | %s\n", $repoFullName, "ERROR (HTTP {$existing['code']})");
|
||||
$this->errors++;
|
||||
}
|
||||
}
|
||||
|
||||
private function fetchOrgRepos(string $giteaUrl, string $token, string $org): ?array
|
||||
{
|
||||
$this->log('INFO', "Fetching repos from org: {$org}");
|
||||
|
||||
$page = 1;
|
||||
$repos = [];
|
||||
|
||||
while (true) {
|
||||
$response = $this->apiRequest(
|
||||
$giteaUrl,
|
||||
$token,
|
||||
'GET',
|
||||
"/api/v1/orgs/{$org}/repos?"
|
||||
. "limit=50&page={$page}"
|
||||
);
|
||||
|
||||
if ($response['code'] < 200 || $response['code'] >= 300) {
|
||||
if ($page === 1) {
|
||||
$this->log('ERROR', "Could not fetch repos "
|
||||
. "(HTTP {$response['code']}).");
|
||||
return null;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$data = json_decode($response['body'], true);
|
||||
|
||||
if (!is_array($data) || count($data) === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($data as $repo) {
|
||||
if (!empty($repo['archived'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$fullName = $repo['full_name'] ?? '';
|
||||
|
||||
if ($fullName !== '') {
|
||||
$repos[] = $fullName;
|
||||
}
|
||||
}
|
||||
|
||||
$page++;
|
||||
}
|
||||
|
||||
return $repos;
|
||||
}
|
||||
|
||||
private function apiRequest(
|
||||
string $giteaUrl,
|
||||
string $token,
|
||||
string $method,
|
||||
string $endpoint,
|
||||
?string $body = null
|
||||
): array {
|
||||
$url = $giteaUrl . $endpoint;
|
||||
|
||||
$ch = curl_init();
|
||||
curl_setopt($ch, CURLOPT_URL, $url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'Accept: application/json',
|
||||
"Authorization: token {$token}",
|
||||
]);
|
||||
|
||||
if ($body !== null) {
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
|
||||
}
|
||||
|
||||
$responseBody = curl_exec($ch);
|
||||
$httpCode = (int) curl_getinfo(
|
||||
$ch,
|
||||
CURLINFO_HTTP_CODE
|
||||
);
|
||||
|
||||
if (curl_errno($ch)) {
|
||||
$error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
return [
|
||||
'code' => 0,
|
||||
'body' => "cURL error: {$error}",
|
||||
];
|
||||
}
|
||||
|
||||
curl_close($ch);
|
||||
|
||||
return ['code' => $httpCode, 'body' => $responseBody];
|
||||
}
|
||||
}
|
||||
|
||||
$app = new BulkWorkflowPushCli();
|
||||
exit($app->execute());
|
||||
|
||||
@@ -20,7 +20,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class BulkWorkflowTriggerCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class ChangelogPromoteCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class ChangelogPruneCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class ClientDashboardCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class ClientHealthCheckCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class ClientInventoryCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class ClientProvisionCli extends CliFramework
|
||||
{
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class CompletionCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class CreateProjectCli extends CliFramework
|
||||
{
|
||||
@@ -80,10 +80,10 @@ class CreateProjectCli extends CliFramework
|
||||
return 2;
|
||||
}
|
||||
|
||||
$config = \MokoEnterprise\Config::load();
|
||||
$config = \MokoCli\Config::load();
|
||||
$platformName = $config->getString('platform', 'gitea');
|
||||
try {
|
||||
$adapter = \MokoEnterprise\PlatformAdapterFactory::create($config);
|
||||
$adapter = \MokoCli\PlatformAdapterFactory::create($config);
|
||||
$api = $adapter->getApiClient();
|
||||
} catch (\Exception $e) {
|
||||
$this->log('ERROR', "Platform initialization failed: " . $e->getMessage());
|
||||
@@ -205,7 +205,7 @@ class CreateProjectCli extends CliFramework
|
||||
return $data['data'] ?? [];
|
||||
}
|
||||
|
||||
private function restGet(string $path, string $token, ?\MokoEnterprise\ApiClient $apiClient = null): array
|
||||
private function restGet(string $path, string $token, ?\MokoCli\ApiClient $apiClient = null): array
|
||||
{
|
||||
if ($apiClient !== null) {
|
||||
try {
|
||||
@@ -217,7 +217,7 @@ class CreateProjectCli extends CliFramework
|
||||
return [];
|
||||
}
|
||||
|
||||
private function detectRepoPlatform(string $org, string $repo, string $token, ?\MokoEnterprise\ApiClient $apiClient = null): string
|
||||
private function detectRepoPlatform(string $org, string $repo, string $token, ?\MokoCli\ApiClient $apiClient = null): string
|
||||
{
|
||||
foreach (['.github/.mokostandards', '.mokogitea/.mokostandards', '.mokostandards'] as $path) {
|
||||
$data = $this->restGet("repos/{$org}/{$repo}/contents/{$path}", $token, $apiClient);
|
||||
|
||||
+4
-4
@@ -20,9 +20,9 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoEnterprise\Config;
|
||||
use MokoEnterprise\PlatformAdapterFactory;
|
||||
use MokoCli\CliFramework;
|
||||
use MokoCli\Config;
|
||||
use MokoCli\PlatformAdapterFactory;
|
||||
|
||||
class CreateRepoCli extends CliFramework
|
||||
{
|
||||
@@ -138,7 +138,7 @@ class CreateRepoCli extends CliFramework
|
||||
echo "Step 4: Creating README.md...\n";
|
||||
$baseUrl = $platformName === 'gitea' ? $config->getString('gitea.url', 'https://git.mokoconsulting.tech') : 'https://github.com';
|
||||
$repoUrl = "{$baseUrl}/{$org}/{$name}";
|
||||
$standardsUrl = "{$baseUrl}/{$org}/MokoStandards";
|
||||
$standardsUrl = "{$baseUrl}/{$org}/MokoCli";
|
||||
$readmeContent = "<!--\n"
|
||||
. "Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>\n"
|
||||
. "SPDX-License-Identifier: GPL-3.0-or-later\n"
|
||||
|
||||
@@ -31,7 +31,7 @@ require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{CliFramework, SourceResolver};
|
||||
use MokoCli\{CliFramework, SourceResolver};
|
||||
use phpseclib3\Net\SFTP;
|
||||
use phpseclib3\Crypt\PublicKeyLoader;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class DevBranchResetCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class GrafanaDashboardCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{CliFramework, SourceResolver};
|
||||
use MokoCli\{CliFramework, SourceResolver};
|
||||
|
||||
class JoomlaBuildCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class JoomlaCompatCheckCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class JoomlaMetadataValidateCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -25,7 +25,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use MokoEnterprise\{ApiClient, AuditLogger, CliFramework, Config, PlatformAdapterFactory, SourceResolver};
|
||||
use MokoCli\{ApiClient, AuditLogger, CliFramework, Config, PlatformAdapterFactory, SourceResolver};
|
||||
|
||||
/**
|
||||
* Joomla Release Manager
|
||||
@@ -57,7 +57,7 @@ class JoomlaRelease extends CliFramework
|
||||
];
|
||||
|
||||
private ApiClient $api;
|
||||
private \MokoEnterprise\GitPlatformAdapter $adapter;
|
||||
private \MokoCli\GitPlatformAdapter $adapter;
|
||||
|
||||
protected function configure(): void
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class LicenseManage extends CliFramework
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{CliFramework, SourceResolver};
|
||||
use MokoCli\{CliFramework, SourceResolver};
|
||||
|
||||
class ManifestDetectCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{CliFramework, SourceResolver};
|
||||
use MokoCli\{CliFramework, SourceResolver};
|
||||
|
||||
class ManifestElementCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class ManifestIntegrityCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{CliFramework, SourceResolver};
|
||||
use MokoCli\{CliFramework, SourceResolver};
|
||||
|
||||
/**
|
||||
* Reads the <licensing> block from .mokogitea/manifest.xml and ensures that the
|
||||
|
||||
+398
-94
@@ -6,126 +6,431 @@
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*
|
||||
* FILE INFORMATION
|
||||
* DEFGROUP: mokoplatform.CLI
|
||||
* INGROUP: mokoplatform
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform
|
||||
* DEFGROUP: mokocli.CLI
|
||||
* INGROUP: mokocli
|
||||
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
|
||||
* PATH: /cli/manifest_read.php
|
||||
* VERSION: 09.29.01
|
||||
* BRIEF: Parse .manifest.xml and output requested field(s) for CI consumption
|
||||
* VERSION: 10.00.00
|
||||
* BRIEF: Read repo metadata from Gitea manifest API, auto-detect the rest
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\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
|
||||
{
|
||||
$this->setDescription('Parse manifest.xml and output requested field(s) for CI consumption');
|
||||
$this->addArgument('--path', 'Repository root path', '.');
|
||||
$this->addArgument('--field', 'Single field name to output', '');
|
||||
$this->addArgument('--all', 'Print all fields as KEY=VALUE lines', false);
|
||||
$this->addArgument('--github-output', 'Append all fields to $GITHUB_OUTPUT', false);
|
||||
$this->addArgument('--json', 'Output all fields as JSON', false);
|
||||
$this->setDescription('Read repo metadata from Gitea API with auto-detection fallback');
|
||||
$this->addArgument('--path', 'Repository root path', '.');
|
||||
$this->addArgument('--field', 'Single field name to output', '');
|
||||
$this->addArgument('--all', 'Print all fields as KEY=VALUE lines', false);
|
||||
$this->addArgument('--github-output', 'Append all fields to $GITHUB_OUTPUT', false);
|
||||
$this->addArgument('--json', 'Output all fields as JSON', false);
|
||||
}
|
||||
|
||||
protected function run(): int
|
||||
{
|
||||
$path = $this->getArgument('--path');
|
||||
$field = $this->getArgument('--field');
|
||||
$path = $this->getArgument('--path');
|
||||
$field = $this->getArgument('--field');
|
||||
$showAll = $this->getArgument('--all');
|
||||
$ghOutput = $this->getArgument('--github-output');
|
||||
$ghOut = $this->getArgument('--github-output');
|
||||
$jsonMode = $this->getArgument('--json');
|
||||
|
||||
// Determine mode
|
||||
if ($ghOutput) {
|
||||
$mode = 'github-output';
|
||||
} elseif ($showAll) {
|
||||
$mode = 'all';
|
||||
} elseif ($jsonMode) {
|
||||
$mode = 'json';
|
||||
} else {
|
||||
$mode = 'field';
|
||||
$mode = match (true) {
|
||||
(bool) $ghOut => 'github-output',
|
||||
(bool) $showAll => 'all',
|
||||
(bool) $jsonMode => 'json',
|
||||
default => 'field',
|
||||
};
|
||||
|
||||
$root = realpath($path) ?: $path;
|
||||
|
||||
// ── 1. Resolve org/repo ──────────────────────────────────────────
|
||||
[$org, $repo] = $this->resolveOrgRepo($root);
|
||||
|
||||
// ── 2. Primary: Gitea manifest API ───────────────────────────────
|
||||
$fields = null;
|
||||
if ($org !== '' && $repo !== '') {
|
||||
$fields = $this->fetchFromApi($org, $repo);
|
||||
}
|
||||
|
||||
// -- Locate manifest --
|
||||
$root = realpath($path) ?: $path;
|
||||
$manifestFile = null;
|
||||
// ── 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);
|
||||
}
|
||||
|
||||
// Priority: manifest.xml (current standard)
|
||||
$candidates = [
|
||||
"{$root}/.mokogitea/manifest.xml",
|
||||
"{$root}/.mokogitea/.manifest.xml", // legacy (dot-prefixed)
|
||||
"{$root}/.mokogitea/.mokoplatform", // legacy v4
|
||||
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',
|
||||
];
|
||||
|
||||
foreach ($candidates as $candidate) {
|
||||
if (file_exists($candidate)) {
|
||||
$manifestFile = $candidate;
|
||||
// Resolve source directory (source/ or src/)
|
||||
$srcDir = null;
|
||||
foreach (['source', 'src'] as $candidate) {
|
||||
if (is_dir("{$root}/{$candidate}")) {
|
||||
$srcDir = $candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($manifestFile === null) {
|
||||
$this->log('ERROR', "No manifest found in {$root}");
|
||||
return 1;
|
||||
// ── Try Joomla detection ─────────────────────────────────────
|
||||
$joomlaResult = $this->detectJoomla($root, $srcDir);
|
||||
if ($joomlaResult !== null) {
|
||||
$fields = array_merge($fields, $joomlaResult);
|
||||
$this->log('INFO', "Auto-detected platform: joomla ({$fields['extension_type']} — {$fields['element_name']})");
|
||||
return $fields;
|
||||
}
|
||||
|
||||
// -- Parse XML --
|
||||
$xml = @simplexml_load_file($manifestFile);
|
||||
// ── Try Dolibarr detection ───────────────────────────────────
|
||||
$dolibarrResult = $this->detectDolibarr($root);
|
||||
if ($dolibarrResult !== null) {
|
||||
$fields = array_merge($fields, $dolibarrResult);
|
||||
$this->log('INFO', "Auto-detected platform: dolibarr");
|
||||
return $fields;
|
||||
}
|
||||
|
||||
if ($xml === false) {
|
||||
// Fallback: try YAML format (.mokostandards legacy)
|
||||
$content = file_get_contents($manifestFile);
|
||||
$fields = [];
|
||||
if (preg_match('/^platform:\s*(.+)/m', $content, $m)) {
|
||||
$fields['platform'] = trim($m[1], " \t\n\r\"'");
|
||||
// ── Generic fallback ─────────────────────────────────────────
|
||||
$fields['platform'] = $this->detectGenericPlatform($root);
|
||||
$fields['element_name'] = strtolower($fields['name']);
|
||||
$fields['extension_type'] = 'application';
|
||||
$fields['language'] = $this->detectLanguage($root);
|
||||
if ($srcDir !== null) {
|
||||
$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)) {
|
||||
$fields['standards-version'] = trim($m[1], " \t\n\r\"'");
|
||||
|
||||
// Also check for pkg_*.xml pattern specifically
|
||||
$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)
|
||||
$fields = [
|
||||
'name' => (string)($xml->identity->name ?? ''),
|
||||
'display-name' => (string)($xml->identity->{"display-name"} ?? ''),
|
||||
'org' => (string)($xml->identity->org ?? ''),
|
||||
'description' => (string)($xml->identity->description ?? ''),
|
||||
'license' => (string)($xml->identity->license ?? ''),
|
||||
'license-spdx' => (string)($xml->identity->license['spdx'] ?? ''),
|
||||
'platform' => (string)($xml->governance->platform ?? ''),
|
||||
'standards-version' => (string)($xml->governance->{"standards-version"} ?? ''),
|
||||
'standards-source' => (string)($xml->governance->{"standards-source"} ?? ''),
|
||||
'language' => (string)($xml->build->language ?? ''),
|
||||
'package-type' => (string)($xml->build->{"package-type"} ?? ''),
|
||||
'entry-point' => (string)($xml->build->{"entry-point"} ?? ''),
|
||||
'version' => (string)($xml->identity->version ?? ''),
|
||||
'source-dir' => (string)($xml->deploy->{"source-dir"} ?? ''),
|
||||
'remote-subdir' => (string)($xml->deploy->{"remote-subdir"} ?? ''),
|
||||
'excludes' => (string)($xml->deploy->excludes ?? ''),
|
||||
'dev-host' => (string)($xml->deploy->{"dev-host"} ?? ''),
|
||||
'demo-host' => (string)($xml->deploy->{"demo-host"} ?? ''),
|
||||
'manifest-file' => $manifestFile,
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect Dolibarr platform by scanning for module descriptor files.
|
||||
*/
|
||||
private function detectDolibarr(string $root): ?array
|
||||
{
|
||||
// Look for mod*.class.php containing DolibarrModules
|
||||
$searchPaths = [
|
||||
"{$root}/core/modules/mod*.class.php",
|
||||
"{$root}/*/core/modules/mod*.class.php",
|
||||
];
|
||||
|
||||
foreach ($searchPaths as $pattern) {
|
||||
$files = glob($pattern) ?: [];
|
||||
foreach ($files as $file) {
|
||||
$content = @file_get_contents($file);
|
||||
if ($content && str_contains($content, 'DolibarrModules')) {
|
||||
$modName = pathinfo($file, PATHINFO_FILENAME);
|
||||
// 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' => './',
|
||||
];
|
||||
}
|
||||
|
||||
// Strip empty values for cleaner output
|
||||
$fields = array_filter($fields, fn($v) => $v !== '');
|
||||
return null;
|
||||
}
|
||||
|
||||
// -- 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) {
|
||||
case 'field':
|
||||
if ($field === '') {
|
||||
$this->log('ERROR', "Usage: manifest_read.php --path <dir> --field <name>");
|
||||
$this->log('ERROR', " manifest_read.php --path <dir> --all");
|
||||
$this->log('ERROR', " manifest_read.php --path <dir> --json");
|
||||
$this->log('ERROR', " manifest_read.php --path <dir> --github-output");
|
||||
$this->log('ERROR', "Usage: manifest:read --path <dir> --field <name>");
|
||||
$this->log('ERROR', " manifest:read --path <dir> --all");
|
||||
$this->log('ERROR', " manifest:read --path <dir> --json");
|
||||
$this->log('ERROR', " manifest:read --path <dir> --github-output");
|
||||
return 2;
|
||||
}
|
||||
echo ($fields[$field] ?? '') . "\n";
|
||||
@@ -142,22 +447,21 @@ class ManifestReadCli extends CliFramework
|
||||
break;
|
||||
|
||||
case 'github-output':
|
||||
$outputFile = getenv('GITHUB_OUTPUT');
|
||||
if ($outputFile === false || $outputFile === '') {
|
||||
$this->log('ERROR', 'GITHUB_OUTPUT not set — printing to stdout instead');
|
||||
foreach ($fields as $k => $v) {
|
||||
// Convert field-name to FIELD_NAME for env var style
|
||||
$envKey = str_replace('-', '_', $k);
|
||||
echo "{$envKey}={$v}\n";
|
||||
}
|
||||
$outputFile = getenv('GITHUB_OUTPUT') ?: getenv('GITEA_OUTPUT') ?: '';
|
||||
$lines = [];
|
||||
foreach ($fields as $k => $v) {
|
||||
$envKey = str_replace('-', '_', $k);
|
||||
$lines[$envKey] = "{$envKey}={$v}\n";
|
||||
}
|
||||
// Deduplicate (aliases may collide after underscore conversion)
|
||||
$output = implode('', $lines);
|
||||
|
||||
if ($outputFile === '') {
|
||||
$this->log('WARNING', 'GITHUB_OUTPUT not set — printing to stdout');
|
||||
echo $output;
|
||||
} else {
|
||||
$fh = fopen($outputFile, 'a');
|
||||
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");
|
||||
file_put_contents($outputFile, $output, FILE_APPEND);
|
||||
$this->log('INFO', "Wrote " . count($lines) . " fields to GITHUB_OUTPUT");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{CliFramework, SourceResolver};
|
||||
use MokoCli\{CliFramework, SourceResolver};
|
||||
|
||||
class PackageBuildCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class PlatformDetectCli extends CliFramework
|
||||
{
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class ReleaseCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class ReleaseBodyUpdateCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class ReleaseCascadeCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{CliFramework, SourceResolver};
|
||||
use MokoCli\{CliFramework, SourceResolver};
|
||||
|
||||
class ReleaseCreateCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class ReleaseManageCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class ReleaseMirrorCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class ReleaseNotesCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{CliFramework, SourceResolver};
|
||||
use MokoCli\{CliFramework, SourceResolver};
|
||||
|
||||
class ReleasePackageCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{CliFramework, SourceResolver};
|
||||
use MokoCli\{CliFramework, SourceResolver};
|
||||
|
||||
class ReleasePromoteCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class ReleasePublishCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{CliFramework, SourceResolver};
|
||||
use MokoCli\{CliFramework, SourceResolver};
|
||||
|
||||
class ReleaseValidateCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class ReleaseVerifyCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class ScaffoldClientCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -20,9 +20,9 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoEnterprise\Config;
|
||||
use MokoEnterprise\PlatformAdapterFactory;
|
||||
use MokoCli\CliFramework;
|
||||
use MokoCli\Config;
|
||||
use MokoCli\PlatformAdapterFactory;
|
||||
|
||||
class SyncRulesetsCli extends CliFramework
|
||||
{
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{CliFramework, SourceResolver};
|
||||
use MokoCli\{CliFramework, SourceResolver};
|
||||
|
||||
class ThemeLintCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{CliFramework, SourceResolver};
|
||||
use MokoCli\{CliFramework, SourceResolver};
|
||||
|
||||
class UpdatesXmlBuildCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class UpdatesXmlSyncCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class VersionAutoBumpCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{CliFramework, SourceResolver};
|
||||
use MokoCli\{CliFramework, SourceResolver};
|
||||
|
||||
class VersionBumpCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{CliFramework, SourceResolver};
|
||||
use MokoCli\{CliFramework, SourceResolver};
|
||||
|
||||
class VersionBumpRemoteCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -18,7 +18,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{CliFramework, SourceResolver};
|
||||
use MokoCli\{CliFramework, SourceResolver};
|
||||
|
||||
class VersionCheckCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{CliFramework, SourceResolver};
|
||||
use MokoCli\{CliFramework, SourceResolver};
|
||||
|
||||
class VersionReadCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class VersionResetDevCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -17,7 +17,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{CliFramework, SourceResolver};
|
||||
use MokoCli\{CliFramework, SourceResolver};
|
||||
|
||||
class VersionSetPlatformCli extends CliFramework
|
||||
{
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class WikiSyncCli extends CliFramework
|
||||
{
|
||||
|
||||
+667
-762
File diff suppressed because it is too large
Load Diff
+6
-6
@@ -43,15 +43,14 @@
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"MokoStandards\\": "src/",
|
||||
"MokoEnterprise\\": "lib/Enterprise/",
|
||||
"MokoStandards\\Plugins\\Joomla\\": "lib/plugins/Joomla/"
|
||||
"MokoCli\\": ["source/", "lib/Enterprise/"],
|
||||
"MokoCli\\Plugins\\Joomla\\": "lib/plugins/Joomla/"
|
||||
},
|
||||
"classmap": [
|
||||
"lib/Enterprise/CliFramework.php"
|
||||
],
|
||||
"files": [
|
||||
"src/functions.php"
|
||||
"source/functions.php"
|
||||
]
|
||||
},
|
||||
"archive": {
|
||||
@@ -64,7 +63,7 @@
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"MokoStandards\\Tests\\": "tests/"
|
||||
"MokoCli\\Tests\\": "tests/"
|
||||
}
|
||||
},
|
||||
"bin": [
|
||||
@@ -93,7 +92,8 @@
|
||||
"check:headers": "php bin/moko check:headers -- --path .",
|
||||
"check:secrets": "php bin/moko check:secrets -- --path .",
|
||||
"check:enterprise": "php bin/moko check:enterprise -- --path .",
|
||||
"drift": "php bin/moko drift"
|
||||
"drift": "php bin/moko drift",
|
||||
"security:advisories": "php bin/moko security:advisories"
|
||||
},
|
||||
"config": {
|
||||
"sort-packages": true,
|
||||
|
||||
@@ -20,7 +20,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class BackupBeforeDeployCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class DeployDolibarrCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -30,7 +30,7 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
use phpseclib3\Net\SFTP;
|
||||
use phpseclib3\Crypt\PublicKeyLoader;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\{CliFramework, SourceResolver};
|
||||
use MokoCli\{CliFramework, SourceResolver};
|
||||
use phpseclib3\Net\SFTP;
|
||||
use phpseclib3\Crypt\PublicKeyLoader;
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class HealthCheckCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class RollbackJoomlaCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -20,7 +20,7 @@ declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoCli\CliFramework;
|
||||
|
||||
class SyncJoomlaCli extends CliFramework
|
||||
{
|
||||
|
||||
@@ -19,8 +19,8 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoEnterprise\FileFixUtility;
|
||||
use MokoCli\CliFramework;
|
||||
use MokoCli\FileFixUtility;
|
||||
|
||||
class FixLineEndings extends CliFramework
|
||||
{
|
||||
|
||||
@@ -19,8 +19,8 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoEnterprise\FileFixUtility;
|
||||
use MokoCli\CliFramework;
|
||||
use MokoCli\FileFixUtility;
|
||||
|
||||
class FixPermissions extends CliFramework
|
||||
{
|
||||
|
||||
+2
-2
@@ -19,8 +19,8 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoEnterprise\FileFixUtility;
|
||||
use MokoCli\CliFramework;
|
||||
use MokoCli\FileFixUtility;
|
||||
|
||||
class FixTabs extends CliFramework
|
||||
{
|
||||
|
||||
@@ -19,8 +19,8 @@ declare(strict_types=1);
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
|
||||
|
||||
use MokoEnterprise\CliFramework;
|
||||
use MokoEnterprise\FileFixUtility;
|
||||
use MokoCli\CliFramework;
|
||||
use MokoCli\FileFixUtility;
|
||||
|
||||
class FixTrailingSpaces extends CliFramework
|
||||
{
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ class Common
|
||||
*/
|
||||
const FALLBACK_VERSION = '04.00.00';
|
||||
|
||||
const REPO_URL = 'https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API';
|
||||
const REPO_URL = 'https://git.mokoconsulting.tech/MokoConsulting/mokocli';
|
||||
const REPO_URL_GITHUB = 'https://git.mokoconsulting.tech/MokoConsulting/mokoplatform';
|
||||
const COPYRIGHT = 'Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>';
|
||||
const LICENSE = 'GPL-3.0-or-later';
|
||||
|
||||
@@ -16,7 +16,7 @@ declare(strict_types=1);
|
||||
* BRIEF: Abstract base class for project plugins
|
||||
*/
|
||||
|
||||
namespace MokoEnterprise;
|
||||
namespace MokoCli;
|
||||
|
||||
/**
|
||||
* Abstract base class for project type plugins
|
||||
|
||||
@@ -37,7 +37,7 @@ declare(strict_types=1);
|
||||
* @license GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace MokoEnterprise;
|
||||
namespace MokoCli;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
@@ -166,7 +166,7 @@ class ApiClient
|
||||
int $circuitBreakerThreshold = 5,
|
||||
int $circuitBreakerTimeout = 60,
|
||||
bool $enableCaching = true,
|
||||
string $userAgent = 'MokoStandards-APIClient/1.0',
|
||||
string $userAgent = 'MokoCli-APIClient/1.0',
|
||||
?LoggerInterface $logger = null,
|
||||
string $authScheme = 'Bearer'
|
||||
) {
|
||||
|
||||
@@ -34,7 +34,7 @@ declare(strict_types=1);
|
||||
* @license GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace MokoEnterprise;
|
||||
namespace MokoCli;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
|
||||
@@ -22,7 +22,7 @@ declare(strict_types=1);
|
||||
* @license GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace MokoEnterprise;
|
||||
namespace MokoCli;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MokoEnterprise;
|
||||
namespace MokoCli;
|
||||
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
|
||||
@@ -38,7 +38,7 @@ declare(strict_types=1);
|
||||
* @license GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
namespace MokoEnterprise;
|
||||
namespace MokoCli;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MokoEnterprise;
|
||||
namespace MokoCli;
|
||||
|
||||
/**
|
||||
* Configuration Validator
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MokoEnterprise;
|
||||
namespace MokoCli;
|
||||
|
||||
/**
|
||||
* Enterprise Readiness Validator
|
||||
|
||||
@@ -30,7 +30,7 @@ declare(strict_types=1);
|
||||
* @deprecated Individual class files should be used instead
|
||||
*/
|
||||
|
||||
namespace MokoEnterprise;
|
||||
namespace MokoCli;
|
||||
|
||||
use Throwable;
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MokoEnterprise;
|
||||
namespace MokoCli;
|
||||
|
||||
use RecursiveDirectoryIterator;
|
||||
use RecursiveIteratorIterator;
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace MokoEnterprise;
|
||||
namespace MokoCli;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user