Compare commits

...

190 Commits

Author SHA1 Message Date
jmiller 98af4abd01 Merge pull request 'chore(rebrand): brand caps MokoGit -> MokoGIT' (#347) from fix/brand-mokogit-caps into main 2026-07-14 21:15:38 +00:00
jmiller baf5f56ca2 chore(rebrand): brand caps MokoGit -> MokoGIT
Branding: the MokoGIT wordmark is caps-GIT. Corrects the display token from
MokoGit to MokoGIT (case-sensitive; lowercase path form .mokogit untouched).

Claude-Session: https://claude.ai/code/session_01DQEMmJPe61ya7HDfA6BHP8
2026-07-14 16:15:10 -05:00
jmiller 6b0822aa0f Merge pull request 'chore(rebrand): gitea -> git (MokoGitea -> MokoGit, .mokogitea -> .mokogit)' (#346) from chore/rebrand-gitea-to-git into main 2026-07-14 20:56:58 +00:00
jmiller 50f253d444 chore(rebrand): gitea -> git (MokoGitea -> MokoGit, .mokogitea -> .mokogit)
Case-preserving gitea->git across the mokocli monorepo: brand text,
functional identifiers (MOKOGITEA_TOKEN, GITEA_URL/ORG/REPO, --gitea-url),
and all nested .mokogitea/ dirs (top-level + mcp/servers/*) -> .mokogit/.
Protected: literal `.gitea` (upstream workflow-detection paths in the
Enterprise plugins) and git.mokoconsulting.tech.

WARNING: CI-breaking until the MokoGit server side is reconfigured.

Claude-Session: https://claude.ai/code/session_01DQEMmJPe61ya7HDfA6BHP8
2026-07-14 15:56:17 -05:00
jmiller dddf89d485 Merge pull request 'fix(workflow-sync): map npm -> Template-NPM (mcp consolidated/renamed)' (#345) from chore/sync-npm-map-v2 into main
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Has been cancelled
Universal: PR Check / Branch Policy (pull_request) Has been cancelled
Universal: PR Check / Require Docs Update (pull_request) Has been cancelled
Universal: PR Check / Wiki Update Reminder (pull_request) Has been cancelled
Universal: PR Check / Secret Scan (pull_request) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Cascade Main -> Dev / Cascade main -> dev (push) Has been cancelled
Platform: mokocli CI / Gate 1: Code Quality (push) Has been cancelled
Generic: Standards Compliance / Secret Scanning (push) Has been cancelled
Generic: Standards Compliance / License Header Validation (push) Has been cancelled
Generic: Standards Compliance / Repository Structure Validation (push) Has been cancelled
Generic: Standards Compliance / Coding Standards Check (push) Has been cancelled
Generic: Standards Compliance / Version Consistency Check (push) Has been cancelled
Generic: Standards Compliance / Workflow Configuration Check (push) Has been cancelled
Generic: Standards Compliance / Documentation Quality Check (push) Has been cancelled
Generic: Standards Compliance / README Completeness Check (push) Has been cancelled
Generic: Standards Compliance / Git Repository Hygiene (push) Has been cancelled
Generic: Standards Compliance / Script Integrity Validation (push) Has been cancelled
Generic: Standards Compliance / Line Length Check (push) Has been cancelled
Generic: Standards Compliance / File Naming Standards (push) Has been cancelled
Generic: Standards Compliance / Insecure Code Pattern Detection (push) Has been cancelled
Generic: Standards Compliance / Code Complexity Analysis (push) Has been cancelled
Generic: Standards Compliance / Code Duplication Detection (push) Has been cancelled
Generic: Standards Compliance / Dead Code Detection (push) Has been cancelled
Generic: Standards Compliance / File Size Limits (push) Has been cancelled
Generic: Standards Compliance / Binary File Detection (push) Has been cancelled
Generic: Standards Compliance / TODO/FIXME Tracking (push) Has been cancelled
Generic: Project CI / Tests (pull_request) Failing after 11s
Generic: Standards Compliance / Dependency Vulnerability Scanning (push) Has been cancelled
Generic: Standards Compliance / Broken Link Detection (push) Has been cancelled
Generic: Standards Compliance / API Documentation Coverage (push) Has been cancelled
Generic: Standards Compliance / Accessibility Check (push) Has been cancelled
Generic: Standards Compliance / Performance Metrics (push) Has been cancelled
Generic: Standards Compliance / Enterprise Readiness Check (push) Has been cancelled
Generic: Standards Compliance / Repository Health Check (push) Has been cancelled
Generic: Standards Compliance / Terraform Configuration Validation (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
Generic: Standards Compliance / Compliance Summary (push) Has been cancelled
Generic: Project CI / Lint & Validate (pull_request) Successful in 37s
Generic: Standards Compliance / Unused Dependencies Check (push) Successful in 50s
2026-07-14 15:28:20 +00:00
jmiller ee78f5ffc8 fix(workflow-sync): map npm -> Template-NPM (mcp consolidated/renamed)
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 11s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Failing after 7s
Generic: Project CI / Tests (pull_request) Failing after 26s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: PR Check / Require Docs Update (pull_request) Failing after 5s
Universal: PR Check / Wiki Update Reminder (pull_request) Successful in 1s
Universal: PR Check / Secret Scan (pull_request) Successful in 6s
Universal: PR Check / Validate PR (pull_request) Failing after 4s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Project CI / Lint & Validate (pull_request) Successful in 52s
Generic: Standards Compliance / Secret Scanning (pull_request) Failing after 4s
Generic: Standards Compliance / License Header Validation (pull_request) Successful in 4s
Generic: Standards Compliance / Repository Structure Validation (pull_request) Failing after 4s
Generic: Standards Compliance / Coding Standards Check (pull_request) Successful in 11s
Generic: Standards Compliance / Workflow Configuration Check (pull_request) Failing after 5s
Generic: Standards Compliance / Documentation Quality Check (pull_request) Successful in 4s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Successful in 1m7s
Generic: Standards Compliance / README Completeness Check (pull_request) Failing after 3s
Generic: Standards Compliance / Git Repository Hygiene (pull_request) Successful in 5s
Generic: Standards Compliance / Script Integrity Validation (pull_request) Successful in 6s
Generic: Standards Compliance / Line Length Check (pull_request) Successful in 4s
Generic: Standards Compliance / File Naming Standards (pull_request) Successful in 3s
Generic: Standards Compliance / Insecure Code Pattern Detection (pull_request) Successful in 3s
Generic: Standards Compliance / Version Consistency Check (pull_request) Successful in 45s
Generic: Standards Compliance / Dead Code Detection (pull_request) Successful in 6s
Generic: Standards Compliance / File Size Limits (pull_request) Successful in 4s
Generic: Standards Compliance / Binary File Detection (pull_request) Successful in 7s
Generic: Standards Compliance / TODO/FIXME Tracking (pull_request) Successful in 3s
Generic: Standards Compliance / Code Duplication Detection (pull_request) Successful in 39s
Generic: Standards Compliance / Code Complexity Analysis (pull_request) Successful in 41s
Generic: Standards Compliance / Broken Link Detection (pull_request) Successful in 5s
Generic: Standards Compliance / API Documentation Coverage (pull_request) Successful in 3s
Generic: Standards Compliance / Accessibility Check (pull_request) Successful in 3s
Generic: Standards Compliance / Performance Metrics (pull_request) Successful in 3s
Generic: Standards Compliance / Dependency Vulnerability Scanning (pull_request) Successful in 43s
Generic: Standards Compliance / Unused Dependencies Check (pull_request) Successful in 43s
Generic: Standards Compliance / Terraform Configuration Validation (pull_request) Successful in 7s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Generic: Standards Compliance / Enterprise Readiness Check (pull_request) Failing after 44s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
Generic: Standards Compliance / Compliance Summary (pull_request) Has been cancelled
Generic: Standards Compliance / Repository Health Check (pull_request) Failing after 46s
PLATFORM_TEMPLATES had no `npm` key and `mcp` pointed at the renamed-away
Template-MCP, so platform=npm children fell back to Template-Generic.
Add `npm => Template-NPM` and keep `mcp => Template-NPM` as a legacy alias.
Bump header VERSION 09.43.00 -> 09.44.00.

Claude-Session: https://claude.ai/code/session_01DQEMmJPe61ya7HDfA6BHP8
2026-07-14 10:27:53 -05:00
jmiller aff3fef279 chore: sync auto-release.yml from Template-Generic [skip ci] 2026-07-14 14:16:32 +00:00
jmiller 7f0e5e6226 Merge pull request 'fix(workflow-sync): platform detection via /metadata API' (#343) from fix/sync-platform-metadata into main
Generic: Project CI / Tests (pull_request) Failing after 12s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: PR Check / Require Docs Update (pull_request) Has been skipped
Universal: PR Check / Wiki Update Reminder (pull_request) Has been skipped
Universal: PR Check / Secret Scan (pull_request) Successful in 8s
Generic: Project CI / Lint & Validate (pull_request) Successful in 39s
Universal: PR Check / Validate PR (pull_request) Failing after 4s
Cascade Main -> Dev / Cascade main -> dev (push) Successful in 9s
Generic: Standards Compliance / Secret Scanning (push) Failing after 5s
Generic: Standards Compliance / License Header Validation (push) Successful in 4s
Generic: Standards Compliance / Repository Structure Validation (push) Failing after 5s
Generic: Standards Compliance / Coding Standards Check (push) Successful in 9s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Successful in 1m17s
Generic: Standards Compliance / Workflow Configuration Check (push) Failing after 4s
Generic: Standards Compliance / Documentation Quality Check (push) Successful in 5s
Generic: Standards Compliance / README Completeness Check (push) Failing after 4s
Generic: Standards Compliance / Git Repository Hygiene (push) Successful in 6s
Platform: mokocli CI / Gate 1: Code Quality (push) Successful in 1m17s
Generic: Standards Compliance / Version Consistency Check (push) Successful in 48s
Generic: Standards Compliance / File Naming Standards (push) Successful in 4s
Generic: Standards Compliance / Script Integrity Validation (push) Successful in 5s
Generic: Standards Compliance / Line Length Check (push) Successful in 6s
Generic: Standards Compliance / Insecure Code Pattern Detection (push) Successful in 5s
Generic: Standards Compliance / Dead Code Detection (push) Successful in 8s
Generic: Standards Compliance / File Size Limits (push) Successful in 5s
Generic: Standards Compliance / Binary File Detection (push) Successful in 8s
Generic: Standards Compliance / TODO/FIXME Tracking (push) Successful in 4s
Generic: Standards Compliance / Code Complexity Analysis (push) Successful in 52s
Generic: Standards Compliance / Code Duplication Detection (push) Successful in 51s
Generic: Standards Compliance / Broken Link Detection (push) Successful in 7s
Generic: Standards Compliance / API Documentation Coverage (push) Successful in 4s
Generic: Standards Compliance / Accessibility Check (push) Successful in 4s
Generic: Standards Compliance / Performance Metrics (push) Successful in 4s
Generic: Standards Compliance / Dependency Vulnerability Scanning (push) Successful in 49s
Generic: Standards Compliance / Unused Dependencies Check (push) Successful in 53s
Generic: Standards Compliance / Terraform Configuration Validation (push) Successful in 8s
Generic: Standards Compliance / Enterprise Readiness Check (push) Failing after 48s
Generic: Standards Compliance / Repository Health Check (push) Failing after 48s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
Generic: Standards Compliance / Compliance Summary (push) Has been cancelled
2026-07-14 04:29:43 +00:00
jmiller 79277e2e20 fix(workflow-sync): detect platform via /metadata API, not retired manifest.xml
Generic: Project CI / Tests (pull_request) Failing after 15s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 8s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: PR Check / Require Docs Update (pull_request) Failing after 6s
Universal: PR Check / Wiki Update Reminder (pull_request) Successful in 1s
Universal: Build & Release / Build & Release Pipeline (pull_request) Failing after 8s
Universal: PR Check / Secret Scan (pull_request) Successful in 8s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Project CI / Lint & Validate (pull_request) Successful in 44s
Generic: Standards Compliance / Secret Scanning (pull_request) Failing after 5s
Generic: Standards Compliance / License Header Validation (pull_request) Successful in 4s
Generic: Standards Compliance / Repository Structure Validation (pull_request) Failing after 4s
Generic: Standards Compliance / Coding Standards Check (pull_request) Successful in 11s
Generic: Standards Compliance / Workflow Configuration Check (pull_request) Failing after 5s
Generic: Standards Compliance / Documentation Quality Check (pull_request) Successful in 4s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Successful in 1m13s
Generic: Standards Compliance / README Completeness Check (pull_request) Failing after 4s
Generic: Standards Compliance / Git Repository Hygiene (pull_request) Successful in 5s
Generic: Standards Compliance / Script Integrity Validation (pull_request) Successful in 5s
Generic: Standards Compliance / File Naming Standards (pull_request) Successful in 3s
Generic: Standards Compliance / Line Length Check (pull_request) Successful in 5s
Generic: Standards Compliance / Insecure Code Pattern Detection (pull_request) Successful in 4s
Generic: Standards Compliance / Version Consistency Check (pull_request) Successful in 45s
Generic: Standards Compliance / Dead Code Detection (pull_request) Successful in 6s
Generic: Standards Compliance / File Size Limits (pull_request) Successful in 3s
Generic: Standards Compliance / Binary File Detection (pull_request) Successful in 8s
Generic: Standards Compliance / TODO/FIXME Tracking (pull_request) Successful in 4s
Generic: Standards Compliance / Code Complexity Analysis (pull_request) Successful in 44s
Generic: Standards Compliance / Code Duplication Detection (pull_request) Successful in 45s
Generic: Standards Compliance / Broken Link Detection (pull_request) Successful in 6s
Generic: Standards Compliance / API Documentation Coverage (pull_request) Successful in 3s
Generic: Standards Compliance / Accessibility Check (pull_request) Successful in 4s
Generic: Standards Compliance / Performance Metrics (pull_request) Successful in 3s
Generic: Standards Compliance / Dependency Vulnerability Scanning (pull_request) Successful in 42s
Generic: Standards Compliance / Unused Dependencies Check (pull_request) Successful in 43s
Generic: Standards Compliance / Terraform Configuration Validation (pull_request) Successful in 8s
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Generic: Standards Compliance / Enterprise Readiness Check (pull_request) Failing after 42s
Generic: Standards Compliance / Repository Health Check (pull_request) Failing after 48s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
Generic: Standards Compliance / Compliance Summary (pull_request) Has been cancelled
getRepoPlatform read .mokogitea/manifest.xml (retired org-wide) -> returned
'generic' for every repo, so --delete-orphans would compare all repos against
Template-Generic and delete platform-specific workflows. Now reads the public
GET /api/v1/repos/{owner}/{repo}/metadata .platform field.
2026-07-14 04:29:40 +00:00
jmiller 3e3e21d779 Merge pull request 'chore(ci): move repo-specific workflows to workflows/custom/' (#342) from chore/move-custom-workflows-to-custom into main
Generic: Project CI / Lint & Validate (pull_request) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Has been cancelled
Universal: PR Check / Branch Policy (pull_request) Has been cancelled
Universal: PR Check / Require Docs Update (pull_request) Has been cancelled
Universal: PR Check / Wiki Update Reminder (pull_request) Has been cancelled
Universal: PR Check / Secret Scan (pull_request) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Cascade Main -> Dev / Cascade main -> dev (push) Has been cancelled
Platform: mokocli CI / Gate 1: Code Quality (push) Has been cancelled
Generic: Standards Compliance / Secret Scanning (push) Has been cancelled
Generic: Standards Compliance / License Header Validation (push) Has been cancelled
Generic: Standards Compliance / Repository Structure Validation (push) Has been cancelled
Generic: Standards Compliance / Coding Standards Check (push) Has been cancelled
Generic: Standards Compliance / Version Consistency Check (push) Has been cancelled
Generic: Standards Compliance / Workflow Configuration Check (push) Has been cancelled
Generic: Standards Compliance / Documentation Quality Check (push) Has been cancelled
Generic: Standards Compliance / README Completeness Check (push) Has been cancelled
Generic: Standards Compliance / Git Repository Hygiene (push) Has been cancelled
Generic: Standards Compliance / Script Integrity Validation (push) Has been cancelled
Generic: Standards Compliance / Line Length Check (push) Has been cancelled
Generic: Standards Compliance / File Naming Standards (push) Has been cancelled
Generic: Standards Compliance / Insecure Code Pattern Detection (push) Has been cancelled
Generic: Standards Compliance / Code Complexity Analysis (push) Has been cancelled
Generic: Standards Compliance / Code Duplication Detection (push) Has been cancelled
Generic: Standards Compliance / Dead Code Detection (push) Has been cancelled
Generic: Standards Compliance / File Size Limits (push) Has been cancelled
Generic: Standards Compliance / Binary File Detection (push) Has been cancelled
Generic: Standards Compliance / TODO/FIXME Tracking (push) Has been cancelled
Generic: Standards Compliance / Dependency Vulnerability Scanning (push) Has been cancelled
Generic: Standards Compliance / Unused Dependencies Check (push) Has been cancelled
Generic: Standards Compliance / Broken Link Detection (push) Has been cancelled
Generic: Standards Compliance / API Documentation Coverage (push) Has been cancelled
Generic: Standards Compliance / Accessibility Check (push) Has been cancelled
Generic: Standards Compliance / Performance Metrics (push) Has been cancelled
Generic: Standards Compliance / Enterprise Readiness Check (push) Has been cancelled
Generic: Standards Compliance / Repository Health Check (push) Has been cancelled
Generic: Standards Compliance / Terraform Configuration Validation (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
Generic: Standards Compliance / Compliance Summary (push) Has been cancelled
2026-07-13 21:20:15 +00:00
jmiller 7ab69f674f chore(ci): move repo-specific workflows to workflows/custom/
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 8s
Generic: Project CI / Lint & Validate (pull_request) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Has been cancelled
Universal: PR Check / Branch Policy (pull_request) Has been cancelled
Universal: PR Check / Require Docs Update (pull_request) Has been cancelled
Universal: PR Check / Wiki Update Reminder (pull_request) Has been cancelled
Universal: PR Check / Secret Scan (pull_request) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Generic: Repo Health / Access control (pull_request) Has been cancelled
Generic: Repo Health / Site Health (pull_request) Has been cancelled
Generic: Standards Compliance / Secret Scanning (pull_request) Has been cancelled
Generic: Standards Compliance / License Header Validation (pull_request) Has been cancelled
Generic: Standards Compliance / Repository Structure Validation (pull_request) Has been cancelled
Generic: Standards Compliance / Coding Standards Check (pull_request) Has been cancelled
Generic: Standards Compliance / Version Consistency Check (pull_request) Has been cancelled
Generic: Standards Compliance / Workflow Configuration Check (pull_request) Has been cancelled
Generic: Standards Compliance / Documentation Quality Check (pull_request) Has been cancelled
Generic: Standards Compliance / README Completeness Check (pull_request) Has been cancelled
Generic: Standards Compliance / Git Repository Hygiene (pull_request) Has been cancelled
Generic: Standards Compliance / Script Integrity Validation (pull_request) Has been cancelled
Generic: Standards Compliance / Line Length Check (pull_request) Has been cancelled
Generic: Standards Compliance / File Naming Standards (pull_request) Has been cancelled
Generic: Standards Compliance / Insecure Code Pattern Detection (pull_request) Has been cancelled
Generic: Standards Compliance / Code Complexity Analysis (pull_request) Has been cancelled
Generic: Standards Compliance / Code Duplication Detection (pull_request) Has been cancelled
Generic: Standards Compliance / Dead Code Detection (pull_request) Has been cancelled
Generic: Standards Compliance / File Size Limits (pull_request) Has been cancelled
Generic: Standards Compliance / Binary File Detection (pull_request) Has been cancelled
Generic: Standards Compliance / TODO/FIXME Tracking (pull_request) Has been cancelled
Generic: Standards Compliance / Dependency Vulnerability Scanning (pull_request) Has been cancelled
Generic: Standards Compliance / Unused Dependencies Check (pull_request) Has been cancelled
Generic: Standards Compliance / Broken Link Detection (pull_request) Has been cancelled
Generic: Standards Compliance / API Documentation Coverage (pull_request) Has been cancelled
Generic: Standards Compliance / Accessibility Check (pull_request) Has been cancelled
Generic: Standards Compliance / Performance Metrics (pull_request) Has been cancelled
Generic: Standards Compliance / Enterprise Readiness Check (pull_request) Has been cancelled
Generic: Standards Compliance / Repository Health Check (pull_request) Has been cancelled
Generic: Standards Compliance / Terraform Configuration Validation (pull_request) Has been cancelled
Branch Cleanup / Delete merged branch (pull_request) Has been cancelled
RC Revert / Rename rc/ back to dev/ (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
Generic: Standards Compliance / Compliance Summary (pull_request) Has been cancelled
These are repo-specific (not platform-standard) and belong in custom/ so
the workflow sync never distributes/clobbers them. They still run here
(Gitea Actions executes custom/). Moved: branch-protection-enforce.yml, ci-platform.yml, composer-publish.yml, sync-feature-versions.yml
2026-07-13 21:20:10 +00:00
jmiller f1379c0245 Merge pull request 'chore(ci): remove template-only sync workflows from root' (#341) from chore/remove-template-sync-leaks into main
Generic: Project CI / Lint & Validate (pull_request) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Has been cancelled
Universal: PR Check / Branch Policy (pull_request) Has been cancelled
Universal: PR Check / Require Docs Update (pull_request) Has been cancelled
Universal: PR Check / Wiki Update Reminder (pull_request) Has been cancelled
Universal: PR Check / Secret Scan (pull_request) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Cascade Main -> Dev / Cascade main -> dev (push) Has been cancelled
Platform: mokocli CI / Gate 1: Code Quality (push) Has been cancelled
Generic: Standards Compliance / Secret Scanning (push) Has been cancelled
Generic: Standards Compliance / License Header Validation (push) Has been cancelled
Generic: Standards Compliance / Repository Structure Validation (push) Has been cancelled
Generic: Standards Compliance / Coding Standards Check (push) Has been cancelled
Generic: Standards Compliance / Version Consistency Check (push) Has been cancelled
Generic: Standards Compliance / Workflow Configuration Check (push) Has been cancelled
Generic: Standards Compliance / Documentation Quality Check (push) Has been cancelled
Generic: Standards Compliance / README Completeness Check (push) Has been cancelled
Generic: Standards Compliance / Git Repository Hygiene (push) Has been cancelled
Generic: Standards Compliance / Script Integrity Validation (push) Has been cancelled
Generic: Standards Compliance / Line Length Check (push) Has been cancelled
Generic: Standards Compliance / File Naming Standards (push) Has been cancelled
Generic: Standards Compliance / Insecure Code Pattern Detection (push) Has been cancelled
Generic: Standards Compliance / Code Complexity Analysis (push) Has been cancelled
Generic: Standards Compliance / Code Duplication Detection (push) Has been cancelled
Generic: Standards Compliance / Dead Code Detection (push) Has been cancelled
Generic: Standards Compliance / File Size Limits (push) Has been cancelled
Generic: Standards Compliance / Binary File Detection (push) Has been cancelled
Generic: Standards Compliance / TODO/FIXME Tracking (push) Has been cancelled
Generic: Standards Compliance / Dependency Vulnerability Scanning (push) Has been cancelled
Generic: Standards Compliance / Unused Dependencies Check (push) Has been cancelled
Generic: Standards Compliance / Broken Link Detection (push) Has been cancelled
Generic: Standards Compliance / API Documentation Coverage (push) Has been cancelled
Generic: Standards Compliance / Accessibility Check (push) Has been cancelled
Generic: Standards Compliance / Performance Metrics (push) Has been cancelled
Generic: Standards Compliance / Enterprise Readiness Check (push) Has been cancelled
Generic: Standards Compliance / Repository Health Check (push) Has been cancelled
Generic: Standards Compliance / Terraform Configuration Validation (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
Generic: Standards Compliance / Compliance Summary (push) Has been cancelled
2026-07-13 20:44:14 +00:00
jmiller 5fc9040493 chore(ci): remove template-only sync workflows from root
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 6s
Generic: Project CI / Lint & Validate (pull_request) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Has been cancelled
Universal: PR Check / Branch Policy (pull_request) Has been cancelled
Universal: PR Check / Require Docs Update (pull_request) Has been cancelled
Universal: PR Check / Wiki Update Reminder (pull_request) Has been cancelled
Universal: PR Check / Secret Scan (pull_request) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Generic: Repo Health / Access control (pull_request) Has been cancelled
Generic: Repo Health / Site Health (pull_request) Has been cancelled
Generic: Standards Compliance / Secret Scanning (pull_request) Has been cancelled
Generic: Standards Compliance / License Header Validation (pull_request) Has been cancelled
Generic: Standards Compliance / Repository Structure Validation (pull_request) Has been cancelled
Generic: Standards Compliance / Coding Standards Check (pull_request) Has been cancelled
Generic: Standards Compliance / Version Consistency Check (pull_request) Has been cancelled
Generic: Standards Compliance / Workflow Configuration Check (pull_request) Has been cancelled
Generic: Standards Compliance / Documentation Quality Check (pull_request) Has been cancelled
Generic: Standards Compliance / README Completeness Check (pull_request) Has been cancelled
Generic: Standards Compliance / Git Repository Hygiene (pull_request) Has been cancelled
Generic: Standards Compliance / Script Integrity Validation (pull_request) Has been cancelled
Generic: Standards Compliance / Line Length Check (pull_request) Has been cancelled
Generic: Standards Compliance / File Naming Standards (pull_request) Has been cancelled
Generic: Standards Compliance / Insecure Code Pattern Detection (pull_request) Has been cancelled
Generic: Standards Compliance / Code Complexity Analysis (pull_request) Has been cancelled
Generic: Standards Compliance / Code Duplication Detection (pull_request) Has been cancelled
Generic: Standards Compliance / Dead Code Detection (pull_request) Has been cancelled
Generic: Standards Compliance / File Size Limits (pull_request) Has been cancelled
Generic: Standards Compliance / Binary File Detection (pull_request) Has been cancelled
Generic: Standards Compliance / TODO/FIXME Tracking (pull_request) Has been cancelled
Generic: Standards Compliance / Dependency Vulnerability Scanning (pull_request) Has been cancelled
Generic: Standards Compliance / Unused Dependencies Check (pull_request) Has been cancelled
Generic: Standards Compliance / Broken Link Detection (pull_request) Has been cancelled
Generic: Standards Compliance / API Documentation Coverage (pull_request) Has been cancelled
Generic: Standards Compliance / Accessibility Check (pull_request) Has been cancelled
Generic: Standards Compliance / Performance Metrics (pull_request) Has been cancelled
Generic: Standards Compliance / Enterprise Readiness Check (pull_request) Has been cancelled
Generic: Standards Compliance / Repository Health Check (pull_request) Has been cancelled
Generic: Standards Compliance / Terraform Configuration Validation (pull_request) Has been cancelled
Branch Cleanup / Delete merged branch (pull_request) Has been cancelled
RC Revert / Rename rc/ back to dev/ (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
Generic: Standards Compliance / Compliance Summary (pull_request) Has been cancelled
sync-on-merge.yml and workflow-sync-trigger.yml are template-only (gated
if: Template-*), dead no-ops in this child repo, inherited at scaffold time.
sync-on-merge.yml is also broken. The real sync lives in the templates'
workflows/custom/.
2026-07-13 20:44:09 +00:00
jmiller 1bda63c38a chore: sync standards-compliance.yml from Template-Generic [skip ci] 2026-07-13 19:51:07 +00:00
jmiller 1e9536dd58 chore: sync pr-check.yml from Template-Generic [skip ci] 2026-07-13 16:21:16 +00:00
jmiller 52e960de33 chore: sync cascade-dev.yml from Template-Generic [skip ci] 2026-07-13 16:21:14 +00:00
jmiller 46fc2e7235 chore: sync auto-release.yml from Template-Generic [skip ci] 2026-07-13 16:21:13 +00:00
jmiller 6c402fcc33 chore: sync standards-compliance.yml from Template-Generic [skip ci] 2026-07-13 15:57:53 +00:00
jmiller 4a94f0d24a chore: sync workflow-sync-trigger.yml from Template-Generic [skip ci] 2026-07-13 15:45:00 +00:00
jmiller c5601632fc chore: sync version-set.yml from Template-Generic [skip ci] 2026-07-13 15:44:59 +00:00
jmiller 3330646e33 chore: sync sync-on-merge.yml from Template-Generic [skip ci] 2026-07-13 15:44:58 +00:00
jmiller 33e0dbb598 chore: sync repo-health.yml from Template-Generic [skip ci] 2026-07-13 15:44:57 +00:00
jmiller cf52b79244 chore: sync rc-revert.yml from Template-Generic [skip ci] 2026-07-13 15:44:56 +00:00
jmiller c38622d4c3 chore: sync pre-release.yml from Template-Generic [skip ci] 2026-07-13 15:44:54 +00:00
jmiller b0c126e990 chore: sync pr-check.yml from Template-Generic [skip ci] 2026-07-13 15:44:53 +00:00
jmiller 44457302df chore: sync notify.yml from Template-Generic [skip ci] 2026-07-13 15:44:52 +00:00
jmiller ccb30937b7 chore: sync issue-branch.yml from Template-Generic [skip ci] 2026-07-13 15:44:50 +00:00
jmiller 36b88872b1 chore: sync gitleaks.yml from Template-Generic [skip ci] 2026-07-13 15:44:49 +00:00
jmiller d19078c603 chore: sync cleanup.yml from Template-Generic [skip ci] 2026-07-13 15:44:48 +00:00
jmiller b2349f9977 chore: sync ci-issue-reporter.yml from Template-Generic [skip ci] 2026-07-13 15:44:47 +00:00
jmiller 688b9daacd chore: sync ci-generic.yml from Template-Generic [skip ci] 2026-07-13 15:44:46 +00:00
jmiller d99a8489ca chore: sync cascade-dev.yml from Template-Generic [skip ci] 2026-07-13 15:44:44 +00:00
jmiller 641505caeb chore: sync branch-cleanup.yml from Template-Generic [skip ci] 2026-07-13 15:44:43 +00:00
jmiller 91a662ed6d chore: sync auto-release.yml from Template-Generic [skip ci] 2026-07-13 15:44:42 +00:00
jmiller 2222877895 chore: sync auto-bump.yml from Template-Generic [skip ci] 2026-07-13 15:44:41 +00:00
jmiller 55f3cdcbdd chore: sync standards-compliance.yml from Template-Generic [skip ci] 2026-07-13 15:28:28 +00:00
jmiller eaca46e7cf chore: sync pr-check.yml from Template-Generic [skip ci] 2026-07-13 09:27:58 +00:00
jmiller ee49474f35 chore: sync notify.yml from Template-Generic [skip ci] 2026-07-13 09:27:57 +00:00
jmiller a48be3ff87 chore: sync pre-release.yml from Template-Generic [skip ci] 2026-07-06 17:03:26 +00:00
jmiller 51f8fcb480 chore: sync pr-check.yml from Template-Generic [skip ci] 2026-07-06 03:49:00 +00:00
jmiller dfb3ca876b chore: sync issue-branch.yml from Template-Generic [skip ci] 2026-07-06 03:48:59 +00:00
jmiller e2e9f6d33b chore: sync ci-generic.yml from Template-Generic [skip ci] 2026-07-06 03:48:58 +00:00
jmiller 9f0546c1a8 chore: sync auto-release.yml from Template-Generic [skip ci] 2026-07-06 03:48:57 +00:00
jmiller 3167233b7c Merge chore/template-client-name
Universal: PR Check / Require Docs Update (pull_request) Has been skipped
Universal: PR Check / Wiki Update Reminder (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 5s
Cascade Main -> Dev / Cascade main -> dev (push) Successful in 11s
Universal: PR Check / Validate PR (pull_request) Failing after 15s
Universal: PR Check / Secret Scan (pull_request) Successful in 21s
Generic: Project CI / Lint & Validate (pull_request) Successful in 1m8s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Successful in 1m51s
Platform: mokocli CI / Gate 1: Code Quality (push) Successful in 1m47s
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
2026-07-06 00:13:04 +00:00
jmiller 94e6084e0f fix(client): Template-Client-WaaS -> Template-Client (repo renamed; drop stale redirect dependency)
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 26s
Authored-by: Moko Consulting
2026-07-06 00:13:03 +00:00
jmiller 55a82d7d21 Merge chore/client-mokosuite-prefix
Universal: PR Check / Require Docs Update (pull_request) Has been skipped
Universal: PR Check / Wiki Update Reminder (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 3s
Universal: PR Check / Validate PR (pull_request) Failing after 12s
Cascade Main -> Dev / Cascade main -> dev (push) Successful in 16s
Universal: PR Check / Secret Scan (pull_request) Successful in 20s
Generic: Project CI / Lint & Validate (pull_request) Successful in 1m13s
Platform: mokocli CI / Gate 1: Code Quality (push) Successful in 1m59s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Successful in 2m17s
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
2026-07-05 23:56:37 +00:00
jmiller 88acf57278 feat(client): accept client-mokosuite-* prefix (backward-compat with client-waas-)
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 21s
Discovery (client_dashboard, client_inventory) matches BOTH client-waas- and
client-mokosuite-; provisioning emits client-mokosuite-. Enables the live client
repo renames without breaking discovery. Refs Template-Client#59.

Authored-by: Moko Consulting
2026-07-05 23:56:36 +00:00
jmiller ac293fd038 Merge chore/normalize-docs: normalize doc/config refs
Cascade Main -> Dev / Cascade main -> dev (push) Successful in 15s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Require Docs Update (pull_request) Has been skipped
Universal: PR Check / Wiki Update Reminder (pull_request) Has been skipped
Generic: Project CI / Lint & Validate (pull_request) Successful in 1m11s
Universal: PR Check / Secret Scan (pull_request) Successful in 23s
Universal: PR Check / Validate PR (pull_request) Failing after 11s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Successful in 2m31s
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
2026-07-05 22:50:14 +00:00
jmiller 20c036f913 chore(docs): normalize refs — MokoStandards->org wiki, moko-platform->mokocli, residual MokoGitea/MokoSuite
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 19s
Non-workflow docs/config only (CLAUDE.md, README, branch-protection, ISSUE_TEMPLATE/config.yml,
ci-issue-reporter). Workflow files, manifest schema, and Makefile install paths left for the
forge-rebrand gate. Authored-by: Moko Consulting
2026-07-05 22:50:13 +00:00
jmiller 062b33b395 chore: sync workflow-sync-trigger.yml from Template-Generic [skip ci] 2026-07-05 21:31:17 +00:00
jmiller 2d8bb5e967 chore: sync version-set.yml from Template-Generic [skip ci] 2026-07-05 21:31:16 +00:00
jmiller a172b21b9b chore: sync repo-health.yml from Template-Generic [skip ci] 2026-07-05 21:31:14 +00:00
jmiller 2ce8caad88 chore: sync rc-revert.yml from Template-Generic [skip ci] 2026-07-05 21:31:12 +00:00
jmiller 0b97aa06c9 chore: sync pre-release.yml from Template-Generic [skip ci] 2026-07-05 21:31:10 +00:00
jmiller f2fb64ec5a chore: sync pr-check.yml from Template-Generic [skip ci] 2026-07-05 21:31:08 +00:00
jmiller eabc96b506 chore: sync notify.yml from Template-Generic [skip ci] 2026-07-05 21:31:07 +00:00
jmiller ed54dd5153 chore: sync gitleaks.yml from Template-Generic [skip ci] 2026-07-05 21:28:53 +00:00
jmiller 40ebb4774c chore: sync cleanup.yml from Template-Generic [skip ci] 2026-07-05 21:28:52 +00:00
jmiller bb2c34abbb chore: sync ci-issue-reporter.yml from Template-Generic [skip ci] 2026-07-05 21:28:51 +00:00
jmiller 5680b32e31 chore: sync ci-generic.yml from Template-Generic [skip ci] 2026-07-05 21:28:50 +00:00
jmiller 88fa860080 chore: sync cascade-dev.yml from Template-Generic [skip ci] 2026-07-05 21:28:49 +00:00
jmiller 0d9c64cd28 chore: sync branch-cleanup.yml from Template-Generic [skip ci] 2026-07-05 21:28:47 +00:00
jmiller 3dbd221c69 chore: sync auto-release.yml from Template-Generic [skip ci] 2026-07-05 21:28:46 +00:00
jmiller 2cedc0b94e chore: sync auto-bump.yml from Template-Generic [skip ci] 2026-07-05 21:28:45 +00:00
jmiller 94fde481d2 chore: sync workflow-sync-trigger.yml from Template-Generic [skip ci] 2026-07-05 21:08:00 +00:00
jmiller b5c38d76e8 chore: sync pr-check.yml from Template-Generic [skip ci] 2026-07-05 21:07:58 +00:00
jmiller ce0502e788 chore: sync version-set.yml from Template-Generic [skip ci] 2026-07-05 20:39:20 +00:00
jmiller cf29777daa chore: sync sync-on-merge.yml from Template-Generic [skip ci] 2026-07-05 20:39:19 +00:00
jmiller 61d52ce1cb chore: sync rc-revert.yml from Template-Generic [skip ci] 2026-07-05 20:39:17 +00:00
jmiller 16305623cc chore: sync pre-release.yml from Template-Generic [skip ci] 2026-07-05 20:39:16 +00:00
jmiller e5768d5e44 chore: sync gitleaks.yml from Template-Generic [skip ci] 2026-07-05 20:39:13 +00:00
jmiller 9342bdf373 chore: sync auto-bump.yml from Template-Generic [skip ci] 2026-07-05 20:39:09 +00:00
jmiller dad002cc08 chore: sync repo-health.yml from Template-Generic [skip ci] 2026-07-05 20:34:02 +00:00
jmiller 37fdef1323 chore: sync rc-revert.yml from Template-Generic [skip ci] 2026-07-05 20:34:00 +00:00
jmiller 845a377045 chore: sync pre-release.yml from Template-Generic [skip ci] 2026-07-05 20:33:58 +00:00
jmiller 1a83a2abd5 chore: sync pr-check.yml from Template-Generic [skip ci] 2026-07-05 20:33:55 +00:00
jmiller 9577ea6058 chore: sync issue-branch.yml from Template-Generic [skip ci] 2026-07-05 20:33:53 +00:00
jmiller c113f0437e chore: sync gitleaks.yml from Template-Generic [skip ci] 2026-07-05 20:33:51 +00:00
jmiller bfaab8a79e chore: sync ci-issue-reporter.yml from Template-Generic [skip ci] 2026-07-05 20:33:48 +00:00
jmiller 84e54c7567 chore: sync cascade-dev.yml from Template-Generic [skip ci] 2026-07-05 20:33:45 +00:00
jmiller f270ce6a78 chore: sync branch-cleanup.yml from Template-Generic [skip ci] 2026-07-05 20:33:43 +00:00
jmiller 7b45f7d92b chore: sync auto-release.yml from Template-Generic [skip ci] 2026-07-05 20:33:41 +00:00
jmiller 9859a06a09 chore: sync auto-bump.yml from Template-Generic [skip ci] 2026-07-05 20:33:39 +00:00
gitea-actions[bot] 926142afa9 chore: promote changelog [Unreleased] → [09.43.00] 2026-07-05 20:31:09 +00:00
gitea-actions[bot] 6e5a3b902c chore(release): build 09.43.00 [skip ci]
Sync Workflows to Repos / sync (push) Failing after 3s
Publish to Composer / Publish Package (release) Successful in 59s
Platform: mokocli CI / Gate 1: Code Quality (push) Successful in 2m11s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
2026-07-05 20:31:03 +00:00
jmiller 08130d1bf3 Merge pull request 'chore: rename all mokoplatform → mokocli references' (#338) from feat/rename-mokoplatform into main
Platform: mokocli CI / Gate 1: Code Quality (push) Successful in 1m48s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
2026-07-05 20:30:38 +00:00
jmiller 4009d68a7a chore: rename all mokoplatform -> mokocli references
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 3s
Universal: PR Check / Validate PR (pull_request) Failing after 14s
Universal: PR Check / Secret Scan (pull_request) Successful in 15s
Generic: Project CI / Lint & Validate (pull_request) Successful in 57s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Successful in 2m4s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 2s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 35s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 28m16s
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
The repo was renamed mokoplatform -> mokocli; this rewrites every stale
reference across the tree (case-sensitive):

  MokoPlatform  -> MokoCLI   (DEFGROUP/INGROUP doc tags)
  mokoplatform  -> mokocli   (repo URLs, /opt & /tmp paths, clone URLs,
                              EXCLUDE lists, XML xmlns + <root> namespace)
  moko-platform -> moko-cli  (marker files)

XML namespace URIs and ManifestParser::NAMESPACE_URI are changed in
lockstep so local manifest-vs-parser validation stays consistent. The
external standards.mokoconsulting.tech namespace endpoint must be updated
to match separately (tracked in #336).

Refs #336

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
2026-07-05 15:25:45 -05:00
jmiller 2312c95dba chore: sync notify.yml from Template-Generic [skip ci] 2026-07-05 00:04:08 +00:00
jmiller 7a06bcf403 chore: sync cleanup.yml from Template-Generic [skip ci] 2026-07-05 00:04:07 +00:00
gitea-actions[bot] a8baf355c4 chore: promote changelog [Unreleased] → [09.42.00] 2026-07-04 23:27:30 +00:00
gitea-actions[bot] 9520ce3bb8 chore(release): build 09.42.00 [skip ci]
Publish to Composer / Publish Package (release) Successful in 58s
2026-07-04 23:27:15 +00:00
jmiller 120dbe3be2 Merge pull request 'fix: branch_rename force-updates target ref instead of delete+recreate' (#335) from fix/promote-rc-force-update into main
Platform: mokocli CI / Gate 1: Code Quality (push) Successful in 2m0s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
2026-07-04 23:26:39 +00:00
jmiller 657e6013de chore: sync issue-branch.yml from Template-Generic [skip ci] 2026-07-04 23:25:48 +00:00
jmiller c7a26c1ce1 fix: promote-rc force-updates target ref instead of delete+recreate
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 9s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: PR Check / Secret Scan (pull_request) Successful in 13s
Universal: PR Check / Validate PR (pull_request) Successful in 9s
Generic: Repo Health / Access control (pull_request) Successful in 3s
Generic: Repo Health / Site Health (pull_request) Has been skipped
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 3s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 44s
Generic: Project CI / Lint & Validate (pull_request) Successful in 1m7s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Successful in 1m52s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 8m27s
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
branch_rename.php deleted the target branch then recreated it. When the target (rc) is a protected branch, the delete silently fails and the recreate returns HTTP 409 'branch already exists', breaking every PR-to-main promote-rc. Force-update the ref in place via PATCH git/refs/heads/{to} (sha+force) when the target exists; still create it when it doesn't. Requires the CI token to have force-push permission on the protected rc branch.
2026-07-04 18:25:43 -05:00
jmiller bdbabd9886 chore: sync auto-release.yml from Template-Generic [skip ci] 2026-07-04 23:25:41 +00:00
gitea-actions[bot] e061537404 chore: promote changelog [Unreleased] → [09.42.00] 2026-07-04 23:23:40 +00:00
gitea-actions[bot] 4637dab159 chore(release): build 09.42.00 [skip ci]
Sync Workflows to Repos / sync (push) Failing after 4s
Publish to Composer / Publish Package (release) Successful in 1m3s
Platform: mokocli CI / Gate 1: Code Quality (push) Successful in 2m3s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
2026-07-04 23:23:30 +00:00
jmiller b885f12ece Merge pull request 'chore(branch-protection): allow actions + moko-deploy bots on protected branches' (#334) from chore/branch-protection-bot-allowlist into main
Platform: mokocli CI / Gate 1: Code Quality (push) Successful in 2m19s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
2026-07-04 23:23:06 +00:00
jmiller e99881ad65 chore(branch-protection): allow actions + moko-deploy bots on protected branches
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 19s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 3s
Universal: PR Check / Validate PR (pull_request) Failing after 10s
Universal: PR Check / Secret Scan (pull_request) Successful in 14s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 3s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 41s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Successful in 2m0s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 13m47s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
Adds the automation identities to the branch-protection rule definitions so
release automation can operate on protected branches:
- all rules: push_whitelist_actions_user=true + moko-deploy in push whitelist
- dev, rc: enable force-push for the bots so dev can be reset to main via a
  single `git push --force origin main:dev` (delete+recreate cannot work on a
  protected branch)
- main keeps force-push disabled

moko-deploy needs org-team write access to take effect (mcp-mokogitea-api#30).
Unblocks the dev-sync + version-persist fixes (Template-Generic#53).

Claude-Session: https://claude.ai/code/session_01WbGBN9VyRK61zczYWcCQ2i
2026-07-04 18:21:50 -05:00
gitea-actions[bot] fd3c847fb1 chore: promote changelog [Unreleased] → [09.41.00] 2026-07-04 21:02:50 +00:00
gitea-actions[bot] 424fe658ba chore(release): build 09.41.00 [skip ci]
Publish to Composer / Publish Package (release) Successful in 9s
2026-07-04 21:02:47 +00:00
jmiller 0ff87f3631 Merge pull request 'fix: baseline pre-existing PHPStan level-6 findings so Gate 1 passes' (#333) from hotfix/phpstan-baseline into main
Platform: mokocli CI / Gate 1: Code Quality (push) Successful in 1m11s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
2026-07-04 21:02:34 +00:00
jmiller 3164f21ba9 fix: rename colliding printSummary() in client_provision to resolve PHPStan override errors
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Successful in 9s
Universal: PR Check / Secret Scan (pull_request) Successful in 14s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 16s
Generic: Project CI / Lint & Validate (pull_request) Successful in 54s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Successful in 1m23s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 19s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Successful in 2m48s
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
2026-07-04 15:55:14 -05:00
jmiller 713cb61efc fix: baseline pre-existing PHPStan level-6 findings + changelog
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 20s
Universal: Build & Release / Promote to RC (pull_request) Failing after 17s
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Generic: Project CI / Lint & Validate (pull_request) Successful in 50s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Secret Scan (pull_request) Successful in 12s
Universal: PR Check / Validate PR (pull_request) Successful in 8s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Failing after 2m13s
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
Gate 1 static analysis reported 194 pre-existing PHPStan level-6 findings across cli/, lib/, and validate/ (missing iterable value types, override signature mismatches, etc.). Add them to phpstan-baseline.neon so Gate 1 passes; the debt stays tracked in the baseline for incremental cleanup. Also adds the changelog entry required by the PR check.
2026-07-04 15:48:06 -05:00
gitea-actions[bot] fa342a278b chore: promote changelog [Unreleased] → [09.41.00] 2026-07-04 20:41:33 +00:00
gitea-actions[bot] d91baf996e chore(release): build 09.41.00 [skip ci]
Publish to Composer / Publish Package (release) Successful in 55s
2026-07-04 20:41:23 +00:00
jmiller 2100a3bec7 Merge pull request 'fix: remove shebang from RecoverySuggestion.php (PSR-12)' (#332) from hotfix/recovery-suggestion-shebang into main
Platform: mokocli CI / Gate 1: Code Quality (push) Failing after 2m4s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
2026-07-04 20:40:58 +00:00
jmiller 8d772df3e7 fix: wrap over-length line in RecoverySuggestion.php (PSR-12)
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 9s
Universal: PR Check / Secret Scan (pull_request) Successful in 12s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 19s
Generic: Project CI / Lint & Validate (pull_request) Successful in 1m18s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Failing after 2m52s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 3s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 39s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Successful in 8m21s
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
The forMissingHeader() return was 177 chars, exceeding the 150-char
limit (Generic.Files.LineLength) that Gate 1 enforces as an error.
Split it across concatenated lines; output is unchanged. (Also keeps
the earlier shebang removal.)
2026-07-04 20:35:50 +00:00
jmiller 3972e4fb84 fix: remove shebang from RecoverySuggestion.php (PSR-12)
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Universal: PR Check / Secret Scan (pull_request) Successful in 13s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 13s
Universal: PR Check / Validate PR (pull_request) Failing after 7s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Build & Release / Promote to RC (pull_request) Failing after 11s
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Generic: Project CI / Lint & Validate (pull_request) Successful in 42s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Failing after 1m0s
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
A library class file must open with <?php as the first line; the
#!/usr/bin/env php shebang tripped the PSR-12 file-header check (Gate 1
Code Quality). Remove it.
2026-07-04 20:27:49 +00:00
jmiller 86b8449eba chore: sync pr-check.yml from Template-Generic [skip ci] 2026-07-04 20:27:18 +00:00
jmiller 601451867d chore: sync pr-check.yml from Template-Generic [skip ci] 2026-07-04 20:21:53 +00:00
jmiller 09ffcdce49 chore: sync ci-generic.yml from Template-Generic [skip ci] 2026-07-04 20:21:51 +00:00
jmiller a49e0d7b2a chore: sync sync-on-merge.yml from Template-Generic [skip ci] 2026-07-04 19:37:14 +00:00
jmiller bc1a111775 chore: sync pr-check.yml from Template-Generic [skip ci] 2026-07-04 19:37:12 +00:00
jmiller 98c41c3a53 chore: sync ci-generic.yml from Template-Generic [skip ci] 2026-07-04 19:37:09 +00:00
jmiller 52ebcde1ff chore: sync branch-cleanup.yml from Template-Generic [skip ci] 2026-07-04 19:37:07 +00:00
gitea-actions[bot] ded17c34b8 chore: promote changelog [Unreleased] → [09.41.00] 2026-07-04 19:36:22 +00:00
gitea-actions[bot] 1b79de20a9 chore(release): build 09.41.00 [skip ci]
Publish to Composer / Publish Package (release) Successful in 46s
2026-07-04 19:36:19 +00:00
jmiller bba1cbc845 Merge pull request 'fix: pr-check platform detection via metadata API, allow fix/patch to main' (#331) from hotfix/pr-check-metadata-platform into main
Sync Workflows to Repos / sync (push) Failing after 3s
Platform: mokocli CI / Gate 1: Code Quality (push) Failing after 1m3s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
2026-07-04 19:36:03 +00:00
jmiller 3f956f5661 fix: drop removed automation/ dir refs and dangling mcp submodule gitlinks
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Successful in 9s
Universal: PR Check / Secret Scan (pull_request) Successful in 11s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 18s
Generic: Project CI / Lint & Validate (pull_request) Successful in 1m6s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Failing after 1m17s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Failing after 2s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 22s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Successful in 6m14s
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
The automation/ directory was removed (bulk_sync.php moved to wrappers/, others deleted) but references remained, hard-failing Gate 1 phpcs and the post-merge sync. Drop automation/ from phpcs/phpstan config and repoint sync-on-merge to wrappers/bulk_sync.php. Also remove the dangling mcp/servers/mokogitea_api and mcp/servers/windows/mcp_windows gitlinks that had no .gitmodules entry and broke submodule init at checkout.
2026-07-04 14:32:22 -05:00
jmiller 9036d20898 fix: pr-check platform detection via metadata API, allow fix/patch to main
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 6s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Successful in 8s
Universal: PR Check / Secret Scan (pull_request) Successful in 10s
Generic: Project CI / Lint & Validate (pull_request) Successful in 25s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Failing after 48s
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
Read the build platform from the MokoGitea metadata API instead of .mokogitea/manifest.xml, which is no longer used and caused the Detect platform step to abort under set -e when absent. Also allow fix/* and patch/* branches to target main per documented branch policy.
2026-07-04 14:19:06 -05:00
jmiller 7c1edcf813 chore: sync sync-on-merge.yml from Template-Generic [skip ci] 2026-07-04 19:03:10 +00:00
jmiller fec5a48b4a chore: sync pre-release.yml from Template-Generic [skip ci] 2026-07-04 19:03:08 +00:00
jmiller 468b962aaa chore: sync pr-check.yml from Template-Generic [skip ci] 2026-07-04 19:03:06 +00:00
jmiller cc66a79fea chore: sync notify.yml from Template-Generic [skip ci] 2026-07-04 19:03:05 +00:00
jmiller 660ae5bed4 chore: sync cleanup.yml from Template-Generic [skip ci] 2026-07-04 19:03:03 +00:00
jmiller 5ffaf95d20 chore: sync ci-generic.yml from Template-Generic [skip ci] 2026-07-04 19:03:02 +00:00
jmiller ddfb479bc2 chore: sync auto-release.yml from Template-Generic [skip ci] 2026-07-04 19:03:01 +00:00
jmiller 13e2729124 Merge pull request 'fix: remove orphaned deploy-manual workflow' (#330) from fix/remove-deploy-manual into main
fix: remove orphaned deploy-manual workflow [skip ci]
2026-06-30 18:33:26 +00:00
jmiller 05ee59d75e fix: remove orphaned deploy-manual workflow [skip ci] 2026-06-30 18:06:51 +00:00
jmiller c4bb08073b Merge pull request 'feat(cli): theme_vars_check governance — forbidden files, topics, protection, element naming' (#329) from feat/theme-vars-governance into main
Platform: mokocli CI / Gate 1: Code Quality (push) Failing after 1m28s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 5s
2026-06-29 17:03:47 +00:00
jmiller b32556fdef feat(cli): theme_vars_check governance — forbidden files, topics, protection, element naming
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Failing after 5s
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Universal: PR Check / Secret Scan (pull_request) Successful in 8s
Universal: PR Check / Validate PR (pull_request) Failing after 5s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Generic: Project CI / Lint & Validate (pull_request) Successful in 55s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Failing after 1m23s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Successful in 3m42s
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
- Forbidden committed files: .mcp.json/.mcp_*.json, sftp-config*.json,
  *.min.css/*.min.js, TODO.md, .claude/.
- Required repo topics (joomla, client-waas, mokoonyx) via Gitea API.
- main branch protection enforced via Gitea API.
- <element> naming convention (file_mokoonyx_*) for file packages.

Authored-by: Moko Consulting
2026-06-29 12:03:14 -05:00
jmiller c57a7e4b96 Merge pull request 'feat(cli): theme_vars_check manifest completeness + fileset integrity' (#328) from feat/theme-vars-manifest into main
Platform: mokocli CI / Gate 1: Code Quality (push) Failing after 1m26s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
2026-06-29 16:58:09 +00:00
jmiller 5846ccc3e8 feat(cli): theme_vars_check manifest completeness + fileset integrity
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Failing after 4s
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Failing after 2s
Universal: PR Check / Validate PR (pull_request) Failing after 10s
Universal: PR Check / Secret Scan (pull_request) Successful in 21s
Generic: Project CI / Lint & Validate (pull_request) Successful in 30s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Failing after 1m12s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Successful in 4m36s
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
- Verify required manifest fields: <name>, <element>, <author>, <creationDate>.
- Verify <scriptfile> target exists in src/.
- Fileset integrity: every <filename>/<folder> in <fileset> must exist in src/
  — catches the "Failed to find source folder" install failure class at CI time.

Authored-by: Moko Consulting
2026-06-29 11:57:35 -05:00
jmiller 6ba0208571 chore: sync pr-check.yml from Template-Generic [skip ci] 2026-06-29 16:50:50 +00:00
jmiller 759b04e088 chore: sync issue-branch.yml from Template-Generic [skip ci] 2026-06-29 16:50:49 +00:00
jmiller 4dfa2d9722 chore: sync ci-issue-reporter.yml from Template-Generic [skip ci] 2026-06-29 16:50:48 +00:00
jmiller 590ad7337a chore: sync branch-cleanup.yml from Template-Generic [skip ci] 2026-06-29 16:50:47 +00:00
jmiller c607966e83 chore: sync auto-release.yml from Template-Generic [skip ci] 2026-06-29 16:50:46 +00:00
jmiller 97f2226288 Merge pull request 'feat(cli): add theme_vars_check.php for client MokoOnyx themes' (#327) from feat/theme-vars-check into main
Platform: mokocli CI / Gate 1: Code Quality (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
2026-06-29 16:45:42 +00:00
jmiller b1f2c391cb feat(cli): derive required vars from MokoOnyx standard theme + repo metadata
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Failing after 6s
Generic: Project CI / Lint & Validate (pull_request) Successful in 16s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Failing after 1m3s
pr-check.yml / Branch Policy (pull_request) Has been cancelled
Universal: PR Check / Secret Scan (pull_request) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Generic: Repo Health / Access control (pull_request) Has been cancelled
Generic: Repo Health / Site Health (pull_request) Has been cancelled
Branch Cleanup / Delete merged branch (pull_request) Has been cancelled
RC Revert / Rename rc/ back to dev/ (pull_request) Has been cancelled
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
- Read the required CSS variable set dynamically from the MokoOnyx standard
  theme (--reference checkout) instead of a hardcoded list, so it auto-syncs.
  Verifies client light/dark custom CSS defines every standard variable.
- Add optional repo-metadata check via Gitea API (--api-base/--repo/--token):
  description, website, topics, default branch.
- Drop unused --strict flag.

Authored-by: Moko Consulting
2026-06-29 11:45:09 -05:00
jmiller 7e4d88eea5 feat(cli): add theme_vars_check.php for client MokoOnyx themes
Universal: Build & Release / Promote to RC (pull_request) Failing after 3s
Universal: Build & Release / Build & Release Pipeline (pull_request) Has been skipped
Generic: Project CI / Lint & Validate (pull_request) Successful in 31s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Failing after 56s
pr-check.yml / Branch Policy (pull_request) Has been cancelled
Universal: PR Check / Secret Scan (pull_request) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Generic: Repo Health / Access control (pull_request) Has been cancelled
Generic: Repo Health / Site Health (pull_request) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
Validates a client theme package: required CSS variables defined in both
light.custom.css and dark.custom.css, required files present, and
templateDetails.xml sanity (version, dynamic update server not raw/branch,
dlid). Standalone (no CliFramework dependency).

Authored-by: Moko Consulting
2026-06-29 11:36:05 -05:00
jmiller 9fdd6c5cf9 Merge pull request 'fix(security): prevent Actions script injection in workflows' (#326) from fix/workflow-actions-injection into main
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
Platform: mokocli CI / Gate 1: Code Quality (push) Failing after 1m22s
2026-06-29 16:19:27 +00:00
jmiller 113af457d9 fix(security): prevent Actions script injection in workflows
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 7s
Generic: Project CI / Lint & Validate (pull_request) Successful in 23s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Failing after 1m12s
pr-check.yml / Branch Policy (pull_request) Has been cancelled
Universal: PR Check / Secret Scan (pull_request) Has been cancelled
Universal: PR Check / Validate PR (pull_request) Has been cancelled
Generic: Repo Health / Access control (pull_request) Has been cancelled
Generic: Repo Health / Site Health (pull_request) Has been cancelled
Branch Cleanup / Delete merged branch (pull_request) Has been cancelled
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Has been cancelled
Generic: Project CI / Tests (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
RC Revert / Rename rc/ back to dev/ (pull_request) Has been cancelled
Untrusted ${{ }} expressions (issue titles, PR head refs, reusable-workflow
inputs) were interpolated directly into run: shell bodies, allowing command
injection. Each is now passed through an env: block and referenced as a shell
variable in the script (env vars are not subject to ${{ }} expansion).

Files:
- ci-issue-reporter.yml  inputs.gate/details/severity/workflow
- issue-branch.yml       github.event.issue.title
- branch-cleanup.yml     github.event.pull_request.head.ref
- pr-check.yml           github.head_ref / github.base_ref
- auto-release.yml       github.event.pull_request.head.ref (x2)

Propagates to all template consumers via the workflow sync.
Refs MokoConsulting/Template-Joomla#35.

Authored-by: Moko Consulting
2026-06-29 11:18:23 -05:00
jmiller abd67f6c43 chore: sync auto-release.yml from Template-Generic [skip ci] 2026-06-29 06:34:23 +00:00
jmiller d3dce5d24e chore: sync ci-generic.yml from Template-Generic [skip ci] 2026-06-27 20:43:28 +00:00
jmiller c9592d8e78 chore: sync rc-revert.yml from Template-Generic [skip ci] 2026-06-27 05:31:49 +00:00
gitea-actions[bot] 6bf6251705 chore: promote changelog [Unreleased] → [09.41.00] 2026-06-27 02:17:47 +00:00
gitea-actions[bot] 520acd4fee chore(release): build 09.41.00 [skip ci]
Publish to Composer / Publish Package (release) Successful in 10s
2026-06-27 02:17:44 +00:00
jmiller a6c4fe0688 Merge pull request 'feat: nested Joomla package support in release_package.php' (#323) from fix/release-dead-variable into main
Platform: mokocli CI / Gate 1: Code Quality (push) Failing after 38s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
2026-06-27 02:17:32 +00:00
jmiller 3c420284e3 fix: harden nested package build against collisions and missing assets
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 10s
Universal: PR Check / Branch Policy (pull_request) Failing after 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: PR Check / Secret Scan (pull_request) Successful in 9s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Failing after 53s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 15s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 2m32s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
- Write nested sub-extension ZIPs to subdirectory to prevent filename
  collisions with outer package ZIPs (deferred ZipArchive::addFile reads)
- Apply exclude patterns to nested top-level files (parity with outer)
- Patch nested manifest with folder="packages" so Joomla finds sub-ZIPs
- Include all top-level directories (not just language/) in nested packages

Claude-Session: https://claude.ai/code/session_01MbEjBtsSjPuTWhqqrMS2wG
2026-06-26 21:11:46 -05:00
jmiller 66543c14d3 chore: sync pre-release.yml from Template-Generic [skip ci] 2026-06-27 00:48:56 +00:00
gitea-actions[bot] d9908e264a chore: promote changelog [Unreleased] → [09.41.00] 2026-06-27 00:48:29 +00:00
gitea-actions[bot] 9206f716e1 chore(release): build 09.41.00 [skip ci]
Publish to Composer / Publish Package (release) Successful in 9s
2026-06-27 00:48:27 +00:00
jmiller 84633e22e5 Merge pull request 'fix: prevent duplicate version tags in Joomla manifests' (#322) from fix/version-bump-duplicates into main
Platform: mokocli CI / Gate 1: Code Quality (push) Failing after 39s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
2026-06-27 00:48:17 +00:00
jmiller 510d1248ab fix: prevent duplicate <version> tags in Joomla manifests
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 9s
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 4s
Universal: PR Check / Secret Scan (pull_request) Successful in 6s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Failing after 36s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 15s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 5m25s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report: Scripts Governance (pull_request) Has been cancelled
Generic: Repo Health / Report: Repository Health (pull_request) Has been cancelled
version_bump, version_set_platform, and version_check all used
unlimited preg_replace on <version> tags. If a duplicate ever got
introduced (merge conflict, manual edit), every subsequent CI bump
faithfully updated both copies — perpetuating the duplicate forever.

Fix: use limit=1 for the primary replacement, then collapse any
remaining duplicate <version> lines in a second pass.

Claude-Session: https://claude.ai/code/session_01MbEjBtsSjPuTWhqqrMS2wG
2026-06-26 19:42:55 -05:00
jmiller 8e30fa54b3 feat: support nested Joomla packages in release_package.php
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 9s
When a sub-package directory contains its own pkg_*.xml manifest and
packages/ directory (e.g. a git submodule), recursively build each
sub-extension into ZIPs before assembling the outer package ZIP.
2026-06-26 19:26:54 -05:00
jmiller 1aaec7d8ff chore: sync repo-health.yml from Template-Generic [skip ci] 2026-06-25 19:45:44 +00:00
jmiller 5efdaf6c05 chore: sync pr-check.yml from Template-Generic [skip ci] 2026-06-25 19:45:42 +00:00
jmiller 448e11568b chore: sync ci-issue-reporter.yml from Template-Generic [skip ci] 2026-06-25 19:45:39 +00:00
jmiller dce0fe0994 feat: add ci_issue_reporter.sh CLI tool (moved from per-repo automation/) [skip ci] 2026-06-25 17:18:57 +00:00
Moko Consulting [bot] 2dc115327b fix: add PHP install step to workflow-sync-trigger for non-PHP runners [skip ci] 2026-06-25 17:08:36 +00:00
Moko Consulting [bot] 4715dba7fc fix: remove duplicate condition line in workflow-sync-trigger [skip ci] 2026-06-25 17:05:51 +00:00
Moko Consulting [bot] 5ab1782715 fix: allow workflow_dispatch to trigger sync job [skip ci] 2026-06-25 17:05:08 +00:00
Moko Consulting [bot] 08db79da17 fix: correct MOKOMOKOGITEA double prefix in workflow-sync-trigger [skip ci] 2026-06-25 17:03:25 +00:00
Moko Consulting [bot] c94564cec6 ci: add workflow_dispatch trigger to workflow-sync-trigger [skip ci] 2026-06-25 17:00:42 +00:00
Moko Consulting [bot] ac52a697e5 fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:58:59 +00:00
Moko Consulting [bot] 57b458b79a fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:58:05 +00:00
Moko Consulting [bot] 2a84b38194 fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:58:01 +00:00
Moko Consulting [bot] 9d9b78b4e9 fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:57:58 +00:00
Moko Consulting [bot] 092543db1c fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:57:51 +00:00
Moko Consulting [bot] 2286514c59 fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:57:47 +00:00
Moko Consulting [bot] 6993f30513 fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:57:42 +00:00
Moko Consulting [bot] 0b30cc5a6d fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:57:38 +00:00
Moko Consulting [bot] 7b0c239dd2 fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:57:36 +00:00
Moko Consulting [bot] cf278cf914 fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:57:33 +00:00
Moko Consulting [bot] fdeacc73c5 fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:57:31 +00:00
Moko Consulting [bot] 117bba7765 fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:57:21 +00:00
Moko Consulting [bot] 65a11a19ac fix: standardize token/URL env vars to MOKOGITEA naming [skip ci] 2026-06-25 16:57:16 +00:00
Moko Consulting [bot] fae2eab41b fix: rename GITEA_URL to MOKOGITEA_URL in workflow-sync-trigger [skip ci] 2026-06-25 16:49:06 +00:00
gitea-actions[bot] f91b05287a chore: promote changelog [Unreleased] → [09.41.00] 2026-06-25 16:48:26 +00:00
gitea-actions[bot] 43fd64a15b chore(release): build 09.41.00 [skip ci]
Publish to Composer / Publish Package (release) Successful in 11s
2026-06-25 16:48:23 +00:00
jmiller 15074fb0ae Merge pull request 'fix: remove dead $cleanupFile variable in release.php' (#320) from fix/release-dead-variable into main
Platform: mokocli CI / Gate 1: Code Quality (push) Failing after 59s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
2026-06-25 16:47:27 +00:00
jmiller 415b5403be fix: remove dead $cleanupFile variable in release.php
Universal: PR Check / Branch Policy (pull_request) Failing after 2s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 2s
Branch Cleanup / Delete merged branch (pull_request) Failing after 2s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: PR Check / Secret Scan (pull_request) Successful in 8s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 3s
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Failing after 6s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Failing after 1m1s
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 24s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Left behind when the cleanup template step was removed in #318.
2026-06-25 11:47:05 -05:00
jmiller 84fdb39d20 chore: sync issue-branch.yml from Template-Generic [skip ci] 2026-06-25 16:43:31 +00:00
gitea-actions[bot] 44b9e991b5 chore: promote changelog [Unreleased] → [09.41.00] 2026-06-25 16:43:24 +00:00
gitea-actions[bot] 9b647b2540 chore(release): build 09.41.00 [skip ci]
Publish to Composer / Publish Package (release) Successful in 8s
Platform: mokocli CI / Gate 1: Code Quality (push) Failing after 53s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
2026-06-25 16:43:21 +00:00
jmiller cedf10bdff Merge pull request 'fix: remove Template-Dolibarr references from sync' (#319) from chore/remove-legacy-templates into main
Platform: mokocli CI / Gate 1: Code Quality (push) Failing after 49s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (push) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: mokocli CI / CI Summary (push) Has been cancelled
2026-06-25 16:42:46 +00:00
jmiller 8422292b46 fix: remove Template-Dolibarr references from sync
Universal: Pre-Release / Build Pre-Release (${{ inputs.stability || github.ref_name }}) (push) Successful in 8s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (pull_request) Successful in 1s
RC Revert / Rename rc/ back to dev/ (pull_request) Has been skipped
Branch Cleanup / Delete merged branch (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Failing after 6s
Universal: PR Check / Secret Scan (pull_request) Successful in 8s
Universal: Build & Release / Promote to RC (pull_request) Has been skipped
Universal: Build & Release / Build & Release Pipeline (pull_request) Successful in 21s
Platform: mokocli CI / Gate 1: Code Quality (pull_request) Failing after 52s
Universal: Workflow Sync Trigger / Sync workflows to live repos (pull_request) Failing after 5m0s
Platform: mokocli CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: mokocli CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: mokocli CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: mokocli CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: mokocli CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Universal: PR Check / Report Issues (pull_request) Has been cancelled
Generic: Repo Health / Scripts governance (pull_request) Has been cancelled
Generic: Repo Health / Repository health (pull_request) Has been cancelled
Generic: Repo Health / Report Issues (pull_request) Has been cancelled
Template-Dolibarr repo was deleted. Map dolibarr/crm-module platforms
to Template-Generic fallback. Remove from TEMPLATE_SYNC_TARGETS.
2026-06-25 11:42:27 -05:00
gitea-actions[bot] b1f3487a43 chore: promote changelog [Unreleased] → [09.40.00] 2026-06-25 16:40:19 +00:00
424 changed files with 7881 additions and 3763 deletions
+4 -4
View File
@@ -38,7 +38,7 @@ composer check # Run all checks
| `lib/Enterprise/Plugins/` | 11 platform plugins (Joomla, Dolibarr, Node.js, Python, etc.) | | `lib/Enterprise/Plugins/` | 11 platform plugins (Joomla, Dolibarr, Node.js, Python, etc.) |
| `deploy/` | SFTP deployment scripts (Joomla, Dolibarr, health checks) | | `deploy/` | SFTP deployment scripts (Joomla, Dolibarr, health checks) |
| `templates/` | Universal templates, configs, governance schema | | `templates/` | Universal templates, configs, governance schema |
| `.mokogitea/workflows/` | CI/CD workflows (Gitea Actions) | | `.mokogit/workflows/` | CI/CD workflows (MokoGIT Actions) |
| `bin/moko` | Unified CLI dispatcher — `php bin/moko <command>` | | `bin/moko` | Unified CLI dispatcher — `php bin/moko <command>` |
| `monitoring/sites.json` | Sites list for mcp_mokomonitor | | `monitoring/sites.json` | Sites list for mcp_mokomonitor |
@@ -50,7 +50,7 @@ After adding a CLI tool, register it in `bin/moko` COMMAND_MAP.
### Platform Adapters ### Platform Adapters
- `MokoGiteaAdapter` — git.mokoconsulting.tech (primary) - `MokoGITAdapter` — git.mokoconsulting.tech (primary)
- `GitHubAdapter` — github.com mirrors - `GitHubAdapter` — github.com mirrors
### Plugin System ### Plugin System
@@ -70,7 +70,7 @@ PHPStan runs with `--memory-limit=512M`. CI enforces PHPCS errors; PHPStan is `c
- **Never commit** `.claude/`, `.mcp.json`, `TODO.md`, `*.min.css`/`*.min.js` - **Never commit** `.claude/`, `.mcp.json`, `TODO.md`, `*.min.css`/`*.min.js`
- **Attribution**: `Authored-by: Moko Consulting` - **Attribution**: `Authored-by: Moko Consulting`
- **Workflow directory**: `.mokogitea/` (not `.gitea/` or `.github/`) - **Workflow directory**: `.mokogit/` (not `.gitea/` or `.github/`)
- **Wiki**: documentation lives in the Gitea wiki, not `docs/` files - **Wiki**: documentation lives in the MokoGIT wiki, not `docs/` files
- **New CLI tools**: extend `CliFramework`, not `CLIApp` (legacy) - **New CLI tools**: extend `CliFramework`, not `CLIApp` (legacy)
- **Standards**: [MokoCli](https://git.mokoconsulting.tech/MokoConsulting/mokocli/wiki/Home) - **Standards**: [MokoCli](https://git.mokoconsulting.tech/MokoConsulting/mokocli/wiki/Home)
@@ -1,7 +1,7 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech> # Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Git.Workflow
# INGROUP: mokocli.Automation # INGROUP: mokocli.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.gitea/workflows/branch-protection.yml # PATH: /.gitea/workflows/branch-protection.yml
@@ -42,8 +42,8 @@ on:
default: '' default: ''
env: env:
GITEA_URL: https://git.mokoconsulting.tech GIT_URL: https://git.mokoconsulting.tech
GITEA_ORG: MokoConsulting GIT_ORG: MokoConsulting
permissions: permissions:
contents: read contents: read
@@ -57,12 +57,12 @@ jobs:
- name: Determine target repos - name: Determine target repos
id: repos id: repos
env: env:
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} GA_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
run: | run: |
API="${GITEA_URL}/api/v1" API="${GIT_URL}/api/v1"
# Platform/standards/infra repos to exclude # Platform/standards/infra repos to exclude
EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private mokocli MokoTesting" EXCLUDE="git-org-config org-profile git-private .mokogit-private mokocli MokoTesting"
EXCLUDE="$EXCLUDE MokoCli-Template-Client MokoCli-Template-Dolibarr MokoCli-Template-Generic MokoCli-Template-Joomla MokoDoliProjTemplate" EXCLUDE="$EXCLUDE MokoCli-Template-Client MokoCli-Template-Dolibarr MokoCli-Template-Generic MokoCli-Template-Joomla MokoDoliProjTemplate"
if [ -n "${{ inputs.repos }}" ]; then if [ -n "${{ inputs.repos }}" ]; then
@@ -75,7 +75,7 @@ jobs:
while true; do while true; do
BATCH=$(curl -sS \ BATCH=$(curl -sS \
-H "Authorization: token ${GA_TOKEN}" \ -H "Authorization: token ${GA_TOKEN}" \
"${API}/orgs/${GITEA_ORG}/repos?page=${PAGE}&limit=50" \ "${API}/orgs/${GIT_ORG}/repos?page=${PAGE}&limit=50" \
| jq -r '.[].name // empty') | jq -r '.[].name // empty')
[ -z "$BATCH" ] && break [ -z "$BATCH" ] && break
REPOS="$REPOS $BATCH" REPOS="$REPOS $BATCH"
@@ -105,10 +105,10 @@ jobs:
- name: Apply protection rules - name: Apply protection rules
env: env:
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} GA_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
DRY_RUN: ${{ inputs.dry_run || 'false' }} DRY_RUN: ${{ inputs.dry_run || 'false' }}
run: | run: |
API="${GITEA_URL}/api/v1" API="${GIT_URL}/api/v1"
REPOS="${{ steps.repos.outputs.repos }}" REPOS="${{ steps.repos.outputs.repos }}"
SUCCESS=0 SUCCESS=0
@@ -124,7 +124,8 @@ jobs:
"rule_name": "main", "rule_name": "main",
"enable_push": true, "enable_push": true,
"enable_push_whitelist": true, "enable_push_whitelist": true,
"push_whitelist_usernames": ["jmiller"], "push_whitelist_usernames": ["jmiller", "moko-deploy"],
"push_whitelist_actions_user": true,
"enable_force_push": false, "enable_force_push": false,
"enable_force_push_allowlist": false, "enable_force_push_allowlist": false,
"force_push_allowlist_usernames": [], "force_push_allowlist_usernames": [],
@@ -140,10 +141,12 @@ jobs:
"rule_name": "dev", "rule_name": "dev",
"enable_push": true, "enable_push": true,
"enable_push_whitelist": true, "enable_push_whitelist": true,
"push_whitelist_usernames": ["jmiller"], "push_whitelist_usernames": ["jmiller", "moko-deploy"],
"enable_force_push": false, "push_whitelist_actions_user": true,
"enable_force_push_allowlist": false, "enable_force_push": true,
"force_push_allowlist_usernames": [], "enable_force_push_allowlist": true,
"force_push_allowlist_usernames": ["jmiller", "moko-deploy"],
"force_push_allowlist_actions_user": true,
"enable_merge_whitelist": false, "enable_merge_whitelist": false,
"required_approvals": 0, "required_approvals": 0,
"block_on_rejected_reviews": false, "block_on_rejected_reviews": false,
@@ -154,10 +157,12 @@ jobs:
"rule_name": "rc", "rule_name": "rc",
"enable_push": true, "enable_push": true,
"enable_push_whitelist": true, "enable_push_whitelist": true,
"push_whitelist_usernames": ["jmiller"], "push_whitelist_usernames": ["jmiller", "moko-deploy"],
"enable_force_push": false, "push_whitelist_actions_user": true,
"enable_force_push_allowlist": false, "enable_force_push": true,
"force_push_allowlist_usernames": [], "enable_force_push_allowlist": true,
"force_push_allowlist_usernames": ["jmiller", "moko-deploy"],
"force_push_allowlist_actions_user": true,
"enable_merge_whitelist": false, "enable_merge_whitelist": false,
"required_approvals": 0, "required_approvals": 0,
"block_on_rejected_reviews": false, "block_on_rejected_reviews": false,
@@ -168,7 +173,8 @@ jobs:
"rule_name": "beta", "rule_name": "beta",
"enable_push": true, "enable_push": true,
"enable_push_whitelist": true, "enable_push_whitelist": true,
"push_whitelist_usernames": ["jmiller"], "push_whitelist_usernames": ["jmiller", "moko-deploy"],
"push_whitelist_actions_user": true,
"enable_force_push": false, "enable_force_push": false,
"enable_force_push_allowlist": false, "enable_force_push_allowlist": false,
"force_push_allowlist_usernames": [], "force_push_allowlist_usernames": [],
@@ -182,7 +188,8 @@ jobs:
"rule_name": "alpha", "rule_name": "alpha",
"enable_push": true, "enable_push": true,
"enable_push_whitelist": true, "enable_push_whitelist": true,
"push_whitelist_usernames": ["jmiller"], "push_whitelist_usernames": ["jmiller", "moko-deploy"],
"push_whitelist_actions_user": true,
"enable_force_push": false, "enable_force_push": false,
"enable_force_push_allowlist": false, "enable_force_push_allowlist": false,
"force_push_allowlist_usernames": [], "force_push_allowlist_usernames": [],
@@ -215,7 +222,7 @@ jobs:
curl -sS -o /dev/null -w "" \ curl -sS -o /dev/null -w "" \
-X DELETE \ -X DELETE \
-H "Authorization: token ${GA_TOKEN}" \ -H "Authorization: token ${GA_TOKEN}" \
"${API}/repos/${GITEA_ORG}/${REPO}/branch_protections/${ENCODED_NAME}" 2>/dev/null || true "${API}/repos/${GIT_ORG}/${REPO}/branch_protections/${ENCODED_NAME}" 2>/dev/null || true
# Create rule # Create rule
RESPONSE=$(curl -sS -w "\n%{http_code}" \ RESPONSE=$(curl -sS -w "\n%{http_code}" \
@@ -223,7 +230,7 @@ jobs:
-H "Authorization: token ${GA_TOKEN}" \ -H "Authorization: token ${GA_TOKEN}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d "$RULE" \ -d "$RULE" \
"${API}/repos/${GITEA_ORG}/${REPO}/branch_protections") "${API}/repos/${GIT_ORG}/${REPO}/branch_protections")
HTTP=$(echo "$RESPONSE" | tail -1) HTTP=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | sed '$d') BODY=$(echo "$RESPONSE" | sed '$d')
@@ -1,7 +1,7 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech> # Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Git.Workflow
# INGROUP: mokocli.Automation # INGROUP: mokocli.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.gitea/workflows/bulk-repo-sync.yml # PATH: /.gitea/workflows/bulk-repo-sync.yml
@@ -64,7 +64,7 @@ jobs:
id: args id: args
run: | run: |
ARGS="--org MokoConsulting" ARGS="--org MokoConsulting"
if [ "${{ inputs.dry_run }}" = "true" ] || [ "${{ gitea.event_name }}" = "schedule" ]; then if [ "${{ inputs.dry_run }}" = "true" ] || [ "${{ git.event_name }}" = "schedule" ]; then
ARGS="$ARGS --dry-run" ARGS="$ARGS --dry-run"
fi fi
if [ -n "${{ inputs.repos }}" ]; then if [ -n "${{ inputs.repos }}" ]; then
@@ -84,18 +84,18 @@ jobs:
echo "Running: php automation/bulk_sync.php ${{ steps.args.outputs.args }}" echo "Running: php automation/bulk_sync.php ${{ steps.args.outputs.args }}"
php automation/bulk_sync.php ${{ steps.args.outputs.args }} 2>&1 | tee /tmp/bulk_sync.log php automation/bulk_sync.php ${{ steps.args.outputs.args }} 2>&1 | tee /tmp/bulk_sync.log
env: env:
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} GA_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
GH_TOKEN: ${{ secrets.GH_PAT }} GH_TOKEN: ${{ secrets.GH_PAT }}
GIT_PLATFORM: gitea GIT_PLATFORM: git
GITEA_URL: https://git.mokoconsulting.tech GIT_URL: https://git.mokoconsulting.tech
GITEA_ORG: MokoConsulting GIT_ORG: MokoConsulting
- name: Commit Updated Definitions - name: Commit Updated Definitions
if: success() && inputs.dry_run != 'true' if: success() && inputs.dry_run != 'true'
run: | run: |
if [ -n "$(git status --porcelain definitions/sync/)" ]; then if [ -n "$(git status --porcelain definitions/sync/)" ]; then
git config user.name "gitea-actions[bot]" git config user.name "git-actions[bot]"
git config user.email "gitea-actions[bot]@git.mokoconsulting.tech" git config user.email "git-actions[bot]@git.mokoconsulting.tech"
git add definitions/sync/*.def.tf git add definitions/sync/*.def.tf
git commit -m "chore: update synced repository definitions" || true git commit -m "chore: update synced repository definitions" || true
git push || true git push || true
@@ -112,9 +112,9 @@ jobs:
bash automation/enforce_tags.sh || echo "Tag enforcement had errors (non-fatal)" bash automation/enforce_tags.sh || echo "Tag enforcement had errors (non-fatal)"
fi fi
env: env:
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} GA_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
GITEA_URL: https://git.mokoconsulting.tech GIT_URL: https://git.mokoconsulting.tech
GITEA_ORG: MokoConsulting GIT_ORG: MokoConsulting
- name: Upload Sync Log - name: Upload Sync Log
if: always() && github.server_url == 'https://github.com' if: always() && github.server_url == 'https://github.com'
@@ -124,7 +124,7 @@ jobs:
path: /tmp/bulk_sync.log path: /tmp/bulk_sync.log
retention-days: 30 retention-days: 30
- name: Log Summary (Gitea) - name: Log Summary (Git)
if: always() && github.server_url != 'https://github.com' if: always() && github.server_url != 'https://github.com'
run: | run: |
if [ -f /tmp/bulk_sync.log ]; then if [ -f /tmp/bulk_sync.log ]; then
@@ -3,7 +3,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Git.Workflow
# INGROUP: mokocli.Automation # INGROUP: mokocli.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.gitea/workflows/renovate.yml # PATH: /.gitea/workflows/renovate.yml
@@ -41,8 +41,8 @@ on:
default: false default: false
env: env:
GITEA_URL: https://git.mokoconsulting.tech GIT_URL: https://git.mokoconsulting.tech
GITEA_ORG: MokoConsulting GIT_ORG: MokoConsulting
RENOVATE_VERSION: '39' RENOVATE_VERSION: '39'
permissions: permissions:
@@ -57,11 +57,11 @@ jobs:
- name: Determine target repos - name: Determine target repos
id: repos id: repos
env: env:
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} GA_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
run: | run: |
API="${GITEA_URL}/api/v1" API="${GIT_URL}/api/v1"
EXCLUDE="gitea-org-config org-profile gitea-private .mokogitea-private mokocli MokoTesting" EXCLUDE="git-org-config org-profile git-private .mokogit-private mokocli MokoTesting"
EXCLUDE="$EXCLUDE MokoCli-Template-Client MokoCli-Template-Dolibarr MokoCli-Template-Generic MokoCli-Template-Joomla MokoDoliProjTemplate" EXCLUDE="$EXCLUDE MokoCli-Template-Client MokoCli-Template-Dolibarr MokoCli-Template-Generic MokoCli-Template-Joomla MokoDoliProjTemplate"
if [ -n "${{ inputs.repos }}" ]; then if [ -n "${{ inputs.repos }}" ]; then
@@ -72,7 +72,7 @@ jobs:
while true; do while true; do
BATCH=$(curl -sS \ BATCH=$(curl -sS \
-H "Authorization: token ${GA_TOKEN}" \ -H "Authorization: token ${GA_TOKEN}" \
"${API}/orgs/${GITEA_ORG}/repos?page=${PAGE}&limit=50" \ "${API}/orgs/${GIT_ORG}/repos?page=${PAGE}&limit=50" \
| jq -r '.[].name // empty') | jq -r '.[].name // empty')
[ -z "$BATCH" ] && break [ -z "$BATCH" ] && break
REPOS="$REPOS $BATCH" REPOS="$REPOS $BATCH"
@@ -94,9 +94,9 @@ jobs:
REPO_LIST="" REPO_LIST=""
for REPO in $REPOS; do for REPO in $REPOS; do
if [ -n "$REPO_LIST" ]; then if [ -n "$REPO_LIST" ]; then
REPO_LIST="${REPO_LIST},${GITEA_ORG}/${REPO}" REPO_LIST="${REPO_LIST},${GIT_ORG}/${REPO}"
else else
REPO_LIST="${GITEA_ORG}/${REPO}" REPO_LIST="${GIT_ORG}/${REPO}"
fi fi
done done
@@ -107,8 +107,8 @@ jobs:
- name: Run Renovate - name: Run Renovate
if: steps.repos.outputs.repo_list != '' if: steps.repos.outputs.repo_list != ''
env: env:
RENOVATE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} RENOVATE_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
RENOVATE_PLATFORM: gitea RENOVATE_PLATFORM: git
RENOVATE_ENDPOINT: ${{ env.GITEA_URL }}/api/v1 RENOVATE_ENDPOINT: ${{ env.GITEA_URL }}/api/v1
RENOVATE_GIT_AUTHOR: 'Renovate Bot <renovate@mokoconsulting.tech>' RENOVATE_GIT_AUTHOR: 'Renovate Bot <renovate@mokoconsulting.tech>'
RENOVATE_REPOSITORIES: ${{ steps.repos.outputs.repo_list }} RENOVATE_REPOSITORIES: ${{ steps.repos.outputs.repo_list }}
@@ -116,8 +116,8 @@ jobs:
LOG_LEVEL: info LOG_LEVEL: info
run: | run: |
npx --yes renovate@${RENOVATE_VERSION} \ npx --yes renovate@${RENOVATE_VERSION} \
--platform=gitea \ --platform=git \
--endpoint="${GITEA_URL}/api/v1" \ --endpoint="${GIT_URL}/api/v1" \
--token="${RENOVATE_TOKEN}" \ --token="${RENOVATE_TOKEN}" \
--git-author="Renovate Bot <renovate@mokoconsulting.tech>" \ --git-author="Renovate Bot <renovate@mokoconsulting.tech>" \
--autodiscover=false \ --autodiscover=false \
@@ -3,11 +3,11 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Git.Workflow
# INGROUP: mokocli.Maintenance # INGROUP: mokocli.Maintenance
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.gitea/workflows/sync-wikis.yml # PATH: /.gitea/workflows/sync-wikis.yml
# BRIEF: Daily sync of all Gitea wikis to consolidated GitHub wiki repo # BRIEF: Daily sync of all Git wikis to consolidated GitHub wiki repo
name: Sync Wikis to GitHub name: Sync Wikis to GitHub
@@ -32,7 +32,7 @@ jobs:
- name: Sync all wikis - name: Sync all wikis
env: env:
GH_TOKEN: ${{ secrets.GH_PAT }} GH_TOKEN: ${{ secrets.GH_PAT }}
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} GIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
run: | run: |
if [ -z "$GH_TOKEN" ]; then if [ -z "$GH_TOKEN" ]; then
echo "::error::GH_TOKEN secret not set" echo "::error::GH_TOKEN secret not set"
@@ -3,10 +3,10 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: MokoGIT.Workflow
# INGROUP: mokocli.Release # INGROUP: MokoCLI.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli # REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
# PATH: /.mokogitea/workflows/auto-bump.yml # PATH: /.mokogit/workflows/auto-bump.yml
# VERSION: 09.02.00 # VERSION: 09.02.00
# BRIEF: Auto patch-bump version on every push to dev (skips merge commits) # BRIEF: Auto patch-bump version on every push to dev (skips merge commits)
@@ -22,7 +22,7 @@ on:
env: env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} MOKOGIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
permissions: permissions:
contents: write contents: write
@@ -34,16 +34,17 @@ jobs:
if: >- if: >-
!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, '[skip ci]') &&
!contains(github.event.head_commit.message, '[skip bump]') && !contains(github.event.head_commit.message, '[skip bump]') &&
!startsWith(github.event.head_commit.message, 'Merge pull request') !startsWith(github.event.head_commit.message, 'Merge pull request') &&
!startsWith(github.event.repository.name, 'Template-')
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with: with:
token: ${{ secrets.MOKOGITEA_TOKEN }} token: ${{ secrets.MOKOGIT_TOKEN }}
fetch-depth: 1 fetch-depth: 1
- name: Setup mokocli tools - name: Setup MokoCLI tools
run: | run: |
if ! command -v composer &> /dev/null; then if ! command -v composer &> /dev/null; then
sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1 sudo apt-get update -qq && sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl composer >/dev/null 2>&1
@@ -52,7 +53,7 @@ jobs:
echo "MOKO_CLI=/opt/mokocli/cli" >> "$GITHUB_ENV" echo "MOKO_CLI=/opt/mokocli/cli" >> "$GITHUB_ENV"
else else
git clone --depth 1 --branch main --quiet \ git clone --depth 1 --branch main --quiet \
"https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/mokocli.git" \ "https://x-access-token:${{ secrets.MOKOGIT_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/mokocli.git" \
/tmp/mokocli /tmp/mokocli
cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet cd /tmp/mokocli && composer install --no-dev --no-interaction --quiet
echo "MOKO_CLI=/tmp/mokocli/cli" >> "$GITHUB_ENV" echo "MOKO_CLI=/tmp/mokocli/cli" >> "$GITHUB_ENV"
@@ -62,5 +63,5 @@ jobs:
run: | run: |
php ${MOKO_CLI}/version_auto_bump.php \ php ${MOKO_CLI}/version_auto_bump.php \
--path . --branch "${GITHUB_REF_NAME}" \ --path . --branch "${GITHUB_REF_NAME}" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" \ --token "${{ secrets.MOKOGIT_TOKEN }}" \
--repo-url "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" --repo-url "https://x-access-token:${{ secrets.MOKOGIT_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
@@ -3,18 +3,18 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: MokoGIT.Workflow
# INGROUP: mokocli.Release # INGROUP: MokoCLI.Release
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli # REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
# PATH: /templates/workflows/universal/auto-release.yml.template # PATH: /.mokogit/workflows/auto-release.yml
# VERSION: 05.00.00 # VERSION: 05.01.00
# BRIEF: Universal build & release detects platform from manifest.xml # BRIEF: Universal build & release detects platform from metadata API
# #
# +=======================================================================+ # +=======================================================================+
# | UNIVERSAL BUILD & RELEASE PIPELINE | # | UNIVERSAL BUILD & RELEASE PIPELINE |
# +=======================================================================+ # +=======================================================================+
# | | # | |
# | Reads manifest.xml (joomla|dolibarr|generic) to branch logic. | # | Reads metadata API (joomla|dolibarr|generic) to branch logic. |
# | | # | |
# | Platform-specific: | # | Platform-specific: |
# | joomla: XML manifest, type-prefixed packages | # | joomla: XML manifest, type-prefixed packages |
@@ -31,7 +31,7 @@ on:
branches: branches:
- main - main
paths-ignore: paths-ignore:
- '.mokogitea/workflows/**' - '.mokogit/workflows/**'
- '*.md' - '*.md'
- 'wiki/**' - 'wiki/**'
- '.editorconfig' - '.editorconfig'
@@ -52,9 +52,9 @@ on:
env: env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} MOKOGIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} GIT_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} GIT_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
permissions: permissions:
contents: write contents: write
@@ -64,21 +64,26 @@ jobs:
promote-rc: promote-rc:
name: Promote to RC name: Promote to RC
runs-on: release runs-on: release
# Skip on template repos (Template-*) — they scaffold other repos and do not release.
if: >- if: >-
(github.event.action == 'opened' && github.event.pull_request.merged != true) || !startsWith(github.event.repository.name, 'Template-') &&
(github.event.action == 'synchronize' && github.event.pull_request.merged != true) || (
(github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc') (github.event.action == 'opened' && github.event.pull_request.merged != true) ||
(github.event.action == 'synchronize' && github.event.pull_request.merged != true) ||
(github.event_name == 'workflow_dispatch' && inputs.action == 'promote-rc')
)
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with: with:
token: ${{ secrets.MOKOGITEA_TOKEN }} token: ${{ secrets.MOKOGIT_TOKEN }}
fetch-depth: 1 fetch-depth: 1
submodules: recursive
- name: Setup mokocli tools - name: Setup MokoCLI tools
env: env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} MOKO_CLONE_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
run: | run: |
if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then if [ -f /opt/mokocli/cli/version_bump.php ] && [ -f /opt/mokocli/vendor/autoload.php ]; then
@@ -99,30 +104,68 @@ jobs:
- name: Rename branch to rc - name: Rename branch to rc
run: | run: |
php ${MOKO_CLI}/branch_rename.php \ API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
--from "${{ github.event.pull_request.head.ref || 'dev' }}" --to rc \ AUTH="Authorization: token ${{ secrets.MOKOGIT_TOKEN }}"
--token "${{ secrets.MOKOGITEA_TOKEN }}" \ FROM="${{ github.event.pull_request.head.ref || 'dev' }}"
--api-base "${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" \ PR="${{ github.event.pull_request.number }}"
--pr "${{ github.event.pull_request.number }}"
# Resolve the source branch HEAD commit.
SRC_JSON=$(curl -sf -H "$AUTH" "${API_BASE}/branches/${FROM}") \
|| { echo "::error::Source branch ${FROM} not found"; exit 1; }
SRC_SHA=$(printf '%s' "$SRC_JSON" | python3 -c "import sys, json; print(json.load(sys.stdin)['commit']['id'])" 2>/dev/null || true)
[ -n "$SRC_SHA" ] || { echo "::error::Could not resolve HEAD of ${FROM}"; exit 1; }
# Point rc at the source commit via git push. Git's git/refs PATCH API
# returns HTTP 405 on ANY protected branch (force or not, even for a user in
# the force-push allowlist), so it cannot move a protected rc. git push honors
# the push + force-push allowlists and creates rc if it is absent.
PUSH_URL="https://x-access-token:${{ secrets.MOKOGIT_TOKEN }}@${MOKOGIT_URL#https://}/${GIT_ORG}/${GIT_REPO}.git"
git config --global user.name "mokogit-actions[bot]"
git config --global user.email "actions@mokoconsulting.tech"
git fetch --no-tags "$PUSH_URL" "${FROM}"
git push --force "$PUSH_URL" "FETCH_HEAD:refs/heads/rc" \
|| { echo "::error::Failed to point rc at ${FROM} (${SRC_SHA}) via git push"; exit 1; }
echo "rc set to ${FROM} (${SRC_SHA})"
# Repoint the PR at rc, then delete the old source branch (non-fatal).
if [ -n "$PR" ]; then
curl -s -X PATCH -H "$AUTH" -H "Content-Type: application/json" \
"${API_BASE}/pulls/${PR}" -d '{"head":"rc"}' >/dev/null || true
fi
# Never delete permanent branches (dev/main/rc/...); only ephemeral feature branches.
case "$FROM" in
dev|main|master|rc|stable|production|release|develop|staging|beta|alpha)
echo "Keeping permanent branch ${FROM} (not deleting)" ;;
*)
curl -s -X DELETE -H "$AUTH" "${API_BASE}/branches/${FROM}" >/dev/null || true ;;
esac
echo "Renamed ${FROM} -> rc"
- name: Trigger RC deploy
run: |
# Workflow-token pushes do NOT wake downstream workflows; dispatch deploy-rc explicitly.
curl -sf -X POST -H "Authorization: token ${{ secrets.MOKOGIT_TOKEN }}" \n -H "Content-Type: application/json" \n "${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}/actions/workflows/deploy-rc.yml/dispatches" \n -d '{"ref":"rc"}' \n && echo "Dispatched deploy-rc on rc" \n || echo "::warning::deploy-rc dispatch failed (no deploy-rc.yml on rc? non-go platform)"
- name: Checkout rc and configure git - name: Checkout rc and configure git
run: | run: |
git fetch origin rc git fetch origin rc
git checkout rc git checkout rc
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" git config --local user.email "mokogit-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]" git config --local user.name "mokogit-actions[bot]"
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" git remote set-url origin "https://x-access-token:${{ secrets.MOKOGIT_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
- name: Publish RC release - name: Publish RC release
continue-on-error: true
run: | run: |
php ${MOKO_CLI}/release_publish.php \ php ${MOKO_CLI}/release_publish.php \
--path . --stability rc --bump minor --branch rc \ --path . --stability rc --bump minor --branch rc \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --token "${{ secrets.MOKOGIT_TOKEN }}"
- name: Update RC release notes from CHANGELOG.md - name: Update RC release notes from CHANGELOG.md
continue-on-error: true
run: | run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" TOKEN="${{ secrets.MOKOGIT_TOKEN }}"
# Extract [Unreleased] section from changelog # Extract [Unreleased] section from changelog
NOTES="" NOTES=""
@@ -163,22 +206,27 @@ jobs:
release: release:
name: Build & Release Pipeline name: Build & Release Pipeline
runs-on: release runs-on: release
# Skip on template repos (Template-*) — they scaffold other repos and do not release.
if: >- if: >-
github.event.pull_request.merged == true || !startsWith(github.event.repository.name, 'Template-') &&
(github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc') (
github.event.pull_request.merged == true ||
(github.event_name == 'workflow_dispatch' && inputs.action != 'promote-rc')
)
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with: with:
token: ${{ secrets.MOKOGITEA_TOKEN }} token: ${{ secrets.MOKOGIT_TOKEN }}
fetch-depth: 0 fetch-depth: 0
submodules: recursive
- name: Configure git for bot pushes - name: Configure git for bot pushes
run: | run: |
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" git config --local user.email "mokogit-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]" git config --local user.name "mokogit-actions[bot]"
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" git remote set-url origin "https://x-access-token:${{ secrets.MOKOGIT_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
- name: Check for merge conflict markers - name: Check for merge conflict markers
run: | run: |
@@ -193,9 +241,9 @@ jobs:
fi fi
echo "No conflict markers found" echo "No conflict markers found"
- name: Setup mokocli tools - name: Setup MokoCLI tools
env: env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} MOKO_CLONE_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}' COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GH_MIRROR_TOKEN }}"}}'
run: | run: |
@@ -242,7 +290,7 @@ jobs:
fi fi
php ${MOKO_CLI}/release_publish.php \ php ${MOKO_CLI}/release_publish.php \
--path . --stability stable ${BUMP_FLAG} --branch main \ --path . --stability stable ${BUMP_FLAG} --branch main \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --token "${{ secrets.MOKOGIT_TOKEN }}"
- name: "Read published version" - name: "Read published version"
id: version id: version
@@ -269,8 +317,8 @@ jobs:
!startsWith(steps.platform.outputs.platform, 'joomla') !startsWith(steps.platform.outputs.platform, 'joomla')
run: | run: |
VERSION="${{ steps.version.outputs.version }}" VERSION="${{ steps.version.outputs.version }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" TOKEN="${{ secrets.MOKOGIT_TOKEN }}"
SEMVER_TAG="v${VERSION}" SEMVER_TAG="v${VERSION}"
echo "Creating semver tag: ${SEMVER_TAG}" echo "Creating semver tag: ${SEMVER_TAG}"
@@ -294,8 +342,8 @@ jobs:
- name: Update release notes and promote changelog - name: Update release notes and promote changelog
run: | run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" TOKEN="${{ secrets.MOKOGIT_TOKEN }}"
# Get the stable release info (version and ID) # Get the stable release info (version and ID)
RELEASE_JSON=$(curl -sf -H "Authorization: token ${TOKEN}" \ RELEASE_JSON=$(curl -sf -H "Authorization: token ${TOKEN}" \
@@ -339,13 +387,47 @@ jobs:
if [ -n "$VERSION" ] && [ -f "CHANGELOG.md" ]; then if [ -n "$VERSION" ] && [ -f "CHANGELOG.md" ]; then
DATE=$(date +%Y-%m-%d) DATE=$(date +%Y-%m-%d)
python3 -c " python3 -c "
import sys import sys, re
version, date = sys.argv[1], sys.argv[2] version, date = sys.argv[1], sys.argv[2]
content = open('CHANGELOG.md').read() lines = open('CHANGELOG.md').read().split('\n')
old = '## [Unreleased]' h2 = re.compile(r'^##\s+\[([^\]]+)\]')
new = f'## [Unreleased]\n\n## [{version}] --- {date}' header, sections, cur = [], [], None
content = content.replace(old, new, 1) for ln in lines:
open('CHANGELOG.md', 'w').write(content) m = h2.match(ln)
if m:
if cur: sections.append(cur)
cur = {'label': m.group(1).strip(), 'head': ln, 'body': []}
elif cur is None:
header.append(ln)
else:
cur['body'].append(ln)
if cur: sections.append(cur)
def nonempty(b): return any(x.strip() for x in b)
def trim(b):
b = b[:]
while b and not b[0].strip(): b.pop(0)
while b and not b[-1].strip(): b.pop()
return b
unreleased, order, bykey = [], [], {}
for s in sections:
key = s['label'].lower()
if key == 'unreleased':
if nonempty(s['body']): unreleased += s['body']
continue
if not nonempty(s['body']): continue # drop blank release sections
if key in bykey: bykey[key]['body'] += [''] + s['body'] # merge duplicate heading (never drop content)
else: bykey[key] = s; order.append(key)
rest, seen = [bykey[k] for k in order], set(order)
out = []
htxt = '\n'.join(header).rstrip()
if htxt: out += [htxt, '']
out += ['## [Unreleased]', '']
promote = bool(unreleased) and version.lower() not in seen
if unreleased and not promote: out += trim(unreleased) + ['']
if promote: out += ['## [%s] --- %s' % (version, date), ''] + trim(unreleased) + ['']
for s in rest: out += [s['head'], ''] + trim(s['body']) + ['']
res = re.sub(r'\n{3,}', '\n\n', '\n'.join(out)).rstrip('\n') + '\n'
open('CHANGELOG.md', 'w').write(res)
" "$VERSION" "$DATE" " "$VERSION" "$DATE"
git add CHANGELOG.md git add CHANGELOG.md
git commit -m "chore: promote changelog [Unreleased] → [${VERSION}]" || true git commit -m "chore: promote changelog [Unreleased] → [${VERSION}]" || true
@@ -363,10 +445,10 @@ jobs:
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
RELEASE_TAG="${{ steps.version.outputs.release_tag }}" RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}" GH_REPO="${{ vars.GH_MIRROR_REPO || github.repository }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
php ${MOKO_CLI}/release_mirror.php \ php ${MOKO_CLI}/release_mirror.php \
--version "$VERSION" --tag "$RELEASE_TAG" \ --version "$VERSION" --tag "$RELEASE_TAG" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ --token "${{ secrets.MOKOGIT_TOKEN }}" --api-base "$API_BASE" \
--gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \ --gh-token "${{ secrets.GH_MIRROR_TOKEN }}" --gh-repo "$GH_REPO" \
--branch main 2>&1 || true --branch main 2>&1 || true
echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY echo "GitHub mirror updated" >> $GITHUB_STEP_SUMMARY
@@ -388,36 +470,28 @@ jobs:
&& echo "main branch pushed to GitHub mirror" \ && echo "main branch pushed to GitHub mirror" \
|| echo "WARNING: GitHub mirror push failed" || echo "WARNING: GitHub mirror push failed"
- name: "Step 11: Delete rc branch and recreate dev from main" - name: "Step 11: Delete rc branch (dev reset moved to cascade-dev.yml)"
if: steps.version.outputs.skip != 'true' if: steps.version.outputs.skip != 'true'
continue-on-error: true continue-on-error: true
run: | run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" TOKEN="${{ secrets.MOKOGIT_TOKEN }}"
# Delete rc branch (ephemeral — created by promote-rc) # Delete rc branch (ephemeral — created by promote-rc)
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \
"${API_BASE}/branches/rc" 2>/dev/null \ "${API_BASE}/branches/rc" 2>/dev/null \
&& echo "Deleted rc branch" || echo "rc branch not found" && echo "Deleted rc branch" || echo "rc branch not found"
# Delete dev branch # dev is reset from main by the dedicated "Cascade Main -> Dev" workflow
curl -sf -X DELETE -H "Authorization: token ${TOKEN}" \ # (cascade-dev.yml), which runs after this release completes.
"${API_BASE}/branches/dev" 2>/dev/null && echo "Deleted dev branch" echo "rc cleaned; dev reset handled by cascade-dev.yml" >> $GITHUB_STEP_SUMMARY
# Recreate dev from main (now includes version bump + changelog promotion)
curl -sf -X POST -H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \
"${API_BASE}/branches" \
-d '{"new_branch_name":"dev","old_branch_name":"main"}' 2>/dev/null && echo "Recreated dev from main"
echo "Pre-release branches cleaned, dev reset from main" >> $GITHUB_STEP_SUMMARY
- name: "Step 12: Create version branch from main" - name: "Step 12: Create version branch from main"
if: steps.version.outputs.skip != 'true' if: steps.version.outputs.skip != 'true'
continue-on-error: true continue-on-error: true
run: | run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" TOKEN="${{ secrets.MOKOGIT_TOKEN }}"
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
BRANCH_NAME="version/${VERSION}" BRANCH_NAME="version/${VERSION}"
MAIN_SHA=$(git rev-parse HEAD) MAIN_SHA=$(git rev-parse HEAD)
@@ -437,9 +511,9 @@ jobs:
if: steps.version.outputs.skip != 'true' if: steps.version.outputs.skip != 'true'
continue-on-error: true continue-on-error: true
run: | run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API_BASE="${MOKOGIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
php ${MOKO_CLI}/version_reset_dev.php \ php ${MOKO_CLI}/version_reset_dev.php \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "${API_BASE}" \ --token "${{ secrets.MOKOGIT_TOKEN }}" --api-base "${API_BASE}" \
--branch dev --path . 2>&1 || true --branch dev --path . 2>&1 || true
# -- Summary -------------------------------------------------------------- # -- Summary --------------------------------------------------------------
@@ -463,5 +537,5 @@ jobs:
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY echo "| Branch | \`${{ steps.version.outputs.branch }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY echo "| Tag | \`${{ steps.version.outputs.tag }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Release | [View](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY echo "| Release | [View](${MOKOGIT_URL}/${GIT_ORG}/${GIT_REPO}/releases/tag/${{ steps.version.outputs.tag }}) |" >> $GITHUB_STEP_SUMMARY
fi fi
@@ -3,10 +3,10 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: MokoGIT.Workflow
# INGROUP: MokoStandards.Universal # INGROUP: MokoCLI.Universal
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli # REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
# PATH: /.mokogitea/workflows/branch-cleanup.yml # PATH: /.mokogit/workflows/branch-cleanup.yml
# VERSION: 01.00.00 # VERSION: 01.00.00
# BRIEF: Delete feature branches after PR merge # BRIEF: Delete feature branches after PR merge
@@ -33,10 +33,11 @@ jobs:
run: | run: |
BRANCH="${{ github.event.pull_request.head.ref }}" BRANCH="${{ github.event.pull_request.head.ref }}"
API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches" API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches"
ENCODED=$(php -r "echo rawurlencode('${BRANCH}');") # URL-encode the branch name's slashes (no PHP dependency on the runner)
ENCODED=$(printf '%s' "${BRANCH}" | sed 's|/|%2F|g')
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \ STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
-H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ -H "Authorization: token ${{ secrets.MOKOGIT_TOKEN }}" \
"${API}/${ENCODED}" 2>/dev/null || true) "${API}/${ENCODED}" 2>/dev/null || true)
if [ "$STATUS" = "204" ]; then if [ "$STATUS" = "204" ]; then
+190
View File
@@ -0,0 +1,190 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: MokoGIT.Workflow
# INGROUP: MokoCLI.Cascade
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
# PATH: /.mokogit/workflows/cascade-dev.yml
# VERSION: 02.01.00
# BRIEF: Cascade main -> dev; auto-merge clean, auto-resolve VERSION-stamp-only conflicts, else notify
name: "Cascade Main -> Dev"
on:
push:
branches:
- main
# Daily safety net: catches drift even when main only received [skip ci] pushes
# (which never fire the push trigger above). Off-round minute to avoid a fleet-wide spike.
schedule:
- cron: '23 7 * * *'
workflow_dispatch:
permissions:
contents: write
pull-requests: write
env:
MOKOGIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
# ntfy destination is configured via repo or org variables (org vars are inherited).
NTFY_URL: ${{ vars.NTFY_URL || 'https://ntfy.mokoconsulting.tech' }}
NTFY_TOPIC: ${{ vars.CASCADE_NTFY_TOPIC || vars.NTFY_TOPIC || 'git-releases' }}
jobs:
cascade:
name: Cascade main -> dev
runs-on: ubuntu-latest
steps:
- name: Checkout (full history for merge/resolve)
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.MOKOGIT_TOKEN }}
- name: Cascade main -> dev (auto-resolve version stamps, else notify)
env:
TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
REPO: ${{ github.repository }}
run: |
set -uo pipefail
API="${MOKOGIT_URL}/api/v1/repos/${REPO}"
AUTH="Authorization: token ${TOKEN}"
jqget() { python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('$1',''))" 2>/dev/null; }
# 0. dev must exist
if ! curl -sf -H "$AUTH" "${API}/branches/dev" >/dev/null 2>&1; then
echo "No dev branch - nothing to cascade."; exit 0
fi
# 1. is main ahead of dev?
AHEAD=$(curl -sf -H "$AUTH" "${API}/compare/dev...main" \
| python3 -c "import sys,json; print(json.load(sys.stdin).get('total_commits',0))" 2>/dev/null || echo 0)
if [ "${AHEAD:-0}" -eq 0 ]; then
echo "dev already up to date with main."; exit 0
fi
echo "main is ${AHEAD} commit(s) ahead of dev."
# 2. reuse an open main->dev PR, else create one
PR=$(curl -sf -H "$AUTH" "${API}/pulls?state=open&base=dev" \
| python3 -c "import sys,json; d=json.load(sys.stdin); print(next((str(p['number']) for p in d if p.get('head',{}).get('ref')=='main'), ''))" 2>/dev/null || echo "")
if [ -z "$PR" ]; then
RESP=$(curl -s -H "$AUTH" -H "Content-Type: application/json" -X POST "${API}/pulls" \
-d '{"head":"main","base":"dev","title":"chore(sync): cascade main -> dev","body":"Automated cascade of main into dev. Auto-merges when conflict-free, auto-resolves VERSION-stamp-only conflicts, otherwise left open for manual resolution."}')
PR=$(printf '%s' "$RESP" | jqget number)
if [ -z "$PR" ]; then
echo "::warning::Could not open cascade PR: $RESP"; exit 0
fi
echo "Opened cascade PR #${PR}"
else
echo "Reusing open cascade PR #${PR}"
fi
notify() {
curl -sS \
-H "Title: ${REPO}: dev cascade needs manual merge" \
-H "Tags: warning,twisted_rightwards_arrows" \
-H "Priority: high" \
-H "Click: ${MOKOGIT_URL}/${REPO}/pulls/${PR}" \
-d "main -> dev cascade PR #${PR} $1 It was NOT auto-merged; resolve it manually." \
"${NTFY_URL}/${NTFY_TOPIC}" || true
}
# 3. wait for MokoGIT to compute mergeability (conflict detection)
MERGEABLE=""
for _ in 1 2 3 4 5 6; do
MERGEABLE=$(curl -sf -H "$AUTH" "${API}/pulls/${PR}" | jqget mergeable)
case "$MERGEABLE" in True|False) break ;; esac
sleep 3
done
echo "mergeable=${MERGEABLE}"
# 4a. conflict-free -> merge via API (existing behaviour)
if [ "$MERGEABLE" = "True" ]; then
CODE=$(curl -s -o /tmp/merge.json -w "%{http_code}" -H "$AUTH" -H "Content-Type: application/json" \
-X POST "${API}/pulls/${PR}/merge" -d '{"Do":"merge","merge_when_checks_succeed":true}')
if [ "$CODE" -ge 200 ] && [ "$CODE" -lt 300 ]; then
echo "Cascade PR #${PR} merged (or scheduled to merge when checks pass)."
exit 0
fi
echo "::warning::Auto-merge returned HTTP ${CODE}: $(cat /tmp/merge.json)"
notify "could not be auto-merged (HTTP ${CODE})."
exit 0
fi
# 4b. conflicts -> try to auto-resolve if they are ONLY VERSION-stamp lines.
echo "PR not cleanly mergeable; checking whether conflicts are VERSION-stamp-only..."
git config user.name "MokoGIT Cascade"
git config user.email "actions@mokoconsulting.tech"
git fetch --quiet origin main dev
git checkout -B dev origin/dev
if git merge --no-ff --no-commit origin/main >/dev/null 2>&1; then
# Became clean at git level (e.g. mergeability was still computing) -> commit + push.
git commit -m "chore(sync): cascade main -> dev [skip ci]" >/dev/null
git push origin dev
echo "Cascade merged cleanly at git level and pushed to dev."
exit 0
fi
CONFLICTS=$(git diff --name-only --diff-filter=U)
echo "Conflicted files:"; echo "${CONFLICTS}"
# A conflict is "stamp-only" when every line inside every conflict block matches
# a version-stamp pattern (VERSION: header, <version> element, or CHANGELOG title).
is_stamp_only() {
awk '
/^<<<<<<< / { inc=1; next }
inc && /^=======$/ { next }
/^>>>>>>> / { inc=0; next }
inc { if ($0 !~ /(VERSION:|<version>|# Changelog)/) { bad=1 } }
END { exit(bad ? 1 : 0) }
' "$1"
}
# Resolve a stamp-only file by keeping dev (ours) for the conflicting lines,
# preserving all auto-merged content around them.
keep_ours() {
awk '
/^<<<<<<< / { inc=1; side="ours"; next }
inc && /^=======$/ { side="theirs"; next }
/^>>>>>>> / { inc=0; next }
{ if (!inc) { print; next } if (side=="ours") print }
' "$1" > "$1.resolved" && mv "$1.resolved" "$1"
}
ALL_STAMP=1
for f in ${CONFLICTS}; do
if ! is_stamp_only "$f"; then
echo "::notice::$f has non-stamp conflicts -> manual resolution required."
ALL_STAMP=0; break
fi
done
if [ "$ALL_STAMP" != "1" ]; then
git merge --abort || true
notify "has non-version-stamp conflicts and cannot be auto-resolved."
exit 0
fi
echo "All conflicts are VERSION-stamp-only; resolving in favour of dev."
for f in ${CONFLICTS}; do
keep_ours "$f"
git add "$f"
done
# Best-effort: normalise stamps to dev's version if mokocli is available.
if [ -f /opt/mokocli/cli/version_check.php ]; then
php /opt/mokocli/cli/version_check.php --fix || true
git add -A
fi
git commit -m "chore(sync): cascade main -> dev (auto-resolved version stamps) [skip ci]" >/dev/null
git push origin dev
echo "Cascade auto-resolved and pushed to dev."
# Close the now-redundant PR (its changes are in dev) with an explanatory comment.
curl -s -H "$AUTH" -H "Content-Type: application/json" -X POST "${API}/issues/${PR}/comments" \
-d '{"body":"Auto-resolved VERSION-stamp-only conflicts and pushed the merge to dev. Closing."}' >/dev/null || true
curl -s -H "$AUTH" -H "Content-Type: application/json" -X PATCH "${API}/pulls/${PR}" \
-d '{"state":"closed"}' >/dev/null || true
@@ -3,16 +3,22 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: MokoGIT.Workflow
# INGROUP: MokoStandards.CI # INGROUP: MokoCLI.CI
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic # REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
# PATH: /.gitea/workflows/ci-generic.yml # PATH: /.mokogit/workflows/ci-generic.yml
# VERSION: 01.00.00 # VERSION: 01.00.00
# BRIEF: CI pipeline — lint, validate, and test for generic projects (PHP + Node.js) # BRIEF: CI pipeline — lint, validate, and test for generic projects (PHP + Node.js)
name: "Generic: Project CI" name: "Generic: Project CI"
on: on:
pull_request:
branches:
- main
- dev
- dev/**
- rc/**
workflow_dispatch: workflow_dispatch:
permissions: permissions:
@@ -26,6 +32,8 @@ jobs:
lint: lint:
name: Lint & Validate name: Lint & Validate
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Skip on template repos (Template-*) — they hold placeholder scaffolding, not buildable source.
if: ${{ !startsWith(github.event.repository.name, 'Template-') }}
steps: steps:
- name: Checkout - name: Checkout
@@ -123,7 +131,11 @@ jobs:
test: test:
name: Tests name: Tests
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: lint # Independent job (no `needs: lint`): the MokoGIT Actions scheduler does not
# offer the dependent 2nd job of a needs-chain to runners, so it stalls in
# "waiting" and is reaped by ABANDONED_JOB_TIMEOUT. Guard template repos
# directly (same condition lint uses) instead of gating on lint's result.
if: ${{ !startsWith(github.event.repository.name, 'Template-') }}
steps: steps:
- name: Checkout - name: Checkout
+68
View File
@@ -0,0 +1,68 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: MokoGIT.Workflow
# INGROUP: MokoCLI.Universal
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
# PATH: /.mokogit/workflows/ci-issue-reporter.yml
# VERSION: 01.00.00
# BRIEF: Reusable workflow — creates/updates a MokoGIT issue when a CI gate fails.
# Clones MokoCLI and runs cli/ci_issue_reporter.sh.
name: "Universal: CI Issue Reporter"
on:
workflow_call:
inputs:
gate:
description: "CI gate name (e.g. PR Validation, Repository Health)"
required: true
type: string
details:
description: "Human-readable failure description"
required: true
type: string
severity:
description: "error or warning"
required: false
type: string
default: "error"
workflow:
description: "Workflow name for the issue title"
required: false
type: string
default: ""
secrets:
MOKOGIT_TOKEN:
required: true
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
report:
name: "Report: ${{ inputs.gate }}"
runs-on: ubuntu-latest
steps:
- name: Clone MokoCLI
env:
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
run: |
MOKOGIT_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
git clone --depth 1 --filter=blob:none --sparse "${MOKOGIT_URL}/MokoConsulting/mokocli.git" /tmp/mokocli
cd /tmp/mokocli && git sparse-checkout set cli/ci_issue_reporter.sh
- name: Report CI failure
env:
MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
MOKOGIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
run: |
chmod +x /tmp/mokocli/cli/ci_issue_reporter.sh
/tmp/mokocli/cli/ci_issue_reporter.sh \
--gate "${{ inputs.gate }}" \
--details "${{ inputs.details }}" \
--severity "${{ inputs.severity }}" \
--workflow "${{ inputs.workflow }}"
@@ -3,10 +3,10 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: MokoGIT.Workflow
# INGROUP: MokoStandards.Maintenance # INGROUP: MokoCLI.Maintenance
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.gitea/workflows/cleanup.yml # PATH: /.mokogit/workflows/cleanup.yml
# VERSION: 01.00.00 # VERSION: 01.00.00
# BRIEF: Scheduled cleanup — delete merged branches and old workflow runs # BRIEF: Scheduled cleanup — delete merged branches and old workflow runs
@@ -21,7 +21,7 @@ permissions:
contents: write contents: write
env: env:
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} MOKOGIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
jobs: jobs:
cleanup: cleanup:
@@ -33,30 +33,30 @@ jobs:
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
token: ${{ secrets.GA_TOKEN }} token: ${{ secrets.MOKOGIT_TOKEN }}
- name: Delete merged branches - name: Delete merged branches
env: env:
GA_TOKEN: ${{ secrets.GA_TOKEN }} MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
run: | run: |
echo "=== Merged Branch Cleanup ===" echo "=== Merged Branch Cleanup ==="
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}" API="${MOKOGIT_URL}/api/v1/repos/${{ github.repository }}"
# List branches via API # List branches via API
BRANCHES=$(curl -sS -H "Authorization: token ${GA_TOKEN}" \ BRANCHES=$(curl -sS -H "Authorization: token ${MOKOGIT_TOKEN}" \
"${API}/branches?limit=50" | jq -r '.[].name') "${API}/branches?limit=50" | jq -r '.[].name')
DELETED=0 DELETED=0
for BRANCH in $BRANCHES; do for BRANCH in $BRANCHES; do
# Skip protected branches # Skip protected branches
case "$BRANCH" in case "$BRANCH" in
main|master|develop|release/*|hotfix/*) continue ;; main|master|dev|develop|rc|beta|alpha|release|release/*|production|stable|staging|hotfix/*|version/*) continue ;;
esac esac
# Check if branch is merged into main # Check if branch is merged into main
if git merge-base --is-ancestor "origin/${BRANCH}" origin/main 2>/dev/null; then if git merge-base --is-ancestor "origin/${BRANCH}" origin/main 2>/dev/null; then
echo " Deleting merged branch: ${BRANCH}" echo " Deleting merged branch: ${BRANCH}"
curl -sS -X DELETE -H "Authorization: token ${GA_TOKEN}" \ curl -sS -X DELETE -H "Authorization: token ${MOKOGIT_TOKEN}" \
"${API}/branches/${BRANCH}" 2>/dev/null || true "${API}/branches/${BRANCH}" 2>/dev/null || true
DELETED=$((DELETED + 1)) DELETED=$((DELETED + 1))
fi fi
@@ -66,20 +66,20 @@ jobs:
- name: Clean old workflow runs - name: Clean old workflow runs
env: env:
GA_TOKEN: ${{ secrets.GA_TOKEN }} MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
run: | run: |
echo "=== Workflow Run Cleanup ===" echo "=== Workflow Run Cleanup ==="
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}" API="${MOKOGIT_URL}/api/v1/repos/${{ github.repository }}"
CUTOFF=$(date -d "30 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -v-30d +%Y-%m-%dT%H:%M:%SZ) CUTOFF=$(date -d "30 days ago" +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -v-30d +%Y-%m-%dT%H:%M:%SZ)
# Get old completed runs # Get old completed runs
RUNS=$(curl -sS -H "Authorization: token ${GA_TOKEN}" \ RUNS=$(curl -sS -H "Authorization: token ${MOKOGIT_TOKEN}" \
"${API}/actions/runs?status=completed&limit=50" | \ "${API}/actions/runs?status=completed&limit=50" | \
jq -r ".workflow_runs[] | select(.created_at < \"${CUTOFF}\") | .id" 2>/dev/null) jq -r ".workflow_runs[] | select(.created_at < \"${CUTOFF}\") | .id" 2>/dev/null)
DELETED=0 DELETED=0
for RUN_ID in $RUNS; do for RUN_ID in $RUNS; do
curl -sS -X DELETE -H "Authorization: token ${GA_TOKEN}" \ curl -sS -X DELETE -H "Authorization: token ${MOKOGIT_TOKEN}" \
"${API}/actions/runs/${RUN_ID}" 2>/dev/null || true "${API}/actions/runs/${RUN_ID}" 2>/dev/null || true
DELETED=$((DELETED + 1)) DELETED=$((DELETED + 1))
done done
@@ -27,7 +27,7 @@ jobs:
- name: Checkout MokoCLI - name: Checkout MokoCLI
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with: with:
token: ${{ secrets.MOKOGITEA_TOKEN }} token: ${{ secrets.MOKOGIT_TOKEN }}
- name: Setup PHP - name: Setup PHP
run: | run: |
@@ -42,7 +42,7 @@ jobs:
DRY_RUN="--dry-run" DRY_RUN="--dry-run"
fi fi
php cli/branch_protect_org.php \ php cli/branch_protect_org.php \
--token "${{ secrets.MOKOGITEA_TOKEN }}" \ --token "${{ secrets.MOKOGIT_TOKEN }}" \
--org "MokoConsulting" \ --org "MokoConsulting" \
$DRY_RUN $DRY_RUN
@@ -3,10 +3,10 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Git.Workflow
# INGROUP: mokocli.CI # INGROUP: mokocli.CI
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/ci-platform.yml # PATH: /.mokogit/workflows/ci-platform.yml
# VERSION: 09.23.00 # VERSION: 09.23.00
# BRIEF: mokocli CI — the standards engine validates itself # BRIEF: mokocli CI — the standards engine validates itself
# #
@@ -41,7 +41,7 @@ on:
paths-ignore: paths-ignore:
- '**.md' - '**.md'
- 'wiki/**' - 'wiki/**'
- '.mokogitea/ISSUE_TEMPLATE/**' - '.mokogit/ISSUE_TEMPLATE/**'
pull_request: pull_request:
branches: branches:
- main - main
@@ -115,7 +115,7 @@ jobs:
- name: "PHPCS (PSR-12)" - name: "PHPCS (PSR-12)"
run: | run: |
vendor/bin/phpcs --standard=phpcs.xml --report=summary --warning-severity=0 lib/ validate/ automation/ 2>&1 || { vendor/bin/phpcs --standard=phpcs.xml --report=summary --warning-severity=0 lib/ validate/ 2>&1 || {
echo "::error::PHPCS found coding standard violations" echo "::error::PHPCS found coding standard violations"
echo "### PHPCS" >> $GITHUB_STEP_SUMMARY echo "### PHPCS" >> $GITHUB_STEP_SUMMARY
echo "Coding standard violations detected. Run \`composer phpcs\` locally." >> $GITHUB_STEP_SUMMARY echo "Coding standard violations detected. Run \`composer phpcs\` locally." >> $GITHUB_STEP_SUMMARY
@@ -356,7 +356,7 @@ jobs:
echo "::error file=${file}::Invalid YAML" echo "::error file=${file}::Invalid YAML"
ERRORS=$((ERRORS + 1)) ERRORS=$((ERRORS + 1))
fi fi
done < <(find .mokogitea/workflows/ -name "*.yml" -o -name "*.yaml" 2>/dev/null | tr '\n' '\0') done < <(find .mokogit/workflows/ -name "*.yml" -o -name "*.yaml" 2>/dev/null | tr '\n' '\0')
{ {
echo "### Template Integrity" echo "### Template Integrity"
@@ -453,8 +453,8 @@ jobs:
needs.governance.result == 'failure' || needs.governance.result == 'failure' ||
needs.templates.result == 'failure') needs.templates.result == 'failure')
env: env:
GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} MOKOGIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
run: | run: |
chmod +x automation/ci-issue-reporter.sh chmod +x automation/ci-issue-reporter.sh
REPORTER="./automation/ci-issue-reporter.sh" REPORTER="./automation/ci-issue-reporter.sh"
@@ -13,7 +13,7 @@ on:
workflow_dispatch: workflow_dispatch:
env: env:
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} MOKOGIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
jobs: jobs:
publish: publish:
@@ -43,13 +43,13 @@ jobs:
echo "version=${VERSION}" >> "$GITHUB_OUTPUT" echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "Package version: ${VERSION}" echo "Package version: ${VERSION}"
# Gitea Composer Registry — auto-publishes from tags # Git Composer Registry — auto-publishes from tags
# The tag push itself registers the package at: # The tag push itself registers the package at:
# https://git.mokoconsulting.tech/api/packages/MokoConsulting/composer # https://git.mokoconsulting.tech/api/packages/MokoConsulting/composer
- name: Verify Gitea registry - name: Verify Git registry
run: | run: |
echo "Gitea Composer registry auto-publishes from tags." echo "Git Composer registry auto-publishes from tags."
echo "Package available at: ${GITEA_URL}/api/packages/MokoConsulting/composer" echo "Package available at: ${MOKOGIT_URL}/api/packages/MokoConsulting/composer"
echo "Install: composer require mokoconsulting/mokocli" echo "Install: composer require mokoconsulting/mokocli"
# Packagist — notify of new version (points to GitHub mirror which Packagist can access) # Packagist — notify of new version (points to GitHub mirror which Packagist can access)
@@ -72,5 +72,5 @@ jobs:
echo "" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY
echo "| Registry | Status |" >> $GITHUB_STEP_SUMMARY echo "| Registry | Status |" >> $GITHUB_STEP_SUMMARY
echo "|----------|--------|" >> $GITHUB_STEP_SUMMARY echo "|----------|--------|" >> $GITHUB_STEP_SUMMARY
echo "| Gitea | \`composer require mokoconsulting/mokocli:${VERSION}\` |" >> $GITHUB_STEP_SUMMARY echo "| Git | \`composer require mokoconsulting/mokocli:${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Packagist | \`composer require mokoconsulting/mokocli\` |" >> $GITHUB_STEP_SUMMARY echo "| Packagist | \`composer require mokoconsulting/mokocli\` |" >> $GITHUB_STEP_SUMMARY
@@ -3,10 +3,10 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: Git.Workflow
# INGROUP: mokocli.Automation # INGROUP: mokocli.Automation
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/sync-feature-versions.yml # PATH: /.mokogit/workflows/sync-feature-versions.yml
# VERSION: 01.00.00 # VERSION: 01.00.00
# BRIEF: Merge dev into open feature branches after version bumps # BRIEF: Merge dev into open feature branches after version bumps
@@ -22,7 +22,7 @@ permissions:
contents: write contents: write
env: env:
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} MOKOGIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
jobs: jobs:
sync: sync:
@@ -38,13 +38,13 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
ref: dev ref: dev
token: ${{ secrets.MOKOGITEA_TOKEN }} token: ${{ secrets.MOKOGIT_TOKEN }}
- name: Configure git - name: Configure git
run: | run: |
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" git config --local user.email "git-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]" git config --local user.name "git-actions[bot]"
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" git remote set-url origin "https://x-access-token:${{ secrets.MOKOGIT_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
- name: Merge dev into feature branches - name: Merge dev into feature branches
run: | run: |
@@ -3,10 +3,10 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: MokoGIT.Workflow
# INGROUP: MokoStandards.Security # INGROUP: MokoCLI.Security
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/MokoStandards-API # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /templates/workflows/gitleaks.yml.template # PATH: /.mokogit/workflows/gitleaks.yml
# VERSION: 01.00.00 # VERSION: 01.00.00
# BRIEF: Secret scanning — detect leaked credentials, API keys, and tokens # BRIEF: Secret scanning — detect leaked credentials, API keys, and tokens
# #
@@ -34,7 +34,7 @@ permissions:
env: env:
NTFY_URL: ${{ vars.NTFY_URL || 'https://ntfy.mokoconsulting.tech' }} NTFY_URL: ${{ vars.NTFY_URL || 'https://ntfy.mokoconsulting.tech' }}
NTFY_TOPIC: ${{ vars.NTFY_TOPIC || 'gitea-security' }} NTFY_TOPIC: ${{ vars.NTFY_TOPIC || 'git-security' }}
jobs: jobs:
gitleaks: gitleaks:
@@ -3,9 +3,9 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: MokoGIT.Workflow
# INGROUP: mokocli.Automation # INGROUP: MokoCLI.Automation
# VERSION: 09.40.00 # VERSION: 01.00.00
# BRIEF: Auto-create feature branch when an issue is opened # BRIEF: Auto-create feature branch when an issue is opened
name: "Universal: Issue Branch" name: "Universal: Issue Branch"
@@ -19,7 +19,7 @@ permissions:
issues: write issues: write
env: env:
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} MOKOGIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
jobs: jobs:
create-branch: create-branch:
@@ -28,8 +28,8 @@ jobs:
steps: steps:
- name: Create branch and comment - name: Create branch and comment
run: | run: |
TOKEN="${{ secrets.GA_TOKEN }}" TOKEN="${{ secrets.MOKOGIT_TOKEN }}"
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}" API="${MOKOGIT_URL}/api/v1/repos/${{ github.repository }}"
ISSUE_NUM="${{ github.event.issue.number }}" ISSUE_NUM="${{ github.event.issue.number }}"
ISSUE_TITLE="${{ github.event.issue.title }}" ISSUE_TITLE="${{ github.event.issue.title }}"
@@ -58,7 +58,7 @@ jobs:
echo "Created branch: ${BRANCH}" echo "Created branch: ${BRANCH}"
# Comment on issue with branch link # Comment on issue with branch link
REPO_URL="${GITEA_URL}/${{ github.repository }}" REPO_URL="${MOKOGIT_URL}/${{ github.repository }}"
BODY="Branch created: [\`${BRANCH}\`](${REPO_URL}/src/branch/${BRANCH})\n\n\`\`\`bash\ngit fetch origin\ngit checkout ${BRANCH}\n\`\`\`" BODY="Branch created: [\`${BRANCH}\`](${REPO_URL}/src/branch/${BRANCH})\n\n\`\`\`bash\ngit fetch origin\ngit checkout ${BRANCH}\n\`\`\`"
curl -sf -X POST \ curl -sf -X POST \
@@ -3,10 +3,10 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: MokoGIT.Workflow
# INGROUP: MokoStandards.Notifications # INGROUP: MokoCLI.Notifications
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards # REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.gitea/workflows/notify.yml # PATH: /.mokogit/workflows/notify.yml
# VERSION: 01.00.00 # VERSION: 01.00.00
# BRIEF: Push notifications via ntfy on release success or workflow failure # BRIEF: Push notifications via ntfy on release success or workflow failure
@@ -15,9 +15,9 @@ name: "Universal: Notifications"
on: on:
workflow_run: workflow_run:
workflows: workflows:
- "Joomla Build & Release" - "Universal: Build & Release"
- "Joomla Extension CI" - "Joomla: Extension CI"
- "Deploy" - "Generic: Project CI"
types: types:
- completed - completed
@@ -26,7 +26,7 @@ permissions:
env: env:
NTFY_URL: ${{ vars.NTFY_URL || 'https://ntfy.mokoconsulting.tech' }} NTFY_URL: ${{ vars.NTFY_URL || 'https://ntfy.mokoconsulting.tech' }}
NTFY_TOPIC: ${{ vars.NTFY_TOPIC || 'gitea-releases' }} NTFY_TOPIC: ${{ vars.NTFY_TOPIC || 'git-releases' }}
jobs: jobs:
notify: notify:
@@ -46,13 +46,13 @@ jobs:
WORKFLOW="${{ github.event.workflow_run.name }}" WORKFLOW="${{ github.event.workflow_run.name }}"
URL="${{ github.event.workflow_run.html_url }}" URL="${{ github.event.workflow_run.html_url }}"
curl -sS \ curl -sS --retry 3 --retry-connrefused --retry-delay 2 --max-time 20 \
-H "Title: ${REPO} released" \ -H "Title: ${REPO} released" \
-H "Tags: white_check_mark,package" \ -H "Tags: white_check_mark,package" \
-H "Priority: default" \ -H "Priority: default" \
-H "Click: ${URL}" \ -H "Click: ${URL}" \
-d "${WORKFLOW} completed successfully." \ -d "${WORKFLOW} completed successfully." \
"${NTFY_URL}/${NTFY_TOPIC}" "${NTFY_URL}/${NTFY_TOPIC}" || echo "::warning::ntfy notification could not be delivered (non-fatal)"
- name: Notify on failure - name: Notify on failure
if: github.event.workflow_run.conclusion == 'failure' if: github.event.workflow_run.conclusion == 'failure'
@@ -61,10 +61,10 @@ jobs:
WORKFLOW="${{ github.event.workflow_run.name }}" WORKFLOW="${{ github.event.workflow_run.name }}"
URL="${{ github.event.workflow_run.html_url }}" URL="${{ github.event.workflow_run.html_url }}"
curl -sS \ curl -sS --retry 3 --retry-connrefused --retry-delay 2 --max-time 20 \
-H "Title: ${REPO} workflow failed" \ -H "Title: ${REPO} workflow failed" \
-H "Tags: x,warning" \ -H "Tags: x,warning" \
-H "Priority: high" \ -H "Priority: high" \
-H "Click: ${URL}" \ -H "Click: ${URL}" \
-d "${WORKFLOW} failed. Check the run for details." \ -d "${WORKFLOW} failed. Check the run for details." \
"${NTFY_URL}/${NTFY_TOPIC}" "${NTFY_URL}/${NTFY_TOPIC}" || echo "::warning::ntfy notification could not be delivered (non-fatal)"
@@ -3,10 +3,10 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: MokoGIT.Workflow
# INGROUP: moko-platform.CI # INGROUP: MokoCLI.CI
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/moko-platform # REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
# PATH: /templates/workflows/universal/pr-check.yml.template # PATH: /.mokogit/workflows/pr-check.yml
# VERSION: 09.23.00 # VERSION: 09.23.00
# BRIEF: PR gate — branch policy + code validation before merge # BRIEF: PR gate — branch policy + code validation before merge
@@ -47,15 +47,15 @@ jobs:
fi fi
;; ;;
fix/*|bugfix/*) fix/*|bugfix/*)
if [ "$BASE" != "dev" ]; then if [ "$BASE" != "dev" ] && [ "$BASE" != "main" ]; then
ALLOWED=false ALLOWED=false
REASON="Fix branches must target 'dev', not '${BASE}'" REASON="Fix branches must target 'dev' or 'main', not '${BASE}'"
fi fi
;; ;;
patch/*) patch/*)
if [ "$BASE" != "dev" ] && [ "$BASE" != "rc" ]; then if [ "$BASE" != "dev" ] && [ "$BASE" != "rc" ] && [ "$BASE" != "main" ]; then
ALLOWED=false ALLOWED=false
REASON="Patch branches must target 'dev' or 'rc', not '${BASE}'" REASON="Patch branches must target 'dev', 'rc', or 'main', not '${BASE}'"
fi fi
;; ;;
hotfix/*) hotfix/*)
@@ -86,7 +86,8 @@ jobs:
echo "" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY
echo "### Allowed merge paths:" >> $GITHUB_STEP_SUMMARY echo "### Allowed merge paths:" >> $GITHUB_STEP_SUMMARY
echo "- \`feature/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY echo "- \`feature/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY
echo "- \`fix/*\` → \`dev\`" >> $GITHUB_STEP_SUMMARY echo "- \`fix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY
echo "- \`patch/*\` → \`dev\`, \`rc\`, or \`main\`" >> $GITHUB_STEP_SUMMARY
echo "- \`hotfix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY echo "- \`hotfix/*\` → \`dev\` or \`main\`" >> $GITHUB_STEP_SUMMARY
echo "- \`dev\` → \`main\`" >> $GITHUB_STEP_SUMMARY echo "- \`dev\` → \`main\`" >> $GITHUB_STEP_SUMMARY
echo "- \`rc/*\` → \`main\`" >> $GITHUB_STEP_SUMMARY echo "- \`rc/*\` → \`main\`" >> $GITHUB_STEP_SUMMARY
@@ -96,6 +97,80 @@ jobs:
echo "Branch policy: OK (${HEAD} → ${BASE})" echo "Branch policy: OK (${HEAD} → ${BASE})"
echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY echo "## Branch Policy: Passed" >> $GITHUB_STEP_SUMMARY
# ── Docs Update Gate (main PRs) ─────────────────────────────────────────
require-docs:
name: Require Docs Update
runs-on: ubuntu-latest
# Enforce only on PRs merging into main: README.md + CHANGELOG.md must both be updated.
if: ${{ github.base_ref == 'main' }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Require README.md and CHANGELOG.md in the PR diff
run: |
BASE="${{ github.event.pull_request.base.sha }}"
HEAD="${{ github.event.pull_request.head.sha }}"
CHANGED="$(git diff --name-only "$BASE" "$HEAD" 2>/dev/null || true)"
if [ -z "$CHANGED" ]; then
git fetch -q origin "${{ github.base_ref }}" 2>/dev/null || true
CHANGED="$(git diff --name-only "origin/${{ github.base_ref }}...HEAD" 2>/dev/null || true)"
fi
echo "Changed files in PR:"
echo "$CHANGED"
MISSING=""
echo "$CHANGED" | grep -qxE 'README\.md' || MISSING="README.md"
echo "$CHANGED" | grep -qxE 'CHANGELOG\.md' || MISSING="${MISSING:+$MISSING, }CHANGELOG.md"
if [ -n "$MISSING" ]; then
echo "::error::PRs into main must update: ${MISSING}"
{
echo "## Docs Update Required"
echo ""
echo "PRs merging into \`main\` must update both **README.md** and **CHANGELOG.md**."
echo ""
echo "Not updated in this PR: **${MISSING}**"
} >> "$GITHUB_STEP_SUMMARY"
exit 1
fi
echo "Docs update present (README.md + CHANGELOG.md)"
echo "## Docs Update: Passed" >> "$GITHUB_STEP_SUMMARY"
# ── Wiki Update Reminder (main PRs, non-blocking) ───────────────────────
wiki-reminder:
name: Wiki Update Reminder
runs-on: ubuntu-latest
if: ${{ github.base_ref == 'main' }}
steps:
- name: Remind to update the wiki
env:
TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
SERVER: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
REPO: ${{ github.repository }}
PR: ${{ github.event.pull_request.number }}
run: |
set -uo pipefail
{
echo "## Wiki Update Reminder"
echo ""
echo "Docs are **wiki-first** at MokoConsulting. If this change affects behavior, usage, configuration, or standards, update the repo wiki:"
echo ""
echo "- ${SERVER}/${REPO}/wiki"
echo ""
echo "_Non-blocking reminder._"
} >> "$GITHUB_STEP_SUMMARY"
# Post a single PR comment (idempotent via hidden marker); best-effort, never fails.
API="${SERVER}/api/v1/repos/${REPO}/issues/${PR}/comments"
if [ -n "${TOKEN:-}" ] && [ -n "${PR:-}" ]; then
existing="$(curl -sf -H "Authorization: token ${TOKEN}" "$API" 2>/dev/null | grep -c 'wiki-reminder' || true)"
if [ "${existing:-0}" -eq 0 ]; then
curl -sf -H "Authorization: token ${TOKEN}" -H "Content-Type: application/json" -X POST "$API" \
-d '{"body":"<!-- wiki-reminder -->\n\n**Wiki reminder:** docs are wiki-first -- if this PR changes behavior, usage, config, or standards, please update the repo wiki before/after merge. _(non-blocking)_"}' >/dev/null 2>&1 || true
fi
fi
echo "Wiki reminder emitted (non-blocking)."
# ── Secret Scanning ────────────────────────────────────────────────── # ── Secret Scanning ──────────────────────────────────────────────────
gitleaks: gitleaks:
name: Secret Scan name: Secret Scan
@@ -126,6 +201,8 @@ jobs:
validate: validate:
name: Validate PR name: Validate PR
runs-on: ubuntu-latest runs-on: ubuntu-latest
# Skip on template repos (Template-*) — no real manifest/source/changelog to validate.
if: ${{ !startsWith(github.event.repository.name, 'Template-') }}
steps: steps:
- name: Checkout - name: Checkout
@@ -133,7 +210,7 @@ jobs:
- name: Check for merge conflict markers - name: Check for merge conflict markers
run: | run: |
CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true) CONFLICTS=$(grep -rn '<<<<<<< \|>>>>>>> \|^=======$' --exclude-dir='.git' --exclude-dir='.mokogit' --include='*.php' --include='*.xml' --include='*.css' --include='*.js' --include='*.json' --include='*.md' --include='*.yml' --include='*.yaml' --include='*.ini' --include='*.txt' . 2>/dev/null | grep -v '.git/' || true)
if [ -n "$CONFLICTS" ]; then if [ -n "$CONFLICTS" ]; then
echo "::error::Merge conflict markers found in source files" echo "::error::Merge conflict markers found in source files"
echo "## Conflict Markers Found" >> $GITHUB_STEP_SUMMARY echo "## Conflict Markers Found" >> $GITHUB_STEP_SUMMARY
@@ -147,11 +224,12 @@ jobs:
- name: Detect platform - name: Detect platform
id: platform id: platform
run: | run: |
# Read platform from XML manifest (<platform> tag) or plain text fallback # Platform comes from the MokoGIT metadata API (public GET).
PLATFORM=$(sed -n 's/.*<platform>\([^<]*\)<\/platform>.*/\1/p' .mokogitea/manifest.xml 2>/dev/null | head -1) API="${GITHUB_SERVER_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${GITHUB_REPOSITORY}/metadata"
[ -z "$PLATFORM" ] && PLATFORM=$(cat .mokogitea/manifest.xml 2>/dev/null | tr -d '[:space:]') PLATFORM="$(curl -sf "$API" 2>/dev/null | python3 -c "import sys, json; print(json.load(sys.stdin).get('platform') or '')" 2>/dev/null || true)"
[ -z "$PLATFORM" ] && PLATFORM="generic" [ -z "$PLATFORM" ] && PLATFORM="generic"
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT" echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
echo "Detected platform: $PLATFORM"
- name: Setup PHP - name: Setup PHP
if: steps.platform.outputs.platform == 'joomla' || steps.platform.outputs.platform == 'dolibarr' if: steps.platform.outputs.platform == 'joomla' || steps.platform.outputs.platform == 'dolibarr'
@@ -180,8 +258,9 @@ jobs:
while IFS= read -r -d '' file; do while IFS= read -r -d '' file; do
# Skip vendor, node_modules, and index.html stub files # Skip vendor, node_modules, and index.html stub files
case "$file" in ./vendor/*|./node_modules/*) continue ;; esac case "$file" in ./vendor/*|./node_modules/*) continue ;; esac
# Check first 10 lines for JEXEC or JPATH guard # Scan the whole file for the JEXEC/JPATH guard: it is placed after
if ! head -20 "$file" | grep -qE "defined\s*\(\s*['\"](_JEXEC|JPATH_BASE|\\\\JPATH_PLATFORM)['\"]"; then # the SPDX/file-header docblock, which commonly runs past 20 lines.
if ! grep -qE "defined\s*\(\s*['\"](_JEXEC|JPATH_BASE|\\\\JPATH_PLATFORM)['\"]" "$file"; then
echo "::error file=${file}::Missing JEXEC guard: ${file}" echo "::error file=${file}::Missing JEXEC guard: ${file}"
ERRORS=$((ERRORS + 1)) ERRORS=$((ERRORS + 1))
fi fi
@@ -272,7 +351,7 @@ jobs:
joomla) joomla)
MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1) MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
if [ -z "$MANIFEST" ]; then if [ -z "$MANIFEST" ]; then
echo "::warning::No Joomla manifest found (WaaS site)" echo "::warning::No Joomla manifest found (MokoSuite site)"
exit 0 exit 0
fi fi
echo "Manifest: ${MANIFEST}" echo "Manifest: ${MANIFEST}"
@@ -282,10 +361,10 @@ jobs:
for ELEMENT in name version description; do for ELEMENT in name version description; do
grep -q "<${ELEMENT}>" "$MANIFEST" || { echo "::error::Missing <${ELEMENT}> in manifest"; exit 1; } grep -q "<${ELEMENT}>" "$MANIFEST" || { echo "::error::Missing <${ELEMENT}> in manifest"; exit 1; }
done done
# Block legacy raw/branch update server URLs on MokoGitea # Block legacy raw/branch update server URLs on MokoGIT
RAW_URLS=$(grep -n 'raw/branch' "$MANIFEST" | grep -i 'mokoconsulting\|mokogitea\|git\.mokoconsulting\.tech' || true) RAW_URLS=$(grep -n 'raw/branch' "$MANIFEST" | grep -i 'mokoconsulting\|mokogit\|git\.mokoconsulting\.tech' || true)
if [ -n "$RAW_URLS" ]; then if [ -n "$RAW_URLS" ]; then
echo "::error::Manifest contains legacy raw/branch update server URL on MokoGitea. Use the Gitea Pages URL instead (e.g. /{REPO}/updates.xml not /{REPO}/raw/branch/main/updates.xml)" echo "::error::Manifest contains legacy raw/branch update server URL on MokoGIT. Use the MokoGIT Pages URL instead (e.g. /{REPO}/updates.xml not /{REPO}/raw/branch/main/updates.xml)"
echo "$RAW_URLS" echo "$RAW_URLS"
exit 1 exit 1
fi fi
@@ -492,43 +571,33 @@ jobs:
name: Build RC Package name: Build RC Package
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [branch-policy, validate] needs: [branch-policy, validate]
# Run only when both gates succeeded; always() forces evaluation so a skipped
# validate (e.g. template repos) skips this job cleanly instead of hanging.
if: ${{ always() && needs.branch-policy.result == 'success' && needs.validate.result == 'success' }}
steps: steps:
- name: Trigger RC pre-release - name: Trigger RC pre-release
env: env:
GA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} MOKOGIT_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
REPO: ${{ github.repository }} REPO: ${{ github.repository }}
BRANCH: ${{ github.head_ref }} BRANCH: ${{ github.head_ref }}
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} MOKOGIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
run: | run: |
curl -s -X POST "${GITEA_URL}/api/v1/repos/${REPO}/actions/workflows/pre-release.yml/dispatches" -H "Authorization: token ${GITEA_TOKEN}" -H "Content-Type: application/json" -d "{\"ref\":\"${BRANCH}\",\"inputs\":{\"stability\":\"release-candidate\"}}" curl -s -X POST "${MOKOGIT_URL}/api/v1/repos/${REPO}/actions/workflows/pre-release.yml/dispatches" -H "Authorization: token ${MOKOGIT_TOKEN}" -H "Content-Type: application/json" -d "{\"ref\":\"${BRANCH}\",\"inputs\":{\"stability\":\"release-candidate\"}}"
echo "### Pre-Release" >> $GITHUB_STEP_SUMMARY echo "### Pre-Release" >> $GITHUB_STEP_SUMMARY
echo "Triggered RC build on branch \`${BRANCH}\`" >> $GITHUB_STEP_SUMMARY echo "Triggered RC build on branch \`${BRANCH}\`" >> $GITHUB_STEP_SUMMARY
# ── Issue Reporter ────────────────────────────────────────────────────── # ── Issue Reporter ──────────────────────────────────────────────────────
report-issues: report-issues:
name: Report Issues name: Report Issues
runs-on: ubuntu-latest
needs: [branch-policy, validate] needs: [branch-policy, validate]
if: >- if: >-
always() && always() &&
needs.validate.result == 'failure' needs.validate.result == 'failure'
uses: ./.mokogit/workflows/ci-issue-reporter.yml
steps: with:
- name: Checkout gate: "PR Validation"
uses: actions/checkout@v4 workflow: "PR Check"
with: severity: error
sparse-checkout: automation/ci-issue-reporter.sh details: "PR validation failed (syntax, manifest, changelog, or source checks). See the CI run for the specific check that failed."
sparse-checkout-cone-mode: false secrets: inherit
- name: "File issue for PR validation failure"
env:
GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
run: |
chmod +x automation/ci-issue-reporter.sh
./automation/ci-issue-reporter.sh \
--gate "PR Validation" \
--workflow "PR Check" \
--severity error \
--details "PR validation failed (syntax, manifest, changelog, or source checks). See the CI run for the specific check that failed."
@@ -3,11 +3,11 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: MokoGIT.Workflow
# INGROUP: mokocli.Release # INGROUP: MokoCLI.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli # REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
# PATH: /templates/workflows/universal/pre-release.yml.template # PATH: /.mokogit/workflows/pre-release.yml
# VERSION: 05.01.00 # VERSION: 05.02.01
# BRIEF: Auto pre-release on push to dev/alpha/beta/rc branches # BRIEF: Auto pre-release on push to dev/alpha/beta/rc branches
name: "Universal: Pre-Release" name: "Universal: Pre-Release"
@@ -40,29 +40,38 @@ permissions:
contents: write contents: write
env: env:
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} GIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} GIT_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} GIT_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
jobs: jobs:
build: build:
name: "Build Pre-Release (${{ inputs.stability || github.ref_name }})" name: "Build Pre-Release (${{ inputs.stability || github.ref_name }})"
runs-on: release runs-on: release
# Skip on template repos (Template-*) — they scaffold other repos and do not release.
if: >- if: >-
github.event_name == 'workflow_dispatch' || !startsWith(github.event.repository.name, 'Template-') &&
github.event_name == 'push' (
github.event_name == 'workflow_dispatch' ||
github.event_name == 'push'
)
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
token: ${{ secrets.MOKOGITEA_TOKEN }} token: ${{ secrets.MOKOGIT_TOKEN }}
ref: ${{ github.ref_name }} ref: ${{ github.ref_name }}
submodules: recursive
- name: Setup mokocli tools - name: Update submodules to main
run: |
git submodule foreach --quiet 'git checkout main && git pull --quiet origin main' 2>/dev/null || true
- name: Setup MokoCLI tools
env: env:
MOKO_CLONE_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} MOKO_CLONE_TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting MOKO_CLONE_HOST: git.mokoconsulting.tech/MokoConsulting
run: | run: |
# Use pre-installed /opt/mokocli if available (updated by cron every 6h) # Use pre-installed /opt/mokocli if available (updated by cron every 6h)
@@ -147,24 +156,30 @@ jobs:
fi fi
# Commit version bump # Commit version bump
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" git config --local user.email "mokogit-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]" git config --local user.name "mokogit-actions[bot]"
git remote set-url origin "https://x-access-token:${{ secrets.MOKOGITEA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" git remote set-url origin "https://x-access-token:${{ secrets.MOKOGIT_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git"
git add -A git add -A
git diff --cached --quiet || { git diff --cached --quiet || {
git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]" git commit -m "chore(version): pre-release bump to ${VERSION} [skip ci]"
git push origin HEAD 2>&1 # Push the bump commit, but do NOT fail the release if the target branch
# is protected and the release identity is not on the push allowlist.
# The build proceeds from the in-tree bumped version regardless; if the
# push is rejected, the next run simply re-bumps from the same base.
if ! git push origin HEAD 2>&1; then
echo "::warning::Version-bump commit could not be pushed (protected branch?). Building from in-tree version ${VERSION} anyway."
fi
} }
# Auto-detect element via manifest_element.php # Auto-detect element via manifest_element.php
php ${MOKO_CLI}/manifest_element.php \ php ${MOKO_CLI}/manifest_element.php \
--path . --version "$VERSION" --stability "$STABILITY" \ --path . --version "$VERSION" --stability "$STABILITY" \
--repo "${GITEA_REPO}" --github-output --repo "${GIT_REPO}" --github-output
# Read back element outputs # Read back element outputs
EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) EXT_ELEMENT=$(grep '^ext_element=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2) ZIP_NAME=$(grep '^zip_name=' "$GITHUB_OUTPUT" | tail -1 | cut -d= -f2)
[ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') [ -z "$EXT_ELEMENT" ] && EXT_ELEMENT=$(echo "${GIT_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
[ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip" [ -z "$ZIP_NAME" ] && ZIP_NAME="${EXT_ELEMENT}-${VERSION}.zip"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT" echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
@@ -182,18 +197,18 @@ jobs:
run: | run: |
TAG="${{ steps.meta.outputs.tag }}" TAG="${{ steps.meta.outputs.tag }}"
VERSION="${{ steps.meta.outputs.version }}" VERSION="${{ steps.meta.outputs.version }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API_BASE="${GIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
php ${MOKO_CLI}/release_create.php \ php ${MOKO_CLI}/release_create.php \
--path . --version "$VERSION" --tag "$TAG" \ --path . --version "$VERSION" --tag "$TAG" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ --token "${{ secrets.MOKOGIT_TOKEN }}" --api-base "$API_BASE" \
--repo "${GITEA_REPO}" --branch "${{ github.ref_name }}" --prerelease --repo "${GIT_REPO}" --branch "${{ github.ref_name }}" --prerelease
- name: Update release notes from CHANGELOG.md - name: Update release notes from CHANGELOG.md
if: steps.eligibility.outputs.proceed == 'true' if: steps.eligibility.outputs.proceed == 'true'
run: | run: |
TAG="${{ steps.meta.outputs.tag }}" TAG="${{ steps.meta.outputs.tag }}"
VERSION="${{ steps.meta.outputs.version }}" VERSION="${{ steps.meta.outputs.version }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API_BASE="${GIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
# Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading) # Extract [Unreleased] section from changelog (everything between [Unreleased] and next ## heading)
if [ -f "CHANGELOG.md" ]; then if [ -f "CHANGELOG.md" ]; then
@@ -204,7 +219,7 @@ jobs:
fi fi
# Update release body via API # Update release body via API
RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGITEA_TOKEN }}" \ RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.MOKOGIT_TOKEN }}" \
"${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true) "${API_BASE}/releases/tags/${TAG}" | python3 -c "import json,sys; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
if [ -n "$RELEASE_ID" ]; then if [ -n "$RELEASE_ID" ]; then
@@ -216,7 +231,7 @@ jobs:
'${API_BASE}/releases/${RELEASE_ID}', '${API_BASE}/releases/${RELEASE_ID}',
data=payload, method='PATCH', data=payload, method='PATCH',
headers={ headers={
'Authorization': 'token ${{ secrets.MOKOGITEA_TOKEN }}', 'Authorization': 'token ${{ secrets.MOKOGIT_TOKEN }}',
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}) })
urllib.request.urlopen(req) urllib.request.urlopen(req)
@@ -230,21 +245,21 @@ jobs:
run: | run: |
VERSION="${{ steps.meta.outputs.version }}" VERSION="${{ steps.meta.outputs.version }}"
TAG="${{ steps.meta.outputs.tag }}" TAG="${{ steps.meta.outputs.tag }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API_BASE="${GIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
php ${MOKO_CLI}/release_package.php \ php ${MOKO_CLI}/release_package.php \
--path . --version "$VERSION" --tag "$TAG" \ --path . --version "$VERSION" --tag "$TAG" \
--token "${{ secrets.MOKOGITEA_TOKEN }}" --api-base "$API_BASE" \ --token "${{ secrets.MOKOGIT_TOKEN }}" --api-base "$API_BASE" \
--repo "${GITEA_REPO}" --output /tmp || true --repo "${GIT_REPO}" --output /tmp || true
# updates.xml is generated dynamically by MokoGitea license server # updates.xml is generated dynamically by MokoGIT license server
# No need to build, commit, or sync updates.xml from workflows # No need to build, commit, or sync updates.xml from workflows
- name: "Delete lesser pre-release channels (cascade)" - name: "Delete lesser pre-release channels (cascade)"
if: steps.eligibility.outputs.proceed == 'true' if: steps.eligibility.outputs.proceed == 'true'
continue-on-error: true continue-on-error: true
run: | run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API_BASE="${GIT_URL}/api/v1/repos/${GIT_ORG}/${GIT_REPO}"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}" TOKEN="${{ secrets.MOKOGIT_TOKEN }}"
php ${MOKO_CLI}/release_cascade.php \ php ${MOKO_CLI}/release_cascade.php \
--stability "${{ steps.meta.outputs.stability }}" \ --stability "${{ steps.meta.outputs.stability }}" \
@@ -3,10 +3,10 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: MokoGIT.Workflow
# INGROUP: mokocli.Universal # INGROUP: MokoCLI.Universal
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli # REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
# PATH: /.mokogitea/workflows/rc-revert.yml # PATH: /.mokogit/workflows/rc-revert.yml
# VERSION: 09.23.00 # VERSION: 09.23.00
# BRIEF: Rename rc/ branch back to dev/ when PR is closed without merge # BRIEF: Rename rc/ branch back to dev/ when PR is closed without merge
@@ -25,16 +25,25 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: >- if: >-
github.event.pull_request.merged == false && github.event.pull_request.merged == false &&
startsWith(github.event.pull_request.head.ref, 'rc/') startsWith(github.event.pull_request.head.ref, 'rc/') &&
!startsWith(github.event.repository.name, 'Template-')
steps: steps:
- name: Rename branch - name: Rename branch
env:
BRANCH: ${{ github.event.pull_request.head.ref }}
REPO: ${{ github.repository }}
GIT_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
TOKEN: ${{ secrets.MOKOGIT_TOKEN }}
run: | run: |
BRANCH="${{ github.event.pull_request.head.ref }}" set -euo pipefail
# BRANCH is attacker-controlled (PR head ref). Strict allowlist before ANY use.
if ! printf '%s' "$BRANCH" | grep -Eq '^rc/[A-Za-z0-9._/-]+$'; then
echo "::error::Refusing unsafe branch name: $BRANCH"; exit 1
fi
SUFFIX="${BRANCH#rc/}" SUFFIX="${BRANCH#rc/}"
DEV_BRANCH="dev/${SUFFIX}" DEV_BRANCH="dev/${SUFFIX}"
API="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}/api/v1/repos/${{ github.repository }}/branches" API="${GIT_URL}/api/v1/repos/${REPO}/branches"
TOKEN="${{ secrets.MOKOGITEA_TOKEN }}"
# Create dev/ branch from rc/ branch # Create dev/ branch from rc/ branch
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X POST \ STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X POST \
@@ -42,25 +51,22 @@ jobs:
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d "{\"new_branch_name\": \"${DEV_BRANCH}\", \"old_branch_name\": \"${BRANCH}\"}" \ -d "{\"new_branch_name\": \"${DEV_BRANCH}\", \"old_branch_name\": \"${BRANCH}\"}" \
"${API}" 2>/dev/null || true) "${API}" 2>/dev/null || true)
if [ "$STATUS" = "201" ]; then if [ "$STATUS" = "201" ]; then
echo "Created branch: ${DEV_BRANCH}" >> $GITHUB_STEP_SUMMARY echo "Created branch: ${DEV_BRANCH}" >> "$GITHUB_STEP_SUMMARY"
else else
echo "::error::Failed to create ${DEV_BRANCH} from ${BRANCH} (HTTP ${STATUS})" echo "::error::Failed to create ${DEV_BRANCH} from ${BRANCH} (HTTP ${STATUS})"; exit 1
exit 1
fi fi
# Delete rc/ branch # Read BRANCH from the environment inside PHP (getenv, no string interpolation -> no PHP injection)
ENCODED=$(php -r "echo rawurlencode('${BRANCH}');") ENCODED=$(php -r 'echo rawurlencode(getenv("BRANCH"));')
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \ STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X DELETE \
-H "Authorization: token ${TOKEN}" \ -H "Authorization: token ${TOKEN}" \
"${API}/${ENCODED}" 2>/dev/null || true) "${API}/${ENCODED}" 2>/dev/null || true)
if [ "$STATUS" = "204" ]; then if [ "$STATUS" = "204" ]; then
echo "Deleted branch: ${BRANCH}" >> $GITHUB_STEP_SUMMARY echo "Deleted branch: ${BRANCH}" >> "$GITHUB_STEP_SUMMARY"
else else
echo "::warning::Failed to delete ${BRANCH} (HTTP ${STATUS})" echo "::warning::Failed to delete ${BRANCH} (HTTP ${STATUS})"
fi fi
echo "### RC Reverted" >> $GITHUB_STEP_SUMMARY echo "### RC Reverted" >> "$GITHUB_STEP_SUMMARY"
echo "${BRANCH} → ${DEV_BRANCH}" >> $GITHUB_STEP_SUMMARY echo "${BRANCH} → ${DEV_BRANCH}" >> "$GITHUB_STEP_SUMMARY"
@@ -6,10 +6,10 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow # DEFGROUP: MokoGIT.Workflow
# INGROUP: mokocli.Validation # INGROUP: MokoCLI.Validation
# REPO: https://git.mokoconsulting.tech/mokoconsulting-tech/mokocli # REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Generic
# PATH: /templates/workflows/joomla/repo_health.yml.template # PATH: /.mokogit/workflows/repo-health.yml
# VERSION: 09.23.00 # VERSION: 09.23.00
# BRIEF: Enforces repository guardrails by validating scripts governance, tooling availability, and core repository health artifacts. # BRIEF: Enforces repository guardrails by validating scripts governance, tooling availability, and core repository health artifacts.
# ============================================================================ # ============================================================================
@@ -45,7 +45,7 @@ env:
SCRIPTS_ALLOWED_DIRS: scripts,scripts/fix,scripts/lib,scripts/release,scripts/run,scripts/validate SCRIPTS_ALLOWED_DIRS: scripts,scripts/fix,scripts/lib,scripts/release,scripts/run,scripts/validate
# Repo health policy # Repo health policy
REPO_REQUIRED_ARTIFACTS: README.md,LICENSE,CHANGELOG.md,CONTRIBUTING.md,CODE_OF_CONDUCT.md,.mokogitea/workflows/ REPO_REQUIRED_ARTIFACTS: README.md,LICENSE,CHANGELOG.md,CONTRIBUTING.md,CODE_OF_CONDUCT.md,.mokogit/workflows/
REPO_OPTIONAL_FILES: SECURITY.md,GOVERNANCE.md,.editorconfig,.gitattributes,.gitignore,README.md,docs/ REPO_OPTIONAL_FILES: SECURITY.md,GOVERNANCE.md,.editorconfig,.gitattributes,.gitignore,README.md,docs/
REPO_DISALLOWED_DIRS: REPO_DISALLOWED_DIRS:
REPO_DISALLOWED_FILES: TODO.md,todo.md REPO_DISALLOWED_FILES: TODO.md,todo.md
@@ -56,7 +56,7 @@ env:
# File / directory variables # File / directory variables
DOCS_INDEX: docs/docs-index.md DOCS_INDEX: docs/docs-index.md
SCRIPT_DIR: scripts SCRIPT_DIR: scripts
WORKFLOWS_DIR: .mokogitea/workflows WORKFLOWS_DIR: .mokogit/workflows
SHELLCHECK_PATTERN: '*.sh' SHELLCHECK_PATTERN: '*.sh'
SPDX_FILE_GLOBS: '*.sh,*.php,*.js,*.ts,*.css,*.xml,*.yml,*.yaml' SPDX_FILE_GLOBS: '*.sh,*.php,*.js,*.ts,*.css,*.xml,*.yml,*.yaml'
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
@@ -77,7 +77,7 @@ jobs:
- name: Check actor permission (admin only) - name: Check actor permission (admin only)
id: perm id: perm
env: env:
TOKEN: ${{ secrets.MOKOGITEA_TOKEN || secrets.MOKOGITEA_TOKEN || github.token }} TOKEN: ${{ secrets.MOKOGIT_TOKEN || github.token }}
REPO: ${{ github.repository }} REPO: ${{ github.repository }}
ACTOR: ${{ github.actor }} ACTOR: ${{ github.actor }}
run: | run: |
@@ -88,14 +88,14 @@ jobs:
# Hardcoded authorized users — always allowed # Hardcoded authorized users — always allowed
case "$ACTOR" in case "$ACTOR" in
jmiller|gitea-actions[bot]) jmiller|mokogit-actions[bot])
ALLOWED=true ALLOWED=true
PERMISSION=admin PERMISSION=admin
METHOD="hardcoded allowlist" METHOD="hardcoded allowlist"
;; ;;
*) *)
# Detect platform and check permissions via API # Detect platform and check permissions via API
API_BASE="${GITHUB_API_URL:-${GITEA_API_URL:-https://api.github.com}}" API_BASE="${GITHUB_API_URL:-${GIT_API_URL:-https://api.github.com}}"
RESP=$(curl -sf -H "Authorization: token ${TOKEN}" \ RESP=$(curl -sf -H "Authorization: token ${TOKEN}" \
"${API_BASE}/repos/${REPO}/collaborators/${ACTOR}/permission" 2>/dev/null || echo '{}') "${API_BASE}/repos/${REPO}/collaborators/${ACTOR}/permission" 2>/dev/null || echo '{}')
PERMISSION=$(echo "$RESP" | grep -oP '"permission"\s*:\s*"\K[^"]+' || echo "unknown") PERMISSION=$(echo "$RESP" | grep -oP '"permission"\s*:\s*"\K[^"]+' || echo "unknown")
@@ -605,7 +605,7 @@ jobs:
printf '%s\n' '| Domain | Status | Notes |' printf '%s\n' '| Domain | Status | Notes |'
printf '%s\n' '|---|---|---|' printf '%s\n' '|---|---|---|'
printf '%s\n' '| Access control | OK | Admin-only execution gate |' printf '%s\n' '| Access control | OK | Admin-only execution gate |'
printf '%s\n' '| Release policy | N/A | Releases handled by MokoGitea |' printf '%s\n' '| Release policy | N/A | Releases handled by MokoGIT |'
printf '%s\n' '| Scripts governance | OK | Directory policy and advisory reporting |' printf '%s\n' '| Scripts governance | OK | Directory policy and advisory reporting |'
printf '%s\n' '| Repo required artifacts | OK | Required, optional, disallowed enforcement |' printf '%s\n' '| Repo required artifacts | OK | Required, optional, disallowed enforcement |'
printf '%s\n' '| Repo content heuristics | OK | Brand, license, changelog structure |' printf '%s\n' '| Repo content heuristics | OK | Brand, license, changelog structure |'
@@ -671,42 +671,30 @@ jobs:
# ═══════════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════════
# Issue Reporter — file issues for failed gates # Issue Reporter — file issues for failed gates
# ═══════════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════════
report-issues: report-scripts:
name: "Report Issues" name: "Report: Scripts Governance"
runs-on: ubuntu-latest needs: [access_check, scripts_governance]
needs: [access_check, scripts_governance, repo_health]
if: >- if: >-
always() && always() &&
(needs.scripts_governance.result == 'failure' || needs.scripts_governance.result == 'failure'
needs.repo_health.result == 'failure') uses: ./.mokogit/workflows/ci-issue-reporter.yml
with:
gate: "Scripts Governance"
workflow: "Repo Health"
severity: error
details: "Scripts directory policy violations detected. Review required and allowed directories."
secrets: inherit
steps: report-health:
- name: Checkout name: "Report: Repository Health"
uses: actions/checkout@v4 needs: [access_check, repo_health]
with: if: >-
sparse-checkout: automation/ci-issue-reporter.sh always() &&
sparse-checkout-cone-mode: false needs.repo_health.result == 'failure'
uses: ./.mokogit/workflows/ci-issue-reporter.yml
- name: "File issues for failed gates" with:
env: gate: "Repository Health"
GITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }} workflow: "Repo Health"
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} severity: error
run: | details: "Repository health checks failed — missing required artifacts, disallowed files, or content warnings. Check the CI run summary."
chmod +x automation/ci-issue-reporter.sh secrets: inherit
REPORTER="./automation/ci-issue-reporter.sh"
WF="Repo Health"
report_gate() {
local gate="$1" result="$2" details="$3"
if [ "$result" = "failure" ]; then
"$REPORTER" --gate "$gate" --details "$details" --workflow "$WF" --severity error
fi
}
report_gate "Scripts Governance" \
"${{ needs.scripts_governance.result }}" \
"Scripts directory policy violations detected. Review required and allowed directories."
report_gate "Repository Health" \
"${{ needs.repo_health.result }}" \
"Repository health checks failed — missing required artifacts, disallowed files, or content warnings. Check the CI run summary."
File diff suppressed because it is too large Load Diff
@@ -3,10 +3,10 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
# #
# FILE INFORMATION # FILE INFORMATION
# DEFGROUP: Gitea.Workflow.Template # DEFGROUP: MokoGIT.Workflow.Template
# INGROUP: MokoStandards.CI # INGROUP: MokoCLI.CI
# REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla # REPO: https://git.mokoconsulting.tech/MokoConsulting/Template-Joomla
# PATH: /.mokogitea/workflows/version-set.yml # PATH: /.mokogit/workflows/version-set.yml
# VERSION: 01.00.00 # VERSION: 01.00.00
# BRIEF: Set or reset the extension version across all version-bearing files # BRIEF: Set or reset the extension version across all version-bearing files
@@ -34,6 +34,7 @@ jobs:
set-version: set-version:
name: Set Version to ${{ inputs.version }} name: Set Version to ${{ inputs.version }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ !startsWith(github.event.repository.name, 'Template-') }}
steps: steps:
- name: Validate version format - name: Validate version format
@@ -48,7 +49,7 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
token: ${{ secrets.MOKOGITEA_TOKEN || secrets.GA_TOKEN || github.token }} token: ${{ secrets.MOKOGIT_TOKEN || github.token }}
ref: ${{ inputs.branch || github.ref }} ref: ${{ inputs.branch || github.ref }}
fetch-depth: 1 fetch-depth: 1
-10
View File
@@ -1,10 +0,0 @@
# DISABLED — auto-release Step 11 recreates dev from main after every release.
# Cascade-dev is redundant and causes version conflicts when both main and dev
# have different version numbers in templateDetails.xml / manifest.xml.
name: "Cascade Main → Dev (DISABLED)"
on: workflow_dispatch
jobs:
noop:
runs-on: ubuntu-latest
steps:
- run: echo "Cascade disabled — auto-release handles dev recreation"
-126
View File
@@ -1,126 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: MokoStandards.Deploy
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoStandards-API
# PATH: /templates/workflows/joomla/deploy-manual.yml.template
# VERSION: 04.07.00
# BRIEF: Manual SFTP deploy to dev server for Joomla repos
name: "Universal: Deploy to Dev (Manual)"
on:
workflow_dispatch:
inputs:
clear_remote:
description: 'Delete all remote files before uploading'
required: false
default: 'false'
type: boolean
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
permissions:
contents: read
jobs:
deploy:
name: SFTP Deploy to Dev
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
- name: Setup PHP
run: |
php -v && composer --version
- name: Setup MokoStandards tools
env:
GA_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
MOKO_CLONE_TOKEN: ${{ secrets.GA_TOKEN || secrets.GA_TOKEN || github.token }}
MOKO_CLONE_HOST: ${{ secrets.GA_TOKEN && 'git.mokoconsulting.tech/MokoConsulting' || 'github.com/mokoconsulting-tech' }}
COMPOSER_AUTH: '{"github-oauth":{"github.com":"${{ secrets.GA_TOKEN || github.token }}"}}'
run: |
git clone --depth 1 --branch main --quiet \
"https://x-access-token:${MOKO_CLONE_TOKEN}@${MOKO_CLONE_HOST}/MokoStandards-API.git" \
/tmp/mokostandards-api 2>/dev/null || true
if [ -d "/tmp/mokostandards-api" ] && [ -f "/tmp/mokostandards-api/composer.json" ]; then
cd /tmp/mokostandards-api && composer install --no-dev --no-interaction --quiet 2>/dev/null || true
fi
- name: Check FTP configuration
id: check
env:
HOST: ${{ vars.DEV_FTP_HOST }}
PATH_VAR: ${{ vars.DEV_FTP_PATH }}
PORT: ${{ vars.DEV_FTP_PORT }}
run: |
if [ -z "$HOST" ] || [ -z "$PATH_VAR" ]; then
echo "DEV_FTP_HOST or DEV_FTP_PATH not configured -- cannot deploy"
echo "skip=true" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "skip=false" >> "$GITHUB_OUTPUT"
echo "host=$HOST" >> "$GITHUB_OUTPUT"
REMOTE="${PATH_VAR%/}"
echo "remote=$REMOTE" >> "$GITHUB_OUTPUT"
[ -z "$PORT" ] && PORT="22"
echo "port=$PORT" >> "$GITHUB_OUTPUT"
- name: Deploy via SFTP
if: steps.check.outputs.skip != 'true'
env:
SFTP_KEY: ${{ secrets.DEV_FTP_KEY }}
SFTP_PASS: ${{ secrets.DEV_FTP_PASSWORD }}
SFTP_USER: ${{ vars.DEV_FTP_USERNAME }}
run: |
SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
[ ! -d "$SOURCE_DIR" ] && { echo "No src/ or htdocs/ -- nothing to deploy"; exit 0; }
printf '{"host":"%s","port":%s,"username":"%s","remotePath":"%s"' \
"${{ steps.check.outputs.host }}" "${{ steps.check.outputs.port }}" "$SFTP_USER" "${{ steps.check.outputs.remote }}" \
> /tmp/sftp-config.json
if [ -n "$SFTP_KEY" ]; then
echo "$SFTP_KEY" > /tmp/deploy_key
chmod 600 /tmp/deploy_key
printf ',"privateKeyPath":"/tmp/deploy_key"}' >> /tmp/sftp-config.json
else
printf ',"password":"%s"}' "$SFTP_PASS" >> /tmp/sftp-config.json
fi
DEPLOY_ARGS=(--path . --src-dir "$SOURCE_DIR" --config /tmp/sftp-config.json)
[ "${{ inputs.clear_remote }}" = "true" ] && DEPLOY_ARGS+=(--clear-remote)
PLATFORM=$(php /tmp/mokostandards-api/cli/platform_detect.php --path . 2>/dev/null || true)
if [ "$PLATFORM" = "waas-component" ] && [ -f "/tmp/mokostandards-api/deploy/deploy-joomla.php" ]; then
php /tmp/mokostandards-api/deploy/deploy-joomla.php "${DEPLOY_ARGS[@]}"
else
php /tmp/mokostandards-api/deploy/deploy-sftp.php "${DEPLOY_ARGS[@]}"
fi
rm -f /tmp/deploy_key /tmp/sftp-config.json
- name: Summary
if: always()
run: |
if [ "${{ steps.check.outputs.skip }}" = "true" ]; then
echo "### Deploy Skipped -- FTP not configured" >> $GITHUB_STEP_SUMMARY
else
echo "### Manual Dev Deploy Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Host | \`${{ steps.check.outputs.host }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Remote | \`${{ steps.check.outputs.remote }}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Clear | ${{ inputs.clear_remote }} |" >> $GITHUB_STEP_SUMMARY
fi
@@ -1,73 +0,0 @@
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Gitea.Workflow
# INGROUP: mokocli.Universal
# REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
# PATH: /.mokogitea/workflows/workflow-sync-trigger.yml
# VERSION: 01.01.00
# BRIEF: Trigger workflow sync to live repos when a PR is merged to main
name: "Universal: Workflow Sync Trigger"
on:
pull_request:
types: [closed]
branches:
- main
env:
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
sync:
name: Sync workflows to live repos
runs-on: ubuntu-latest
if: >-
github.event.pull_request.merged == true &&
!contains(github.event.pull_request.title, '[skip sync]')
steps:
- name: Determine platform from repo name
id: platform
run: |
REPO="${{ github.event.repository.name }}"
case "$REPO" in
Template-Joomla) PLATFORM="joomla" ;;
Template-Dolibarr) PLATFORM="dolibarr" ;;
Template-Go) PLATFORM="go" ;;
Template-MCP) PLATFORM="mcp" ;;
Template-Generic) PLATFORM="" ;;
*) PLATFORM="" ;;
esac
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
echo "Platform: ${PLATFORM:-all}"
- name: Clone mokocli
env:
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
run: |
GITEA_URL="${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}"
git clone --depth 1 "${GITEA_URL}/MokoConsulting/mokocli.git" /tmp/mokocli
- name: Install dependencies
run: |
cd /tmp/mokocli
composer install --no-dev --no-interaction --quiet 2>/dev/null || true
- name: Run workflow sync
env:
MOKOGITEA_TOKEN: ${{ secrets.MOKOGITEA_TOKEN }}
run: |
ARGS="--token ${MOKOGITEA_TOKEN}"
ARGS="${ARGS} --org ${{ vars.GITEA_ORG || github.repository_owner }}"
ARGS="${ARGS} --phase repos"
PLATFORM="${{ steps.platform.outputs.platform }}"
if [ -n "$PLATFORM" ]; then
ARGS="${ARGS} --platform-filter ${PLATFORM}"
fi
php /tmp/mokocli/cli/workflow_sync.php ${ARGS}
+11 -6
View File
@@ -4,7 +4,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
FILE INFORMATION FILE INFORMATION
DEFGROUP: MokoCli.Root DEFGROUP: MokoCli.Root
INGROUP: MokoCli INGROUP: MokoCli
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokoplatform REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
PATH: /CHANGELOG.md PATH: /CHANGELOG.md
BRIEF: Release changelog BRIEF: Release changelog
--> -->
@@ -12,12 +12,17 @@ BRIEF: Release changelog
# Changelog # Changelog
## [Unreleased] ## [Unreleased]
## [09.40.00] --- 2026-06-25 ## [09.43.00] --- 2026-07-05
## [09.39.00] --- 2026-06-25 ## [09.43.00] --- 2026-07-05
## [09.39.00] --- 2026-06-25 ## [09.42.00] --- 2026-07-04
## [09.39.00] --- 2026-06-23 ## [09.42.00] --- 2026-07-04
## [09.39.00] --- 2026-06-23 ### Fixed
- branch_rename.php: force-update the target branch ref in place instead of delete+recreate, so `moko repo:rename-branch` works when the target (e.g. `rc`) is a protected branch that cannot be deleted — previously the delete silently failed and the recreate returned HTTP 409
## [09.42.00] --- 2026-07-04
## [09.42.00] --- 2026-07-04
+2 -2
View File
@@ -34,7 +34,7 @@ feature/* ──PR──> dev ──draft PR──> (renamed to rc) ──merge
7. **Merging to main** triggers the stable release pipeline: 7. **Merging to main** triggers the stable release pipeline:
- Minor version bump (e.g., `02.09.xx` → `02.10.00`) - Minor version bump (e.g., `02.09.xx` → `02.10.00`)
- Stability suffix stripped (clean version) - Stability suffix stripped (clean version)
- Gitea release created with ZIP/tar.gz packages - MokoGIT release created with ZIP/tar.gz packages
- `updates.xml` updated (Joomla extensions) - `updates.xml` updated (Joomla extensions)
- `dev` branch recreated from `main` - `dev` branch recreated from `main`
@@ -119,7 +119,7 @@ This ensures Joomla sites on ANY stability channel see the update (Joomla only s
The version tools update all files containing version stamps: The version tools update all files containing version stamps:
- `.mokogitea/manifest.xml` (canonical source) - `.mokogit/manifest.xml` (canonical source)
- Joomla XML manifests (`<version>` tag) - Joomla XML manifests (`<version>` tag)
- `README.md`, `CHANGELOG.md` (`VERSION:` pattern) - `README.md`, `CHANGELOG.md` (`VERSION:` pattern)
- `package.json`, `pyproject.toml` - `package.json`, `pyproject.toml`
+2 -2
View File
@@ -2,8 +2,8 @@
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech> Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later SPDX-License-Identifier: GPL-3.0-or-later
FILE INFORMATION FILE INFORMATION
DEFGROUP: MokoPlatform.Root DEFGROUP: MokoCLI.Root
INGROUP: MokoPlatform INGROUP: MokoCLI
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
PATH: /PLUGIN_SCRIPTS.md PATH: /PLUGIN_SCRIPTS.md
BRIEF: Plugin system CLI documentation BRIEF: Plugin system CLI documentation
+10 -10
View File
@@ -2,11 +2,11 @@
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech> Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later SPDX-License-Identifier: GPL-3.0-or-later
FILE INFORMATION FILE INFORMATION
DEFGROUP: MokoPlatform.Root DEFGROUP: MokoCLI.Root
INGROUP: MokoPlatform INGROUP: MokoCLI
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
PATH: /README.md PATH: /README.md
VERSION: 09.40.00 VERSION: 09.43.00
BRIEF: Project overview and documentation BRIEF: Project overview and documentation
--> -->
@@ -16,7 +16,7 @@ BRIEF: Project overview and documentation
PHP implementation of mokocli — enterprise standards, automation framework, workflow templates, and bulk sync tooling. PHP implementation of mokocli — enterprise standards, automation framework, workflow templates, and bulk sync tooling.
> **Primary platform**: [Gitea — git.mokoconsulting.tech](https://git.mokoconsulting.tech/MokoConsulting/MokoCli-API) > **Primary platform**: [MokoGIT — git.mokoconsulting.tech](https://git.mokoconsulting.tech/MokoConsulting/MokoCli-API)
> **Backup mirror**: [GitHub](https://github.com/MokoConsulting/MokoCli-API) *(read-only mirror)* > **Backup mirror**: [GitHub](https://github.com/MokoConsulting/MokoCli-API) *(read-only mirror)*
## What Lives Here ## What Lives Here
@@ -25,7 +25,7 @@ PHP implementation of mokocli — enterprise standards, automation framework, wo
|-----------|---------| |-----------|---------|
| `lib/Enterprise/` | 38 PHP enterprise library classes (platform adapters, sync, validation, plugins) | | `lib/Enterprise/` | 38 PHP enterprise library classes (platform adapters, sync, validation, plugins) |
| `cli/` | CLI scripts (archive, create, release, sync rulesets, version management) | | `cli/` | CLI scripts (archive, create, release, sync rulesets, version management) |
| `automation/` | Bulk sync, push files, repo cleanup, Gitea migration | | `automation/` | Bulk sync, push files, repo cleanup, MokoGIT migration |
| `validate/` | 18 validation scripts (health, structure, secrets, syntax, drift) | | `validate/` | 18 validation scripts (health, structure, secrets, syntax, drift) |
| `templates/` | **Workflow templates** and config templates synced to governed repos | | `templates/` | **Workflow templates** and config templates synced to governed repos |
| `definitions/` | Repository structure definitions (`.tf` format) | | `definitions/` | Repository structure definitions (`.tf` format) |
@@ -42,7 +42,7 @@ composer require mokoconsulting-tech/enterprise
### Composer Registry ### Composer Registry
This package is served from Gitea package registry. Add this to your `composer.json`: This package is served from MokoGIT package registry. Add this to your `composer.json`:
```json ```json
{ {
@@ -72,11 +72,11 @@ vendor/bin/moko inventory -- --path .
| Variable | Purpose | | Variable | Purpose |
|----------|---------| |----------|---------|
| `GIT_PLATFORM` | `gitea` (default) or `github` | | `GIT_PLATFORM` | `git` (default) or `github` |
| `GA_TOKEN` | Gitea API / Gitea Actions token | | `GA_TOKEN` | MokoGIT API / MokoGIT Actions token |
| `GH_TOKEN` | GitHub API token (for mirror sync) | | `GH_TOKEN` | GitHub API token (for mirror sync) |
| `GITEA_URL` | Gitea instance URL (default: `https://git.mokoconsulting.tech`) | | `GIT_URL` | MokoGIT instance URL (default: `https://git.mokoconsulting.tech`) |
| `GITEA_ORG` | Gitea organization (default: `MokoConsulting`) | | `GIT_ORG` | MokoGIT organization (default: `MokoConsulting`) |
## License ## License
+2 -2
View File
@@ -2,8 +2,8 @@
Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech> Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
SPDX-License-Identifier: GPL-3.0-or-later SPDX-License-Identifier: GPL-3.0-or-later
FILE INFORMATION FILE INFORMATION
DEFGROUP: MokoPlatform.Index DEFGROUP: MokoCLI.Index
INGROUP: MokoPlatform.Analysis INGROUP: MokoCLI.Analysis
REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
PATH: /analysis/index.md PATH: /analysis/index.md
BRIEF: Analysis directory index BRIEF: Analysis directory index
+2 -2
View File
@@ -21,7 +21,7 @@
* *
* COMMANDS (run `php bin/moko list` for the full list — 97 commands) * COMMANDS (run `php bin/moko list` for the full list — 97 commands)
* *
* Automation sync, automation:cleanup, automation:migrate-gitea * Automation sync, automation:cleanup, automation:migrate-git
* Validation health, detect, drift, check:syntax, check:version, ... * Validation health, detect, drift, check:syntax, check:version, ...
* Release release, release:joomla, release:create, release:publish, ... * Release release, release:joomla, release:create, release:publish, ...
* Version version:read, version:bump, version:auto-bump, ... * Version version:read, version:bump, version:auto-bump, ...
@@ -91,7 +91,7 @@ const COMMAND_MAP = [
'sync' => 'automation/bulk_sync.php', 'sync' => 'automation/bulk_sync.php',
'deps:update' => 'automation/update_dependencies.php', 'deps:update' => 'automation/update_dependencies.php',
'automation:cleanup' => 'automation/repo_cleanup.php', 'automation:cleanup' => 'automation/repo_cleanup.php',
'automation:migrate-gitea' => 'automation/migrate_to_gitea.php', 'automation:migrate-git' => 'automation/migrate_to_git.php',
// Maintenance // Maintenance
'inventory' => 'maintenance/update_repo_inventory.php', 'inventory' => 'maintenance/update_repo_inventory.php',
+2 -2
View File
@@ -14,8 +14,8 @@
* (at your option) any later version. * (at your option) any later version.
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.Enterprise.CLI * DEFGROUP: MokoCLI.Enterprise.CLI
* INGROUP: MokoPlatform.Enterprise * INGROUP: MokoCLI.Enterprise
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/audit_query.php * PATH: /cli/audit_query.php
* BRIEF: Search, filter, and export audit logs * BRIEF: Search, filter, and export audit logs
+1 -1
View File
@@ -25,7 +25,7 @@ if (isset($options['help']) || empty($options['token'])) {
echo "Usage: php cli/branch_protect_org.php --token TOKEN [--org ORG] [--api-base URL] [--dry-run]\n"; echo "Usage: php cli/branch_protect_org.php --token TOKEN [--org ORG] [--api-base URL] [--dry-run]\n";
echo "\n"; echo "\n";
echo "Options:\n"; echo "Options:\n";
echo " --token Gitea API token (required)\n"; echo " --token Git API token (required)\n";
echo " --org Organization name (default: MokoConsulting)\n"; echo " --org Organization name (default: MokoConsulting)\n";
echo " --api-base API base URL (default: https://git.mokoconsulting.tech/api/v1)\n"; echo " --api-base API base URL (default: https://git.mokoconsulting.tech/api/v1)\n";
echo " --dry-run Show what would be changed without making changes\n"; echo " --dry-run Show what would be changed without making changes\n";
+39 -19
View File
@@ -10,8 +10,8 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/branch_rename.php * PATH: /cli/branch_rename.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Rename a git branch via Gitea API (create new, update PR, delete old) * BRIEF: Rename a git branch via Git API (create new, update PR, delete old)
*/ */
declare(strict_types=1); declare(strict_types=1);
@@ -24,7 +24,7 @@ class BranchRenameCli extends CliFramework
{ {
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Rename a git branch via Gitea API (create new, update PR, delete old)'); $this->setDescription('Rename a git branch via Git API (create new, update PR, delete old)');
$this->addArgument('--from', 'Source branch name', ''); $this->addArgument('--from', 'Source branch name', '');
$this->addArgument('--to', 'Target branch name', ''); $this->addArgument('--to', 'Target branch name', '');
$this->addArgument('--token', 'API token', ''); $this->addArgument('--token', 'API token', '');
@@ -64,26 +64,46 @@ class BranchRenameCli extends CliFramework
return 1; return 1;
} }
// Step 2: Delete target branch if it already exists // Step 2: Point the target branch at the source commit.
// If the target already exists (e.g. a protected `rc` branch that cannot be
// deleted), force-update its ref in place instead of delete+recreate: deleting a
// protected branch fails, which then makes the recreate return HTTP 409.
$sourceSha = '';
if (isset($check['body']['commit']['id']) && is_string($check['body']['commit']['id'])) {
$sourceSha = $check['body']['commit']['id'];
}
$targetCheck = $this->apiRequest('GET', "{$apiBase}/branches/{$to}", $headers); $targetCheck = $this->apiRequest('GET', "{$apiBase}/branches/{$to}", $headers);
if ($targetCheck['code'] === 200) { if ($targetCheck['code'] === 200) {
echo "Target branch '{$to}' already exists — deleting\n"; echo "Target branch '{$to}' already exists - force-updating to {$from}
";
if (!$this->dryRun) { if (!$this->dryRun) {
$this->apiRequest('DELETE', "{$apiBase}/branches/{$to}", $headers); if ($sourceSha === '') {
$this->log('ERROR', "Cannot resolve HEAD commit of source '{$from}'");
return 1;
}
$ref = $this->apiRequest('PATCH', "{$apiBase}/git/refs/heads/{$to}", $headers, [
'sha' => $sourceSha,
'force' => true,
]);
if ($ref['code'] < 200 || $ref['code'] >= 300) {
$this->log('ERROR', "Failed to force-update '{$to}': HTTP {$ref['code']} (needs force-push perm)");
$this->log('ERROR', json_encode($ref['body']));
return 1;
}
} }
} } else {
echo "Creating branch: {$to} (from {$from})
// Step 3: Create new branch from source ";
echo "Creating branch: {$to} (from {$from})\n"; if (!$this->dryRun) {
if (!$this->dryRun) { $create = $this->apiRequest('POST', "{$apiBase}/branches", $headers, [
$create = $this->apiRequest('POST', "{$apiBase}/branches", $headers, [ 'new_branch_name' => $to,
'new_branch_name' => $to, 'old_branch_name' => $from,
'old_branch_name' => $from, ]);
]); if ($create['code'] < 200 || $create['code'] >= 300) {
if ($create['code'] < 200 || $create['code'] >= 300) { $this->log('ERROR', "Failed to create branch '{$to}': HTTP {$create['code']}");
$this->log('ERROR', "Failed to create branch '{$to}': HTTP {$create['code']}"); $this->log('ERROR', json_encode($create['body']));
$this->log('ERROR', json_encode($create['body'])); return 1;
return 1; }
} }
} }
+19 -19
View File
@@ -12,8 +12,8 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/bulk_workflow_push.php * PATH: /cli/bulk_workflow_push.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Push a workflow file to all governed repos via the Gitea Contents API * BRIEF: Push a workflow file to all governed repos via the Git Contents API
*/ */
declare(strict_types=1); declare(strict_types=1);
@@ -31,18 +31,18 @@ class BulkWorkflowPushCli extends CliFramework
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Push a workflow file to all governed repos via the Gitea Contents API'); $this->setDescription('Push a workflow file to all governed repos via the Git Contents API');
$this->addArgument('--gitea-url', 'Gitea URL (default: https://git.mokoconsulting.tech)', 'https://git.mokoconsulting.tech'); $this->addArgument('--git-url', 'Git URL (default: https://git.mokoconsulting.tech)', 'https://git.mokoconsulting.tech');
$this->addArgument('--token', 'Gitea API token', ''); $this->addArgument('--token', 'Git API token', '');
$this->addArgument('--org', 'Target organization', ''); $this->addArgument('--org', 'Target organization', '');
$this->addArgument('--file', 'Local workflow file to push', ''); $this->addArgument('--file', 'Local workflow file to push', '');
$this->addArgument('--dest', 'Destination path in repos (default: .mokogitea/workflows/<filename>)', ''); $this->addArgument('--dest', 'Destination path in repos (default: .mokogit/workflows/<filename>)', '');
$this->addArgument('--branch', 'Target branch (default: main)', 'main'); $this->addArgument('--branch', 'Target branch (default: main)', 'main');
} }
protected function run(): int protected function run(): int
{ {
$giteaUrl = rtrim($this->getArgument('--gitea-url'), '/'); $gitUrl = rtrim($this->getArgument('--git-url'), '/');
$token = $this->getArgument('--token'); $token = $this->getArgument('--token');
$org = $this->getArgument('--org'); $org = $this->getArgument('--org');
$workflowFile = $this->getArgument('--file'); $workflowFile = $this->getArgument('--file');
@@ -70,7 +70,7 @@ class BulkWorkflowPushCli extends CliFramework
} }
if ($destPath === '') { if ($destPath === '') {
$destPath = '.mokogitea/workflows/' . basename($workflowFile); $destPath = '.mokogit/workflows/' . basename($workflowFile);
} }
$localContent = file_get_contents($workflowFile); $localContent = file_get_contents($workflowFile);
@@ -82,7 +82,7 @@ class BulkWorkflowPushCli extends CliFramework
$this->log('INFO', "Pushing: {$workflowFile}"); $this->log('INFO', "Pushing: {$workflowFile}");
$this->log('INFO', " -> {$destPath} (branch: {$branch})"); $this->log('INFO', " -> {$destPath} (branch: {$branch})");
$this->log('INFO', " -> Org: {$org} @ {$giteaUrl}"); $this->log('INFO', " -> Org: {$org} @ {$gitUrl}");
if ($this->dryRun) { if ($this->dryRun) {
$this->log('INFO', '[DRY RUN] No changes will be made.'); $this->log('INFO', '[DRY RUN] No changes will be made.');
@@ -90,7 +90,7 @@ class BulkWorkflowPushCli extends CliFramework
echo "\n"; echo "\n";
$repos = $this->fetchOrgRepos($giteaUrl, $token, $org); $repos = $this->fetchOrgRepos($gitUrl, $token, $org);
if ($repos === null) { if ($repos === null) {
return 1; return 1;
@@ -104,7 +104,7 @@ class BulkWorkflowPushCli extends CliFramework
$encodedContent = base64_encode($localContent); $encodedContent = base64_encode($localContent);
foreach ($repos as $repo) { foreach ($repos as $repo) {
$this->pushToRepo($giteaUrl, $token, $repo, $encodedContent, $localContent, $destPath, $branch); $this->pushToRepo($gitUrl, $token, $repo, $encodedContent, $localContent, $destPath, $branch);
} }
echo "\n"; echo "\n";
@@ -115,7 +115,7 @@ class BulkWorkflowPushCli extends CliFramework
} }
private function pushToRepo( private function pushToRepo(
string $giteaUrl, string $gitUrl,
string $token, string $token,
string $repoFullName, string $repoFullName,
string $encodedContent, string $encodedContent,
@@ -126,7 +126,7 @@ class BulkWorkflowPushCli extends CliFramework
[$owner, $repoName] = explode('/', $repoFullName, 2); [$owner, $repoName] = explode('/', $repoFullName, 2);
$existing = $this->apiRequest( $existing = $this->apiRequest(
$giteaUrl, $gitUrl,
$token, $token,
'GET', 'GET',
"/api/v1/repos/{$owner}/{$repoName}/contents/" "/api/v1/repos/{$owner}/{$repoName}/contents/"
@@ -159,7 +159,7 @@ class BulkWorkflowPushCli extends CliFramework
]); ]);
$response = $this->apiRequest( $response = $this->apiRequest(
$giteaUrl, $gitUrl,
$token, $token,
'PUT', 'PUT',
"/api/v1/repos/{$owner}/{$repoName}/contents/" "/api/v1/repos/{$owner}/{$repoName}/contents/"
@@ -189,7 +189,7 @@ class BulkWorkflowPushCli extends CliFramework
]); ]);
$response = $this->apiRequest( $response = $this->apiRequest(
$giteaUrl, $gitUrl,
$token, $token,
'POST', 'POST',
"/api/v1/repos/{$owner}/{$repoName}/contents/" "/api/v1/repos/{$owner}/{$repoName}/contents/"
@@ -210,7 +210,7 @@ class BulkWorkflowPushCli extends CliFramework
} }
} }
private function fetchOrgRepos(string $giteaUrl, string $token, string $org): ?array private function fetchOrgRepos(string $gitUrl, string $token, string $org): ?array
{ {
$this->log('INFO', "Fetching repos from org: {$org}"); $this->log('INFO', "Fetching repos from org: {$org}");
@@ -219,7 +219,7 @@ class BulkWorkflowPushCli extends CliFramework
while (true) { while (true) {
$response = $this->apiRequest( $response = $this->apiRequest(
$giteaUrl, $gitUrl,
$token, $token,
'GET', 'GET',
"/api/v1/orgs/{$org}/repos?" "/api/v1/orgs/{$org}/repos?"
@@ -261,13 +261,13 @@ class BulkWorkflowPushCli extends CliFramework
} }
private function apiRequest( private function apiRequest(
string $giteaUrl, string $gitUrl,
string $token, string $token,
string $method, string $method,
string $endpoint, string $endpoint,
?string $body = null ?string $body = null
): array { ): array {
$url = $giteaUrl . $endpoint; $url = $gitUrl . $endpoint;
$ch = curl_init(); $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_URL, $url);
+7 -7
View File
@@ -12,7 +12,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/bulk_workflow_trigger.php * PATH: /cli/bulk_workflow_trigger.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Trigger a workflow across multiple repos at once * BRIEF: Trigger a workflow across multiple repos at once
*/ */
@@ -24,7 +24,7 @@ use MokoCli\CliFramework;
class BulkWorkflowTriggerCli extends CliFramework class BulkWorkflowTriggerCli extends CliFramework
{ {
private string $giteaUrl = 'https://git.mokoconsulting.tech'; private string $gitUrl = 'https://git.mokoconsulting.tech';
private string $token = ''; private string $token = '';
private string $reposFile = ''; private string $reposFile = '';
private string $org = ''; private string $org = '';
@@ -35,8 +35,8 @@ class BulkWorkflowTriggerCli extends CliFramework
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Trigger a workflow across multiple repos at once'); $this->setDescription('Trigger a workflow across multiple repos at once');
$this->addArgument('--gitea-url', 'Gitea URL (default: https://git.mokoconsulting.tech)', 'https://git.mokoconsulting.tech'); $this->addArgument('--git-url', 'Git URL (default: https://git.mokoconsulting.tech)', 'https://git.mokoconsulting.tech');
$this->addArgument('--token', 'Gitea API token', ''); $this->addArgument('--token', 'Git API token', '');
$this->addArgument('--repos', 'File with newline-separated owner/repo list', ''); $this->addArgument('--repos', 'File with newline-separated owner/repo list', '');
$this->addArgument('--org', 'Trigger on all repos in an org', ''); $this->addArgument('--org', 'Trigger on all repos in an org', '');
$this->addArgument('--workflow', 'Workflow file (e.g., "sync-servers.yml")', ''); $this->addArgument('--workflow', 'Workflow file (e.g., "sync-servers.yml")', '');
@@ -46,7 +46,7 @@ class BulkWorkflowTriggerCli extends CliFramework
protected function run(): int protected function run(): int
{ {
$this->giteaUrl = rtrim($this->getArgument('--gitea-url'), '/'); $this->gitUrl = rtrim($this->getArgument('--git-url'), '/');
$this->token = $this->getArgument('--token'); $this->token = $this->getArgument('--token');
$this->reposFile = $this->getArgument('--repos'); $this->reposFile = $this->getArgument('--repos');
$this->org = $this->getArgument('--org'); $this->org = $this->getArgument('--org');
@@ -78,7 +78,7 @@ class BulkWorkflowTriggerCli extends CliFramework
} }
$this->log('INFO', "Triggering workflow \"{$this->workflow}\" on ref \"{$this->ref}\" across " . count($repos) . " repo(s)."); $this->log('INFO', "Triggering workflow \"{$this->workflow}\" on ref \"{$this->ref}\" across " . count($repos) . " repo(s).");
$this->log('INFO', "Gitea URL: {$this->giteaUrl}"); $this->log('INFO', "Git URL: {$this->gitUrl}");
if ($this->dryRun) { if ($this->dryRun) {
$this->log('INFO', '[DRY RUN] No requests will be sent.'); $this->log('INFO', '[DRY RUN] No requests will be sent.');
@@ -209,7 +209,7 @@ class BulkWorkflowTriggerCli extends CliFramework
private function apiRequest(string $method, string $endpoint, ?string $body = null): array private function apiRequest(string $method, string $endpoint, ?string $body = null): array
{ {
$url = $this->giteaUrl . $endpoint; $url = $this->gitUrl . $endpoint;
$ch = curl_init(); $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_URL, $url);
+230
View File
@@ -0,0 +1,230 @@
#!/usr/bin/env bash
# ============================================================================
# Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
#
# SPDX-License-Identifier: GPL-3.0-or-later
#
# FILE INFORMATION
# DEFGROUP: Automation.CI
# INGROUP: mokocli.CLI
# REPO: https://git.mokoconsulting.tech/MokoConsulting/MokoCLI
# PATH: /cli/ci_issue_reporter.sh
# VERSION: 10.00.00
# BRIEF: Creates or updates a Git issue when a CI gate fails.
# Deduplicates by searching open issues with the "ci-auto" label
# whose title matches the gate. If a matching issue exists, a comment
# is appended instead of opening a duplicate.
# ============================================================================
set -euo pipefail
# ── Defaults ────────────────────────────────────────────────────────────────
MOKOGIT_URL="${MOKOGIT_URL:-https://git.mokoconsulting.tech}"
MOKOGIT_TOKEN="${MOKOGIT_TOKEN:-}"
REPO="${GITHUB_REPOSITORY:-}"
RUN_URL="${GITHUB_SERVER_URL:-${MOKOGIT_URL}}/${REPO}/actions/runs/${GITHUB_RUN_ID:-0}"
LABEL_NAME="ci-auto"
LABEL_COLOR="#e11d48"
GATE=""
DETAILS=""
SEVERITY="error"
WORKFLOW=""
# ── Parse arguments ─────────────────────────────────────────────────────────
usage() {
cat <<EOF
Usage: ci_issue_reporter.sh --gate NAME --details TEXT [OPTIONS]
Required:
--gate CI gate name (e.g. "Code Quality", "Self-Health")
--details Human-readable failure description
Optional:
--severity "error" (default) or "warning"
--workflow Workflow name for the issue title
--repo owner/repo (default: \$GITHUB_REPOSITORY)
--run-url URL to the CI run (auto-detected from env)
--token Git API token (default: \$MOKOGIT_TOKEN)
--url Git base URL (default: \$MOKOGIT_URL)
EOF
exit 1
}
while [[ $# -gt 0 ]]; do
case "$1" in
--gate) GATE="$2"; shift 2 ;;
--details) DETAILS="$2"; shift 2 ;;
--severity) SEVERITY="$2"; shift 2 ;;
--workflow) WORKFLOW="$2"; shift 2 ;;
--repo) REPO="$2"; shift 2 ;;
--run-url) RUN_URL="$2"; shift 2 ;;
--token) MOKOGIT_TOKEN="$2"; shift 2 ;;
--url) MOKOGIT_URL="$2"; shift 2 ;;
-h|--help) usage ;;
*) echo "Unknown option: $1"; usage ;;
esac
done
[[ -z "$GATE" ]] && { echo "ERROR: --gate is required"; usage; }
[[ -z "$DETAILS" ]] && { echo "ERROR: --details is required"; usage; }
[[ -z "$MOKOGIT_TOKEN" ]] && { echo "ERROR: MOKOGIT_TOKEN not set"; exit 1; }
[[ -z "$REPO" ]] && { echo "ERROR: GITHUB_REPOSITORY not set"; exit 1; }
API="${MOKOGIT_URL}/api/v1/repos/${REPO}"
# ── Build title ─────────────────────────────────────────────────────────────
if [[ -n "$WORKFLOW" ]]; then
TITLE="[CI] ${WORKFLOW}: ${GATE} failed"
else
TITLE="[CI] ${GATE} failed"
fi
# ── Ensure label exists ─────────────────────────────────────────────────────
ensure_label() {
local exists
exists=$(curl -sf -o /dev/null -w '%{http_code}' \
-H "Authorization: token ${MOKOGIT_TOKEN}" \
"${API}/labels" 2>/dev/null || echo "000")
if [[ "$exists" == "200" ]]; then
local found
found=$(curl -sf \
-H "Authorization: token ${MOKOGIT_TOKEN}" \
"${API}/labels" 2>/dev/null \
| grep -o "\"name\":\"${LABEL_NAME}\"" || true)
if [[ -z "$found" ]]; then
curl -sf -X POST \
-H "Authorization: token ${MOKOGIT_TOKEN}" \
-H "Content-Type: application/json" \
"${API}/labels" \
-d "{\"name\":\"${LABEL_NAME}\",\"color\":\"${LABEL_COLOR}\",\"description\":\"Auto-created by CI issue reporter\"}" \
> /dev/null 2>&1 || true
fi
fi
}
# ── Search for existing open issue ──────────────────────────────────────────
find_existing_issue() {
local query
query=$(printf '%s' "[CI] ${GATE}" | sed 's/ /%20/g; s/\[/%5B/g; s/\]/%5D/g')
local response
response=$(curl -sf \
-H "Authorization: token ${MOKOGIT_TOKEN}" \
"${API}/issues?type=issues&state=open&labels=${LABEL_NAME}&q=${query}&limit=5" \
2>/dev/null || echo "[]")
echo "$response" \
| grep -oP '"number":\s*\K[0-9]+' \
| head -1
}
# ── Build issue body ────────────────────────────────────────────────────────
build_body() {
local severity_badge
if [[ "$SEVERITY" == "error" ]]; then
severity_badge="**Severity:** Error"
else
severity_badge="**Severity:** Warning"
fi
cat <<BODY
## CI Gate Failure: ${GATE}
${severity_badge}
**Workflow:** ${WORKFLOW:-unknown}
**Branch:** ${GITHUB_REF_NAME:-unknown}
**Commit:** \`${GITHUB_SHA:0:8}\`
**Run:** [View CI run](${RUN_URL})
### Details
${DETAILS}
### Resolution
Fix the issue described above and push a new commit. This issue will be closed automatically when the gate passes, or can be closed manually.
---
*Auto-created by [ci-issue-reporter](${MOKOGIT_URL}/MokoConsulting/MokoCLI/src/branch/main/cli/ci_issue_reporter.sh)*
BODY
}
# ── Build comment body (for existing issues) ────────────────────────────────
build_comment() {
cat <<COMMENT
### CI failure recurrence
**Branch:** ${GITHUB_REF_NAME:-unknown}
**Commit:** \`${GITHUB_SHA:0:8}\`
**Run:** [View CI run](${RUN_URL})
${DETAILS}
COMMENT
}
# ── Main ────────────────────────────────────────────────────────────────────
ensure_label
EXISTING=$(find_existing_issue)
if [[ -n "$EXISTING" ]]; then
COMMENT_BODY=$(build_comment)
COMMENT_JSON=$(printf '%s' "$COMMENT_BODY" | python3 -c "
import sys, json
print(json.dumps({'body': sys.stdin.read()}))" 2>/dev/null)
HTTP=$(curl -sf -o /dev/null -w '%{http_code}' -X POST \
-H "Authorization: token ${MOKOGIT_TOKEN}" \
-H "Content-Type: application/json" \
"${API}/issues/${EXISTING}/comments" \
-d "${COMMENT_JSON}" 2>/dev/null || echo "000")
if [[ "$HTTP" == "201" ]]; then
echo "Commented on existing issue #${EXISTING}"
else
echo "WARNING: Failed to comment on issue #${EXISTING} (HTTP ${HTTP})"
fi
else
ISSUE_BODY=$(build_body)
ISSUE_JSON=$(python3 -c "
import sys, json
body = sys.stdin.read()
print(json.dumps({
'title': sys.argv[1],
'body': body,
'labels': []
}))" "$TITLE" <<< "$ISSUE_BODY" 2>/dev/null)
RESPONSE=$(curl -sf -X POST \
-H "Authorization: token ${MOKOGIT_TOKEN}" \
-H "Content-Type: application/json" \
"${API}/issues" \
-d "${ISSUE_JSON}" 2>/dev/null || echo "{}")
ISSUE_NUM=$(echo "$RESPONSE" | grep -oP '"number":\s*\K[0-9]+' | head -1)
if [[ -n "$ISSUE_NUM" ]]; then
LABEL_ID=$(curl -sf \
-H "Authorization: token ${MOKOGIT_TOKEN}" \
"${API}/labels" 2>/dev/null \
| grep -oP "\"id\":\s*\K[0-9]+(?=[^}]*\"name\":\s*\"${LABEL_NAME}\")" \
| head -1 || true)
if [[ -n "$LABEL_ID" ]]; then
curl -sf -X POST \
-H "Authorization: token ${MOKOGIT_TOKEN}" \
-H "Content-Type: application/json" \
"${API}/issues/${ISSUE_NUM}/labels" \
-d "{\"labels\":[${LABEL_ID}]}" \
> /dev/null 2>&1 || true
fi
echo "Created issue #${ISSUE_NUM}: ${TITLE}"
else
echo "WARNING: Failed to create issue"
echo "Response: ${RESPONSE}"
fi
fi
+10 -10
View File
@@ -12,7 +12,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/client_dashboard.php * PATH: /cli/client_dashboard.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Generate unified client dashboard HTML * BRIEF: Generate unified client dashboard HTML
*/ */
@@ -24,7 +24,7 @@ use MokoCli\CliFramework;
class ClientDashboardCli extends CliFramework class ClientDashboardCli extends CliFramework
{ {
private string $giteaUrl = 'https://git.mokoconsulting.tech'; private string $gitUrl = 'https://git.mokoconsulting.tech';
private string $token = ''; private string $token = '';
private string $org = 'MokoConsulting'; private string $org = 'MokoConsulting';
private string $outputFile = ''; private string $outputFile = '';
@@ -36,8 +36,8 @@ class ClientDashboardCli extends CliFramework
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Generate unified client dashboard HTML'); $this->setDescription('Generate unified client dashboard HTML');
$this->addArgument('--token', 'Gitea token (or MOKOGITEA_TOKEN)', ''); $this->addArgument('--token', 'Git token (or MOKOGIT_TOKEN)', '');
$this->addArgument('--gitea-url', 'Gitea URL', 'https://git.mokoconsulting.tech'); $this->addArgument('--git-url', 'Git URL', 'https://git.mokoconsulting.tech');
$this->addArgument('--org', 'Primary org (default: MokoConsulting)', 'MokoConsulting'); $this->addArgument('--org', 'Primary org (default: MokoConsulting)', 'MokoConsulting');
$this->addArgument('--output', 'Output HTML file (default: stdout)', ''); $this->addArgument('--output', 'Output HTML file (default: stdout)', '');
$this->addArgument('-o', 'Output HTML file (alias)', ''); $this->addArgument('-o', 'Output HTML file (alias)', '');
@@ -48,7 +48,7 @@ class ClientDashboardCli extends CliFramework
protected function run(): int protected function run(): int
{ {
$this->giteaUrl = rtrim($this->getArgument('--gitea-url'), '/'); $this->gitUrl = rtrim($this->getArgument('--git-url'), '/');
$this->token = $this->getArgument('--token'); $this->token = $this->getArgument('--token');
$this->org = $this->getArgument('--org'); $this->org = $this->getArgument('--org');
$this->outputFile = $this->getArgument('--output') ?: $this->getArgument('-o'); $this->outputFile = $this->getArgument('--output') ?: $this->getArgument('-o');
@@ -57,11 +57,11 @@ class ClientDashboardCli extends CliFramework
$this->sslWarnDays = (int) $this->getArgument('--ssl-warn-days'); $this->sslWarnDays = (int) $this->getArgument('--ssl-warn-days');
if ($this->token === '') { if ($this->token === '') {
$this->token = getenv('MOKOGITEA_TOKEN') ?: ''; $this->token = getenv('MOKOGIT_TOKEN') ?: '';
} }
if ($this->token === '') { if ($this->token === '') {
$this->log('ERROR', '--token or MOKOGITEA_TOKEN required.'); $this->log('ERROR', '--token or MOKOGIT_TOKEN required.');
return 1; return 1;
} }
@@ -127,7 +127,7 @@ class ClientDashboardCli extends CliFramework
$name = $repo['name'] ?? ''; $name = $repo['name'] ?? '';
if ( if (
!str_starts_with($name, 'client-waas-') !str_starts_with($name, 'client-waas-') && !str_starts_with($name, 'client-mokosuite-')
|| !empty($repo['archived']) || !empty($repo['archived'])
) { ) {
continue; continue;
@@ -135,7 +135,7 @@ class ClientDashboardCli extends CliFramework
$clients[] = [ $clients[] = [
'repo' => $repo['full_name'] ?? '', 'repo' => $repo['full_name'] ?? '',
'name' => str_replace('client-waas-', '', $name), 'name' => str_replace(['client-waas-', 'client-mokosuite-'], '', $name),
'description' => $repo['description'] ?? '', 'description' => $repo['description'] ?? '',
'updated' => $repo['updated_at'] ?? '', 'updated' => $repo['updated_at'] ?? '',
'url' => $repo['html_url'] ?? '', 'url' => $repo['html_url'] ?? '',
@@ -460,7 +460,7 @@ CARD;
/** @return array{code: int, body: string} */ /** @return array{code: int, body: string} */
private function api(string $method, string $endpoint): array private function api(string $method, string $endpoint): array
{ {
$url = $this->giteaUrl . $endpoint; $url = $this->gitUrl . $endpoint;
$ch = curl_init(); $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+8 -8
View File
@@ -12,7 +12,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/client_inventory.php * PATH: /cli/client_inventory.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Discover and list all client-waas repos with their server configuration status * BRIEF: Discover and list all client-waas repos with their server configuration status
*/ */
@@ -24,21 +24,21 @@ use MokoCli\CliFramework;
class ClientInventoryCli extends CliFramework class ClientInventoryCli extends CliFramework
{ {
private string $giteaUrl = 'https://git.mokoconsulting.tech'; private string $gitUrl = 'https://git.mokoconsulting.tech';
private string $token = ''; private string $token = '';
private bool $jsonOutput = false; private bool $jsonOutput = false;
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Discover and list all client-waas repos with their server configuration status'); $this->setDescription('Discover and list all client-waas repos with their server configuration status');
$this->addArgument('--gitea-url', 'Gitea URL (default: https://git.mokoconsulting.tech)', 'https://git.mokoconsulting.tech'); $this->addArgument('--git-url', 'Git URL (default: https://git.mokoconsulting.tech)', 'https://git.mokoconsulting.tech');
$this->addArgument('--token', 'Gitea API token', ''); $this->addArgument('--token', 'Git API token', '');
$this->addArgument('--json', 'Output results as JSON', false); $this->addArgument('--json', 'Output results as JSON', false);
} }
protected function run(): int protected function run(): int
{ {
$this->giteaUrl = rtrim($this->getArgument('--gitea-url'), '/'); $this->gitUrl = rtrim($this->getArgument('--git-url'), '/');
$this->token = $this->getArgument('--token'); $this->token = $this->getArgument('--token');
$this->jsonOutput = (bool) $this->getArgument('--json'); $this->jsonOutput = (bool) $this->getArgument('--json');
@@ -47,7 +47,7 @@ class ClientInventoryCli extends CliFramework
return 1; return 1;
} }
$this->log('INFO', "Scanning Gitea instance: {$this->giteaUrl}"); $this->log('INFO', "Scanning Git instance: {$this->gitUrl}");
// Step 1: List all orgs // Step 1: List all orgs
$orgs = $this->fetchOrgs(); $orgs = $this->fetchOrgs();
@@ -79,7 +79,7 @@ class ClientInventoryCli extends CliFramework
foreach ($repos as $repo) { foreach ($repos as $repo) {
$repoName = $repo['name'] ?? ''; $repoName = $repo['name'] ?? '';
if (strpos($repoName, 'client-waas') === false) { if (strpos($repoName, 'client-waas') === false && strpos($repoName, 'client-mokosuite') === false) {
continue; continue;
} }
@@ -241,7 +241,7 @@ class ClientInventoryCli extends CliFramework
private function apiRequest(string $method, string $endpoint, ?string $body = null): array private function apiRequest(string $method, string $endpoint, ?string $body = null): array
{ {
$url = $this->giteaUrl . $endpoint; $url = $this->gitUrl . $endpoint;
$ch = curl_init(); $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_URL, $url);
+23 -23
View File
@@ -12,7 +12,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/client_provision.php * PATH: /cli/client_provision.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Provision a new client environment end-to-end * BRIEF: Provision a new client environment end-to-end
*/ */
@@ -24,8 +24,8 @@ use MokoCli\CliFramework;
class ClientProvisionCli extends CliFramework class ClientProvisionCli extends CliFramework
{ {
private string $giteaUrl = 'https://git.mokoconsulting.tech'; private string $gitUrl = 'https://git.mokoconsulting.tech';
private string $giteaToken = ''; private string $gitToken = '';
private string $grafanaUrl = ''; private string $grafanaUrl = '';
private string $grafanaToken = ''; private string $grafanaToken = '';
private string $configFile = ''; private string $configFile = '';
@@ -65,17 +65,17 @@ class ClientProvisionCli extends CliFramework
return 1; return 1;
} }
$this->giteaToken = $this->config['gitea_token'] $this->gitToken = $this->config['git_token']
?? getenv('MOKOGITEA_TOKEN') ?: ''; ?? getenv('MOKOGIT_TOKEN') ?: '';
$this->grafanaUrl = $this->config['grafana_url'] $this->grafanaUrl = $this->config['grafana_url']
?? getenv('GRAFANA_URL') ?: ''; ?? getenv('GRAFANA_URL') ?: '';
$this->grafanaToken = $this->config['grafana_token'] $this->grafanaToken = $this->config['grafana_token']
?? getenv('GRAFANA_TOKEN') ?: ''; ?? getenv('GRAFANA_TOKEN') ?: '';
$this->giteaUrl = $this->config['gitea_url'] $this->gitUrl = $this->config['git_url']
?? $this->giteaUrl; ?? $this->gitUrl;
if ($this->giteaToken === '') { if ($this->gitToken === '') {
$this->log('ERROR', 'gitea_token or MOKOGITEA_TOKEN required.'); $this->log('ERROR', 'git_token or MOKOGIT_TOKEN required.');
return 1; return 1;
} }
@@ -87,7 +87,7 @@ class ClientProvisionCli extends CliFramework
return 1; return 1;
} }
$this->repoName = 'client-waas-' . $clientName; $this->repoName = 'client-mokosuite-' . $clientName;
$this->log('INFO', "=== Client Provisioning: {$clientName} ==="); $this->log('INFO', "=== Client Provisioning: {$clientName} ===");
$this->log('INFO', " Org: {$this->org}"); $this->log('INFO', " Org: {$this->org}");
@@ -104,7 +104,7 @@ class ClientProvisionCli extends CliFramework
'variables' => 'setVariables', 'variables' => 'setVariables',
'secrets' => 'setSecrets', 'secrets' => 'setSecrets',
'monitoring' => 'setupMonitoring', 'monitoring' => 'setupMonitoring',
'summary' => 'printSummary', 'summary' => 'printProvisioningSummary',
]; ];
$exitCode = 0; $exitCode = 0;
@@ -128,7 +128,7 @@ class ClientProvisionCli extends CliFramework
{ {
$this->log('INFO', '[1/5] Creating repository...'); $this->log('INFO', '[1/5] Creating repository...');
$check = $this->giteaApi( $check = $this->gitApi(
'GET', 'GET',
"/api/v1/repos/{$this->org}/{$this->repoName}" "/api/v1/repos/{$this->org}/{$this->repoName}"
); );
@@ -153,10 +153,10 @@ class ClientProvisionCli extends CliFramework
'labels' => true, 'labels' => true,
]); ]);
$resp = $this->giteaApi( $resp = $this->gitApi(
'POST', 'POST',
'/api/v1/repos/MokoConsulting/' '/api/v1/repos/MokoConsulting/'
. 'Template-Client-WaaS/generate', . 'Template-Client/generate',
$payload $payload
); );
@@ -167,7 +167,7 @@ class ClientProvisionCli extends CliFramework
$this->log('INFO', ' OK: Repo created'); $this->log('INFO', ' OK: Repo created');
$this->giteaApi( $this->gitApi(
'POST', 'POST',
"/api/v1/repos/{$this->org}/{$this->repoName}/branches", "/api/v1/repos/{$this->org}/{$this->repoName}/branches",
json_encode([ json_encode([
@@ -250,7 +250,7 @@ class ClientProvisionCli extends CliFramework
continue; continue;
} }
$resp = $this->giteaApi( $resp = $this->gitApi(
'PUT', 'PUT',
"{$api}/{$name}", "{$api}/{$name}",
json_encode(['data' => $value]) json_encode(['data' => $value])
@@ -383,7 +383,7 @@ class ClientProvisionCli extends CliFramework
return 0; return 0;
} }
private function printSummary(): int private function printProvisioningSummary(): int
{ {
$vars = $this->config['variables'] ?? []; $vars = $this->config['variables'] ?? [];
$secrets = $this->config['secrets'] ?? []; $secrets = $this->config['secrets'] ?? [];
@@ -391,7 +391,7 @@ class ClientProvisionCli extends CliFramework
echo "\n"; echo "\n";
$this->log('INFO', '[5/5] Provisioning summary'); $this->log('INFO', '[5/5] Provisioning summary');
echo str_repeat('=', 60) . "\n"; echo str_repeat('=', 60) . "\n";
echo " Repo: {$this->giteaUrl}/{$this->org}/{$this->repoName}\n"; echo " Repo: {$this->gitUrl}/{$this->org}/{$this->repoName}\n";
echo ' Variables: ' . count($vars) . " set\n"; echo ' Variables: ' . count($vars) . " set\n";
echo ' Secrets: ' . count($secrets) . " set\n"; echo ' Secrets: ' . count($secrets) . " set\n";
echo "\n"; echo "\n";
@@ -409,14 +409,14 @@ class ClientProvisionCli extends CliFramework
string $name, string $name,
string $value string $value
): bool { ): bool {
$resp = $this->giteaApi( $resp = $this->gitApi(
'PUT', 'PUT',
"{$api}/{$name}", "{$api}/{$name}",
json_encode(['value' => $value]) json_encode(['value' => $value])
); );
if ($resp['code'] === 404) { if ($resp['code'] === 404) {
$resp = $this->giteaApi( $resp = $this->gitApi(
'POST', 'POST',
$api, $api,
json_encode(['name' => $name, 'value' => $value]) json_encode(['name' => $name, 'value' => $value])
@@ -426,15 +426,15 @@ class ClientProvisionCli extends CliFramework
return $resp['code'] >= 200 && $resp['code'] < 300; return $resp['code'] >= 200 && $resp['code'] < 300;
} }
private function giteaApi( private function gitApi(
string $method, string $method,
string $endpoint, string $endpoint,
?string $body = null ?string $body = null
): array { ): array {
return $this->httpRequest( return $this->httpRequest(
$this->giteaUrl . $endpoint, $this->gitUrl . $endpoint,
$method, $method,
"token {$this->giteaToken}", "token {$this->gitToken}",
$body $body
); );
} }
+5 -5
View File
@@ -81,7 +81,7 @@ class CreateProjectCli extends CliFramework
} }
$config = \MokoCli\Config::load(); $config = \MokoCli\Config::load();
$platformName = $config->getString('platform', 'gitea'); $platformName = $config->getString('platform', 'git');
try { try {
$adapter = \MokoCli\PlatformAdapterFactory::create($config); $adapter = \MokoCli\PlatformAdapterFactory::create($config);
$api = $adapter->getApiClient(); $api = $adapter->getApiClient();
@@ -89,8 +89,8 @@ class CreateProjectCli extends CliFramework
$this->log('ERROR', "Platform initialization failed: " . $e->getMessage()); $this->log('ERROR', "Platform initialization failed: " . $e->getMessage());
return 1; return 1;
} }
$token = $platformName === 'gitea' $token = $platformName === 'git'
? $config->getString('gitea.token', '') ? $config->getString('git.token', '')
: $config->getString('github.token', ''); : $config->getString('github.token', '');
$repoRoot = dirname(__DIR__, 2); $repoRoot = dirname(__DIR__, 2);
@@ -173,7 +173,7 @@ class CreateProjectCli extends CliFramework
return $failed > 0 ? 1 : 0; return $failed > 0 ? 1 : 0;
} }
private function graphql(string $query, array $variables, string $token, string $platformName = 'gitea'): array private function graphql(string $query, array $variables, string $token, string $platformName = 'git'): array
{ {
if ($platformName !== 'github') { if ($platformName !== 'github') {
return []; return [];
@@ -223,7 +223,7 @@ class CreateProjectCli extends CliFramework
private function detectRepoPlatform(string $org, string $repo, string $token, ?\MokoCli\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) { foreach (['.github/.mokostandards', '.mokogit/.mokostandards', '.mokostandards'] as $path) {
$data = $this->restGet("repos/{$org}/{$repo}/contents/{$path}", $token, $apiClient); $data = $this->restGet("repos/{$org}/{$repo}/contents/{$path}", $token, $apiClient);
if (!empty($data['content'])) { if (!empty($data['content'])) {
$content = base64_decode($data['content']); $content = base64_decode($data['content']);
+4 -4
View File
@@ -116,14 +116,14 @@ class CreateRepoCli extends CliFramework
echo " (dry-run) would set topics: " . implode(', ', $topics) . "\n"; echo " (dry-run) would set topics: " . implode(', ', $topics) . "\n";
} }
echo "Step 3: Creating .mokogitea/manifest.xml...\n"; echo "Step 3: Creating .mokogit/manifest.xml...\n";
$mokoContent = "platform: {$platform}\nversion: 04.02.30\nmanaged: true\n"; $mokoContent = "platform: {$platform}\nversion: 04.02.30\nmanaged: true\n";
if (!$this->dryRun) { if (!$this->dryRun) {
try { try {
$adapter->createOrUpdateFile( $adapter->createOrUpdateFile(
$org, $org,
$name, $name,
'.mokogitea/manifest.xml', '.mokogit/manifest.xml',
$mokoContent, $mokoContent,
'chore: add manifest.xml platform config [skip ci]' 'chore: add manifest.xml platform config [skip ci]'
); );
@@ -132,11 +132,11 @@ class CreateRepoCli extends CliFramework
echo " Warning: " . $e->getMessage() . "\n"; echo " Warning: " . $e->getMessage() . "\n";
} }
} else { } else {
echo " (dry-run) would create .mokogitea/manifest.xml\n"; echo " (dry-run) would create .mokogit/manifest.xml\n";
} }
echo "Step 4: Creating README.md...\n"; echo "Step 4: Creating README.md...\n";
$baseUrl = $platformName === 'gitea' ? $config->getString('gitea.url', 'https://git.mokoconsulting.tech') : 'https://github.com'; $baseUrl = $platformName === 'git' ? $config->getString('git.url', 'https://git.mokoconsulting.tech') : 'https://github.com';
$repoUrl = "{$baseUrl}/{$org}/{$name}"; $repoUrl = "{$baseUrl}/{$org}/{$name}";
$standardsUrl = "{$baseUrl}/{$org}/MokoCli"; $standardsUrl = "{$baseUrl}/{$org}/MokoCli";
$readmeContent = "<!--\n" $readmeContent = "<!--\n"
+5 -5
View File
@@ -8,8 +8,8 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.CLI * DEFGROUP: MokoCLI.CLI
* INGROUP: MokoPlatform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/deploy_joomla.php * PATH: /cli/deploy_joomla.php
* BRIEF: Smart Joomla deploy — routes files to correct server directories by extension type * BRIEF: Smart Joomla deploy — routes files to correct server directories by extension type
@@ -833,7 +833,7 @@ class DeployJoomla extends CliFramework
/** /**
* Resolve source directory with smart fallback chain: * Resolve source directory with smart fallback chain:
* 1. --src-dir flag (explicit) * 1. --src-dir flag (explicit)
* 2. .mokogitea/manifest.xml <deploy><source-dir> * 2. .mokogit/manifest.xml <deploy><source-dir>
* 3. src/ directory * 3. src/ directory
* 4. htdocs/ directory * 4. htdocs/ directory
* 5. Repo root (for flat-layout extensions) * 5. Repo root (for flat-layout extensions)
@@ -851,8 +851,8 @@ class DeployJoomla extends CliFramework
exit(1); exit(1);
} }
// 2. Read from .mokogitea/manifest.xml // 2. Read from .mokogit/manifest.xml
$mokoManifest = "{$repoPath}/.mokogitea/manifest.xml"; $mokoManifest = "{$repoPath}/.mokogit/manifest.xml";
if (file_exists($mokoManifest)) { if (file_exists($mokoManifest)) {
$xml = @simplexml_load_file($mokoManifest); $xml = @simplexml_load_file($mokoManifest);
if ($xml !== false) { if ($xml !== false) {
+5 -5
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/dev_branch_reset.php * PATH: /cli/dev_branch_reset.php
* BRIEF: Delete and recreate dev branch from main via Gitea API * BRIEF: Delete and recreate dev branch from main via Git API
*/ */
declare(strict_types=1); declare(strict_types=1);
@@ -23,9 +23,9 @@ class DevBranchResetCli extends CliFramework
{ {
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Delete and recreate dev branch from main via Gitea API'); $this->setDescription('Delete and recreate dev branch from main via Git API');
$this->addArgument('--token', 'Gitea API token', ''); $this->addArgument('--token', 'Git API token', '');
$this->addArgument('--api-base', 'Gitea API base URL', ''); $this->addArgument('--api-base', 'Git API base URL', '');
$this->addArgument('--branch', 'Branch to reset', 'dev'); $this->addArgument('--branch', 'Branch to reset', 'dev');
$this->addArgument('--from', 'Source branch', 'main'); $this->addArgument('--from', 'Source branch', 'main');
$this->addArgument('--output-summary', 'Write to $GITHUB_STEP_SUMMARY', false); $this->addArgument('--output-summary', 'Write to $GITHUB_STEP_SUMMARY', false);
@@ -33,7 +33,7 @@ class DevBranchResetCli extends CliFramework
protected function run(): int protected function run(): int
{ {
$token = $this->getArgument('--token') ?: getenv('MOKOGITEA_TOKEN') ?: getenv('GITEA_TOKEN') ?: ''; $token = $this->getArgument('--token') ?: getenv('MOKOGIT_TOKEN') ?: getenv('GIT_TOKEN') ?: '';
$apiBase = $this->getArgument('--api-base'); $apiBase = $this->getArgument('--api-base');
$branch = $this->getArgument('--branch'); $branch = $this->getArgument('--branch');
$from = $this->getArgument('--from'); $from = $this->getArgument('--from');
+1 -1
View File
@@ -12,7 +12,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/grafana_dashboard.php * PATH: /cli/grafana_dashboard.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Manage Grafana dashboards via API * BRIEF: Manage Grafana dashboards via API
*/ */
+1 -1
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/joomla_build.php * PATH: /cli/joomla_build.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Build a Joomla extension ZIP from manifest — all types supported * BRIEF: Build a Joomla extension ZIP from manifest — all types supported
* NOTE: Called by pre-release and auto-release workflows. * NOTE: Called by pre-release and auto-release workflows.
*/ */
+14 -14
View File
@@ -10,8 +10,8 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/joomla_metadata_validate.php * PATH: /cli/joomla_metadata_validate.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Validate MokoGitea repo metadata against Joomla extension manifest XML * BRIEF: Validate MokoGIT repo metadata against Joomla extension manifest XML
*/ */
declare(strict_types=1); declare(strict_types=1);
@@ -22,7 +22,7 @@ use MokoCli\CliFramework;
class JoomlaMetadataValidateCli extends CliFramework class JoomlaMetadataValidateCli extends CliFramework
{ {
/** Joomla element prefix map — must match MokoGitea's cleanJoomlaElement() */ /** Joomla element prefix map — must match MokoGIT's cleanJoomlaElement() */
private const JOOMLA_PREFIX = [ private const JOOMLA_PREFIX = [
'package' => 'pkg_', 'package' => 'pkg_',
'component' => 'com_', 'component' => 'com_',
@@ -34,12 +34,12 @@ class JoomlaMetadataValidateCli extends CliFramework
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Validate MokoGitea repo metadata against Joomla extension manifest XML'); $this->setDescription('Validate MokoGIT repo metadata against Joomla extension manifest XML');
$this->addArgument('--path', 'Repo root path (default: current directory)', '.'); $this->addArgument('--path', 'Repo root path (default: current directory)', '.');
$this->addArgument('--token', 'Gitea API token (or GITEA_TOKEN env)', ''); $this->addArgument('--token', 'Git API token (or GIT_TOKEN env)', '');
$this->addArgument('--org', 'Gitea org', 'MokoConsulting'); $this->addArgument('--org', 'Git org', 'MokoConsulting');
$this->addArgument('--repo', 'Repo name (auto-detected from git if empty)', ''); $this->addArgument('--repo', 'Repo name (auto-detected from git if empty)', '');
$this->addArgument('--api-base', 'Gitea API base URL', 'https://git.mokoconsulting.tech/api/v1'); $this->addArgument('--api-base', 'Git API base URL', 'https://git.mokoconsulting.tech/api/v1');
$this->addArgument('--ci', 'CI mode: exit 1 on any error', false); $this->addArgument('--ci', 'CI mode: exit 1 on any error', false);
$this->addArgument('--json', 'Output as JSON', false); $this->addArgument('--json', 'Output as JSON', false);
} }
@@ -47,7 +47,7 @@ class JoomlaMetadataValidateCli extends CliFramework
protected function run(): int protected function run(): int
{ {
$path = realpath($this->getArgument('--path')) ?: $this->getArgument('--path'); $path = realpath($this->getArgument('--path')) ?: $this->getArgument('--path');
$token = $this->getArgument('--token') ?: getenv('GITEA_TOKEN') ?: ''; $token = $this->getArgument('--token') ?: getenv('GIT_TOKEN') ?: '';
$org = $this->getArgument('--org'); $org = $this->getArgument('--org');
$repoName = $this->getArgument('--repo'); $repoName = $this->getArgument('--repo');
$apiBase = rtrim($this->getArgument('--api-base'), '/'); $apiBase = rtrim($this->getArgument('--api-base'), '/');
@@ -73,11 +73,11 @@ class JoomlaMetadataValidateCli extends CliFramework
$this->log('INFO', "Joomla manifest: {$joomlaXml['path']}"); $this->log('INFO', "Joomla manifest: {$joomlaXml['path']}");
// ── Step 2: Load MokoGitea metadata ───────────────────────── // ── Step 2: Load MokoGIT metadata ─────────────────────────
$metadata = $this->loadMetadata($path, $org, $repoName, $token, $apiBase); $metadata = $this->loadMetadata($path, $org, $repoName, $token, $apiBase);
if ($metadata === null) { if ($metadata === null) {
$this->log('ERROR', 'Could not load MokoGitea metadata'); $this->log('ERROR', 'Could not load MokoGIT metadata');
return 1; return 1;
} }
@@ -174,7 +174,7 @@ class JoomlaMetadataValidateCli extends CliFramework
private function loadMetadata(string $root, string $org, string $repoName, string $token, string $apiBase): ?array private function loadMetadata(string $root, string $org, string $repoName, string $token, string $apiBase): ?array
{ {
if ($token === '') { if ($token === '') {
$this->log('ERROR', 'No API token provided (use --token or set GITEA_TOKEN env var)'); $this->log('ERROR', 'No API token provided (use --token or set GIT_TOKEN env var)');
return null; return null;
} }
@@ -202,7 +202,7 @@ class JoomlaMetadataValidateCli extends CliFramework
if ($httpCode === 404) { if ($httpCode === 404) {
$this->log('ERROR', "API endpoint not found: {$url}"); $this->log('ERROR', "API endpoint not found: {$url}");
$this->log('ERROR', 'Server may need MokoGitea-Fork >= #650 (metadata endpoint rename)'); $this->log('ERROR', 'Server may need MokoGIT-Fork >= #650 (metadata endpoint rename)');
return null; return null;
} }
@@ -328,7 +328,7 @@ class JoomlaMetadataValidateCli extends CliFramework
// ================================================================= // =================================================================
/** /**
* Normalize extension_type — map MokoGitea types to Joomla types. * Normalize extension_type — map MokoGIT types to Joomla types.
*/ */
private function normalizeExtensionType(string $type): string private function normalizeExtensionType(string $type): string
{ {
@@ -340,7 +340,7 @@ class JoomlaMetadataValidateCli extends CliFramework
/** /**
* Derive the Joomla element name from type + name. * Derive the Joomla element name from type + name.
* Replicates MokoGitea's cleanJoomlaElement() + prefix logic. * Replicates MokoGIT's cleanJoomlaElement() + prefix logic.
*/ */
private function deriveElement(string $type, string $name): string private function deriveElement(string $type, string $name): string
{ {
+9 -9
View File
@@ -30,7 +30,7 @@ use MokoCli\{ApiClient, AuditLogger, CliFramework, Config, PlatformAdapterFactor
/** /**
* Joomla Release Manager * Joomla Release Manager
* *
* Creates and manages Joomla extension releases on Gitea, including * Creates and manages Joomla extension releases on Git, including
* package building, asset upload, and update stream management. * package building, asset upload, and update stream management.
* *
* @since 04.06.00 * @since 04.06.00
@@ -425,9 +425,9 @@ class JoomlaRelease extends CliFramework
} }
$releaseConfig = Config::load(); $releaseConfig = Config::load();
$releasePlatform = $releaseConfig->getString('platform', 'gitea'); $releasePlatform = $releaseConfig->getString('platform', 'git');
$releaseToken = $releasePlatform === 'gitea' $releaseToken = $releasePlatform === 'git'
? $releaseConfig->getString('gitea.token', '') ? $releaseConfig->getString('git.token', '')
: $releaseConfig->getString('github.token', ''); : $releaseConfig->getString('github.token', '');
$acceptHeader = $releasePlatform === 'github' ? 'application/vnd.github+json' : 'application/json'; $acceptHeader = $releasePlatform === 'github' ? 'application/vnd.github+json' : 'application/json';
$ch = curl_init(); $ch = curl_init();
@@ -512,12 +512,12 @@ class JoomlaRelease extends CliFramework
$this->rmdir($tmpDir); $this->rmdir($tmpDir);
} }
$config = Config::load(); $config = Config::load();
$platform = $config->getString('platform', 'gitea'); $platform = $config->getString('platform', 'git');
$token = $platform === 'gitea' $token = $platform === 'git'
? $config->getString('gitea.token', '') ? $config->getString('git.token', '')
: $config->getString('github.token', ''); : $config->getString('github.token', '');
$cloneHost = $platform === 'gitea' $cloneHost = $platform === 'git'
? rtrim($config->getString('gitea.url', 'https://git.mokoconsulting.tech'), '/') ? rtrim($config->getString('git.url', 'https://git.mokoconsulting.tech'), '/')
: 'https://github.com'; : 'https://github.com';
$url = "https://x-access-token:{$token}@" . preg_replace('#^https?://#', '', $cloneHost) . '/' . self::ORG . "/{$repo}.git"; $url = "https://x-access-token:{$token}@" . preg_replace('#^https?://#', '', $cloneHost) . '/' . self::ORG . "/{$repo}.git";
$cmd = ['git', 'clone', '--depth', '1', '--quiet', $url, $tmpDir]; $cmd = ['git', 'clone', '--depth', '1', '--quiet', $url, $tmpDir];
+5 -5
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/license_manage.php * PATH: /cli/license_manage.php
* BRIEF: Manage license packages and keys via MokoGitea licensing API * BRIEF: Manage license packages and keys via MokoGIT licensing API
* *
* Usage: * Usage:
* php bin/moko license:list --org MokoConsulting * php bin/moko license:list --org MokoConsulting
@@ -38,9 +38,9 @@ class LicenseManage extends CliFramework
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Manage license packages and keys via MokoGitea licensing API'); $this->setDescription('Manage license packages and keys via MokoGIT licensing API');
$this->addArgument('--org', 'Organization name', ''); $this->addArgument('--org', 'Organization name', '');
$this->addArgument('--api-base', 'Gitea API base URL', ''); $this->addArgument('--api-base', 'Git API base URL', '');
$this->addArgument('--token', 'API token (or set GH_TOKEN env)', ''); $this->addArgument('--token', 'API token (or set GH_TOKEN env)', '');
// Package args // Package args
@@ -71,7 +71,7 @@ class LicenseManage extends CliFramework
{ {
// Resolve API base // Resolve API base
$this->apiBase = $this->getArgument('--api-base') $this->apiBase = $this->getArgument('--api-base')
?: getenv('GITEA_URL') ?: getenv('GIT_URL')
?: 'https://git.mokoconsulting.tech'; ?: 'https://git.mokoconsulting.tech';
$this->apiBase = rtrim($this->apiBase, '/'); $this->apiBase = rtrim($this->apiBase, '/');
@@ -657,7 +657,7 @@ HELP;
if ($httpCode === 404) { if ($httpCode === 404) {
$this->log("API endpoint not found: {$path}", 'ERROR'); $this->log("API endpoint not found: {$path}", 'ERROR');
$this->log('The licensing API may not be deployed yet. Check MokoGitea version.', 'WARN'); $this->log('The licensing API may not be deployed yet. Check MokoGIT version.', 'WARN');
return null; return null;
} }
+7 -7
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/manifest_detect.php * PATH: /cli/manifest_detect.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Auto-detect manifest fields from source files and optionally push to API * BRIEF: Auto-detect manifest fields from source files and optionally push to API
*/ */
@@ -29,10 +29,10 @@ class ManifestDetectCli extends CliFramework
$this->addArgument('--json', 'Output as JSON', false); $this->addArgument('--json', 'Output as JSON', false);
$this->addArgument('--diff', 'Show diff against current manifest API values', false); $this->addArgument('--diff', 'Show diff against current manifest API values', false);
$this->addArgument('--update', 'Push detected fields to manifest API', false); $this->addArgument('--update', 'Push detected fields to manifest API', false);
$this->addArgument('--token', 'Gitea API token (or GITEA_TOKEN env)', ''); $this->addArgument('--token', 'Git API token (or GIT_TOKEN env)', '');
$this->addArgument('--api-base', 'Gitea API base URL', 'https://git.mokoconsulting.tech/api/v1'); $this->addArgument('--api-base', 'Git API base URL', 'https://git.mokoconsulting.tech/api/v1');
$this->addArgument('--org', 'Gitea org', 'MokoConsulting'); $this->addArgument('--org', 'Git org', 'MokoConsulting');
$this->addArgument('--repo', 'Gitea repo name (auto-detected from remote if empty)', ''); $this->addArgument('--repo', 'Git repo name (auto-detected from remote if empty)', '');
$this->addArgument('--github-output', 'Append fields to $GITHUB_OUTPUT', false); $this->addArgument('--github-output', 'Append fields to $GITHUB_OUTPUT', false);
} }
@@ -43,7 +43,7 @@ class ManifestDetectCli extends CliFramework
$diffMode = (bool) $this->getArgument('--diff'); $diffMode = (bool) $this->getArgument('--diff');
$updateMode = (bool) $this->getArgument('--update'); $updateMode = (bool) $this->getArgument('--update');
$ghOutput = (bool) $this->getArgument('--github-output'); $ghOutput = (bool) $this->getArgument('--github-output');
$token = $this->getArgument('--token') ?: getenv('GITEA_TOKEN') ?: ''; $token = $this->getArgument('--token') ?: getenv('GIT_TOKEN') ?: '';
$apiBase = rtrim($this->getArgument('--api-base'), '/'); $apiBase = rtrim($this->getArgument('--api-base'), '/');
$org = $this->getArgument('--org'); $org = $this->getArgument('--org');
$repoName = $this->getArgument('--repo'); $repoName = $this->getArgument('--repo');
@@ -74,7 +74,7 @@ class ManifestDetectCli extends CliFramework
// ── Output ────────────────────────────────────────────────── // ── Output ──────────────────────────────────────────────────
if ($diffMode || $updateMode) { if ($diffMode || $updateMode) {
if ($token === '') { if ($token === '') {
$this->log('ERROR', 'API token required for --diff/--update (use --token or GITEA_TOKEN env)'); $this->log('ERROR', 'API token required for --diff/--update (use --token or GIT_TOKEN env)');
return 1; return 1;
} }
if ($repoName === '') { if ($repoName === '') {
+1 -1
View File
@@ -40,7 +40,7 @@ class ManifestElementCli extends CliFramework
$githubOutput = (bool) $this->getArgument('--github-output'); $githubOutput = (bool) $this->getArgument('--github-output');
$root = realpath($path) ?: $path; $root = realpath($path) ?: $path;
$platform = 'generic'; $platform = 'generic';
$manifestXml = "{$root}/.mokogitea/manifest.xml"; $manifestXml = "{$root}/.mokogit/manifest.xml";
if (file_exists($manifestXml)) { if (file_exists($manifestXml)) {
$content = file_get_contents($manifestXml); $content = file_get_contents($manifestXml);
if (preg_match('/<platform>([^<]+)<\/platform>/', $content, $pm)) { if (preg_match('/<platform>([^<]+)<\/platform>/', $content, $pm)) {
+6 -6
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/manifest_integrity.php * PATH: /cli/manifest_integrity.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Cross-check manifest API fields against repo contents across the org * BRIEF: Cross-check manifest API fields against repo contents across the org
*/ */
@@ -26,10 +26,10 @@ class ManifestIntegrityCli extends CliFramework
{ {
$this->setDescription('Cross-check manifest fields against repo contents across the org'); $this->setDescription('Cross-check manifest fields against repo contents across the org');
$this->addArgument('--path', 'Single repo path (local mode)', ''); $this->addArgument('--path', 'Single repo path (local mode)', '');
$this->addArgument('--org', 'Gitea org (bulk mode)', 'MokoConsulting'); $this->addArgument('--org', 'Git org (bulk mode)', 'MokoConsulting');
$this->addArgument('--repo', 'Single repo name (remote mode)', ''); $this->addArgument('--repo', 'Single repo name (remote mode)', '');
$this->addArgument('--token', 'Gitea API token (or GITEA_TOKEN env)', ''); $this->addArgument('--token', 'Git API token (or GIT_TOKEN env)', '');
$this->addArgument('--api-base', 'Gitea API base URL', 'https://git.mokoconsulting.tech/api/v1'); $this->addArgument('--api-base', 'Git API base URL', 'https://git.mokoconsulting.tech/api/v1');
$this->addArgument('--fix', 'Push fixes for detected drift', false); $this->addArgument('--fix', 'Push fixes for detected drift', false);
$this->addArgument('--json', 'Output as JSON', false); $this->addArgument('--json', 'Output as JSON', false);
$this->addArgument('--quiet', 'Only show repos with issues', false); $this->addArgument('--quiet', 'Only show repos with issues', false);
@@ -40,14 +40,14 @@ class ManifestIntegrityCli extends CliFramework
$path = $this->getArgument('--path'); $path = $this->getArgument('--path');
$org = $this->getArgument('--org'); $org = $this->getArgument('--org');
$repoName = $this->getArgument('--repo'); $repoName = $this->getArgument('--repo');
$token = $this->getArgument('--token') ?: getenv('GITEA_TOKEN') ?: ''; $token = $this->getArgument('--token') ?: getenv('GIT_TOKEN') ?: '';
$apiBase = rtrim($this->getArgument('--api-base'), '/'); $apiBase = rtrim($this->getArgument('--api-base'), '/');
$fixMode = (bool) $this->getArgument('--fix'); $fixMode = (bool) $this->getArgument('--fix');
$jsonMode = (bool) $this->getArgument('--json'); $jsonMode = (bool) $this->getArgument('--json');
$quiet = (bool) $this->getArgument('--quiet'); $quiet = (bool) $this->getArgument('--quiet');
if ($token === '') { if ($token === '') {
$this->log('ERROR', 'API token required (use --token or GITEA_TOKEN env)'); $this->log('ERROR', 'API token required (use --token or GIT_TOKEN env)');
return 1; return 1;
} }
+3 -3
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/manifest_licensing.php * PATH: /cli/manifest_licensing.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Ensure licensing tags (updateservers, dlid) in Joomla extension manifests * BRIEF: Ensure licensing tags (updateservers, dlid) in Joomla extension manifests
*/ */
@@ -21,7 +21,7 @@ require_once __DIR__ . '/../lib/Enterprise/CliFramework.php';
use MokoCli\{CliFramework, SourceResolver}; use MokoCli\{CliFramework, SourceResolver};
/** /**
* Reads the <licensing> block from .mokogitea/manifest.xml and ensures that the * Reads the <licensing> block from .mokogit/manifest.xml and ensures that the
* Joomla extension manifest contains the correct <updateservers> and <dlid> tags. * Joomla extension manifest contains the correct <updateservers> and <dlid> tags.
* *
* manifest.xml licensing block example: * manifest.xml licensing block example:
@@ -53,7 +53,7 @@ class ManifestLicensingCli extends CliFramework
$ghOutput = (bool) $this->getArgument('--github-output'); $ghOutput = (bool) $this->getArgument('--github-output');
// ── 1. Read manifest.xml ────────────────────────────────────────── // ── 1. Read manifest.xml ──────────────────────────────────────────
$manifestFile = "{$root}/.mokogitea/manifest.xml"; $manifestFile = "{$root}/.mokogit/manifest.xml";
if (!file_exists($manifestFile)) { if (!file_exists($manifestFile)) {
$this->log('WARN', "No manifest.xml found at {$manifestFile}"); $this->log('WARN', "No manifest.xml found at {$manifestFile}");
+10 -10
View File
@@ -10,8 +10,8 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/manifest_read.php * PATH: /cli/manifest_read.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Read repo metadata from Gitea manifest API, auto-detect the rest * BRIEF: Read repo metadata from Git manifest API, auto-detect the rest
*/ */
declare(strict_types=1); declare(strict_types=1);
@@ -27,7 +27,7 @@ class ManifestReadCli extends CliFramework
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Read repo metadata from Gitea API with auto-detection fallback'); $this->setDescription('Read repo metadata from Git API with auto-detection fallback');
$this->addArgument('--path', 'Repository root path', '.'); $this->addArgument('--path', 'Repository root path', '.');
$this->addArgument('--field', 'Single field name to output', ''); $this->addArgument('--field', 'Single field name to output', '');
$this->addArgument('--all', 'Print all fields as KEY=VALUE lines', false); $this->addArgument('--all', 'Print all fields as KEY=VALUE lines', false);
@@ -55,7 +55,7 @@ class ManifestReadCli extends CliFramework
// ── 1. Resolve org/repo ────────────────────────────────────────── // ── 1. Resolve org/repo ──────────────────────────────────────────
[$org, $repo] = $this->resolveOrgRepo($root); [$org, $repo] = $this->resolveOrgRepo($root);
// ── 2. Primary: Gitea manifest API ─────────────────────────────── // ── 2. Primary: Git manifest API ───────────────────────────────
$fields = null; $fields = null;
if ($org !== '' && $repo !== '') { if ($org !== '' && $repo !== '') {
$fields = $this->fetchFromApi($org, $repo); $fields = $this->fetchFromApi($org, $repo);
@@ -82,12 +82,12 @@ class ManifestReadCli extends CliFramework
return $this->outputFields($fields, $mode, $field); return $this->outputFields($fields, $mode, $field);
} }
// ── Gitea manifest API ─────────────────────────────────────────────── // ── Git manifest API ───────────────────────────────────────────────
private function fetchFromApi(string $org, string $repo): ?array private function fetchFromApi(string $org, string $repo): ?array
{ {
$token = getenv('GA_TOKEN') ?: getenv('GITEA_TOKEN') ?: ''; $token = getenv('GA_TOKEN') ?: getenv('GIT_TOKEN') ?: '';
$baseUrl = getenv('GITEA_URL') ?: 'https://git.mokoconsulting.tech'; $baseUrl = getenv('GIT_URL') ?: 'https://git.mokoconsulting.tech';
$baseUrl = rtrim($baseUrl, '/'); $baseUrl = rtrim($baseUrl, '/');
if ($token === '') { if ($token === '') {
@@ -123,7 +123,7 @@ class ManifestReadCli extends CliFramework
return null; return null;
} }
$this->log('INFO', "Loaded metadata from Gitea manifest API ({$org}/{$repo})"); $this->log('INFO', "Loaded metadata from Git manifest API ({$org}/{$repo})");
return $data; return $data;
} }
@@ -370,7 +370,7 @@ class ManifestReadCli extends CliFramework
*/ */
private function resolveOrgRepo(string $root): array private function resolveOrgRepo(string $root): array
{ {
// 1. GITHUB_REPOSITORY env (set in Gitea Actions / GitHub Actions) // 1. GITHUB_REPOSITORY env (set in Git Actions / GitHub Actions)
$envRepo = getenv('GITHUB_REPOSITORY') ?: ''; $envRepo = getenv('GITHUB_REPOSITORY') ?: '';
if ($envRepo !== '' && str_contains($envRepo, '/')) { if ($envRepo !== '' && str_contains($envRepo, '/')) {
return explode('/', $envRepo, 2); return explode('/', $envRepo, 2);
@@ -447,7 +447,7 @@ class ManifestReadCli extends CliFramework
break; break;
case 'github-output': case 'github-output':
$outputFile = getenv('GITHUB_OUTPUT') ?: getenv('GITEA_OUTPUT') ?: ''; $outputFile = getenv('GITHUB_OUTPUT') ?: getenv('GIT_OUTPUT') ?: '';
$lines = []; $lines = [];
foreach ($fields as $k => $v) { foreach ($fields as $k => $v) {
$envKey = str_replace('-', '_', $k); $envKey = str_replace('-', '_', $k);
+8 -8
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/platform_detect.php * PATH: /cli/platform_detect.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Auto-detect repository platform type and optionally update manifest * BRIEF: Auto-detect repository platform type and optionally update manifest
*/ */
@@ -26,8 +26,8 @@ class PlatformDetectCli extends CliFramework
{ {
$this->setDescription('Auto-detect repository platform type and optionally update manifest'); $this->setDescription('Auto-detect repository platform type and optionally update manifest');
$this->addArgument('--path', 'Local repo path to scan (default: .)', '.'); $this->addArgument('--path', 'Local repo path to scan (default: .)', '.');
$this->addArgument('--token', 'Gitea API token for updating manifest', ''); $this->addArgument('--token', 'Git API token for updating manifest', '');
$this->addArgument('--gitea-url', 'Gitea URL (default: https://git.mokoconsulting.tech)', 'https://git.mokoconsulting.tech'); $this->addArgument('--git-url', 'Git URL (default: https://git.mokoconsulting.tech)', 'https://git.mokoconsulting.tech');
$this->addArgument('--owner', 'Repo owner for API update', ''); $this->addArgument('--owner', 'Repo owner for API update', '');
$this->addArgument('--repo', 'Repo name for API update', ''); $this->addArgument('--repo', 'Repo name for API update', '');
$this->addArgument('--update', 'Update manifest.platform via API (flag)', 'false'); $this->addArgument('--update', 'Update manifest.platform via API (flag)', 'false');
@@ -40,7 +40,7 @@ class PlatformDetectCli extends CliFramework
$root = realpath($path) ?: $path; $root = realpath($path) ?: $path;
$token = $this->getArgument('--token'); $token = $this->getArgument('--token');
$giteaUrl = rtrim($this->getArgument('--gitea-url'), '/'); $gitUrl = rtrim($this->getArgument('--git-url'), '/');
$owner = $this->getArgument('--owner'); $owner = $this->getArgument('--owner');
$repo = $this->getArgument('--repo'); $repo = $this->getArgument('--repo');
$doUpdate = $this->isFlagSet('--update'); $doUpdate = $this->isFlagSet('--update');
@@ -79,7 +79,7 @@ class PlatformDetectCli extends CliFramework
$this->log('INFO', "Updating manifest.platform for {$owner}/{$repo} to \"{$platform}\"..."); $this->log('INFO', "Updating manifest.platform for {$owner}/{$repo} to \"{$platform}\"...");
$response = $this->apiRequest( $response = $this->apiRequest(
$giteaUrl, $gitUrl,
$token, $token,
'PATCH', 'PATCH',
"/api/v1/repos/{$owner}/{$repo}/metadata", "/api/v1/repos/{$owner}/{$repo}/metadata",
@@ -136,7 +136,7 @@ class PlatformDetectCli extends CliFramework
// 5. Platform — is mokocli itself or org-config // 5. Platform — is mokocli itself or org-config
$repoName = basename($root); $repoName = basename($root);
if (in_array($repoName, ['mokocli', 'mokogitea-org-config'])) { if (in_array($repoName, ['mokocli', 'mokogit-org-config'])) {
return 'platform'; return 'platform';
} }
@@ -152,13 +152,13 @@ class PlatformDetectCli extends CliFramework
} }
private function apiRequest( private function apiRequest(
string $giteaUrl, string $gitUrl,
string $token, string $token,
string $method, string $method,
string $endpoint, string $endpoint,
?string $body = null ?string $body = null
): array { ): array {
$url = $giteaUrl . $endpoint; $url = $gitUrl . $endpoint;
$ch = curl_init(); $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_URL, $url);
+2 -3
View File
@@ -37,10 +37,9 @@ class ReleaseCli extends CliFramework
$repoRoot = dirname(__DIR__, 2); $repoRoot = dirname(__DIR__, 2);
$syncFile = "{$repoRoot}/lib/Enterprise/RepositorySynchronizer.php"; $syncFile = "{$repoRoot}/lib/Enterprise/RepositorySynchronizer.php";
// Check both workflow directories for the bulk-repo-sync workflow // Check both workflow directories for the bulk-repo-sync workflow
$bulkSyncFile = file_exists("{$repoRoot}/.mokogitea/workflows/bulk-repo-sync.yml") $bulkSyncFile = file_exists("{$repoRoot}/.mokogit/workflows/bulk-repo-sync.yml")
? "{$repoRoot}/.mokogitea/workflows/bulk-repo-sync.yml" ? "{$repoRoot}/.mokogit/workflows/bulk-repo-sync.yml"
: "{$repoRoot}/.github/workflows/bulk-repo-sync.yml"; : "{$repoRoot}/.github/workflows/bulk-repo-sync.yml";
$cleanupFile = "{$repoRoot}/templates/workflows/shared/repository-cleanup.yml.template";
// -- Step 1: Read current version -- // -- Step 1: Read current version --
$readme = "{$repoRoot}/README.md"; $readme = "{$repoRoot}/README.md";
+6 -6
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/release_body_update.php * PATH: /cli/release_body_update.php
* BRIEF: Update Gitea release body with changelog extract and checksums * BRIEF: Update Git release body with changelog extract and checksums
*/ */
declare(strict_types=1); declare(strict_types=1);
@@ -23,12 +23,12 @@ class ReleaseBodyUpdateCli extends CliFramework
{ {
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Update Gitea release body with changelog extract and checksums'); $this->setDescription('Update Git release body with changelog extract and checksums');
$this->addArgument('--path', 'Repo root for CHANGELOG.md', '.'); $this->addArgument('--path', 'Repo root for CHANGELOG.md', '.');
$this->addArgument('--version', 'Version string', ''); $this->addArgument('--version', 'Version string', '');
$this->addArgument('--release-tag', 'Gitea release tag', ''); $this->addArgument('--release-tag', 'Git release tag', '');
$this->addArgument('--token', 'Gitea API token', ''); $this->addArgument('--token', 'Git API token', '');
$this->addArgument('--api-base', 'Gitea API base URL', ''); $this->addArgument('--api-base', 'Git API base URL', '');
$this->addArgument('--zip-name', 'ZIP filename for checksum table', ''); $this->addArgument('--zip-name', 'ZIP filename for checksum table', '');
$this->addArgument('--tar-name', 'tar.gz filename for checksum table', ''); $this->addArgument('--tar-name', 'tar.gz filename for checksum table', '');
$this->addArgument('--zip-sha', 'SHA256 of ZIP', ''); $this->addArgument('--zip-sha', 'SHA256 of ZIP', '');
@@ -50,7 +50,7 @@ class ReleaseBodyUpdateCli extends CliFramework
$outputSummary = $this->getArgument('--output-summary'); $outputSummary = $this->getArgument('--output-summary');
if (empty($token)) { if (empty($token)) {
$token = getenv('MOKOGITEA_TOKEN') ?: getenv('GITEA_TOKEN') ?: ''; $token = getenv('MOKOGIT_TOKEN') ?: getenv('GIT_TOKEN') ?: '';
} }
if (empty($version) || empty($releaseTag) || empty($token) || empty($apiBase)) { if (empty($version) || empty($releaseTag) || empty($token) || empty($apiBase)) {
+12 -12
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/release_cascade.php * PATH: /cli/release_cascade.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Cascade release zip to all lower stability channels * BRIEF: Cascade release zip to all lower stability channels
*/ */
@@ -40,8 +40,8 @@ class ReleaseCascadeCli extends CliFramework
{ {
$this->setDescription('Cascade release zip to all lower stability channels'); $this->setDescription('Cascade release zip to all lower stability channels');
$this->addArgument('--stability', 'Source stability channel (required)', ''); $this->addArgument('--stability', 'Source stability channel (required)', '');
$this->addArgument('--token', 'Gitea API token (required)', ''); $this->addArgument('--token', 'Git API token (required)', '');
$this->addArgument('--api-base', 'Gitea API base URL for the repo (required)', ''); $this->addArgument('--api-base', 'Git API base URL for the repo (required)', '');
} }
protected function run(): int protected function run(): int
@@ -51,9 +51,9 @@ class ReleaseCascadeCli extends CliFramework
$apiBase = rtrim($this->getArgument('--api-base'), '/'); $apiBase = rtrim($this->getArgument('--api-base'), '/');
if ($token === '') { if ($token === '') {
$envToken = getenv('MOKOGITEA_TOKEN'); $envToken = getenv('MOKOGIT_TOKEN');
if ($envToken === false || $envToken === '') { if ($envToken === false || $envToken === '') {
$envToken = getenv('GITEA_TOKEN'); $envToken = getenv('GIT_TOKEN');
} }
if ($envToken !== false && $envToken !== '') { if ($envToken !== false && $envToken !== '') {
$token = $envToken; $token = $envToken;
@@ -85,7 +85,7 @@ class ReleaseCascadeCli extends CliFramework
} }
// 1. Get source release // 1. Get source release
$sourceRelease = $this->giteaApi("{$apiBase}/releases/tags/{$sourceTag}", $token); $sourceRelease = $this->gitApi("{$apiBase}/releases/tags/{$sourceTag}", $token);
if ($sourceRelease === null) { if ($sourceRelease === null) {
$this->log('WARN', "No release found at tag '{$sourceTag}' — nothing to cascade."); $this->log('WARN', "No release found at tag '{$sourceTag}' — nothing to cascade.");
return 0; return 0;
@@ -179,7 +179,7 @@ class ReleaseCascadeCli extends CliFramework
} }
// Find existing release at target tag // Find existing release at target tag
$existing = $this->giteaApi("{$apiBase}/releases/tags/{$targetTag}", $token); $existing = $this->gitApi("{$apiBase}/releases/tags/{$targetTag}", $token);
if ($existing !== null && !empty($existing['id'])) { if ($existing !== null && !empty($existing['id'])) {
$releaseId = (int) $existing['id']; $releaseId = (int) $existing['id'];
@@ -189,7 +189,7 @@ class ReleaseCascadeCli extends CliFramework
foreach ($existingAssets as $asset) { foreach ($existingAssets as $asset) {
$assetId = $asset['id'] ?? 0; $assetId = $asset['id'] ?? 0;
if ($assetId > 0) { if ($assetId > 0) {
$this->giteaApi( $this->gitApi(
"{$apiBase}/releases/{$releaseId}/assets/{$assetId}", "{$apiBase}/releases/{$releaseId}/assets/{$assetId}",
$token, 'DELETE' $token, 'DELETE'
); );
@@ -201,7 +201,7 @@ class ReleaseCascadeCli extends CliFramework
'name' => $sourceVersion, 'name' => $sourceVersion,
'body' => $sourceBody, 'body' => $sourceBody,
]); ]);
$this->giteaApi( $this->gitApi(
"{$apiBase}/releases/{$releaseId}", "{$apiBase}/releases/{$releaseId}",
$token, 'PATCH', $updatePayload $token, 'PATCH', $updatePayload
); );
@@ -217,7 +217,7 @@ class ReleaseCascadeCli extends CliFramework
'body' => $sourceBody, 'body' => $sourceBody,
'prerelease' => ($targetTag !== 'stable'), 'prerelease' => ($targetTag !== 'stable'),
]); ]);
$newRelease = $this->giteaApi("{$apiBase}/releases", $token, 'POST', $createPayload); $newRelease = $this->gitApi("{$apiBase}/releases", $token, 'POST', $createPayload);
if ($newRelease === null || empty($newRelease['id'])) { if ($newRelease === null || empty($newRelease['id'])) {
$this->log('ERROR', " Failed to create release at tag '{$targetTag}'"); $this->log('ERROR', " Failed to create release at tag '{$targetTag}'");
return false; return false;
@@ -302,9 +302,9 @@ class ReleaseCascadeCli extends CliFramework
} }
/** /**
* Make an HTTP request to the Gitea API. * Make an HTTP request to the Git API.
*/ */
private function giteaApi( private function gitApi(
string $url, string $url,
string $token, string $token,
string $method = 'GET', string $method = 'GET',
+13 -13
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/release_create.php * PATH: /cli/release_create.php
* BRIEF: Create or overwrite a Gitea release with proper naming * BRIEF: Create or overwrite a Git release with proper naming
*/ */
declare(strict_types=1); declare(strict_types=1);
@@ -23,12 +23,12 @@ class ReleaseCreateCli extends CliFramework
{ {
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Create or overwrite a Gitea release with proper naming'); $this->setDescription('Create or overwrite a Git release with proper naming');
$this->addArgument('--path', 'Repo root for manifest detection (default: .)', '.'); $this->addArgument('--path', 'Repo root for manifest detection (default: .)', '.');
$this->addArgument('--version', 'Version string (required)', ''); $this->addArgument('--version', 'Version string (required)', '');
$this->addArgument('--tag', 'Release tag name (required)', ''); $this->addArgument('--tag', 'Release tag name (required)', '');
$this->addArgument('--token', 'Gitea API token (required)', ''); $this->addArgument('--token', 'Git API token (required)', '');
$this->addArgument('--api-base', 'Gitea API base URL for the repo (required)', ''); $this->addArgument('--api-base', 'Git API base URL for the repo (required)', '');
$this->addArgument('--branch', 'Target commitish (default: main)', 'main'); $this->addArgument('--branch', 'Target commitish (default: main)', 'main');
$this->addArgument('--repo', 'Repo name for fallback element detection', ''); $this->addArgument('--repo', 'Repo name for fallback element detection', '');
$this->addArgument('--prerelease', 'Mark release as prerelease', false); $this->addArgument('--prerelease', 'Mark release as prerelease', false);
@@ -47,9 +47,9 @@ class ReleaseCreateCli extends CliFramework
// Allow token from environment // Allow token from environment
if ($token === '') { if ($token === '') {
$envToken = getenv('MOKOGITEA_TOKEN'); $envToken = getenv('MOKOGIT_TOKEN');
if ($envToken === false || $envToken === '') { if ($envToken === false || $envToken === '') {
$envToken = getenv('GITEA_TOKEN'); $envToken = getenv('GIT_TOKEN');
} }
if ($envToken !== false && $envToken !== '') { if ($envToken !== false && $envToken !== '') {
$token = $envToken; $token = $envToken;
@@ -62,7 +62,7 @@ class ReleaseCreateCli extends CliFramework
$this->log('ERROR', " --branch main Target commitish (default: main)"); $this->log('ERROR', " --branch main Target commitish (default: main)");
$this->log('ERROR', " --repo REPO Repo name for fallback element detection"); $this->log('ERROR', " --repo REPO Repo name for fallback element detection");
$this->log('ERROR', " --prerelease Mark release as prerelease"); $this->log('ERROR', " --prerelease Mark release as prerelease");
$this->log('ERROR', " Token can also be set via MOKOGITEA_TOKEN or GITEA_TOKEN env var"); $this->log('ERROR', " Token can also be set via MOKOGIT_TOKEN or GIT_TOKEN env var");
return 1; return 1;
} }
@@ -79,7 +79,7 @@ class ReleaseCreateCli extends CliFramework
// Detect platform and display name from manifest.xml // Detect platform and display name from manifest.xml
$platform = 'generic'; $platform = 'generic';
$prettyName = ''; $prettyName = '';
$manifestXml = "{$root}/.mokogitea/manifest.xml"; $manifestXml = "{$root}/.mokogit/manifest.xml";
if (file_exists($manifestXml)) { if (file_exists($manifestXml)) {
$content = file_get_contents($manifestXml); $content = file_get_contents($manifestXml);
if ($content !== false) { if ($content !== false) {
@@ -238,16 +238,16 @@ class ReleaseCreateCli extends CliFramework
// ── Delete existing release at tag (if present) ───────────────────────────── // ── Delete existing release at tag (if present) ─────────────────────────────
$existing = $this->giteaApi("{$apiBase}/releases/tags/{$tag}", $token); $existing = $this->gitApi("{$apiBase}/releases/tags/{$tag}", $token);
if ($existing !== null && !empty($existing['id'])) { if ($existing !== null && !empty($existing['id'])) {
$existingId = $existing['id']; $existingId = $existing['id'];
echo "Deleting existing release: {$tag} (id: {$existingId})\n"; echo "Deleting existing release: {$tag} (id: {$existingId})\n";
// Delete release // Delete release
$this->giteaApi("{$apiBase}/releases/{$existingId}", $token, 'DELETE'); $this->gitApi("{$apiBase}/releases/{$existingId}", $token, 'DELETE');
// Delete tag // Delete tag
$this->giteaApi("{$apiBase}/tags/{$tag}", $token, 'DELETE'); $this->gitApi("{$apiBase}/tags/{$tag}", $token, 'DELETE');
} }
// ── Create new release ────────────────────────────────────────────────────── // ── Create new release ──────────────────────────────────────────────────────
@@ -260,7 +260,7 @@ class ReleaseCreateCli extends CliFramework
'prerelease' => $prerelease, 'prerelease' => $prerelease,
]); ]);
$newRelease = $this->giteaApi("{$apiBase}/releases", $token, 'POST', $payload !== false ? $payload : '{}'); $newRelease = $this->gitApi("{$apiBase}/releases", $token, 'POST', $payload !== false ? $payload : '{}');
if ($newRelease === null || empty($newRelease['id'])) { if ($newRelease === null || empty($newRelease['id'])) {
$this->log('ERROR', "Failed to create release at tag: {$tag}"); $this->log('ERROR', "Failed to create release at tag: {$tag}");
return 1; return 1;
@@ -274,7 +274,7 @@ class ReleaseCreateCli extends CliFramework
return 0; return 0;
} }
private function giteaApi(string $url, string $token, string $method = 'GET', ?string $body = null): ?array private function gitApi(string $url, string $token, string $method = 'GET', ?string $body = null): ?array
{ {
$ch = curl_init($url); $ch = curl_init($url);
if ($ch === false) { if ($ch === false) {
+16 -16
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/release_manage.php * PATH: /cli/release_manage.php
* BRIEF: Create/update Gitea releases, upload assets, update release body * BRIEF: Create/update Git releases, upload assets, update release body
*/ */
declare(strict_types=1); declare(strict_types=1);
@@ -23,7 +23,7 @@ class ReleaseManageCli extends CliFramework
{ {
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Create/update Gitea releases, upload assets, update release body'); $this->setDescription('Create/update Git releases, upload assets, update release body');
$this->addArgument('--action', 'create | upload | update-body | delete', null); $this->addArgument('--action', 'create | upload | update-body | delete', null);
$this->addArgument('--tag', 'Release tag name', null); $this->addArgument('--tag', 'Release tag name', null);
$this->addArgument('--name', 'Release name/title', null); $this->addArgument('--name', 'Release name/title', null);
@@ -31,8 +31,8 @@ class ReleaseManageCli extends CliFramework
$this->addArgument('--body-file', 'Read body from file', null); $this->addArgument('--body-file', 'Read body from file', null);
$this->addArgument('--target', 'Target branch/commitish', 'main'); $this->addArgument('--target', 'Target branch/commitish', 'main');
$this->addArgument('--files', 'Comma-separated file paths to upload', null); $this->addArgument('--files', 'Comma-separated file paths to upload', null);
$this->addArgument('--token', 'Gitea API token', null); $this->addArgument('--token', 'Git API token', null);
$this->addArgument('--api-base', 'Gitea API base URL', null); $this->addArgument('--api-base', 'Git API base URL', null);
} }
protected function run(): int protected function run(): int
@@ -48,7 +48,7 @@ class ReleaseManageCli extends CliFramework
$apiBase = $this->getArgument('--api-base'); $apiBase = $this->getArgument('--api-base');
$files = $filesArg !== null ? array_filter(explode(',', $filesArg)) : []; $files = $filesArg !== null ? array_filter(explode(',', $filesArg)) : [];
if ($token === null) { if ($token === null) {
$token = getenv('MOKOGITEA_TOKEN') ?: getenv('GITEA_TOKEN') ?: null; $token = getenv('MOKOGIT_TOKEN') ?: getenv('GIT_TOKEN') ?: null;
} }
if ($bodyFile !== null && file_exists($bodyFile)) { if ($bodyFile !== null && file_exists($bodyFile)) {
$body = file_get_contents($bodyFile); $body = file_get_contents($bodyFile);
@@ -62,12 +62,12 @@ class ReleaseManageCli extends CliFramework
$existing = $this->getReleaseByTag($apiBase, $tag, $token); $existing = $this->getReleaseByTag($apiBase, $tag, $token);
if ($existing !== null) { if ($existing !== null) {
$existingId = $existing['id']; $existingId = $existing['id'];
$this->releaseGiteaApi("{$apiBase}/releases/{$existingId}", 'DELETE', $token); $this->releaseGitApi("{$apiBase}/releases/{$existingId}", 'DELETE', $token);
$this->releaseGiteaApi("{$apiBase}/tags/{$tag}", 'DELETE', $token); $this->releaseGitApi("{$apiBase}/tags/{$tag}", 'DELETE', $token);
echo "Deleted previous release: {$tag} (id: {$existingId})\n"; echo "Deleted previous release: {$tag} (id: {$existingId})\n";
} }
$payload = json_encode(['tag_name' => $tag, 'name' => $name ?? $tag, 'body' => $body ?? '', 'target_commitish' => $target]); $payload = json_encode(['tag_name' => $tag, 'name' => $name ?? $tag, 'body' => $body ?? '', 'target_commitish' => $target]);
$result = $this->releaseGiteaApi("{$apiBase}/releases", 'POST', $token, $payload); $result = $this->releaseGitApi("{$apiBase}/releases", 'POST', $token, $payload);
if ($result['code'] >= 200 && $result['code'] < 300) { if ($result['code'] >= 200 && $result['code'] < 300) {
$releaseId = $result['data']['id'] ?? 'unknown'; $releaseId = $result['data']['id'] ?? 'unknown';
echo "Release created: {$name} (tag: {$tag}, id: {$releaseId})\n"; echo "Release created: {$name} (tag: {$tag}, id: {$releaseId})\n";
@@ -87,7 +87,7 @@ class ReleaseManageCli extends CliFramework
return 1; return 1;
} }
$releaseId = $release['id']; $releaseId = $release['id'];
$assetsResult = $this->releaseGiteaApi("{$apiBase}/releases/{$releaseId}/assets", 'GET', $token); $assetsResult = $this->releaseGitApi("{$apiBase}/releases/{$releaseId}/assets", 'GET', $token);
$existingAssets = $assetsResult['data'] ?? []; $existingAssets = $assetsResult['data'] ?? [];
foreach ($files as $filePath) { foreach ($files as $filePath) {
$filePath = trim($filePath); $filePath = trim($filePath);
@@ -98,13 +98,13 @@ class ReleaseManageCli extends CliFramework
$fileName = basename($filePath); $fileName = basename($filePath);
foreach ($existingAssets as $asset) { foreach ($existingAssets as $asset) {
if (($asset['name'] ?? '') === $fileName) { if (($asset['name'] ?? '') === $fileName) {
$this->releaseGiteaApi("{$apiBase}/releases/{$releaseId}/assets/{$asset['id']}", 'DELETE', $token); $this->releaseGitApi("{$apiBase}/releases/{$releaseId}/assets/{$asset['id']}", 'DELETE', $token);
echo "Deleted existing asset: {$fileName}\n"; echo "Deleted existing asset: {$fileName}\n";
break; break;
} }
} }
$uploadUrl = "{$apiBase}/releases/{$releaseId}/assets?name=" . urlencode($fileName); $uploadUrl = "{$apiBase}/releases/{$releaseId}/assets?name=" . urlencode($fileName);
$result = $this->releaseGiteaApi($uploadUrl, 'POST', $token, null, $filePath); $result = $this->releaseGitApi($uploadUrl, 'POST', $token, null, $filePath);
if ($result['code'] >= 200 && $result['code'] < 300) { if ($result['code'] >= 200 && $result['code'] < 300) {
echo "Uploaded: {$fileName}\n"; echo "Uploaded: {$fileName}\n";
} else { } else {
@@ -119,7 +119,7 @@ class ReleaseManageCli extends CliFramework
return 1; return 1;
} }
$payload = json_encode(['body' => $body ?? '']); $payload = json_encode(['body' => $body ?? '']);
$result = $this->releaseGiteaApi("{$apiBase}/releases/{$release['id']}", 'PATCH', $token, $payload); $result = $this->releaseGitApi("{$apiBase}/releases/{$release['id']}", 'PATCH', $token, $payload);
if ($result['code'] >= 200 && $result['code'] < 300) { if ($result['code'] >= 200 && $result['code'] < 300) {
echo "Release body updated for tag: {$tag}\n"; echo "Release body updated for tag: {$tag}\n";
} else { } else {
@@ -130,8 +130,8 @@ class ReleaseManageCli extends CliFramework
case 'delete': case 'delete':
$existing = $this->getReleaseByTag($apiBase, $tag, $token); $existing = $this->getReleaseByTag($apiBase, $tag, $token);
if ($existing !== null) { if ($existing !== null) {
$this->releaseGiteaApi("{$apiBase}/releases/{$existing['id']}", 'DELETE', $token); $this->releaseGitApi("{$apiBase}/releases/{$existing['id']}", 'DELETE', $token);
$this->releaseGiteaApi("{$apiBase}/tags/{$tag}", 'DELETE', $token); $this->releaseGitApi("{$apiBase}/tags/{$tag}", 'DELETE', $token);
echo "Deleted: {$tag} (id: {$existing['id']})\n"; echo "Deleted: {$tag} (id: {$existing['id']})\n";
} else { } else {
echo "No release found for tag: {$tag}\n"; echo "No release found for tag: {$tag}\n";
@@ -144,7 +144,7 @@ class ReleaseManageCli extends CliFramework
return 0; return 0;
} }
private function releaseGiteaApi(string $url, string $method, string $token, ?string $jsonBody = null, ?string $filePath = null): array private function releaseGitApi(string $url, string $method, string $token, ?string $jsonBody = null, ?string $filePath = null): array
{ {
$ch = curl_init($url); $ch = curl_init($url);
$headers = ["Authorization: token {$token}"]; $headers = ["Authorization: token {$token}"];
@@ -166,7 +166,7 @@ class ReleaseManageCli extends CliFramework
private function getReleaseByTag(string $apiBase, string $tag, string $token): ?array private function getReleaseByTag(string $apiBase, string $tag, string $token): ?array
{ {
$result = $this->releaseGiteaApi("{$apiBase}/releases/tags/{$tag}", 'GET', $token); $result = $this->releaseGitApi("{$apiBase}/releases/tags/{$tag}", 'GET', $token);
return ($result['code'] === 200 && isset($result['data']['id'])) ? $result['data'] : null; return ($result['code'] === 200 && isset($result['data']['id'])) ? $result['data'] : null;
} }
} }
+19 -19
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/release_mirror.php * PATH: /cli/release_mirror.php
* BRIEF: Mirror a Gitea release (with assets) to a GitHub repository * BRIEF: Mirror a Git release (with assets) to a GitHub repository
*/ */
declare(strict_types=1); declare(strict_types=1);
@@ -23,11 +23,11 @@ class ReleaseMirrorCli extends CliFramework
{ {
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Mirror a Gitea release (with assets) to a GitHub repository'); $this->setDescription('Mirror a Git release (with assets) to a GitHub repository');
$this->addArgument('--version', 'Version string (required)', ''); $this->addArgument('--version', 'Version string (required)', '');
$this->addArgument('--tag', 'Release tag name (required)', ''); $this->addArgument('--tag', 'Release tag name (required)', '');
$this->addArgument('--token', 'Gitea API token', ''); $this->addArgument('--token', 'Git API token', '');
$this->addArgument('--api-base', 'Gitea API base URL for the repo (required)', ''); $this->addArgument('--api-base', 'Git API base URL for the repo (required)', '');
$this->addArgument('--gh-token', 'GitHub personal access token', ''); $this->addArgument('--gh-token', 'GitHub personal access token', '');
$this->addArgument('--gh-repo', 'GitHub org/repo (required)', ''); $this->addArgument('--gh-repo', 'GitHub org/repo (required)', '');
$this->addArgument('--branch', 'Target branch (default: main)', 'main'); $this->addArgument('--branch', 'Target branch (default: main)', 'main');
@@ -44,30 +44,30 @@ class ReleaseMirrorCli extends CliFramework
$branch = $this->getArgument('--branch'); $branch = $this->getArgument('--branch');
// Allow tokens from environment // Allow tokens from environment
$token = $token ?: (getenv('MOKOGITEA_TOKEN') ?: (getenv('GITEA_TOKEN') ?: '')); $token = $token ?: (getenv('MOKOGIT_TOKEN') ?: (getenv('GIT_TOKEN') ?: ''));
$ghToken = $ghToken ?: (getenv('GH_MIRROR_TOKEN') ?: ''); $ghToken = $ghToken ?: (getenv('GH_MIRROR_TOKEN') ?: '');
if ($version === '' || $tag === '' || $token === '' || $apiBase === '' || $ghToken === '' || $ghRepo === '') { if ($version === '' || $tag === '' || $token === '' || $apiBase === '' || $ghToken === '' || $ghRepo === '') {
$this->log('ERROR', "Usage: release_mirror.php --version VER --tag TAG --token TOKEN " . $this->log('ERROR', "Usage: release_mirror.php --version VER --tag TAG --token TOKEN " .
"--api-base URL --gh-token GH_MIRROR_TOKEN --gh-repo org/repo [--branch main]"); "--api-base URL --gh-token GH_MIRROR_TOKEN --gh-repo org/repo [--branch main]");
$this->log('ERROR', " --token: Gitea token (or MOKOGITEA_TOKEN / GITEA_TOKEN env)"); $this->log('ERROR', " --token: Git token (or MOKOGIT_TOKEN / GIT_TOKEN env)");
$this->log('ERROR', " --gh-token: GitHub token (or GH_MIRROR_TOKEN env)"); $this->log('ERROR', " --gh-token: GitHub token (or GH_MIRROR_TOKEN env)");
return 1; return 1;
} }
// ── Step 1: Get Gitea release by tag ───────────────────────────────────────── // ── Step 1: Get Git release by tag ─────────────────────────────────────────
echo "Fetching Gitea release: {$tag}\n"; echo "Fetching Git release: {$tag}\n";
$giteaRelease = $this->giteaApi("{$apiBase}/releases/tags/{$tag}", $token); $gitRelease = $this->gitApi("{$apiBase}/releases/tags/{$tag}", $token);
if (!$giteaRelease || empty($giteaRelease['id'])) { if (!$gitRelease || empty($gitRelease['id'])) {
$this->log('ERROR', "No Gitea release found with tag: {$tag}"); $this->log('ERROR', "No Git release found with tag: {$tag}");
return 1; return 1;
} }
$giteaId = $giteaRelease['id']; $gitId = $gitRelease['id'];
$releaseName = $giteaRelease['name'] ?? "{$version}"; $releaseName = $gitRelease['name'] ?? "{$version}";
$releaseBody = $giteaRelease['body'] ?? ''; $releaseBody = $gitRelease['body'] ?? '';
$assets = $giteaRelease['assets'] ?? []; $assets = $gitRelease['assets'] ?? [];
echo " Name: {$releaseName}\n"; echo " Name: {$releaseName}\n";
echo " Assets: " . count($assets) . " file(s)\n"; echo " Assets: " . count($assets) . " file(s)\n";
@@ -109,7 +109,7 @@ class ReleaseMirrorCli extends CliFramework
echo " Created GitHub release (id: {$ghReleaseId})\n"; echo " Created GitHub release (id: {$ghReleaseId})\n";
} }
// ── Step 3: Download assets from Gitea ─────────────────────────────────────── // ── Step 3: Download assets from Git ───────────────────────────────────────
$tmpDir = sys_get_temp_dir() . '/moko-mirror-' . getmypid(); $tmpDir = sys_get_temp_dir() . '/moko-mirror-' . getmypid();
@mkdir($tmpDir, 0755, true); @mkdir($tmpDir, 0755, true);
@@ -126,7 +126,7 @@ class ReleaseMirrorCli extends CliFramework
$localPath = "{$tmpDir}/{$name}"; $localPath = "{$tmpDir}/{$name}";
echo " Downloading: {$name}\n"; echo " Downloading: {$name}\n";
if (!$this->giteaDownload($downloadUrl, $token, $localPath)) { if (!$this->gitDownload($downloadUrl, $token, $localPath)) {
$this->log('ERROR', " Failed to download: {$name}"); $this->log('ERROR', " Failed to download: {$name}");
continue; continue;
} }
@@ -151,7 +151,7 @@ class ReleaseMirrorCli extends CliFramework
return 0; return 0;
} }
private function giteaApi(string $url, string $token, string $method = 'GET', ?string $body = null): ?array private function gitApi(string $url, string $token, string $method = 'GET', ?string $body = null): ?array
{ {
$ch = curl_init($url); $ch = curl_init($url);
curl_setopt_array($ch, [ curl_setopt_array($ch, [
@@ -176,7 +176,7 @@ class ReleaseMirrorCli extends CliFramework
return json_decode($response, true) ?: null; return json_decode($response, true) ?: null;
} }
private function giteaDownload(string $url, string $token, string $dest): bool private function gitDownload(string $url, string $token, string $dest): bool
{ {
$ch = curl_init($url); $ch = curl_init($url);
$fp = fopen($dest, 'wb'); $fp = fopen($dest, 'wb');
+94 -16
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/release_package.php * PATH: /cli/release_package.php
* BRIEF: Build packages (ZIP + tar.gz) with SHA-256 and upload to Gitea release * BRIEF: Build packages (ZIP + tar.gz) with SHA-256 and upload to Git release
*/ */
declare(strict_types=1); declare(strict_types=1);
@@ -35,12 +35,12 @@ class ReleasePackageCli extends CliFramework
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Build packages (ZIP + tar.gz) with SHA-256 and upload to Gitea release'); $this->setDescription('Build packages (ZIP + tar.gz) with SHA-256 and upload to Git release');
$this->addArgument('--path', 'Repository root path', '.'); $this->addArgument('--path', 'Repository root path', '.');
$this->addArgument('--version', 'Release version', ''); $this->addArgument('--version', 'Release version', '');
$this->addArgument('--tag', 'Release tag name', ''); $this->addArgument('--tag', 'Release tag name', '');
$this->addArgument('--token', 'Gitea API token', ''); $this->addArgument('--token', 'Git API token', '');
$this->addArgument('--api-base', 'Gitea API base URL', ''); $this->addArgument('--api-base', 'Git API base URL', '');
$this->addArgument('--repo', 'Repo name for element detection fallback', ''); $this->addArgument('--repo', 'Repo name for element detection fallback', '');
$this->addArgument('--output', 'Output directory for built packages', ''); $this->addArgument('--output', 'Output directory for built packages', '');
} }
@@ -61,23 +61,23 @@ class ReleasePackageCli extends CliFramework
// Allow token from environment // Allow token from environment
if ($token === '' || $token === null) { if ($token === '' || $token === null) {
$token = getenv('MOKOGITEA_TOKEN') ?: (getenv('GITEA_TOKEN') ?: ''); $token = getenv('MOKOGIT_TOKEN') ?: (getenv('GIT_TOKEN') ?: '');
} }
if ($version === '' || $tag === '' || $token === '' || $apiBase === '') { if ($version === '' || $tag === '' || $token === '' || $apiBase === '') {
$this->log('ERROR', "Usage: release_package.php --path . --version VER --tag TAG --token TOKEN --api-base URL"); $this->log('ERROR', "Usage: release_package.php --path . --version VER --tag TAG --token TOKEN --api-base URL");
$this->log('ERROR', " --repo REPO Repo name for element detection fallback"); $this->log('ERROR', " --repo REPO Repo name for element detection fallback");
$this->log('ERROR', " --output DIR Output directory for built packages (default: sys_get_temp_dir())"); $this->log('ERROR', " --output DIR Output directory for built packages (default: sys_get_temp_dir())");
$this->log('ERROR', " Token can also be set via MOKOGITEA_TOKEN or GITEA_TOKEN env var"); $this->log('ERROR', " Token can also be set via MOKOGIT_TOKEN or GIT_TOKEN env var");
return 1; return 1;
} }
$root = realpath($path) ?: $path; $root = realpath($path) ?: $path;
// ── Read platform from .mokogitea/manifest.xml ─────────────────────── // ── Read platform from .mokogit/manifest.xml ───────────────────────
$detectedPlatform = 'generic'; $detectedPlatform = 'generic';
$detectedEntryPoint = ''; $detectedEntryPoint = '';
$mokoManifest = "{$root}/.mokogitea/manifest.xml"; $mokoManifest = "{$root}/.mokogit/manifest.xml";
if (file_exists($mokoManifest)) { if (file_exists($mokoManifest)) {
$mokoXml = @simplexml_load_file($mokoManifest); $mokoXml = @simplexml_load_file($mokoManifest);
if ($mokoXml !== false) { if ($mokoXml !== false) {
@@ -270,12 +270,90 @@ class ReleasePackageCli extends CliFramework
} }
} }
// Check if sub-source is itself a Joomla package (nested package)
$nestedPkgManifests = glob("{$subSourceDir}/pkg_*.xml") ?: [];
$isNestedPackage = !empty($nestedPkgManifests) && is_dir("{$subSourceDir}/packages");
$subZip = new \ZipArchive(); $subZip = new \ZipArchive();
if ($subZip->open($subZipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) { if ($subZip->open($subZipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) {
$this->log('ERROR', "Failed to create sub-package ZIP: {$subZipPath}"); $this->log('ERROR', "Failed to create sub-package ZIP: {$subZipPath}");
continue; continue;
} }
$this->addDirToZip($subZip, $subSourceDir, '', $this->excludePatterns);
if ($isNestedPackage) {
// Build nested package: zip each sub-extension, then assemble
echo " Building nested package: {$subName}\n";
$nestedPkgDirs = glob("{$subSourceDir}/packages/*", GLOB_ONLYDIR) ?: [];
$nestedManifested = [];
foreach ($nestedPkgManifests as $npmf) {
$npmXml = @simplexml_load_file($npmf);
if ($npmXml && isset($npmXml->files)) {
foreach ($npmXml->files->file as $fn) {
$nzn = pathinfo((string) $fn, PATHINFO_FILENAME);
if (!empty($nzn)) {
$nestedManifested[$nzn] = true;
}
}
}
}
// Use a subdirectory to avoid filename collisions with outer ZIPs
$nestedOutputDir = "{$outputDir}/nested_{$subName}";
@mkdir($nestedOutputDir, 0755, true);
foreach ($nestedPkgDirs as $npd) {
$nestedSubName = basename($npd);
if (!empty($nestedManifested) && !isset($nestedManifested[$nestedSubName])) {
continue;
}
$nestedSubZipPath = "{$nestedOutputDir}/{$nestedSubName}.zip";
$nsZip = new \ZipArchive();
if ($nsZip->open($nestedSubZipPath, \ZipArchive::CREATE | \ZipArchive::OVERWRITE) !== true) {
continue;
}
$this->addDirToZip($nsZip, $npd, '', $this->excludePatterns);
$nsZip->close();
$subZip->addFile($nestedSubZipPath, "packages/{$nestedSubName}.zip");
echo " Nested sub: {$nestedSubName}.zip\n";
}
// Patch nested manifest: ensure folder="packages" so Joomla finds sub-ZIPs
foreach ($nestedPkgManifests as $npmf) {
$npmContent = file_get_contents($npmf);
if ($npmContent !== false
&& strpos($npmContent, '<files>') !== false
&& strpos($npmContent, 'folder="packages"') === false
) {
$npmContent = str_replace('<files>', '<files folder="packages">', $npmContent);
file_put_contents($npmf, $npmContent);
echo " Fixed: added folder=\"packages\" to " . basename($npmf) . "\n";
}
}
// Add top-level files (manifest, script, etc.) with exclude filter
$nestedTopFiles = array_merge(
glob("{$subSourceDir}/*.xml") ?: [],
glob("{$subSourceDir}/*.php") ?: []
);
foreach ($nestedTopFiles as $ntf) {
if (!$this->isExcluded(basename($ntf), $this->excludePatterns)) {
$subZip->addFile($ntf, basename($ntf));
}
}
// Add all top-level directories except packages/
$nestedTopDirs = glob("{$subSourceDir}/*", GLOB_ONLYDIR) ?: [];
foreach ($nestedTopDirs as $ntd) {
$ndName = basename($ntd);
if ($ndName === 'packages') {
continue;
}
$this->addDirToZip($subZip, $ntd, $ndName, $this->excludePatterns);
}
} else {
$this->addDirToZip($subZip, $subSourceDir, '', $this->excludePatterns);
}
$subZip->close(); $subZip->close();
$zip->addFile($subZipPath, "packages/{$subName}.zip"); $zip->addFile($subZipPath, "packages/{$subName}.zip");
@@ -380,7 +458,7 @@ class ReleasePackageCli extends CliFramework
} }
// ── Get release ID from tag ────────────────────────────────────────── // ── Get release ID from tag ──────────────────────────────────────────
$result = $this->giteaApiRequest("{$apiBase}/releases/tags/{$tag}", $token); $result = $this->gitApiRequest("{$apiBase}/releases/tags/{$tag}", $token);
if ($result['data'] === null || !isset($result['data']['id'])) { if ($result['data'] === null || !isset($result['data']['id'])) {
$this->log('ERROR', "No release found for tag: {$tag} (HTTP {$result['code']})"); $this->log('ERROR', "No release found for tag: {$tag} (HTTP {$result['code']})");
return 1; return 1;
@@ -390,7 +468,7 @@ class ReleasePackageCli extends CliFramework
echo "Release ID: {$releaseId} (tag: {$tag})\n"; echo "Release ID: {$releaseId} (tag: {$tag})\n";
// ── Delete existing assets with same names ─────────────────────────── // ── Delete existing assets with same names ───────────────────────────
$assetsResult = $this->giteaApiRequest("{$apiBase}/releases/{$releaseId}/assets", $token); $assetsResult = $this->gitApiRequest("{$apiBase}/releases/{$releaseId}/assets", $token);
$existingAssets = $assetsResult['data'] ?? []; $existingAssets = $assetsResult['data'] ?? [];
$uploadNames = [ $uploadNames = [
@@ -407,7 +485,7 @@ class ReleasePackageCli extends CliFramework
$assetName = $asset['name'] ?? ''; $assetName = $asset['name'] ?? '';
$assetId = $asset['id'] ?? 0; $assetId = $asset['id'] ?? 0;
if (in_array($assetName, $uploadNames, true) && $assetId > 0) { if (in_array($assetName, $uploadNames, true) && $assetId > 0) {
$this->giteaApiRequest("{$apiBase}/releases/{$releaseId}/assets/{$assetId}", $token, 'DELETE'); $this->gitApiRequest("{$apiBase}/releases/{$releaseId}/assets/{$assetId}", $token, 'DELETE');
echo "Deleted existing asset: {$assetName}\n"; echo "Deleted existing asset: {$assetName}\n";
} }
} }
@@ -428,7 +506,7 @@ class ReleasePackageCli extends CliFramework
} }
$uploadUrl = "{$apiBase}/releases/{$releaseId}/assets?name=" . urlencode($name); $uploadUrl = "{$apiBase}/releases/{$releaseId}/assets?name=" . urlencode($name);
$httpCode = $this->giteaUploadAsset($uploadUrl, $token, $localPath); $httpCode = $this->gitUploadAsset($uploadUrl, $token, $localPath);
$status = ($httpCode >= 200 && $httpCode < 300) ? 'OK' : "FAILED ({$httpCode})"; $status = ($httpCode >= 200 && $httpCode < 300) ? 'OK' : "FAILED ({$httpCode})";
echo "Upload: {$name}{$status}\n"; echo "Upload: {$name}{$status}\n";
@@ -449,11 +527,11 @@ class ReleasePackageCli extends CliFramework
} }
/** /**
* Perform a Gitea API request. * Perform a Git API request.
* *
* @return array{data: array<string, mixed>|null, code: int} * @return array{data: array<string, mixed>|null, code: int}
*/ */
private function giteaApiRequest(string $url, string $token, string $method = 'GET', ?string $body = null): array private function gitApiRequest(string $url, string $token, string $method = 'GET', ?string $body = null): array
{ {
$ch = curl_init($url); $ch = curl_init($url);
if ($ch === false) { if ($ch === false) {
@@ -486,7 +564,7 @@ class ReleasePackageCli extends CliFramework
/** /**
* Upload a file as a release asset. * Upload a file as a release asset.
*/ */
private function giteaUploadAsset(string $url, string $token, string $filePath): int private function gitUploadAsset(string $url, string $token, string $filePath): int
{ {
$ch = curl_init($url); $ch = curl_init($url);
if ($ch === false) { if ($ch === false) {
+17 -17
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/release_promote.php * PATH: /cli/release_promote.php
* BRIEF: Promote a Gitea release from one channel to another (rename release, tag, assets) * BRIEF: Promote a Git release from one channel to another (rename release, tag, assets)
*/ */
declare(strict_types=1); declare(strict_types=1);
@@ -23,11 +23,11 @@ class ReleasePromoteCli extends CliFramework
{ {
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Promote a Gitea release from one channel to another'); $this->setDescription('Promote a Git release from one channel to another');
$this->addArgument('--from', 'Source channel (or "auto")', ''); $this->addArgument('--from', 'Source channel (or "auto")', '');
$this->addArgument('--to', 'Target channel (required)', ''); $this->addArgument('--to', 'Target channel (required)', '');
$this->addArgument('--token', 'Gitea API token', ''); $this->addArgument('--token', 'Git API token', '');
$this->addArgument('--api-base', 'Gitea API base URL for the repo', ''); $this->addArgument('--api-base', 'Git API base URL for the repo', '');
$this->addArgument('--path', 'Repository root for type prefix detection', '.'); $this->addArgument('--path', 'Repository root for type prefix detection', '.');
$this->addArgument('--branch', 'Target branch', 'main'); $this->addArgument('--branch', 'Target branch', 'main');
} }
@@ -41,7 +41,7 @@ class ReleasePromoteCli extends CliFramework
$path = $this->getArgument('--path'); $path = $this->getArgument('--path');
$branch = $this->getArgument('--branch'); $branch = $this->getArgument('--branch');
$token = $token ?: (getenv('MOKOGITEA_TOKEN') ?: (getenv('GITEA_TOKEN') ?: null)); $token = $token ?: (getenv('MOKOGIT_TOKEN') ?: (getenv('GIT_TOKEN') ?: null));
if ($to === null || $token === null || $apiBase === null) { if ($to === null || $token === null || $apiBase === null) {
$this->log('ERROR', "Usage: release_promote.php --from <channel|auto> --to <channel> --token TOKEN --api-base URL [--path .]"); $this->log('ERROR', "Usage: release_promote.php --from <channel|auto> --to <channel> --token TOKEN --api-base URL [--path .]");
@@ -64,7 +64,7 @@ class ReleasePromoteCli extends CliFramework
// ── Resolve --from auto ────────────────────────────────────────────────────── // ── Resolve --from auto ──────────────────────────────────────────────────────
if ($from === 'auto') { if ($from === 'auto') {
foreach ($channelOrder as $candidate) { foreach ($channelOrder as $candidate) {
$data = $this->giteaApi("{$apiBase}/releases/tags/{$candidate}", $token); $data = $this->gitApi("{$apiBase}/releases/tags/{$candidate}", $token);
if ($data && !empty($data['id'])) { if ($data && !empty($data['id'])) {
$from = $candidate; $from = $candidate;
echo "Auto-detected source channel: {$from}\n"; echo "Auto-detected source channel: {$from}\n";
@@ -78,7 +78,7 @@ class ReleasePromoteCli extends CliFramework
} }
// ── Find source release ────────────────────────────────────────────────────── // ── Find source release ──────────────────────────────────────────────────────
$sourceRelease = $this->giteaApi("{$apiBase}/releases/tags/{$from}", $token); $sourceRelease = $this->gitApi("{$apiBase}/releases/tags/{$from}", $token);
if (!$sourceRelease || empty($sourceRelease['id'])) { if (!$sourceRelease || empty($sourceRelease['id'])) {
$this->log('ERROR', "No release found with tag: {$from}"); $this->log('ERROR', "No release found with tag: {$from}");
return 1; return 1;
@@ -90,7 +90,7 @@ class ReleasePromoteCli extends CliFramework
echo "Source: {$from} (id: {$sourceId}) — {$sourceName}\n"; echo "Source: {$from} (id: {$sourceId}) — {$sourceName}\n";
// ── Get source assets ──────────────────────────────────────────────────────── // ── Get source assets ────────────────────────────────────────────────────────
$assets = $this->giteaApi("{$apiBase}/releases/{$sourceId}/assets", $token) ?: []; $assets = $this->gitApi("{$apiBase}/releases/{$sourceId}/assets", $token) ?: [];
echo "Assets: " . count($assets) . " file(s)\n"; echo "Assets: " . count($assets) . " file(s)\n";
// ── Download assets to temp ────────────────────────────────────────────────── // ── Download assets to temp ──────────────────────────────────────────────────
@@ -101,7 +101,7 @@ class ReleasePromoteCli extends CliFramework
$name = $asset['name']; $name = $asset['name'];
$downloadUrl = $asset['browser_download_url']; $downloadUrl = $asset['browser_download_url'];
echo " Downloading: {$name}\n"; echo " Downloading: {$name}\n";
$this->giteaDownload($downloadUrl, $token, "{$tmpDir}/{$name}"); $this->gitDownload($downloadUrl, $token, "{$tmpDir}/{$name}");
} }
// ── Detect type prefix for stable promotion ────────────────────────────────── // ── Detect type prefix for stable promotion ──────────────────────────────────
@@ -188,15 +188,15 @@ class ReleasePromoteCli extends CliFramework
} }
// ── Delete source release + tag ────────────────────────────────────────────── // ── Delete source release + tag ──────────────────────────────────────────────
$this->giteaApi("{$apiBase}/releases/{$sourceId}", $token, 'DELETE'); $this->gitApi("{$apiBase}/releases/{$sourceId}", $token, 'DELETE');
$this->giteaApi("{$apiBase}/tags/{$from}", $token, 'DELETE'); $this->gitApi("{$apiBase}/tags/{$from}", $token, 'DELETE');
echo "Deleted source: {$from} release + tag\n"; echo "Deleted source: {$from} release + tag\n";
// ── Delete existing target release + tag (if any) ──────────────────────────── // ── Delete existing target release + tag (if any) ────────────────────────────
$existingTarget = $this->giteaApi("{$apiBase}/releases/tags/{$to}", $token); $existingTarget = $this->gitApi("{$apiBase}/releases/tags/{$to}", $token);
if ($existingTarget && !empty($existingTarget['id'])) { if ($existingTarget && !empty($existingTarget['id'])) {
$this->giteaApi("{$apiBase}/releases/{$existingTarget['id']}", $token, 'DELETE'); $this->gitApi("{$apiBase}/releases/{$existingTarget['id']}", $token, 'DELETE');
$this->giteaApi("{$apiBase}/tags/{$to}", $token, 'DELETE'); $this->gitApi("{$apiBase}/tags/{$to}", $token, 'DELETE');
echo "Deleted existing target: {$to} release + tag\n"; echo "Deleted existing target: {$to} release + tag\n";
} }
@@ -217,7 +217,7 @@ class ReleasePromoteCli extends CliFramework
'prerelease' => $isPrerelease, 'prerelease' => $isPrerelease,
]); ]);
$newRelease = $this->giteaApi("{$apiBase}/releases", $token, 'POST', $payload); $newRelease = $this->gitApi("{$apiBase}/releases", $token, 'POST', $payload);
if (!$newRelease || empty($newRelease['id'])) { if (!$newRelease || empty($newRelease['id'])) {
$this->log('ERROR', "Failed to create {$to} release"); $this->log('ERROR', "Failed to create {$to} release");
return 1; return 1;
@@ -263,7 +263,7 @@ class ReleasePromoteCli extends CliFramework
return 0; return 0;
} }
private function giteaApi(string $url, string $token, string $method = 'GET', ?string $body = null): ?array private function gitApi(string $url, string $token, string $method = 'GET', ?string $body = null): ?array
{ {
$ch = curl_init($url); $ch = curl_init($url);
curl_setopt_array($ch, [ curl_setopt_array($ch, [
@@ -288,7 +288,7 @@ class ReleasePromoteCli extends CliFramework
return json_decode($response, true) ?: null; return json_decode($response, true) ?: null;
} }
private function giteaDownload(string $url, string $token, string $dest): bool private function gitDownload(string $url, string $token, string $dest): bool
{ {
$ch = curl_init($url); $ch = curl_init($url);
$fp = fopen($dest, 'wb'); $fp = fopen($dest, 'wb');
+17 -17
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/release_publish.php * PATH: /cli/release_publish.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Publish a release and create copies for all lesser stability streams. * BRIEF: Publish a release and create copies for all lesser stability streams.
*/ */
@@ -27,10 +27,10 @@ class ReleasePublishCli extends CliFramework
$this->setDescription('Publish a release and update stability streams'); $this->setDescription('Publish a release and update stability streams');
$this->addArgument('--path', 'Repository root (default: .)', '.'); $this->addArgument('--path', 'Repository root (default: .)', '.');
$this->addArgument('--stability', 'Target stability: dev|alpha|beta|rc|stable (required)', ''); $this->addArgument('--stability', 'Target stability: dev|alpha|beta|rc|stable (required)', '');
$this->addArgument('--token', 'Gitea API token (required)', ''); $this->addArgument('--token', 'Git API token (required)', '');
$this->addArgument('--bump', 'Version bump type: patch|minor|none (default: none)', 'none'); $this->addArgument('--bump', 'Version bump type: patch|minor|none (default: none)', 'none');
$this->addArgument('--branch', 'Current branch (default: auto-detect)', ''); $this->addArgument('--branch', 'Current branch (default: auto-detect)', '');
$this->addArgument('--gitea-url', 'Gitea URL', ''); $this->addArgument('--git-url', 'Git URL', '');
$this->addArgument('--org', 'Organization', ''); $this->addArgument('--org', 'Organization', '');
$this->addArgument('--repo', 'Repository name', ''); $this->addArgument('--repo', 'Repository name', '');
$this->addArgument('--repo-url', 'Repository URL for git auth', ''); $this->addArgument('--repo-url', 'Repository URL for git auth', '');
@@ -44,9 +44,9 @@ class ReleasePublishCli extends CliFramework
$token = $this->getArgument('--token'); $token = $this->getArgument('--token');
$bumpType = $this->getArgument('--bump'); $bumpType = $this->getArgument('--bump');
$branch = $this->getArgument('--branch'); $branch = $this->getArgument('--branch');
$giteaUrl = $this->getArgument('--gitea-url') ?: (getenv('GITEA_URL') ?: 'https://git.mokoconsulting.tech'); $gitUrl = $this->getArgument('--git-url') ?: (getenv('GIT_URL') ?: 'https://git.mokoconsulting.tech');
$org = $this->getArgument('--org') ?: (getenv('GITEA_ORG') ?: ''); $org = $this->getArgument('--org') ?: (getenv('GIT_ORG') ?: '');
$repo = $this->getArgument('--repo') ?: (getenv('GITEA_REPO') ?: ''); $repo = $this->getArgument('--repo') ?: (getenv('GIT_REPO') ?: '');
$repoUrl = $this->getArgument('--repo-url'); $repoUrl = $this->getArgument('--repo-url');
$skipUpdateStream = $this->getArgument('--skip-update-stream'); $skipUpdateStream = $this->getArgument('--skip-update-stream');
@@ -57,7 +57,7 @@ class ReleasePublishCli extends CliFramework
$cli = __DIR__; $cli = __DIR__;
$php = '"' . PHP_BINARY . '"'; $php = '"' . PHP_BINARY . '"';
$giteaUrl = rtrim($giteaUrl, '/'); $gitUrl = rtrim($gitUrl, '/');
// Resolve path early for shell commands (Windows needs native paths) // Resolve path early for shell commands (Windows needs native paths)
$resolvedPath = realpath($path) ?: $path; $resolvedPath = realpath($path) ?: $path;
@@ -81,7 +81,7 @@ class ReleasePublishCli extends CliFramework
// Auto-construct repo URL for git auth if not provided // Auto-construct repo URL for git auth if not provided
if (empty($repoUrl) && !empty($token) && !empty($org) && !empty($repo)) { if (empty($repoUrl) && !empty($token) && !empty($org) && !empty($repo)) {
$host = preg_replace('#^https?://#', '', $giteaUrl); $host = preg_replace('#^https?://#', '', $gitUrl);
$repoUrl = "https://x-access-token:{$token}@{$host}/{$org}/{$repo}.git"; $repoUrl = "https://x-access-token:{$token}@{$host}/{$org}/{$repo}.git";
} }
@@ -95,7 +95,7 @@ class ReleasePublishCli extends CliFramework
)); ));
} }
$apiBase = "{$giteaUrl}/api/v1/repos/{$org}/{$repo}"; $apiBase = "{$gitUrl}/api/v1/repos/{$org}/{$repo}";
// Stability ordering and suffix mapping // Stability ordering and suffix mapping
$allStabilities = ['dev', 'alpha', 'beta', 'rc', 'stable']; $allStabilities = ['dev', 'alpha', 'beta', 'rc', 'stable'];
@@ -189,11 +189,11 @@ class ReleasePublishCli extends CliFramework
$cdR = $cdPfx . escapeshellarg($root); $cdR = $cdPfx . escapeshellarg($root);
@shell_exec( @shell_exec(
$cdR . " && git config --local user.email" $cdR . " && git config --local user.email"
. " \"gitea-actions[bot]@mokoconsulting.tech\"" . " \"git-actions[bot]@mokoconsulting.tech\""
); );
@shell_exec( @shell_exec(
$cdR . " && git config --local user.name" $cdR . " && git config --local user.name"
. " \"gitea-actions[bot]\"" . " \"git-actions[bot]\""
); );
if (!empty($repoUrl)) { if (!empty($repoUrl)) {
@shell_exec( @shell_exec(
@@ -239,8 +239,8 @@ class ReleasePublishCli extends CliFramework
@shell_exec( @shell_exec(
$cdR . " && git commit -m " $cdR . " && git commit -m "
. escapeshellarg($commitMsg) . escapeshellarg($commitMsg)
. " --author=\"gitea-actions[bot]" . " --author=\"git-actions[bot]"
. " <gitea-actions[bot]@mokoconsulting.tech>\"" . " <git-actions[bot]@mokoconsulting.tech>\""
); );
$pushResult = @shell_exec( $pushResult = @shell_exec(
$cdR . " && git push origin " $cdR . " && git push origin "
@@ -314,7 +314,7 @@ class ReleasePublishCli extends CliFramework
passthru("{$php} {$cli}/updates_xml_build.php --path " . escapeshellarg($path) passthru("{$php} {$cli}/updates_xml_build.php --path " . escapeshellarg($path)
. " --version " . escapeshellarg($streamVersion) . " --version " . escapeshellarg($streamVersion)
. " --stability " . escapeshellarg($stream) . " --stability " . escapeshellarg($stream)
. " --gitea-url " . escapeshellarg($giteaUrl) . " --git-url " . escapeshellarg($gitUrl)
. " --org " . escapeshellarg($org) . " --org " . escapeshellarg($org)
. " --repo " . escapeshellarg($repo) . " --repo " . escapeshellarg($repo)
. " {$shaFlag} 2>&1"); . " {$shaFlag} 2>&1");
@@ -339,8 +339,8 @@ class ReleasePublishCli extends CliFramework
@shell_exec( @shell_exec(
$cdRt . " && git commit -m " $cdRt . " && git commit -m "
. escapeshellarg($chMsg) . escapeshellarg($chMsg)
. " --author=\"gitea-actions[bot]" . " --author=\"git-actions[bot]"
. " <gitea-actions[bot]@mokoconsulting.tech>\"" . " <git-actions[bot]@mokoconsulting.tech>\""
); );
@shell_exec( @shell_exec(
$cdRt . " && git push origin " $cdRt . " && git push origin "
@@ -354,7 +354,7 @@ class ReleasePublishCli extends CliFramework
. " --current " . escapeshellarg($branch) . " --all" . " --current " . escapeshellarg($branch) . " --all"
. " --version " . escapeshellarg($releaseVersion) . " --version " . escapeshellarg($releaseVersion)
. " --token " . escapeshellarg($token) . " --token " . escapeshellarg($token)
. " --gitea-url " . escapeshellarg($giteaUrl) . " --git-url " . escapeshellarg($gitUrl)
. " --org " . escapeshellarg($org) . " --org " . escapeshellarg($org)
. " --repo " . escapeshellarg($repo) . " 2>&1"); . " --repo " . escapeshellarg($repo) . " 2>&1");
} else { } else {
+1 -1
View File
@@ -49,7 +49,7 @@ class ReleaseValidateCli extends CliFramework
} }
$root = realpath($path) ?: $path; $root = realpath($path) ?: $path;
if ($platform === null) { if ($platform === null) {
$manifestXml = "{$root}/.mokogitea/manifest.xml"; $manifestXml = "{$root}/.mokogit/manifest.xml";
if (file_exists($manifestXml)) { if (file_exists($manifestXml)) {
$mContent = file_get_contents($manifestXml); $mContent = file_get_contents($manifestXml);
if (preg_match('/<platform>([^<]+)<\/platform>/', $mContent, $pm)) { if (preg_match('/<platform>([^<]+)<\/platform>/', $mContent, $pm)) {
+5 -5
View File
@@ -22,7 +22,7 @@ use MokoCli\{CliFramework, Config, PlatformAdapterFactory};
* Interactive repo setup wizard. * Interactive repo setup wizard.
* *
* Walks through platform selection, generates config files, workflows, * Walks through platform selection, generates config files, workflows,
* and optionally creates the repo on Gitea via API. * and optionally creates the repo on Git via API.
* *
* @see https://git.mokoconsulting.tech/MokoConsulting/mokocli/issues/145 * @see https://git.mokoconsulting.tech/MokoConsulting/mokocli/issues/145
*/ */
@@ -54,7 +54,7 @@ class RepoWizard extends CliFramework
{ {
$this->setDescription('Interactive configuration wizard for new repositories'); $this->setDescription('Interactive configuration wizard for new repositories');
$this->addArgument('--path', 'Directory to generate files in', '.'); $this->addArgument('--path', 'Directory to generate files in', '.');
$this->addArgument('--create-remote', 'Create repo on Gitea via API', false); $this->addArgument('--create-remote', 'Create repo on Git via API', false);
$this->addArgument('--non-interactive', 'Use defaults (no prompts)', false); $this->addArgument('--non-interactive', 'Use defaults (no prompts)', false);
} }
@@ -155,8 +155,8 @@ class RepoWizard extends CliFramework
break; break;
} }
// .mokogitea/workflows // .mokogit/workflows
$generated[] = $this->writeFile($path, '.mokogitea/workflows/pr-check.yml', $generated[] = $this->writeFile($path, '.mokogit/workflows/pr-check.yml',
"# PR check workflow — synced from mokocli templates\n# Run: moko sync to update\n"); "# PR check workflow — synced from mokocli templates\n# Run: moko sync to update\n");
// .gitignore // .gitignore
@@ -205,7 +205,7 @@ class RepoWizard extends CliFramework
$org = $this->answers['org']; $org = $this->answers['org'];
if ($this->dryRun) { if ($this->dryRun) {
$this->log('INFO', "[dry-run] Would create {$org}/{$this->answers['name']} on Gitea"); $this->log('INFO', "[dry-run] Would create {$org}/{$this->answers['name']} on Git");
return; return;
} }
+19 -19
View File
@@ -12,8 +12,8 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/scaffold_client.php * PATH: /cli/scaffold_client.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Scaffold a new client-waas repo from Template-Client-WaaS with pre-configured settings * BRIEF: Scaffold a new client-waas repo from Template-Client with pre-configured settings
*/ */
declare(strict_types=1); declare(strict_types=1);
@@ -26,18 +26,18 @@ class ScaffoldClientCli extends CliFramework
{ {
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Scaffold a new client-waas repo from Template-Client-WaaS'); $this->setDescription('Scaffold a new client-waas repo from Template-Client');
$this->addArgument('--name', 'Client name', ''); $this->addArgument('--name', 'Client name', '');
$this->addArgument('--org', 'Gitea organization', ''); $this->addArgument('--org', 'Git organization', '');
$this->addArgument('--gitea-url', 'Gitea URL', 'https://git.mokoconsulting.tech'); $this->addArgument('--git-url', 'Git URL', 'https://git.mokoconsulting.tech');
$this->addArgument('--token', 'Gitea API token', ''); $this->addArgument('--token', 'Git API token', '');
} }
protected function run(): int protected function run(): int
{ {
$name = $this->getArgument('--name'); $name = $this->getArgument('--name');
$org = $this->getArgument('--org'); $org = $this->getArgument('--org');
$giteaUrl = rtrim($this->getArgument('--gitea-url'), '/'); $gitUrl = rtrim($this->getArgument('--git-url'), '/');
$token = $this->getArgument('--token'); $token = $this->getArgument('--token');
if ($name === '' || $org === '' || $token === '') { if ($name === '' || $org === '' || $token === '') {
$this->log('ERROR', '--name, --org, and --token are required.'); $this->log('ERROR', '--name, --org, and --token are required.');
@@ -45,11 +45,11 @@ class ScaffoldClientCli extends CliFramework
} }
$repoName = 'client-waas-' . $name; $repoName = 'client-waas-' . $name;
$this->log('INFO', "Scaffolding client repo: {$org}/{$repoName}"); $this->log('INFO', "Scaffolding client repo: {$org}/{$repoName}");
$this->log('INFO', "Gitea URL: {$giteaUrl}"); $this->log('INFO', "Git URL: {$gitUrl}");
if ($this->dryRun) { if ($this->dryRun) {
$this->log('INFO', '[DRY RUN] Would create repo from template MokoConsulting/Template-Client-WaaS'); $this->log('INFO', '[DRY RUN] Would create repo from template MokoConsulting/Template-Client');
$this->log('INFO', "[DRY RUN] Repo: {$org}/{$repoName}"); $this->log('INFO', "[DRY RUN] Repo: {$org}/{$repoName}");
$this->printPostSetupInstructions($repoName, $giteaUrl, $org); $this->printPostSetupInstructions($repoName, $gitUrl, $org);
return 0; return 0;
} }
$this->log('INFO', 'Step 1: Creating repo from template...'); $this->log('INFO', 'Step 1: Creating repo from template...');
@@ -64,8 +64,8 @@ class ScaffoldClientCli extends CliFramework
]); ]);
$response = $this->apiRequest( $response = $this->apiRequest(
'POST', 'POST',
"/api/v1/repos/MokoConsulting/Template-Client-WaaS/generate", "/api/v1/repos/MokoConsulting/Template-Client/generate",
$giteaUrl, $gitUrl,
$token, $token,
$createPayload $createPayload
); );
@@ -75,12 +75,12 @@ class ScaffoldClientCli extends CliFramework
} }
$this->log('INFO', "Repo created: {$org}/{$repoName}"); $this->log('INFO', "Repo created: {$org}/{$repoName}");
$this->log('INFO', 'Step 2: Updating repo description...'); $this->log('INFO', 'Step 2: Updating repo description...');
$this->apiRequest('PATCH', "/api/v1/repos/{$org}/{$repoName}", $giteaUrl, $token, json_encode(['description' => "{$name} WaaS site"])); $this->apiRequest('PATCH', "/api/v1/repos/{$org}/{$repoName}", $gitUrl, $token, json_encode(['description' => "{$name} WaaS site"]));
$this->log('INFO', 'Step 3: Creating dev branch from main...'); $this->log('INFO', 'Step 3: Creating dev branch from main...');
$response = $this->apiRequest( $response = $this->apiRequest(
'POST', 'POST',
"/api/v1/repos/{$org}/{$repoName}/branches", "/api/v1/repos/{$org}/{$repoName}/branches",
$giteaUrl, $gitUrl,
$token, $token,
json_encode([ json_encode([
'new_branch_name' => 'dev', 'new_branch_name' => 'dev',
@@ -92,15 +92,15 @@ class ScaffoldClientCli extends CliFramework
} else { } else {
$this->log('WARN', "Could not create dev branch (HTTP {$response['code']})."); $this->log('WARN', "Could not create dev branch (HTTP {$response['code']}).");
} }
$this->printPostSetupInstructions($repoName, $giteaUrl, $org); $this->printPostSetupInstructions($repoName, $gitUrl, $org);
$this->log('INFO', 'Scaffold complete.'); $this->log('INFO', 'Scaffold complete.');
return 0; return 0;
} }
private function printPostSetupInstructions(string $repoName, string $giteaUrl, string $org): void private function printPostSetupInstructions(string $repoName, string $gitUrl, string $org): void
{ {
fwrite(STDERR, "\n=== POST-SETUP INSTRUCTIONS ===\n\n" fwrite(STDERR, "\n=== POST-SETUP INSTRUCTIONS ===\n\n"
. "Navigate to: {$giteaUrl}/{$org}/{$repoName}/settings\n\n" . "Navigate to: {$gitUrl}/{$org}/{$repoName}/settings\n\n"
. "Set REPO VARIABLES:\n" . "Set REPO VARIABLES:\n"
. " DEV_SYNC_HOST, DEV_SYNC_PORT, DEV_SYNC_USER, DEV_SYNC_PATH\n" . " DEV_SYNC_HOST, DEV_SYNC_PORT, DEV_SYNC_USER, DEV_SYNC_PATH\n"
. " LIVE_SSH_HOST, LIVE_SSH_PORT, LIVE_SSH_USER, LIVE_SYNC_PATH\n\n" . " LIVE_SSH_HOST, LIVE_SSH_PORT, LIVE_SSH_USER, LIVE_SYNC_PATH\n\n"
@@ -109,10 +109,10 @@ class ScaffoldClientCli extends CliFramework
. "================================\n"); . "================================\n");
} }
private function apiRequest(string $method, string $endpoint, string $giteaUrl, string $token, ?string $body = null): array private function apiRequest(string $method, string $endpoint, string $gitUrl, string $token, ?string $body = null): array
{ {
$ch = curl_init(); $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $giteaUrl . $endpoint); curl_setopt($ch, CURLOPT_URL, $gitUrl . $endpoint);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json', "Authorization: token {$token}"]); curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', 'Accept: application/json', "Authorization: token {$token}"]);
+294
View File
@@ -0,0 +1,294 @@
#!/usr/bin/env php
<?php
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* FILE INFORMATION
* DEFGROUP: mokocli.CLI
* INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/theme_vars_check.php
* BRIEF: Validate a client MokoOnyx theme package — required CSS variables
* (derived dynamically from the MokoOnyx standard theme) are defined in
* the client's light/dark custom CSS, required files exist, the manifest
* is sane, and (optionally) the repo's Git metadata is set.
*
* Standalone (no CliFramework dependency) so it runs even when the shared
* framework autoloader is unavailable.
*
* Usage:
* php theme_vars_check.php --path . --reference /tmp/mokoonyx [--github-output]
* [--api-base <url> --repo <owner/repo> --token <token>]
*/
declare(strict_types=1);
$opts = getopt('', ['path:', 'reference:', 'github-output', 'api-base:', 'repo:', 'token:']);
$path = isset($opts['path']) && is_string($opts['path']) && $opts['path'] !== '' ? $opts['path'] : '.';
$ref = isset($opts['reference']) && is_string($opts['reference']) ? $opts['reference'] : '';
$gh = array_key_exists('github-output', $opts);
$root = realpath($path);
if ($root === false) { $root = $path; }
$src = is_dir("$root/src") ? "$root/src" : (is_dir("$root/source") ? "$root/source" : null);
if ($src === null) {
fwrite(STDERR, "ERROR: no src/ or source/ directory under $root\n");
exit(1);
}
$errors = 0;
$summary = [];
$fail = function (string $msg) use (&$errors, &$summary): void {
echo " [FAIL] $msg\n";
$summary[] = "FAIL: $msg";
$errors++;
};
$ok = function (string $msg): void { echo " [ok] $msg\n"; };
$note = function (string $msg): void { echo " [note] $msg\n"; };
/** Extract the set of CSS custom-property names DEFINED in a CSS string. */
$definedVars = static function (string $css): array {
// Matches "--name:" at a declaration position (not var(--name) uses).
preg_match_all('/(?:^|[\s;{])(--[a-z0-9_-]+)\s*:/i', $css, $m);
return array_values(array_unique(array_map('strtolower', $m[1] ?? [])));
};
/** Find the MokoOnyx standard theme file for a mode inside a reference checkout. */
$findStandard = static function (string $ref, string $mode): ?string {
if ($ref === '') { return null; }
foreach ([
"$ref/source/media/css/theme/$mode.standard.css",
"$ref/media/templates/site/mokoonyx/css/theme/$mode.standard.css",
"$ref/$mode.standard.css",
] as $cand) {
if (is_file($cand)) { return $cand; }
}
return null;
};
$cssDir = "$src/media/templates/site/mokoonyx/css";
$themeDir = "$cssDir/theme";
$manifest = "$src/templateDetails.xml";
$customs = ['light' => "$themeDir/light.custom.css", 'dark' => "$themeDir/dark.custom.css"];
echo "MokoOnyx theme validation: $src\n\n";
// 1) Required files -------------------------------------------------------
echo "=== Required files ===\n";
$requiredFiles = [
'templateDetails.xml' => $manifest,
'theme/light.custom.css' => $customs['light'],
'theme/dark.custom.css' => $customs['dark'],
'user.css' => "$cssDir/user.css",
];
foreach ($requiredFiles as $label => $file) {
is_file($file) ? $ok("$label present") : $fail("missing required file: $label");
}
// 1b) Forbidden committed files ------------------------------------------
echo "\n=== Forbidden files ===\n";
$scan = static function (string $dir, array $skip) use (&$scan): array {
$out = [];
foreach (scandir($dir) ?: [] as $e) {
if ($e === '.' || $e === '..') { continue; }
$p = "$dir/$e";
if (is_dir($p)) {
if (in_array($e, $skip, true)) { continue; }
$out = array_merge($out, $scan($p, $skip));
} else {
$out[] = $p;
}
}
return $out;
};
$forbidden = [];
if (is_dir("$root/.claude")) { $forbidden[] = '.claude/'; }
foreach ($scan($root, ['.git', 'vendor', 'node_modules']) as $f) {
$b = basename($f);
$rel = ltrim(str_replace('\\', '/', substr($f, strlen($root))), '/');
if ($b === '.mcp.json' || $b === 'TODO.md'
|| fnmatch('.mcp_*.json', $b) || fnmatch('sftp-config*.json', $b)
|| fnmatch('*.min.css', $b) || fnmatch('*.min.js', $b)) {
$forbidden[] = $rel;
}
}
$forbidden = array_values(array_unique($forbidden));
if ($forbidden) {
$shown = array_slice($forbidden, 0, 10);
$more = count($forbidden) - count($shown);
$fail('forbidden file(s) committed: ' . implode(', ', $shown) . ($more > 0 ? " (+$more more)" : ''));
} else {
$ok('no forbidden files committed');
}
// 2) CSS variables — required set derived from the MokoOnyx standard theme
echo "\n=== CSS variables vs MokoOnyx standard theme ===\n";
if ($ref === '') {
$note('no --reference given; skipping variable parity check');
} else {
foreach ($customs as $mode => $customFile) {
$std = $findStandard($ref, $mode);
if ($std === null) {
$fail("$mode: standard theme not found under reference '$ref'");
continue;
}
if (!is_file($customFile)) {
continue; // already reported missing
}
$required = $definedVars((string) file_get_contents($std));
$defined = $definedVars((string) file_get_contents($customFile));
$missing = array_values(array_diff($required, $defined));
if ($missing) {
$shown = array_slice($missing, 0, 15);
$more = count($missing) - count($shown);
$fail("$mode mode missing " . count($missing) . '/' . count($required)
. ' standard variable(s): ' . implode(', ', $shown) . ($more > 0 ? " (+$more more)" : ''));
} else {
$ok("$mode mode defines all " . count($required) . ' standard variables');
}
}
}
// 3) Manifest sanity ------------------------------------------------------
echo "\n=== Manifest (templateDetails.xml) ===\n";
if (is_file($manifest)) {
$xml = @simplexml_load_file($manifest);
if ($xml === false) {
$fail('templateDetails.xml is not well-formed XML');
} else {
$version = isset($xml->version) ? trim((string) $xml->version) : '';
$version !== '' ? $ok("version $version") : $fail('<version> is missing or empty');
$server = isset($xml->updateservers->server) ? trim((string) $xml->updateservers->server) : '';
if ($server === '') {
$fail('<updateservers><server> is missing');
} elseif (strpos($server, '/raw/branch/') !== false) {
$fail('update server uses a legacy raw/branch URL; use the dynamic MokoGIT feed');
} else {
$ok("update server: $server");
}
isset($xml->dlid) ? $ok('<dlid> license-key field present')
: $fail('<dlid prefix="dlid=" suffix=""/> is missing');
// Element naming convention (MokoOnyx client theme file package)
$type = trim((string) ($xml['type'] ?? ''));
$element = isset($xml->element) ? trim((string) $xml->element) : '';
if ($type === 'file' && $element !== '') {
strpos($element, 'file_mokoonyx_') === 0
? $ok("element naming ok ($element)")
: $fail("element '$element' should start with 'file_mokoonyx_' for a MokoOnyx client theme package");
}
// Required manifest fields
foreach (['name', 'element', 'author', 'creationDate'] as $field) {
(isset($xml->$field) && trim((string) $xml->$field) !== '')
? $ok("<$field> present")
: $fail("<$field> is missing or empty");
}
// scriptfile (if declared) must exist
if (isset($xml->scriptfile)) {
$sf = trim((string) $xml->scriptfile);
is_file("$src/$sf") ? $ok("scriptfile '$sf' present")
: $fail("<scriptfile> references '$sf' which is not in src/");
}
// Fileset integrity — every referenced file/folder must exist in src/
if (isset($xml->fileset)) {
$missingFs = [];
foreach ($xml->fileset->files as $files) {
$folder = trim((string) ($files['folder'] ?? ''));
$baseDir = $folder !== '' ? "$src/$folder" : $src;
foreach ($files->filename as $fn) {
$rel = ($folder !== '' ? "$folder/" : '') . trim((string) $fn);
if (!is_file("$baseDir/" . trim((string) $fn))) { $missingFs[] = $rel; }
}
foreach ($files->folder as $fd) {
$rel = ($folder !== '' ? "$folder/" : '') . trim((string) $fd) . '/';
if (!is_dir("$baseDir/" . trim((string) $fd))) { $missingFs[] = $rel; }
}
}
if ($missingFs) {
$shown = array_slice($missingFs, 0, 10);
$more = count($missingFs) - count($shown);
$fail('fileset references ' . count($missingFs) . ' missing path(s): '
. implode(', ', $shown) . ($more > 0 ? " (+$more more)" : ''));
} else {
$ok('all fileset paths exist in src/');
}
}
}
}
// 4) Repository metadata via Git API (optional) -------------------------
$apiBase = isset($opts['api-base']) && is_string($opts['api-base']) ? rtrim($opts['api-base'], '/') : '';
$repoSlug = isset($opts['repo']) && is_string($opts['repo']) ? trim($opts['repo']) : '';
$token = isset($opts['token']) && is_string($opts['token']) ? trim($opts['token']) : '';
if ($apiBase !== '' && $repoSlug !== '' && $token !== '') {
echo "\n=== Repository metadata (Git API) ===\n";
$url = "$apiBase/repos/$repoSlug";
$ctx = stream_context_create(['http' => [
'method' => 'GET',
'header' => "Authorization: token $token\r\nAccept: application/json\r\n",
'ignore_errors' => true,
'timeout' => 15,
]]);
$resp = @file_get_contents($url, false, $ctx);
$data = $resp !== false ? json_decode($resp, true) : null;
if (!is_array($data) || !isset($data['name'])) {
$fail("could not read repo metadata from Git API ($url)");
} else {
trim((string) ($data['description'] ?? '')) !== '' ? $ok('description set')
: $fail('repo description is empty');
trim((string) ($data['website'] ?? '')) !== '' ? $ok('website set: ' . $data['website'])
: $fail('repo website is empty');
$topics = $data['topics'] ?? [];
(is_array($topics) && count($topics) > 0) ? $ok(count($topics) . ' topic(s) set')
: $fail('repo has no topics');
// Required topics
$requiredTopics = ['joomla', 'client-waas', 'mokoonyx'];
$haveTopics = array_map('strtolower', is_array($topics) ? $topics : []);
$missingTopics = array_values(array_diff($requiredTopics, $haveTopics));
$missingTopics
? $fail('missing required topic(s): ' . implode(', ', $missingTopics))
: $ok('required topics present (' . implode(', ', $requiredTopics) . ')');
((string) ($data['default_branch'] ?? '')) === 'main' ? $ok('default branch is main')
: $fail("default branch is '" . ($data['default_branch'] ?? '') . "' (expected main)");
// Branch protection on main
$bresp = @file_get_contents("$apiBase/repos/$repoSlug/branches/main", false, $ctx);
$bdata = $bresp !== false ? json_decode($bresp, true) : null;
if (is_array($bdata) && array_key_exists('protected', $bdata)) {
$bdata['protected'] ? $ok('main branch is protected')
: $fail('main branch is not protected');
} else {
$note('could not read branch protection state for main');
}
}
} else {
echo "\n=== Repository metadata (Git API) ===\n";
$note('API args not provided; skipping repo metadata check');
}
// Output / exit -----------------------------------------------------------
echo "\n";
if ($gh && ($f = getenv('GITHUB_STEP_SUMMARY'))) {
$md = "## MokoOnyx Theme Validation\n\n";
$md .= $errors === 0
? "All checks passed.\n"
: implode("\n", array_map(static fn ($l) => "- $l", $summary)) . "\n";
@file_put_contents($f, $md, FILE_APPEND);
}
if ($errors > 0) {
echo "FAILED: $errors issue(s) found.\n";
exit(1);
}
echo "All MokoOnyx theme validation checks passed.\n";
exit(0);
+10 -10
View File
@@ -28,7 +28,7 @@ class UpdatesXmlBuildCli extends CliFramework
$this->addArgument('--version', 'Version string (required)', ''); $this->addArgument('--version', 'Version string (required)', '');
$this->addArgument('--stability', 'One of: stable, rc, beta, alpha, development (default: stable)', 'stable'); $this->addArgument('--stability', 'One of: stable, rc, beta, alpha, development (default: stable)', 'stable');
$this->addArgument('--sha', 'SHA-256 hash of the ZIP package', ''); $this->addArgument('--sha', 'SHA-256 hash of the ZIP package', '');
$this->addArgument('--gitea-url', 'Gitea instance URL', ''); $this->addArgument('--git-url', 'Git instance URL', '');
$this->addArgument('--org', 'Organization', ''); $this->addArgument('--org', 'Organization', '');
$this->addArgument('--repo', 'Repository name', ''); $this->addArgument('--repo', 'Repository name', '');
$this->addArgument('--output', 'Output file path (default: updates.xml in --path)', ''); $this->addArgument('--output', 'Output file path (default: updates.xml in --path)', '');
@@ -41,9 +41,9 @@ class UpdatesXmlBuildCli extends CliFramework
$version = $this->getArgument('--version'); $version = $this->getArgument('--version');
$stability = $this->getArgument('--stability'); $stability = $this->getArgument('--stability');
$sha = $this->getArgument('--sha') ?: null; $sha = $this->getArgument('--sha') ?: null;
$giteaUrl = $this->getArgument('--gitea-url') ?: (getenv('GITEA_URL') ?: 'https://git.mokoconsulting.tech'); $gitUrl = $this->getArgument('--git-url') ?: (getenv('GIT_URL') ?: 'https://git.mokoconsulting.tech');
$org = $this->getArgument('--org') ?: (getenv('GITEA_ORG') ?: ''); $org = $this->getArgument('--org') ?: (getenv('GIT_ORG') ?: '');
$repo = $this->getArgument('--repo') ?: (getenv('GITEA_REPO') ?: ''); $repo = $this->getArgument('--repo') ?: (getenv('GIT_REPO') ?: '');
$outputFile = $this->getArgument('--output') ?: null; $outputFile = $this->getArgument('--output') ?: null;
$githubOutput = $this->getArgument('--github-output'); $githubOutput = $this->getArgument('--github-output');
@@ -57,12 +57,12 @@ class UpdatesXmlBuildCli extends CliFramework
$root = realpath($path) ?: $path; $root = realpath($path) ?: $path;
// -- Read platform from .mokogitea/manifest.xml -------------------------------- // -- Read platform from .mokogit/manifest.xml --------------------------------
$detectedPlatform = 'joomla'; $detectedPlatform = 'joomla';
$detectedName = $repo; $detectedName = $repo;
$detectedPackageType = ''; $detectedPackageType = '';
$detectedDisplayName = ''; $detectedDisplayName = '';
$mokoManifest = "{$root}/.mokogitea/manifest.xml"; $mokoManifest = "{$root}/.mokogit/manifest.xml";
if (file_exists($mokoManifest)) { if (file_exists($mokoManifest)) {
$mokoXml = @simplexml_load_file($mokoManifest); $mokoXml = @simplexml_load_file($mokoManifest);
if ($mokoXml !== false) { if ($mokoXml !== false) {
@@ -312,12 +312,12 @@ class UpdatesXmlBuildCli extends CliFramework
// -- Write ONLY the single channel being released -------------------------------- // -- Write ONLY the single channel being released --------------------------------
$entries = []; $entries = [];
$giteaTag = $releaseTagMap[$stability] ?? $stability; $gitTag = $releaseTagMap[$stability] ?? $stability;
$channelVersion = $version . ($stabilitySuffixMap[$stability] ?? ''); $channelVersion = $version . ($stabilitySuffixMap[$stability] ?? '');
$channelDownloadUrl = "{$giteaUrl}/{$org}/{$repo}/releases/download/{$giteaTag}/{$typePrefix}{$extElement}-{$channelVersion}.zip"; $channelDownloadUrl = "{$gitUrl}/{$org}/{$repo}/releases/download/{$gitTag}/{$typePrefix}{$extElement}-{$channelVersion}.zip";
$channelInfoUrl = "{$giteaUrl}/{$org}/{$repo}/releases/tag/{$giteaTag}"; $channelInfoUrl = "{$gitUrl}/{$org}/{$repo}/releases/tag/{$gitTag}";
$joomlaTag = $stabilityTagMap[$stability] ?? $stability; $joomlaTag = $stabilityTagMap[$stability] ?? $stability;
$changelogUrl = "{$giteaUrl}/{$org}/{$repo}/raw/branch/main/CHANGELOG.md"; $changelogUrl = "{$gitUrl}/{$org}/{$repo}/raw/branch/main/CHANGELOG.md";
$entries[] = $this->buildEntry( $entries[] = $this->buildEntry(
$joomlaTag, $joomlaTag,
+17 -17
View File
@@ -10,8 +10,8 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/updates_xml_sync.php * PATH: /cli/updates_xml_sync.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Sync updates.xml to target branches via Gitea API * BRIEF: Sync updates.xml to target branches via Git API
* NOTE: Called by pre-release and auto-release workflows after updates.xml * NOTE: Called by pre-release and auto-release workflows after updates.xml
* is modified on the current branch. Pushes the file to other branches * is modified on the current branch. Pushes the file to other branches
* without requiring a git checkout (avoids merge conflicts). * without requiring a git checkout (avoids merge conflicts).
@@ -27,14 +27,14 @@ class UpdatesXmlSyncCli extends CliFramework
{ {
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Sync updates.xml to target branches via Gitea API'); $this->setDescription('Sync updates.xml to target branches via Git API');
$this->addArgument('--path', 'Repository root containing updates.xml', '.'); $this->addArgument('--path', 'Repository root containing updates.xml', '.');
$this->addArgument('--branches', 'Comma-separated target branches to sync to', 'main,dev'); $this->addArgument('--branches', 'Comma-separated target branches to sync to', 'main,dev');
$this->addArgument('--all', 'Auto-discover all branches via Gitea API', false); $this->addArgument('--all', 'Auto-discover all branches via Git API', false);
$this->addArgument('--current', 'Current branch to skip (required)', ''); $this->addArgument('--current', 'Current branch to skip (required)', '');
$this->addArgument('--version', 'Version string for commit message', ''); $this->addArgument('--version', 'Version string for commit message', '');
$this->addArgument('--token', 'Gitea API token', ''); $this->addArgument('--token', 'Git API token', '');
$this->addArgument('--gitea-url', 'Gitea instance URL', ''); $this->addArgument('--git-url', 'Git instance URL', '');
$this->addArgument('--org', 'Organization', ''); $this->addArgument('--org', 'Organization', '');
$this->addArgument('--repo', 'Repository name', ''); $this->addArgument('--repo', 'Repository name', '');
} }
@@ -47,22 +47,22 @@ class UpdatesXmlSyncCli extends CliFramework
$current = $this->getArgument('--current'); $current = $this->getArgument('--current');
$version = $this->getArgument('--version'); $version = $this->getArgument('--version');
$token = $this->getArgument('--token'); $token = $this->getArgument('--token');
$giteaUrl = $this->getArgument('--gitea-url'); $gitUrl = $this->getArgument('--git-url');
$org = $this->getArgument('--org'); $org = $this->getArgument('--org');
$repo = $this->getArgument('--repo'); $repo = $this->getArgument('--repo');
// Fall back to environment variables // Fall back to environment variables
if ($token === '') { if ($token === '') {
$token = getenv('MOKOGITEA_TOKEN') ?: ''; $token = getenv('MOKOGIT_TOKEN') ?: '';
} }
if ($giteaUrl === '') { if ($gitUrl === '') {
$giteaUrl = getenv('GITEA_URL') ?: 'https://git.mokoconsulting.tech'; $gitUrl = getenv('GIT_URL') ?: 'https://git.mokoconsulting.tech';
} }
if ($org === '') { if ($org === '') {
$org = getenv('GITEA_ORG') ?: ''; $org = getenv('GIT_ORG') ?: '';
} }
if ($repo === '') { if ($repo === '') {
$repo = getenv('GITEA_REPO') ?: ''; $repo = getenv('GIT_REPO') ?: '';
} }
if ($current === '') { if ($current === '') {
@@ -71,18 +71,18 @@ class UpdatesXmlSyncCli extends CliFramework
} }
if ($token === '') { if ($token === '') {
$this->log('ERROR', '--token or MOKOGITEA_TOKEN env is required'); $this->log('ERROR', '--token or MOKOGIT_TOKEN env is required');
return 1; return 1;
} }
if ($org === '' || $repo === '') { if ($org === '' || $repo === '') {
$this->log('ERROR', '--org and --repo (or GITEA_ORG/GITEA_REPO env) are required'); $this->log('ERROR', '--org and --repo (or GIT_ORG/GIT_REPO env) are required');
return 1; return 1;
} }
// Auto-discover branches if --all flag is set // Auto-discover branches if --all flag is set
if ($discoverAll) { if ($discoverAll) {
$apiUrl = "{$giteaUrl}/api/v1/repos/{$org}/{$repo}/branches?limit=50"; $apiUrl = "{$gitUrl}/api/v1/repos/{$org}/{$repo}/branches?limit=50";
$ch = curl_init($apiUrl); $ch = curl_init($apiUrl);
curl_setopt_array($ch, [ curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true, CURLOPT_RETURNTRANSFER => true,
@@ -118,8 +118,8 @@ class UpdatesXmlSyncCli extends CliFramework
$content = file_get_contents($updatesFile); $content = file_get_contents($updatesFile);
$encoded = base64_encode($content); $encoded = base64_encode($content);
$giteaUrl = rtrim($giteaUrl, '/'); $gitUrl = rtrim($gitUrl, '/');
$apiBase = "{$giteaUrl}/api/v1/repos/{$org}/{$repo}"; $apiBase = "{$gitUrl}/api/v1/repos/{$org}/{$repo}";
$vLabel = $version !== '' ? " {$version}" : ''; $vLabel = $version !== '' ? " {$version}" : '';
$targets = array_filter( $targets = array_filter(
+6 -6
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/version_auto_bump.php * PATH: /cli/version_auto_bump.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Auto patch-bump, set stability suffix, and commit — single CLI replacing inline workflow bash * BRIEF: Auto patch-bump, set stability suffix, and commit — single CLI replacing inline workflow bash
*/ */
@@ -70,7 +70,7 @@ class VersionAutoBumpCli extends CliFramework
// Auto-detect watch path from manifest.xml if not provided // Auto-detect watch path from manifest.xml if not provided
if (empty($watchPath)) { if (empty($watchPath)) {
$manifestFile = realpath($path) . '/.mokogitea/manifest.xml'; $manifestFile = realpath($path) . '/.mokogit/manifest.xml';
if (file_exists($manifestFile)) { if (file_exists($manifestFile)) {
$xml = @simplexml_load_file($manifestFile); $xml = @simplexml_load_file($manifestFile);
if ($xml && isset($xml->build->{'entry-point'})) { if ($xml && isset($xml->build->{'entry-point'})) {
@@ -176,11 +176,11 @@ class VersionAutoBumpCli extends CliFramework
$cdRoot = $cd . escapeshellarg($root); $cdRoot = $cd . escapeshellarg($root);
@shell_exec( @shell_exec(
$cdRoot . " && git config --local user.email" $cdRoot . " && git config --local user.email"
. " \"gitea-actions[bot]@mokoconsulting.tech\"" . " \"git-actions[bot]@mokoconsulting.tech\""
); );
@shell_exec( @shell_exec(
$cdRoot . " && git config --local user.name" $cdRoot . " && git config --local user.name"
. " \"gitea-actions[bot]\"" . " \"git-actions[bot]\""
); );
if (!empty($repoUrl)) { if (!empty($repoUrl)) {
@@ -196,8 +196,8 @@ class VersionAutoBumpCli extends CliFramework
: "chore(version): set {$stability} suffix {$displayVersion} [skip ci]"; : "chore(version): set {$stability} suffix {$displayVersion} [skip ci]";
@shell_exec( @shell_exec(
$cdRoot . " && git commit -m " . escapeshellarg($commitMsg) $cdRoot . " && git commit -m " . escapeshellarg($commitMsg)
. " --author=\"gitea-actions[bot]" . " --author=\"git-actions[bot]"
. " <gitea-actions[bot]@mokoconsulting.tech>\"" . " <git-actions[bot]@mokoconsulting.tech>\""
); );
$pushResult = @shell_exec( $pushResult = @shell_exec(
+10 -5
View File
@@ -44,7 +44,7 @@ class VersionBumpCli extends CliFramework
$mokoVersion = null; $mokoVersion = null;
$existingSuffix = ''; $existingSuffix = '';
$versionPrefix = ''; $versionPrefix = '';
$mokoManifest = "{$root}/.mokogitea/manifest.xml"; $mokoManifest = "{$root}/.mokogit/manifest.xml";
$mokoContent = ''; $mokoContent = '';
if (file_exists($mokoManifest)) { if (file_exists($mokoManifest)) {
$mokoContent = file_get_contents($mokoManifest); $mokoContent = file_get_contents($mokoManifest);
@@ -214,7 +214,8 @@ class VersionBumpCli extends CliFramework
$newContent = preg_replace( $newContent = preg_replace(
$xmlPattern, $xmlPattern,
'${1}' . $newBase . '</version>', '${1}' . $newBase . '</version>',
$content $content,
1
); );
} else { } else {
$xmlPattern = '#<version>\d{2}\.\d{2}\.\d{2}' $xmlPattern = '#<version>\d{2}\.\d{2}\.\d{2}'
@@ -222,9 +223,13 @@ class VersionBumpCli extends CliFramework
$newContent = preg_replace( $newContent = preg_replace(
$xmlPattern, $xmlPattern,
"<version>{$newFull}</version>", "<version>{$newFull}</version>",
$content $content,
1
); );
} }
if ($newContent !== null) {
$newContent = preg_replace('#(\s*<version>[^<]*</version>)\s*<version>[^<]*</version>#', '$1', $newContent);
}
if ($newContent !== null && $newContent !== $content) { if ($newContent !== null && $newContent !== $content) {
file_put_contents($xmlFile, $newContent); file_put_contents($xmlFile, $newContent);
$updatedFiles[] = substr($xmlFile, strlen($root) + 1); $updatedFiles[] = substr($xmlFile, strlen($root) + 1);
@@ -358,7 +363,7 @@ class VersionBumpCli extends CliFramework
if (in_array($relPath, $updatedFiles ?? [], true)) { if (in_array($relPath, $updatedFiles ?? [], true)) {
continue; continue;
} }
if (strpos($relPath, '.mokogitea/manifest.xml') !== false) { if (strpos($relPath, '.mokogit/manifest.xml') !== false) {
continue; continue;
} }
$content = @file_get_contents($filePath); $content = @file_get_contents($filePath);
@@ -384,7 +389,7 @@ class VersionBumpCli extends CliFramework
/** /**
* Scan git release tags for the highest version across all channels. * Scan git release tags for the highest version across all channels.
* *
* Checks release names like "MokoSuiteClient (VERSION: 09.40.00)" in * Checks release names like "MokoSuiteClient (VERSION: 09.43.00)" in
* git tags (stable, release-candidate, development, etc.) to find the * git tags (stable, release-candidate, development, etc.) to find the
* highest version that has been released on any channel. * highest version that has been released on any channel.
*/ */
+13 -13
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/version_bump_remote.php * PATH: /cli/version_bump_remote.php
* BRIEF: Bump version in manifest XML and CHANGELOG.md on a remote branch via Gitea API * BRIEF: Bump version in manifest XML and CHANGELOG.md on a remote branch via Git API
*/ */
declare(strict_types=1); declare(strict_types=1);
@@ -23,15 +23,15 @@ class VersionBumpRemoteCli extends CliFramework
{ {
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Bump version in manifest XML and CHANGELOG.md on a remote branch via Gitea API'); $this->setDescription('Bump version in manifest XML and CHANGELOG.md on a remote branch via Git API');
$this->addArgument('--path', 'Repository root', '.'); $this->addArgument('--path', 'Repository root', '.');
$this->addArgument('--branch', 'Target branch to bump (required)', null); $this->addArgument('--branch', 'Target branch to bump (required)', null);
$this->addArgument('--bump', 'Bump type: patch | minor | major', 'minor'); $this->addArgument('--bump', 'Bump type: patch | minor | major', 'minor');
$this->addArgument('--token', 'Gitea API token (or MOKOGITEA_TOKEN env var)', null); $this->addArgument('--token', 'Git API token (or MOKOGIT_TOKEN env var)', null);
$this->addArgument('--api-base', 'Gitea API base URL for the repo', null); $this->addArgument('--api-base', 'Git API base URL for the repo', null);
$this->addArgument('--no-changelog', 'Skip CHANGELOG.md bump', false); $this->addArgument('--no-changelog', 'Skip CHANGELOG.md bump', false);
$this->addArgument('--repo', 'Repository path (owner/repo)', null); $this->addArgument('--repo', 'Repository path (owner/repo)', null);
$this->addArgument('--gitea-url', 'Gitea instance URL', null); $this->addArgument('--git-url', 'Git instance URL', null);
} }
protected function run(): int protected function run(): int
@@ -43,15 +43,15 @@ class VersionBumpRemoteCli extends CliFramework
$apiBase = $this->getArgument('--api-base'); $apiBase = $this->getArgument('--api-base');
$noChangelog = (bool) $this->getArgument('--no-changelog'); $noChangelog = (bool) $this->getArgument('--no-changelog');
$repo = $this->getArgument('--repo'); $repo = $this->getArgument('--repo');
$giteaUrl = $this->getArgument('--gitea-url'); $gitUrl = $this->getArgument('--git-url');
if ($token === null) { if ($token === null) {
$token = getenv('MOKOGITEA_TOKEN') ?: getenv('GITEA_TOKEN') ?: null; $token = getenv('MOKOGIT_TOKEN') ?: getenv('GIT_TOKEN') ?: null;
} }
if ($giteaUrl === null) { if ($gitUrl === null) {
$giteaUrl = getenv('GITEA_URL') ?: 'https://git.mokoconsulting.tech'; $gitUrl = getenv('GIT_URL') ?: 'https://git.mokoconsulting.tech';
} }
if ($apiBase === null && $repo !== null) { if ($apiBase === null && $repo !== null) {
$apiBase = rtrim($giteaUrl, '/') . '/api/v1/repos/' . $repo; $apiBase = rtrim($gitUrl, '/') . '/api/v1/repos/' . $repo;
} }
if ($branch === null || $token === null || $apiBase === null) { if ($branch === null || $token === null || $apiBase === null) {
$this->log('ERROR', "Usage: version_bump_remote.php --branch BRANCH --token TOKEN --api-base URL [--bump minor|patch|major]"); $this->log('ERROR', "Usage: version_bump_remote.php --branch BRANCH --token TOKEN --api-base URL [--bump minor|patch|major]");
@@ -148,7 +148,7 @@ class VersionBumpRemoteCli extends CliFramework
return 0; return 0;
} }
private function giteaApi(string $method, string $url, string $token, ?string $body = null): ?array private function gitApi(string $method, string $url, string $token, ?string $body = null): ?array
{ {
$ch = curl_init($url); $ch = curl_init($url);
curl_setopt_array($ch, [ curl_setopt_array($ch, [
@@ -180,7 +180,7 @@ class VersionBumpRemoteCli extends CliFramework
callable $transform, callable $transform,
string $commitMessage string $commitMessage
): bool { ): bool {
$file = $this->giteaApi('GET', "{$apiBase}/contents/{$filePath}?ref={$branch}", $token); $file = $this->gitApi('GET', "{$apiBase}/contents/{$filePath}?ref={$branch}", $token);
if ($file === null || !isset($file['sha']) || !isset($file['content'])) { if ($file === null || !isset($file['sha']) || !isset($file['content'])) {
return false; return false;
} }
@@ -191,7 +191,7 @@ class VersionBumpRemoteCli extends CliFramework
return true; return true;
} }
$payload = json_encode(['content' => base64_encode($newContent), 'sha' => $file['sha'], 'message' => $commitMessage, 'branch' => $branch]); $payload = json_encode(['content' => base64_encode($newContent), 'sha' => $file['sha'], 'message' => $commitMessage, 'branch' => $branch]);
$result = $this->giteaApi('PUT', "{$apiBase}/contents/{$filePath}", $token, $payload); $result = $this->gitApi('PUT', "{$apiBase}/contents/{$filePath}", $token, $payload);
if ($result === null) { if ($result === null) {
$this->log('ERROR', "{$filePath}: failed to update"); $this->log('ERROR', "{$filePath}: failed to update");
return false; return false;
+11 -8
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/version_check.php * PATH: /cli/version_check.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Validate version consistency across README, manifests, and sub-packages * BRIEF: Validate version consistency across README, manifests, and sub-packages
*/ */
@@ -38,14 +38,14 @@ class VersionCheckCli extends CliFramework
$root = realpath($path) ?: $path; $root = realpath($path) ?: $path;
$errors = 0; $errors = 0;
$versions = []; $versions = [];
$mokoManifest = "{$root}/.mokogitea/manifest.xml"; $mokoManifest = "{$root}/.mokogit/manifest.xml";
if (file_exists($mokoManifest)) { if (file_exists($mokoManifest)) {
$xml = @simplexml_load_file($mokoManifest); $xml = @simplexml_load_file($mokoManifest);
if ($xml !== false) { if ($xml !== false) {
$v = (string)($xml->identity->version ?? ''); $v = (string)($xml->identity->version ?? '');
$base = preg_replace('/(-(dev|alpha|beta|rc))+$/', '', $v); $base = preg_replace('/(-(dev|alpha|beta|rc))+$/', '', $v);
if (preg_match('/^\d{2}\.\d{2}\.\d{2}$/', $base)) { if (preg_match('/^\d{2}\.\d{2}\.\d{2}$/', $base)) {
$versions['.mokogitea/manifest.xml'] = $base; $versions['.mokogit/manifest.xml'] = $base;
} }
} }
} }
@@ -124,18 +124,20 @@ class VersionCheckCli extends CliFramework
file_put_contents($readme, $updated); file_put_contents($readme, $updated);
} echo " Fixed: README.md -> {$highestVersion}\n"; } echo " Fixed: README.md -> {$highestVersion}\n";
} }
if (isset($versions['.mokogitea/manifest.xml']) && $versions['.mokogitea/manifest.xml'] !== $highestVersion) { if (isset($versions['.mokogit/manifest.xml']) && $versions['.mokogit/manifest.xml'] !== $highestVersion) {
$content = file_get_contents($mokoManifest); $content = file_get_contents($mokoManifest);
$vPat = '#<version>\d{2}\.\d{2}\.\d{2}' $vPat = '#<version>\d{2}\.\d{2}\.\d{2}'
. '(?:(?:-(?:dev|alpha|beta|rc))+)?</version>#'; . '(?:(?:-(?:dev|alpha|beta|rc))+)?</version>#';
$updated = preg_replace( $updated = preg_replace(
$vPat, $vPat,
"<version>{$highestVersion}</version>", "<version>{$highestVersion}</version>",
$content $content,
1
); );
if ($updated !== null) { if ($updated !== null) {
$updated = preg_replace('#(\s*<version>[^<]*</version>)\s*<version>[^<]*</version>#', '$1', $updated);
file_put_contents($mokoManifest, $updated); file_put_contents($mokoManifest, $updated);
} echo " Fixed: .mokogitea/manifest.xml -> {$highestVersion}\n"; } echo " Fixed: .mokogit/manifest.xml -> {$highestVersion}\n";
} }
if (isset($versions['CHANGELOG.md']) && $versions['CHANGELOG.md'] !== $highestVersion) { if (isset($versions['CHANGELOG.md']) && $versions['CHANGELOG.md'] !== $highestVersion) {
$content = file_get_contents($changelog); $content = file_get_contents($changelog);
@@ -177,7 +179,7 @@ class VersionCheckCli extends CliFramework
} echo " Fixed: pyproject.toml -> {$highestVersion}\n"; } echo " Fixed: pyproject.toml -> {$highestVersion}\n";
} }
foreach ($versions as $source => $ver) { foreach ($versions as $source => $ver) {
if (in_array($source, ['README.md', 'CHANGELOG.md', '.mokogitea/manifest.xml', 'package.json', 'pyproject.toml'], true)) { if (in_array($source, ['README.md', 'CHANGELOG.md', '.mokogit/manifest.xml', 'package.json', 'pyproject.toml'], true)) {
continue; continue;
} if ($ver === $highestVersion) { } if ($ver === $highestVersion) {
continue; continue;
@@ -185,8 +187,9 @@ class VersionCheckCli extends CliFramework
if (!file_exists($file)) { if (!file_exists($file)) {
continue; continue;
} $content = file_get_contents($file); } $content = file_get_contents($file);
$updated = preg_replace('#<version>[^<]*</version>#', "<version>{$highestVersion}</version>", $content); $updated = preg_replace('#<version>[^<]*</version>#', "<version>{$highestVersion}</version>", $content, 1);
if ($updated !== null) { if ($updated !== null) {
$updated = preg_replace('#(\s*<version>[^<]*</version>)\s*<version>[^<]*</version>#', '$1', $updated);
file_put_contents($file, $updated); file_put_contents($file, $updated);
} echo " Fixed: {$source} -> {$highestVersion}\n"; } echo " Fixed: {$source} -> {$highestVersion}\n";
} }
+2 -2
View File
@@ -32,10 +32,10 @@ class VersionReadCli extends CliFramework
$path = $this->getArgument('--path'); $path = $this->getArgument('--path');
$root = realpath($path) ?: $path; $root = realpath($path) ?: $path;
// -- 1. Read from .mokogitea/manifest.xml (canonical source) -- // -- 1. Read from .mokogit/manifest.xml (canonical source) --
$mokoVersion = null; $mokoVersion = null;
$versionPrefix = ''; $versionPrefix = '';
$mokoManifest = "{$root}/.mokogitea/manifest.xml"; $mokoManifest = "{$root}/.mokogit/manifest.xml";
if (file_exists($mokoManifest)) { if (file_exists($mokoManifest)) {
$xml = @simplexml_load_file($mokoManifest); $xml = @simplexml_load_file($mokoManifest);
if ($xml !== false) { if ($xml !== false) {
+11 -11
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/version_reset_dev.php * PATH: /cli/version_reset_dev.php
* BRIEF: Reset platform version to 'development' on a branch via Gitea API * BRIEF: Reset platform version to 'development' on a branch via Git API
*/ */
declare(strict_types=1); declare(strict_types=1);
@@ -23,9 +23,9 @@ class VersionResetDevCli extends CliFramework
{ {
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Reset platform version to development on a branch via Gitea API'); $this->setDescription('Reset platform version to development on a branch via Git API');
$this->addArgument('--token', 'Gitea API token (also reads MOKOGITEA_TOKEN / GITEA_TOKEN env)', ''); $this->addArgument('--token', 'Git API token (also reads MOKOGIT_TOKEN / GIT_TOKEN env)', '');
$this->addArgument('--api-base', 'Gitea API base URL for the repo', ''); $this->addArgument('--api-base', 'Git API base URL for the repo', '');
$this->addArgument('--branch', 'Target branch (default: dev)', 'dev'); $this->addArgument('--branch', 'Target branch (default: dev)', 'dev');
$this->addArgument('--platform', 'Platform type: dolibarr, crm-module, joomla, waas-component', ''); $this->addArgument('--platform', 'Platform type: dolibarr, crm-module, joomla, waas-component', '');
$this->addArgument('--path', 'Repo root for auto-detecting platform from manifest.xml', ''); $this->addArgument('--path', 'Repo root for auto-detecting platform from manifest.xml', '');
@@ -41,13 +41,13 @@ class VersionResetDevCli extends CliFramework
// Allow token from environment // Allow token from environment
if ($token === '') { if ($token === '') {
$envToken = getenv('MOKOGITEA_TOKEN'); $envToken = getenv('MOKOGIT_TOKEN');
if ($envToken !== false && $envToken !== '') { if ($envToken !== false && $envToken !== '') {
$token = $envToken; $token = $envToken;
} }
} }
if ($token === '') { if ($token === '') {
$envToken = getenv('GITEA_TOKEN'); $envToken = getenv('GIT_TOKEN');
if ($envToken !== false && $envToken !== '') { if ($envToken !== false && $envToken !== '') {
$token = $envToken; $token = $envToken;
} }
@@ -93,7 +93,7 @@ class VersionResetDevCli extends CliFramework
private function detectPlatform(string $repoPath): ?string private function detectPlatform(string $repoPath): ?string
{ {
$root = realpath($repoPath) ?: $repoPath; $root = realpath($repoPath) ?: $repoPath;
$manifestXml = "{$root}/.mokogitea/manifest.xml"; $manifestXml = "{$root}/.mokogit/manifest.xml";
if (!file_exists($manifestXml)) { if (!file_exists($manifestXml)) {
return null; return null;
@@ -114,7 +114,7 @@ class VersionResetDevCli extends CliFramework
return null; return null;
} }
private function giteaApiCall(string $url, string $token, string $method = 'GET', ?string $body = null): ?array private function gitApiCall(string $url, string $token, string $method = 'GET', ?string $body = null): ?array
{ {
$ch = curl_init($url); $ch = curl_init($url);
if ($ch === false) { if ($ch === false) {
@@ -161,7 +161,7 @@ class VersionResetDevCli extends CliFramework
{ {
// Search the repo tree for mod*.class.php files // Search the repo tree for mod*.class.php files
$treeUrl = "{$apiBase}/git/trees/{$branch}?recursive=true"; $treeUrl = "{$apiBase}/git/trees/{$branch}?recursive=true";
$tree = $this->giteaApiCall($treeUrl, $token); $tree = $this->gitApiCall($treeUrl, $token);
if ($tree === null || !isset($tree['tree']) || !is_array($tree['tree'])) { if ($tree === null || !isset($tree['tree']) || !is_array($tree['tree'])) {
$this->log('ERROR', "Could not read repository tree for branch '{$branch}'."); $this->log('ERROR', "Could not read repository tree for branch '{$branch}'.");
@@ -191,7 +191,7 @@ class VersionResetDevCli extends CliFramework
// GET file contents via API // GET file contents via API
$encodedPath = implode('/', array_map('rawurlencode', explode('/', $filePath))); $encodedPath = implode('/', array_map('rawurlencode', explode('/', $filePath)));
$fileUrl = "{$apiBase}/contents/{$encodedPath}?ref={$branch}"; $fileUrl = "{$apiBase}/contents/{$encodedPath}?ref={$branch}";
$fileData = $this->giteaApiCall($fileUrl, $token); $fileData = $this->gitApiCall($fileUrl, $token);
if ($fileData === null || !isset($fileData['content'])) { if ($fileData === null || !isset($fileData['content'])) {
echo "Skipping {$filePath}: could not fetch contents.\n"; echo "Skipping {$filePath}: could not fetch contents.\n";
@@ -233,7 +233,7 @@ class VersionResetDevCli extends CliFramework
]); ]);
$putUrl = "{$apiBase}/contents/{$encodedPath}"; $putUrl = "{$apiBase}/contents/{$encodedPath}";
$result = $this->giteaApiCall($putUrl, $token, 'PUT', $putBody); $result = $this->gitApiCall($putUrl, $token, 'PUT', $putBody);
if ($result !== null) { if ($result !== null) {
echo "Reset: {$filePath} -> \$this->version = 'development'\n"; echo "Reset: {$filePath} -> \$this->version = 'development'\n";
+8 -4
View File
@@ -80,8 +80,8 @@ class VersionSetPlatformCli extends CliFramework
// Detect platform — check manifest.xml first, then legacy .mokostandards // Detect platform — check manifest.xml first, then legacy .mokostandards
$platform = ''; $platform = '';
// New format: .mokogitea/manifest.xml (XML with <platform> tag) // New format: .mokogit/manifest.xml (XML with <platform> tag)
$manifestXml = "{$root}/.mokogitea/manifest.xml"; $manifestXml = "{$root}/.mokogit/manifest.xml";
if (file_exists($manifestXml)) { if (file_exists($manifestXml)) {
$xml = @simplexml_load_file($manifestXml); $xml = @simplexml_load_file($manifestXml);
if ($xml && isset($xml->governance->platform)) { if ($xml && isset($xml->governance->platform)) {
@@ -93,7 +93,7 @@ class VersionSetPlatformCli extends CliFramework
if (empty($platform)) { if (empty($platform)) {
$mokoStandards = "{$root}/.github/.mokostandards"; $mokoStandards = "{$root}/.github/.mokostandards";
if (!file_exists($mokoStandards)) { if (!file_exists($mokoStandards)) {
$mokoStandards = "{$root}/.mokogitea/manifest.xml"; $mokoStandards = "{$root}/.mokogit/manifest.xml";
} }
if (!file_exists($mokoStandards)) { if (!file_exists($mokoStandards)) {
$mokoStandards = "{$root}/.mokostandards"; $mokoStandards = "{$root}/.mokostandards";
@@ -164,8 +164,12 @@ class VersionSetPlatformCli extends CliFramework
$updated = preg_replace( $updated = preg_replace(
'|<version>[^<]*</version>|', '|<version>[^<]*</version>|',
"<version>{$version}</version>", "<version>{$version}</version>",
$content $content,
1
); );
if ($updated !== null) {
$updated = preg_replace('#(\s*<version>[^<]*</version>)\s*<version>[^<]*</version>#', '$1', $updated);
}
if ($updated !== null && $updated !== $content) { if ($updated !== null && $updated !== $content) {
file_put_contents($file, $updated); file_put_contents($file, $updated);
$relPath = str_replace($root . '/', '', $file); $relPath = str_replace($root . '/', '', $file);
+5 -5
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/wiki_sync.php * PATH: /cli/wiki_sync.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Sync select wiki pages from mokocli to all template repos * BRIEF: Sync select wiki pages from mokocli to all template repos
*/ */
@@ -22,7 +22,7 @@ use MokoCli\CliFramework;
class WikiSyncCli extends CliFramework class WikiSyncCli extends CliFramework
{ {
private string $giteaUrl = 'https://git.mokoconsulting.tech'; private string $gitUrl = 'https://git.mokoconsulting.tech';
private string $token = ''; private string $token = '';
private string $org = 'MokoConsulting'; private string $org = 'MokoConsulting';
private string $sourceRepo = 'mokocli'; private string $sourceRepo = 'mokocli';
@@ -39,7 +39,7 @@ class WikiSyncCli extends CliFramework
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Sync wiki pages from mokocli to template repos'); $this->setDescription('Sync wiki pages from mokocli to template repos');
$this->addArgument('--token', 'Gitea API token (required)', ''); $this->addArgument('--token', 'Git API token (required)', '');
$this->addArgument('--org', 'Organization (default: MokoConsulting)', 'MokoConsulting'); $this->addArgument('--org', 'Organization (default: MokoConsulting)', 'MokoConsulting');
$this->addArgument('--source', 'Source repo (default: mokocli)', 'mokocli'); $this->addArgument('--source', 'Source repo (default: mokocli)', 'mokocli');
$this->addArgument('--target', 'Target repo (can repeat)', ''); $this->addArgument('--target', 'Target repo (can repeat)', '');
@@ -195,7 +195,7 @@ class WikiSyncCli extends CliFramework
private function apiGet(string $endpoint): ?array private function apiGet(string $endpoint): ?array
{ {
$url = "{$this->giteaUrl}/api/v1{$endpoint}"; $url = "{$this->gitUrl}/api/v1{$endpoint}";
$opts = [ $opts = [
'http' => [ 'http' => [
'method' => 'GET', 'method' => 'GET',
@@ -224,7 +224,7 @@ class WikiSyncCli extends CliFramework
private function apiWrite(string $method, string $endpoint, string $payload): ?array private function apiWrite(string $method, string $endpoint, string $payload): ?array
{ {
$url = "{$this->giteaUrl}/api/v1{$endpoint}"; $url = "{$this->gitUrl}/api/v1{$endpoint}";
$opts = [ $opts = [
'http' => [ 'http' => [
'method' => $method, 'method' => $method,
+54 -114
View File
@@ -10,7 +10,7 @@
* INGROUP: mokocli * INGROUP: mokocli
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /cli/workflow_sync.php * PATH: /cli/workflow_sync.php
* VERSION: 09.40.00 * VERSION: 09.44.00
* BRIEF: Sync workflows from Generic → platform templates → live repos based on manifest.platform * BRIEF: Sync workflows from Generic → platform templates → live repos based on manifest.platform
*/ */
@@ -26,7 +26,8 @@ class WorkflowSyncCli extends CliFramework
'joomla' => 'Template-Joomla', 'joomla' => 'Template-Joomla',
'dolibarr' => 'Template-Dolibarr', 'dolibarr' => 'Template-Dolibarr',
'go' => 'Template-Go', 'go' => 'Template-Go',
'mcp' => 'Template-MCP', 'npm' => 'Template-NPM',
'mcp' => 'Template-NPM', // legacy alias: mcp was consolidated into npm; repo renamed Template-MCP → Template-NPM
'platform' => 'Template-Generic', 'platform' => 'Template-Generic',
'generic' => 'Template-Generic', 'generic' => 'Template-Generic',
]; ];
@@ -54,8 +55,8 @@ class WorkflowSyncCli extends CliFramework
protected function configure(): void protected function configure(): void
{ {
$this->setDescription('Sync workflows from Generic → platform templates → live repos based on manifest.platform'); $this->setDescription('Sync workflows from Generic → platform templates → live repos based on manifest.platform');
$this->addArgument('--gitea-url', 'Gitea URL (default: https://git.mokoconsulting.tech)', 'https://git.mokoconsulting.tech'); $this->addArgument('--git-url', 'Git URL (default: https://git.mokoconsulting.tech)', 'https://git.mokoconsulting.tech');
$this->addArgument('--token', 'Gitea API token', ''); $this->addArgument('--token', 'Git API token', '');
$this->addArgument('--org', 'Target organization', ''); $this->addArgument('--org', 'Target organization', '');
$this->addArgument('--branch', 'Target branch (default: main)', 'main'); $this->addArgument('--branch', 'Target branch (default: main)', 'main');
$this->addArgument('--phase', 'Phase to run: all, templates, repos (default: all)', 'all'); $this->addArgument('--phase', 'Phase to run: all, templates, repos (default: all)', 'all');
@@ -65,7 +66,7 @@ class WorkflowSyncCli extends CliFramework
protected function run(): int protected function run(): int
{ {
$giteaUrl = rtrim($this->getArgument('--gitea-url'), '/'); $gitUrl = rtrim($this->getArgument('--git-url'), '/');
$token = $this->getArgument('--token'); $token = $this->getArgument('--token');
$org = $this->getArgument('--org'); $org = $this->getArgument('--org');
$branch = $this->getArgument('--branch'); $branch = $this->getArgument('--branch');
@@ -101,7 +102,7 @@ class WorkflowSyncCli extends CliFramework
// Phase 1: Sync Generic → Platform Templates // Phase 1: Sync Generic → Platform Templates
if ($phase === 'all' || $phase === 'templates') { if ($phase === 'all' || $phase === 'templates') {
$result = $this->syncGenericToTemplates($giteaUrl, $token, $org, $branch, $platformFilter); $result = $this->syncGenericToTemplates($gitUrl, $token, $org, $branch, $platformFilter);
if ($result !== 0) { if ($result !== 0) {
return $result; return $result;
@@ -110,7 +111,7 @@ class WorkflowSyncCli extends CliFramework
// Phase 2: Sync Platform Templates → Live Repos // Phase 2: Sync Platform Templates → Live Repos
if ($phase === 'all' || $phase === 'repos') { if ($phase === 'all' || $phase === 'repos') {
$result = $this->syncTemplatesToRepos($giteaUrl, $token, $org, $branch, $platformFilter); $result = $this->syncTemplatesToRepos($gitUrl, $token, $org, $branch, $platformFilter);
if ($result !== 0) { if ($result !== 0) {
return $result; return $result;
@@ -129,7 +130,7 @@ class WorkflowSyncCli extends CliFramework
* Skips platform-specific overrides (files that exist in the platform template but NOT in Generic). * Skips platform-specific overrides (files that exist in the platform template but NOT in Generic).
*/ */
private function syncGenericToTemplates( private function syncGenericToTemplates(
string $giteaUrl, string $gitUrl,
string $token, string $token,
string $org, string $org,
string $branch, string $branch,
@@ -139,7 +140,7 @@ class WorkflowSyncCli extends CliFramework
echo "\n"; echo "\n";
// Get all workflow files from Template-Generic // Get all workflow files from Template-Generic
$genericWorkflows = $this->listWorkflows($giteaUrl, $token, $org, self::GENERIC_TEMPLATE, $branch); $genericWorkflows = $this->listWorkflows($gitUrl, $token, $org, self::GENERIC_TEMPLATE, $branch);
if ($genericWorkflows === null) { if ($genericWorkflows === null) {
$this->log('ERROR', 'Could not list workflows from ' . self::GENERIC_TEMPLATE); $this->log('ERROR', 'Could not list workflows from ' . self::GENERIC_TEMPLATE);
@@ -185,12 +186,12 @@ class WorkflowSyncCli extends CliFramework
$this->skipped++; $this->skipped++;
continue; continue;
} }
$destPath = '.mokogitea/workflows/' . $filename; $destPath = '.mokogit/workflows/' . $filename;
$label = "{$templateRepo}/{$filename}"; $label = "{$templateRepo}/{$filename}";
// Get file content from Generic // Get file content from Generic
$sourceContent = $this->getFileContent( $sourceContent = $this->getFileContent(
$giteaUrl, $token, $org, $gitUrl, $token, $org,
self::GENERIC_TEMPLATE, $destPath, $branch self::GENERIC_TEMPLATE, $destPath, $branch
); );
@@ -203,7 +204,7 @@ class WorkflowSyncCli extends CliFramework
$commitMsg = "chore: sync {$filename} from " . self::GENERIC_TEMPLATE . " [skip ci]"; $commitMsg = "chore: sync {$filename} from " . self::GENERIC_TEMPLATE . " [skip ci]";
$this->pushFile( $this->pushFile(
$giteaUrl, $token, $org, $templateRepo, $gitUrl, $token, $org, $templateRepo,
$destPath, $sourceContent, $branch, $commitMsg, $label $destPath, $sourceContent, $branch, $commitMsg, $label
); );
} }
@@ -217,7 +218,7 @@ class WorkflowSyncCli extends CliFramework
* Phase 2: Sync platform template workflows to live repos based on manifest.platform. * Phase 2: Sync platform template workflows to live repos based on manifest.platform.
*/ */
private function syncTemplatesToRepos( private function syncTemplatesToRepos(
string $giteaUrl, string $gitUrl,
string $token, string $token,
string $org, string $org,
string $branch, string $branch,
@@ -226,7 +227,7 @@ class WorkflowSyncCli extends CliFramework
$this->log('INFO', '=== Phase 2: Sync Platform Templates → Live Repos ==='); $this->log('INFO', '=== Phase 2: Sync Platform Templates → Live Repos ===');
echo "\n"; echo "\n";
$repos = $this->fetchOrgRepos($giteaUrl, $token, $org); $repos = $this->fetchOrgRepos($gitUrl, $token, $org);
if ($repos === null) { if ($repos === null) {
return 1; return 1;
@@ -250,7 +251,7 @@ class WorkflowSyncCli extends CliFramework
} }
// Read manifest.platform // Read manifest.platform
$platform = $this->getRepoPlatform($giteaUrl, $token, $org, $repoName, $branch); $platform = $this->getRepoPlatform($gitUrl, $token, $org, $repoName, $branch);
// Apply platform filter // Apply platform filter
if ($platformFilter !== '' && $platform !== $platformFilter) { if ($platformFilter !== '' && $platform !== $platformFilter) {
@@ -262,11 +263,11 @@ class WorkflowSyncCli extends CliFramework
// Get workflows from the template (cached) // Get workflows from the template (cached)
if (!isset($templateWorkflowCache[$templateRepo])) { if (!isset($templateWorkflowCache[$templateRepo])) {
$workflows = $this->listWorkflows($giteaUrl, $token, $org, $templateRepo, $branch); $workflows = $this->listWorkflows($gitUrl, $token, $org, $templateRepo, $branch);
if ($workflows === null) { if ($workflows === null) {
$this->log('WARN', "Could not list workflows from {$templateRepo}, falling back to " . self::GENERIC_TEMPLATE); $this->log('WARN', "Could not list workflows from {$templateRepo}, falling back to " . self::GENERIC_TEMPLATE);
$workflows = $this->listWorkflows($giteaUrl, $token, $org, self::GENERIC_TEMPLATE, $branch); $workflows = $this->listWorkflows($gitUrl, $token, $org, self::GENERIC_TEMPLATE, $branch);
} }
$templateWorkflowCache[$templateRepo] = $workflows ?? []; $templateWorkflowCache[$templateRepo] = $workflows ?? [];
@@ -280,7 +281,7 @@ class WorkflowSyncCli extends CliFramework
foreach ($workflows as $workflow) { foreach ($workflows as $workflow) {
$filename = $workflow['name']; $filename = $workflow['name'];
$destPath = '.mokogitea/workflows/' . $filename; $destPath = '.mokogit/workflows/' . $filename;
$label = "{$repoFullName}/{$filename}"; $label = "{$repoFullName}/{$filename}";
// Skip platform-excluded workflows // Skip platform-excluded workflows
@@ -292,7 +293,7 @@ class WorkflowSyncCli extends CliFramework
// Get source content from template // Get source content from template
$sourceContent = $this->getFileContent( $sourceContent = $this->getFileContent(
$giteaUrl, $token, $org, $gitUrl, $token, $org,
$templateRepo, $destPath, $branch $templateRepo, $destPath, $branch
); );
@@ -305,7 +306,7 @@ class WorkflowSyncCli extends CliFramework
$commitMsg = "chore: sync {$filename} from {$templateRepo} [skip ci]"; $commitMsg = "chore: sync {$filename} from {$templateRepo} [skip ci]";
$this->pushFile( $this->pushFile(
$giteaUrl, $token, $org, $repoName, $gitUrl, $token, $org, $repoName,
$destPath, $sourceContent, $branch, $commitMsg, $label $destPath, $sourceContent, $branch, $commitMsg, $label
); );
} }
@@ -314,7 +315,7 @@ class WorkflowSyncCli extends CliFramework
if ($this->getArgument('--delete-orphans', false)) { if ($this->getArgument('--delete-orphans', false)) {
$templateNames = array_map(fn($w) => $w['name'], $workflows); $templateNames = array_map(fn($w) => $w['name'], $workflows);
$this->deleteOrphanWorkflows( $this->deleteOrphanWorkflows(
$giteaUrl, $token, $org, $repoName, $branch, $templateNames, $platform $gitUrl, $token, $org, $repoName, $branch, $templateNames, $platform
); );
} }
} }
@@ -327,7 +328,7 @@ class WorkflowSyncCli extends CliFramework
* Push a file to a repo — create or update, skip if identical. * Push a file to a repo — create or update, skip if identical.
*/ */
private function pushFile( private function pushFile(
string $giteaUrl, string $gitUrl,
string $token, string $token,
string $org, string $org,
string $repoName, string $repoName,
@@ -338,7 +339,7 @@ class WorkflowSyncCli extends CliFramework
string $label string $label
): void { ): void {
$existing = $this->apiRequest( $existing = $this->apiRequest(
$giteaUrl, $gitUrl,
$token, $token,
'GET', 'GET',
"/api/v1/repos/{$org}/{$repoName}/contents/" "/api/v1/repos/{$org}/{$repoName}/contents/"
@@ -372,7 +373,7 @@ class WorkflowSyncCli extends CliFramework
]); ]);
$response = $this->apiRequest( $response = $this->apiRequest(
$giteaUrl, $gitUrl,
$token, $token,
'PUT', 'PUT',
"/api/v1/repos/{$org}/{$repoName}/contents/" . $destPath, "/api/v1/repos/{$org}/{$repoName}/contents/" . $destPath,
@@ -400,7 +401,7 @@ class WorkflowSyncCli extends CliFramework
]); ]);
$response = $this->apiRequest( $response = $this->apiRequest(
$giteaUrl, $gitUrl,
$token, $token,
'POST', 'POST',
"/api/v1/repos/{$org}/{$repoName}/contents/" . $destPath, "/api/v1/repos/{$org}/{$repoName}/contents/" . $destPath,
@@ -430,7 +431,7 @@ class WorkflowSyncCli extends CliFramework
* - Platform-excluded workflows * - Platform-excluded workflows
*/ */
private function deleteOrphanWorkflows( private function deleteOrphanWorkflows(
string $giteaUrl, string $gitUrl,
string $token, string $token,
string $org, string $org,
string $repoName, string $repoName,
@@ -438,7 +439,7 @@ class WorkflowSyncCli extends CliFramework
array $templateNames, array $templateNames,
string $platform string $platform
): void { ): void {
$repoWorkflows = $this->listWorkflows($giteaUrl, $token, $org, $repoName, $branch); $repoWorkflows = $this->listWorkflows($gitUrl, $token, $org, $repoName, $branch);
if ($repoWorkflows === null) { if ($repoWorkflows === null) {
return; return;
} }
@@ -468,7 +469,7 @@ class WorkflowSyncCli extends CliFramework
} }
// Delete orphan // Delete orphan
$filePath = '.mokogitea/workflows/' . $name; $filePath = '.mokogit/workflows/' . $name;
$label = "{$org}/{$repoName}/{$name}"; $label = "{$org}/{$repoName}/{$name}";
if ($this->dryRun) { if ($this->dryRun) {
@@ -477,7 +478,7 @@ class WorkflowSyncCli extends CliFramework
continue; continue;
} }
$deleted = $this->deleteFile($giteaUrl, $token, $org, $repoName, $filePath, $branch); $deleted = $this->deleteFile($gitUrl, $token, $org, $repoName, $filePath, $branch);
if ($deleted) { if ($deleted) {
fprintf(STDERR, "%-45s | %s\n", $label, 'DELETED'); fprintf(STDERR, "%-45s | %s\n", $label, 'DELETED');
$this->deleted++; $this->deleted++;
@@ -489,10 +490,10 @@ class WorkflowSyncCli extends CliFramework
} }
/** /**
* Delete a file from a repo via the Gitea Contents API. * Delete a file from a repo via the Git Contents API.
*/ */
private function deleteFile( private function deleteFile(
string $giteaUrl, string $gitUrl,
string $token, string $token,
string $org, string $org,
string $repoName, string $repoName,
@@ -501,7 +502,7 @@ class WorkflowSyncCli extends CliFramework
): bool { ): bool {
// Get SHA first // Get SHA first
$existing = $this->apiRequest( $existing = $this->apiRequest(
$giteaUrl, $token, 'GET', $gitUrl, $token, 'GET',
"/api/v1/repos/{$org}/{$repoName}/contents/{$filePath}?ref={$branch}" "/api/v1/repos/{$org}/{$repoName}/contents/{$filePath}?ref={$branch}"
); );
@@ -522,7 +523,7 @@ class WorkflowSyncCli extends CliFramework
]); ]);
$response = $this->apiRequest( $response = $this->apiRequest(
$giteaUrl, $token, 'DELETE', $gitUrl, $token, 'DELETE',
"/api/v1/repos/{$org}/{$repoName}/contents/{$filePath}", "/api/v1/repos/{$org}/{$repoName}/contents/{$filePath}",
$payload $payload
); );
@@ -531,20 +532,20 @@ class WorkflowSyncCli extends CliFramework
} }
/** /**
* List workflow files in a repo's .mokogitea/workflows/ directory. * List workflow files in a repo's .mokogit/workflows/ directory.
*/ */
private function listWorkflows( private function listWorkflows(
string $giteaUrl, string $gitUrl,
string $token, string $token,
string $org, string $org,
string $repoName, string $repoName,
string $branch string $branch
): ?array { ): ?array {
$response = $this->apiRequest( $response = $this->apiRequest(
$giteaUrl, $gitUrl,
$token, $token,
'GET', 'GET',
"/api/v1/repos/{$org}/{$repoName}/contents/.mokogitea/workflows?ref={$branch}" "/api/v1/repos/{$org}/{$repoName}/contents/.mokogit/workflows?ref={$branch}"
); );
if ($response['code'] !== 200) { if ($response['code'] !== 200) {
@@ -565,7 +566,7 @@ class WorkflowSyncCli extends CliFramework
* Get file content from a repo as a raw string. * Get file content from a repo as a raw string.
*/ */
private function getFileContent( private function getFileContent(
string $giteaUrl, string $gitUrl,
string $token, string $token,
string $org, string $org,
string $repoName, string $repoName,
@@ -573,7 +574,7 @@ class WorkflowSyncCli extends CliFramework
string $branch string $branch
): ?string { ): ?string {
$response = $this->apiRequest( $response = $this->apiRequest(
$giteaUrl, $gitUrl,
$token, $token,
'GET', 'GET',
"/api/v1/repos/{$org}/{$repoName}/contents/{$filePath}?ref={$branch}" "/api/v1/repos/{$org}/{$repoName}/contents/{$filePath}?ref={$branch}"
@@ -593,21 +594,23 @@ class WorkflowSyncCli extends CliFramework
} }
/** /**
* Read a repo's manifest.xml and extract the platform value. * Read a repo's platform from the MokoGIT metadata API.
* Returns 'generic' if the manifest is missing or has no platform field. * Returns 'generic' if metadata is missing or has no platform field.
* (Replaces the retired .mokogit/manifest.xml lookup — platform now lives
* in the repo Metadata endpoint: GET /api/v1/repos/{owner}/{repo}/metadata.)
*/ */
private function getRepoPlatform( private function getRepoPlatform(
string $giteaUrl, string $gitUrl,
string $token, string $token,
string $org, string $org,
string $repoName, string $repoName,
string $branch string $branch
): string { ): string {
$response = $this->apiRequest( $response = $this->apiRequest(
$giteaUrl, $gitUrl,
$token, $token,
'GET', 'GET',
"/api/v1/repos/{$org}/{$repoName}/contents/.mokogitea/manifest.xml?ref={$branch}" "/api/v1/repos/{$org}/{$repoName}/metadata"
); );
if ($response['code'] !== 200) { if ($response['code'] !== 200) {
@@ -616,80 +619,17 @@ class WorkflowSyncCli extends CliFramework
$data = json_decode($response['body'], true); $data = json_decode($response['body'], true);
if (!is_array($data) || !isset($data['content'])) { if (!is_array($data) || empty($data['platform'])) {
return 'generic'; return 'generic';
} }
$xmlContent = base64_decode($data['content']); return strtolower(trim((string) $data['platform']));
if ($xmlContent === false || $xmlContent === '') {
return 'generic';
}
// Suppress XML warnings for malformed manifests
$previous = libxml_use_internal_errors(true);
$xml = simplexml_load_string($xmlContent);
libxml_use_internal_errors($previous);
if ($xml === false) {
return 'generic';
}
// Try <governance><platform> (standard location)
$platform = '';
// Register namespace if present
$namespaces = $xml->getNamespaces(true);
if (!empty($namespaces)) {
$ns = reset($namespaces);
$xml->registerXPathNamespace('mp', $ns);
$nodes = $xml->xpath('//mp:governance/mp:platform');
if (!empty($nodes)) {
$platform = trim((string) $nodes[0]);
}
// Fallback: <identity><platform>
if ($platform === '') {
$nodes = $xml->xpath('//mp:identity/mp:platform');
if (!empty($nodes)) {
$platform = trim((string) $nodes[0]);
}
}
// Fallback: top-level <platform>
if ($platform === '') {
$nodes = $xml->xpath('//mp:platform');
if (!empty($nodes)) {
$platform = trim((string) $nodes[0]);
}
}
} else {
// No namespace
if (isset($xml->governance->platform)) {
$platform = trim((string) $xml->governance->platform);
} elseif (isset($xml->identity->platform)) {
$platform = trim((string) $xml->identity->platform);
} elseif (isset($xml->platform)) {
$platform = trim((string) $xml->platform);
}
}
if ($platform === '') {
return 'generic';
}
return strtolower($platform);
} }
/** /**
* Fetch all non-archived repos in an org (paginated). * Fetch all non-archived repos in an org (paginated).
*/ */
private function fetchOrgRepos(string $giteaUrl, string $token, string $org): ?array private function fetchOrgRepos(string $gitUrl, string $token, string $org): ?array
{ {
$this->log('INFO', "Fetching repos from org: {$org}"); $this->log('INFO', "Fetching repos from org: {$org}");
@@ -698,7 +638,7 @@ class WorkflowSyncCli extends CliFramework
while (true) { while (true) {
$response = $this->apiRequest( $response = $this->apiRequest(
$giteaUrl, $gitUrl,
$token, $token,
'GET', 'GET',
"/api/v1/orgs/{$org}/repos?" "/api/v1/orgs/{$org}/repos?"
@@ -740,16 +680,16 @@ class WorkflowSyncCli extends CliFramework
} }
/** /**
* Make an HTTP request to the Gitea API. * Make an HTTP request to the Git API.
*/ */
private function apiRequest( private function apiRequest(
string $giteaUrl, string $gitUrl,
string $token, string $token,
string $method, string $method,
string $endpoint, string $endpoint,
?string $body = null ?string $body = null
): array { ): array {
$url = $giteaUrl . $endpoint; $url = $gitUrl . $endpoint;
$ch = curl_init(); $ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_URL, $url);
+2 -2
View File
@@ -71,8 +71,8 @@
], ],
"scripts": { "scripts": {
"test": "phpunit", "test": "phpunit",
"phpcs": "phpcs --standard=phpcs.xml lib/ validate/ automation/", "phpcs": "phpcs --standard=phpcs.xml lib/ validate/",
"phpstan": "phpstan analyse -c phpstan.neon lib/ validate/ automation/", "phpstan": "phpstan analyse -c phpstan.neon lib/ validate/",
"psalm": "psalm --config=psalm.xml", "psalm": "psalm --config=psalm.xml",
"check": [ "check": [
"@phpcs", "@phpcs",
+3 -3
View File
@@ -8,11 +8,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.Scripts.Deploy * DEFGROUP: MokoCLI.Scripts.Deploy
* INGROUP: MokoPlatform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /deploy/backup-before-deploy.php * PATH: /deploy/backup-before-deploy.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Snapshot Joomla directories before deployment for rollback capability * BRIEF: Snapshot Joomla directories before deployment for rollback capability
*/ */
+2 -2
View File
@@ -4,8 +4,8 @@
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech> /* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.Scripts.Deploy * DEFGROUP: MokoCLI.Scripts.Deploy
* INGROUP: MokoPlatform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /deploy/deploy-and-verify.php * PATH: /deploy/deploy-and-verify.php
* BRIEF: Deploy with automatic health check and rollback on failure * BRIEF: Deploy with automatic health check and rollback on failure
+3 -3
View File
@@ -8,11 +8,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.Scripts.Deploy * DEFGROUP: MokoCLI.Scripts.Deploy
* INGROUP: MokoPlatform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /deploy/deploy-dolibarr.php * PATH: /deploy/deploy-dolibarr.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Deploy Dolibarr module files to a remote server via SFTP/rsync * BRIEF: Deploy Dolibarr module files to a remote server via SFTP/rsync
*/ */
+2 -2
View File
@@ -8,8 +8,8 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.Scripts.Deploy * DEFGROUP: MokoCLI.Scripts.Deploy
* INGROUP: MokoPlatform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /deploy/deploy-joomla.php * PATH: /deploy/deploy-joomla.php
* BRIEF: Smart Joomla deploy — routes files to correct Joomla directories based on XML manifest * BRIEF: Smart Joomla deploy — routes files to correct Joomla directories based on XML manifest
+2 -2
View File
@@ -8,8 +8,8 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.Scripts.Deploy * DEFGROUP: MokoCLI.Scripts.Deploy
* INGROUP: MokoPlatform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /deploy/deploy-sftp.php * PATH: /deploy/deploy-sftp.php
* BRIEF: Deploy a repository src/ directory to a remote web server via SFTP * BRIEF: Deploy a repository src/ directory to a remote web server via SFTP
+3 -3
View File
@@ -8,11 +8,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.Scripts.Deploy * DEFGROUP: MokoCLI.Scripts.Deploy
* INGROUP: MokoPlatform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /deploy/health-check.php * PATH: /deploy/health-check.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Post-deploy health check — verify a Joomla site is responding correctly * BRIEF: Post-deploy health check — verify a Joomla site is responding correctly
*/ */
+3 -3
View File
@@ -8,11 +8,11 @@
* SPDX-License-Identifier: GPL-3.0-or-later * SPDX-License-Identifier: GPL-3.0-or-later
* *
* FILE INFORMATION * FILE INFORMATION
* DEFGROUP: MokoPlatform.Scripts.Deploy * DEFGROUP: MokoCLI.Scripts.Deploy
* INGROUP: MokoPlatform * INGROUP: MokoCLI
* REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli * REPO: https://git.mokoconsulting.tech/MokoConsulting/mokocli
* PATH: /deploy/rollback-joomla.php * PATH: /deploy/rollback-joomla.php
* VERSION: 09.40.00 * VERSION: 09.43.00
* BRIEF: Rollback a Joomla deployment by restoring from a pre-deploy snapshot * BRIEF: Rollback a Joomla deployment by restoring from a pre-deploy snapshot
*/ */

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