Compare commits

...

36 Commits

Author SHA1 Message Date
jmiller 1fe4f83e73 Merge pull request 'chore(release): v09.00.00' (#140) from dev into main
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 3s
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 4s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 50s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
2026-05-26 04:33:40 +00:00
Jonathan Miller 7e5c322792 chore(release): bump to 09.00.00
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 4s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 5s
Generic: Repo Health / Access control (pull_request) Successful in 6s
Universal: PR Check / Validate PR (pull_request) Successful in 11s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 12s
Universal: Security Audit / Dependency Audit (pull_request) Successful in 9s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 1m21s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Successful in 59s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Generic: Repo Health / Release configuration (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
PHPDoc standard, CI enforcement, updates_xml_build fixes.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 23:32:42 -05:00
jmiller b010677d75 Merge pull request 'chore: cascade main → dev (9275e58) [skip ci]' (#139) from main into dev
chore: cascade main → dev [skip ci]
2026-05-26 04:29:33 +00:00
jmiller 9275e581c2 Merge pull request 'chore: PHPDoc Priority 1 + Coding Standards wiki' (#138) from dev into main
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 3s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 46s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
2026-05-26 04:29:29 +00:00
Jonathan Miller 3f3b1f79a0 chore: add PHPDoc to Priority 1 Enterprise classes
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 42s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 4s
Universal: PR Check / Validate PR (pull_request) Successful in 4s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Successful in 45s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Generic: Repo Health / Release configuration (push) Has been cancelled
Generic: Repo Health / Scripts governance (push) Has been cancelled
Generic: Repo Health / Repository health (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Universal: PR Check / Build RC Package (pull_request) Has been cancelled
Generic: Repo Health / Release configuration (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
Added @since, @param, @see tags to:
- CliFramework: class-level @since, 2 undocumented methods
- GitHubAdapter: class @since/@see, constructor @param, property docs
- MokoGiteaAdapter: class @since/@see, constructor @param, property docs
- ApiClient: class @since

Wiki: created Coding-Standards page with full PHPDoc standard,
PHPCS exclusion rationale, and file structure patterns.

Partial progress on #137

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 23:26:07 -05:00
Jonathan Miller 83842c50ad docs(changelog): add updates_xml_build fixes to Unreleased
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Scripts governance (push) Successful in 4s
Generic: Repo Health / Release configuration (push) Successful in 5s
Generic: Repo Health / Repository health (push) Successful in 10s
Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 23:12:12 -05:00
Jonathan Miller fbedd5966c fix(updates_xml): cascade entries down, fix Gitea release tag URLs, fix client tag
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Release configuration (push) Successful in 4s
Generic: Repo Health / Scripts governance (push) Successful in 4s
Generic: Repo Health / Repository health (push) Successful in 13s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 46s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
- Cascade: when stable releases, write all 5 channel entries pointing to stable
- Separate Joomla tags from Gitea release tags via releaseTagMap
- Only add client tag for templates and modules, not packages
- Preservation logic matches against Joomla tag names correctly

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 23:10:29 -05:00
jmiller eca2c13018 Merge pull request 'chore: cascade main → dev (48d0001) [skip ci]' (#136) from main into dev
chore: cascade main → dev [skip ci]
2026-05-26 04:09:19 +00:00
jmiller 48d000107d Merge pull request 'fix(ci): enforce PHPStan + PHPUnit in CI' (#135) from dev into main
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Blocked by required conditions
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Blocked by required conditions
Platform: moko-platform CI / Gate 4: Governance (push) Blocked by required conditions
Generic: Repo Health / Site Health (push) Has been skipped
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 3s
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Release configuration (push) Successful in 5s
Generic: Repo Health / Scripts governance (push) Successful in 6s
Generic: Repo Health / Repository health (push) Successful in 11s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 49s
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been skipped
Platform: moko-platform CI / CI Summary (push) Has been cancelled
2026-05-26 04:09:14 +00:00
Jonathan Miller 7ceb9528cc fix(ci): enforce PHPStan + PHPUnit in CI gates
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Release configuration (push) Successful in 4s
Generic: Repo Health / Scripts governance (push) Successful in 4s
Generic: Repo Health / Repository health (push) Successful in 10s
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 2s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 4s
Universal: PR Check / Validate PR (pull_request) Successful in 4s
Universal: PR Check / Build RC Package (pull_request) Successful in 2s
Generic: Repo Health / Release configuration (pull_request) Successful in 3s
Generic: Repo Health / Scripts governance (pull_request) Successful in 4s
Generic: Repo Health / Repository health (pull_request) Successful in 11s
Platform: moko-platform CI / Gate 1: Code Quality (push) Failing after 51s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Failing after 51s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
- PHPStan: remove continue-on-error, update label to Level 6,
  add --memory-limit=512M, fail on errors (was advisory)
- PHPUnit: add error handling — tests now block merges on failure
  (was silently passing even on test failures)

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 23:08:33 -05:00
jmiller 5fabaec477 Merge pull request 'chore: cascade main → dev (e40b799) [skip ci]' (#134) from main into dev
chore: cascade main → dev [skip ci]
2026-05-26 03:55:23 +00:00
jmiller e40b799101 Merge pull request 'chore(release): v08.00.00' (#133) from dev into main
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 5s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 1m17s
Generic: Repo Health / Repository health (push) Successful in 16s
Generic: Repo Health / Access control (push) Successful in 3s
Generic: Repo Health / Release configuration (push) Successful in 13s
Generic: Repo Health / Scripts governance (push) Successful in 10s
Generic: Repo Health / Site Health (push) Has been skipped
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (push) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (push) Has been cancelled
Platform: moko-platform CI / CI Summary (push) Has been cancelled
2026-05-26 03:53:20 +00:00
Jonathan Miller 7e9784e723 chore(release): bump to 08.00.00
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 5s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: PR Check / Branch Policy (pull_request) Successful in 3s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Successful in 6s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 7s
Universal: Security Audit / Dependency Audit (pull_request) Successful in 7s
Generic: Repo Health / Release configuration (push) Successful in 7s
Generic: Repo Health / Scripts governance (push) Successful in 11s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 1m43s
Generic: Repo Health / Repository health (push) Successful in 21s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Successful in 1m24s
Universal: PR Check / Build RC Package (pull_request) Successful in 4s
Generic: Repo Health / Scripts governance (pull_request) Successful in 7s
Generic: Repo Health / Release configuration (pull_request) Successful in 9s
Generic: Repo Health / Repository health (pull_request) Successful in 16s
Platform: moko-platform CI / Gate 5: Template Integrity (push) Failing after 4s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Successful in 47s
Platform: moko-platform CI / Gate 4: Governance (push) Successful in 47s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Successful in 51s
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Failing after 50s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Successful in 54s
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
PHPStan level 0 → 6, branch protection restored, workflows synced,
44 stale runners flushed. Found and fixed real metrics bug at level 5.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 22:52:00 -05:00
jmiller 209dee14fd Merge pull request 'chore: cascade main → dev (81351f4) [skip ci]' (#132) from main into dev
chore: cascade main → dev [skip ci]
2026-05-26 03:50:21 +00:00
Jonathan Miller 81351f45fd fix: updates_xml_build — tag 'dev' not 'development', client for all types
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 3s
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 8s
Generic: Repo Health / Release configuration (push) Successful in 11s
Generic: Repo Health / Scripts governance (push) Successful in 5s
Generic: Repo Health / Repository health (push) Successful in 15s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 1m25s
Platform: moko-platform CI / Gate 5: Template Integrity (push) Failing after 14s
Platform: moko-platform CI / Gate 4: Governance (push) Successful in 1m14s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Successful in 1m19s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Successful in 1m27s
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Failing after 1m27s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Successful in 1m29s
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Two root causes of Joomla updater not finding MokoWaaS updates:

1. stabilityTagToInteger('development') looks for STABILITY_DEVELOPMENT
   which doesn't exist → defaults to STABLE. Changed to 'dev' which
   maps to STABILITY_DEV (0).

2. Missing <client> tag defaults to client_id=1 (administrator) in
   Joomla's ExtensionAdapter. Packages install with client_id=0 (site).
   Now adds <client>site</client> for all extension types.

Fixes: #129

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 22:50:07 -05:00
jmiller fd451b4b73 Merge pull request 'chore: cascade main → dev (d0dbd1d) [skip ci]' (#131) from main into dev
chore: cascade main → dev [skip ci]
2026-05-26 03:48:35 +00:00
jmiller d0dbd1dceb Merge pull request 'fix: PHPStan level 6 with baseline' (#130) from dev into main
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 5s
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 9s
Generic: Repo Health / Release configuration (push) Successful in 7s
Generic: Repo Health / Scripts governance (push) Successful in 7s
Generic: Repo Health / Repository health (push) Successful in 17s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 2m19s
Platform: moko-platform CI / Gate 5: Template Integrity (push) Failing after 8s
Platform: moko-platform CI / Gate 4: Governance (push) Successful in 50s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Successful in 55s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Successful in 57s
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Failing after 1m0s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Successful in 1m4s
Platform: moko-platform CI / CI Summary (push) Has been cancelled
2026-05-26 03:48:21 +00:00
Jonathan Miller 3e2e291819 fix: PHPStan level 5 → 6 — baseline 360 missing array generics
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Release configuration (push) Successful in 5s
Generic: Repo Health / Scripts governance (push) Successful in 4s
Generic: Repo Health / Repository health (push) Successful in 12s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 4s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Universal: PR Check / Validate PR (pull_request) Successful in 4s
Universal: PR Check / Build RC Package (pull_request) Successful in 2s
Generic: Repo Health / Release configuration (pull_request) Successful in 5s
Generic: Repo Health / Scripts governance (pull_request) Successful in 5s
Generic: Repo Health / Repository health (pull_request) Successful in 14s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 1m10s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Successful in 1m24s
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Failing after 10s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Successful in 1m9s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Successful in 1m10s
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Failing after 58s
Platform: moko-platform CI / Gate 4: Governance (pull_request) Successful in 59s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Successful in 1m6s
Platform: moko-platform CI / Gate 5: Template Integrity (push) Failing after 7s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Successful in 52s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Successful in 52s
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Failing after 51s
Platform: moko-platform CI / Gate 4: Governance (push) Successful in 50s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Successful in 54s
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Level 6 requires generic type annotations on all arrays. 357 of 360
errors are missingType.iterableValue (bare array without generics).
Baselined — these are PHPDoc-only changes with no functional impact.

PHPStan level 6: 0 errors with baseline.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 22:47:39 -05:00
jmiller 5975ea38d8 Merge pull request 'chore: cascade main → dev (8ad548f) [skip ci]' (#128) from main into dev
chore: cascade main → dev [skip ci]
2026-05-26 03:43:49 +00:00
jmiller 8ad548f4a3 Merge pull request 'fix: PHPStan level 5 - fix metrics increment bug' (#127) from dev into main
Generic: Repo Health / Access control (push) Successful in 5s
Generic: Repo Health / Site Health (push) Has been skipped
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 8s
Generic: Repo Health / Release configuration (push) Successful in 5s
Generic: Repo Health / Scripts governance (push) Successful in 4s
Generic: Repo Health / Repository health (push) Successful in 11s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 56s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Successful in 1m20s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Successful in 1m15s
Platform: moko-platform CI / Gate 5: Template Integrity (push) Failing after 10s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Successful in 54s
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Failing after 1m17s
Platform: moko-platform CI / Gate 4: Governance (push) Successful in 1m14s
Platform: moko-platform CI / CI Summary (push) Has been cancelled
2026-05-26 03:43:42 +00:00
Jonathan Miller cbb4d73df5 fix: PHPStan level 4 → 5 — fix 4 errors
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Release configuration (push) Successful in 7s
Generic: Repo Health / Scripts governance (push) Successful in 7s
Generic: Repo Health / Repository health (push) Successful in 19s
Universal: PR Check / Branch Policy (pull_request) Successful in 1s
Generic: Repo Health / Access control (pull_request) Successful in 1s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 6s
Universal: PR Check / Validate PR (pull_request) Successful in 6s
Generic: Repo Health / Release configuration (pull_request) Successful in 6s
Generic: Repo Health / Scripts governance (pull_request) Successful in 6s
Universal: PR Check / Build RC Package (pull_request) Successful in 5s
Generic: Repo Health / Repository health (pull_request) Successful in 15s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 1m19s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Successful in 59s
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Failing after 5s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Successful in 58s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Successful in 1m1s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Successful in 1m0s
Platform: moko-platform CI / Gate 4: Governance (pull_request) Successful in 49s
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Failing after 51s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Successful in 1m2s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Successful in 1m7s
Platform: moko-platform CI / Gate 5: Template Integrity (push) Failing after 7s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Successful in 1m9s
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Failing after 55s
Platform: moko-platform CI / Gate 4: Governance (push) Successful in 1m0s
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
- bulk_sync: remove redundant array_values on already-list array
- RepositorySynchronizer: fix metrics increment() — labels passed as
  2nd param (value) instead of 3rd (labels), was a real bug

PHPStan level 5: 0 errors.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 22:42:50 -05:00
jmiller 47cb47ebdb Merge pull request 'chore: cascade main → dev (22b0f8a) [skip ci]' (#126) from main into dev
chore: cascade main → dev [skip ci]
2026-05-26 03:34:38 +00:00
jmiller 22b0f8af7e Merge pull request 'fix: PHPStan level 4 with baseline' (#125) from dev into main
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 2s
Generic: Repo Health / Release configuration (push) Successful in 4s
Generic: Repo Health / Scripts governance (push) Successful in 4s
Generic: Repo Health / Repository health (push) Successful in 11s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 44s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Successful in 1m6s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Successful in 1m10s
Platform: moko-platform CI / Gate 4: Governance (push) Successful in 1m8s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Successful in 1m12s
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Failing after 1m11s
Platform: moko-platform CI / Gate 5: Template Integrity (push) Failing after 7s
Platform: moko-platform CI / CI Summary (push) Has been cancelled
2026-05-26 03:34:34 +00:00
jmiller 08ca1429ae Merge branch 'main' into dev
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 3s
Universal: PR Check / Branch Policy (pull_request) Successful in 4s
Generic: Repo Health / Site Health (pull_request) Has been skipped
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 8s
Generic: Repo Health / Access control (pull_request) Successful in 4s
Universal: PR Check / Validate PR (pull_request) Successful in 8s
Generic: Repo Health / Scripts governance (push) Successful in 10s
Generic: Repo Health / Release configuration (push) Successful in 10s
Generic: Repo Health / Repository health (push) Successful in 18s
Generic: Repo Health / Release configuration (pull_request) Successful in 13s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 1m30s
Universal: PR Check / Build RC Package (pull_request) Successful in 5s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Successful in 1m32s
Generic: Repo Health / Scripts governance (pull_request) Successful in 11s
Generic: Repo Health / Repository health (pull_request) Successful in 19s
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Failing after 9s
Platform: moko-platform CI / Gate 4: Governance (pull_request) Successful in 1m49s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Successful in 1m49s
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Failing after 1m51s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Successful in 1m52s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Successful in 1m55s
Platform: moko-platform CI / Gate 5: Template Integrity (push) Failing after 7s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Successful in 54s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Successful in 55s
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Failing after 55s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Successful in 57s
Platform: moko-platform CI / Gate 4: Governance (push) Successful in 52s
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
2026-05-26 03:32:18 +00:00
Jonathan Miller e8da1a30ff fix: PHPStan level 3 → 4 — remove dead code, baseline 41 items
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Generic: Repo Health / Release configuration (push) Successful in 10s
Generic: Repo Health / Scripts governance (push) Successful in 9s
Generic: Repo Health / Repository health (push) Successful in 17s
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: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 8s
Universal: PR Check / Validate PR (pull_request) Successful in 8s
Generic: Repo Health / Release configuration (pull_request) Successful in 6s
Generic: Repo Health / Scripts governance (pull_request) Successful in 8s
Universal: PR Check / Build RC Package (pull_request) Successful in 5s
Generic: Repo Health / Repository health (pull_request) Successful in 19s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 1m16s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Successful in 1m29s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Successful in 41s
Platform: moko-platform CI / Gate 5: Template Integrity (push) Failing after 6s
Platform: moko-platform CI / Gate 4: Governance (push) Successful in 1m38s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Successful in 1m40s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Successful in 1m41s
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Failing after 1m40s
Platform: moko-platform CI / Gate 4: Governance (pull_request) Successful in 1m20s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Successful in 1m24s
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Failing after 1m24s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Successful in 1m26s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Successful in 1m30s
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Failing after 12s
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
Removed 13 write-only properties and unused code. Remaining 41
baselined items are defensive patterns (null coalesce on API responses,
boolean safety checks) that are intentional.

PHPStan level 4: 0 errors with baseline.

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 22:31:25 -05:00
gitea-actions[bot] fb754b1a07 refactor(ci): clean up auto-release, move logic to CLI [skip ci] 2026-05-25 22:21:10 -05:00
jmiller 9a2c164207 Merge pull request 'chore: cascade main → dev (78c1329) [skip ci]' (#124) from main into dev
chore: cascade main → dev [skip ci]
2026-05-26 03:19:46 +00:00
jmiller 78c1329a83 Merge pull request 'fix: PHPStan level 3 - 12 return type errors fixed' (#123) from dev into main
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 4s
Generic: Repo Health / Scripts governance (push) Successful in 5s
Generic: Repo Health / Release configuration (push) Successful in 5s
Generic: Repo Health / Repository health (push) Successful in 11s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 45s
Platform: moko-platform CI / Gate 5: Template Integrity (push) Failing after 5s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Successful in 55s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Successful in 57s
Platform: moko-platform CI / Gate 4: Governance (push) Successful in 57s
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Failing after 58s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Successful in 1m0s
Platform: moko-platform CI / CI Summary (push) Has been cancelled
2026-05-26 03:19:41 +00:00
jmiller 05f43ed88f Merge branch 'main' into dev
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Site Health (pull_request) Has been skipped
Generic: Repo Health / Access control (push) Successful in 2s
Universal: PR Check / Branch Policy (pull_request) Successful in 2s
Generic: Repo Health / Access control (pull_request) Successful in 2s
Universal: PR Check / Validate PR (pull_request) Successful in 5s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 5s
Generic: Repo Health / Release configuration (push) Successful in 4s
Generic: Repo Health / Scripts governance (push) Successful in 5s
Universal: PR Check / Build RC Package (pull_request) Successful in 3s
Generic: Repo Health / Release configuration (pull_request) Successful in 5s
Generic: Repo Health / Scripts governance (pull_request) Successful in 5s
Generic: Repo Health / Repository health (push) Successful in 13s
Generic: Repo Health / Repository health (pull_request) Successful in 13s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 57s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Successful in 57s
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Failing after 8s
Platform: moko-platform CI / Gate 4: Governance (pull_request) Successful in 44s
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Failing after 47s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Successful in 48s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Successful in 49s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Successful in 50s
Platform: moko-platform CI / Gate 5: Template Integrity (push) Failing after 6s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Successful in 1m0s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Successful in 1m7s
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Failing after 1m4s
Platform: moko-platform CI / Gate 4: Governance (push) Successful in 1m4s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Successful in 1m13s
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
2026-05-26 03:18:36 +00:00
Jonathan Miller 05e4f39e7d fix: PHPStan level 2 → 3 — fix 12 return type errors
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Generic: Repo Health / Release configuration (push) Successful in 4s
Generic: Repo Health / Scripts governance (push) Successful in 4s
Generic: Repo Health / Repository health (push) Successful in 11s
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 2s
Universal: Secret Scanning / Gitleaks Secret Scan (pull_request) Successful in 7s
Universal: PR Check / Validate PR (pull_request) Successful in 7s
Generic: Repo Health / Release configuration (pull_request) Successful in 5s
Generic: Repo Health / Scripts governance (pull_request) Successful in 5s
Universal: PR Check / Build RC Package (pull_request) Successful in 2s
Generic: Repo Health / Repository health (pull_request) Successful in 15s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 59s
Platform: moko-platform CI / Gate 1: Code Quality (pull_request) Successful in 57s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Successful in 51s
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Failing after 57s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Successful in 1m4s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Successful in 1m6s
Platform: moko-platform CI / Gate 5: Template Integrity (push) Failing after 11s
Platform: moko-platform CI / Gate 4: Governance (push) Successful in 54s
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 3: Self-Health Check (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 4: Governance (pull_request) Has been cancelled
Platform: moko-platform CI / Gate 5: Template Integrity (pull_request) Has been cancelled
Platform: moko-platform CI / CI Summary (pull_request) Has been cancelled
- Interface return types: narrowed list types to array<mixed> for API
  responses (ApiClient returns array<string, mixed>, not typed lists)
- paginateAll(): wrap return with array_values() for numeric keys
- listLabels: include id in return type
- check_file_integrity: fix sftpConfig default value type

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 22:18:07 -05:00
jmiller 3dcb3b6d3a chore: sync .mokogitea/workflows/auto-release.yml from moko-platform [skip ci] 2026-05-26 03:07:21 +00:00
jmiller db4e6f5c6b Merge pull request 'chore: cascade main → dev (aa7fc45) [skip ci]' (#121) from main into dev
chore: cascade main → dev [skip ci]
2026-05-26 03:07:10 +00:00
Jonathan Miller aa7fc45a67 feat: version_check.php — validate version consistency across files
Generic: Repo Health / Site Health (push) Has been skipped
Generic: Repo Health / Access control (push) Successful in 1s
Universal: Cascade Main → Dev / Cascade main → branches (push) Successful in 4s
Generic: Repo Health / Release configuration (push) Successful in 5s
Generic: Repo Health / Scripts governance (push) Successful in 5s
Generic: Repo Health / Repository health (push) Successful in 16s
Platform: moko-platform CI / Gate 1: Code Quality (push) Successful in 55s
Platform: moko-platform CI / Gate 5: Template Integrity (push) Failing after 7s
Platform: moko-platform CI / Gate 2: Unit Tests (8.3) (push) Successful in 52s
Platform: moko-platform CI / Gate 2: Unit Tests (8.1) (push) Successful in 52s
Platform: moko-platform CI / Gate 2: Unit Tests (8.2) (push) Successful in 54s
Platform: moko-platform CI / Gate 3: Self-Health Check (push) Failing after 54s
Platform: moko-platform CI / Gate 4: Governance (push) Successful in 53s
Platform: moko-platform CI / CI Summary (push) Has been cancelled
Checks README.md VERSION header and all manifest XML <version> tags.
Flags mismatches, reports highest version, and optionally fixes them.

Usage:
  php version_check.php --path /repo           # report only
  php version_check.php --path /repo --strict  # exit 1 on mismatch
  php version_check.php --path /repo --fix     # fix to highest version

Authored-by: Moko Consulting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-25 22:07:04 -05:00
jmiller 03fe66238f chore: sync .mokogitea/workflows/pre-release.yml from moko-platform [skip ci] 2026-05-26 03:05:28 +00:00
gitea-actions[bot] a5ae616a94 fix(ci): auto-release preserves all update channels [skip ci] 2026-05-25 21:59:33 -05:00
jmiller ff7924de7d Merge pull request 'chore: cascade main → dev (1690e29) [skip ci]' (#120) from main into dev
chore: cascade main → dev [skip ci]
2026-05-26 02:56:24 +00:00
24 changed files with 3080 additions and 630 deletions
+67 -162
View File
@@ -26,7 +26,8 @@
name: "Universal: Build & Release" name: "Universal: Build & Release"
on: on:
push: pull_request:
types: [closed]
branches: branches:
- main - main
paths: paths:
@@ -47,7 +48,8 @@ jobs:
release: release:
name: Build & Release Pipeline name: Build & Release Pipeline
runs-on: release runs-on: release
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' if: >-
github.event.pull_request.merged == true || github.event_name == 'workflow_dispatch'
steps: steps:
- name: Checkout repository - name: Checkout repository
@@ -94,9 +96,9 @@ jobs:
fi fi
MAJOR=$(echo "$VERSION" | cut -d. -f1) MAJOR=$(echo "$VERSION" | cut -d. -f1)
echo "version=${VERSION}" >> "$GITHUB_OUTPUT" echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "release_tag=v${MAJOR}" >> "$GITHUB_OUTPUT" echo "release_tag=stable" >> "$GITHUB_OUTPUT"
echo "skip=false" >> "$GITHUB_OUTPUT" echo "skip=false" >> "$GITHUB_OUTPUT"
echo "branch=version/${MAJOR}" >> "$GITHUB_OUTPUT" echo "branch=main" >> "$GITHUB_OUTPUT"
- name: "Step 1b: Bump version" - name: "Step 1b: Bump version"
id: bump id: bump
@@ -261,6 +263,7 @@ jobs:
run: | run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
php /tmp/moko-platform-api/cli/badge_update.php --path . --version "${VERSION}" 2>/dev/null || true php /tmp/moko-platform-api/cli/badge_update.php --path . --version "${VERSION}" 2>/dev/null || true
php /tmp/moko-platform-api/cli/version_check.php --path . --fix 2>/dev/null || true
- name: "Step 5: Write update stream" - name: "Step 5: Write update stream"
if: >- if: >-
@@ -268,6 +271,15 @@ jobs:
steps.platform.outputs.platform == 'joomla' steps.platform.outputs.platform == 'joomla'
run: | run: |
VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}" VERSION="${{ steps.bump.outputs.version || steps.version.outputs.version }}"
# Fetch latest updates.xml from main so preserve logic has all channels
GA_TOKEN="${{ secrets.GA_TOKEN }}"
API="${GITEA_URL}/api/v1/repos/${{ github.repository }}"
curl -sf -H "Authorization: token ${GA_TOKEN}" \
"${API}/contents/updates.xml?ref=main" 2>/dev/null | \
python3 -c "import sys,json,base64; print(base64.b64decode(json.load(sys.stdin)['content']).decode())" \
> updates.xml 2>/dev/null || true
php /tmp/moko-platform-api/cli/updates_xml_build.php \ php /tmp/moko-platform-api/cli/updates_xml_build.php \
--path . --version "${VERSION}" --stability stable \ --path . --version "${VERSION}" --stability stable \
--gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \ --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
@@ -295,9 +307,7 @@ jobs:
# -- STEP 6: Create tag --------------------------------------------------- # -- STEP 6: Create tag ---------------------------------------------------
- name: "Step 6: Create git tag" - name: "Step 6: Create git tag"
if: >- if: >-
steps.version.outputs.skip != 'true' && steps.version.outputs.skip != 'true'
steps.check.outputs.tag_exists != 'true' &&
steps.version.outputs.is_minor == 'true'
run: | run: |
RELEASE_TAG="${{ steps.version.outputs.release_tag }}" RELEASE_TAG="${{ steps.version.outputs.release_tag }}"
# Only create the major release tag if it doesn't exist yet # Only create the major release tag if it doesn't exist yet
@@ -337,6 +347,8 @@ jobs:
[ -z "$NOTES" ] && NOTES="Release ${VERSION}" [ -z "$NOTES" ] && NOTES="Release ${VERSION}"
# Build release name: "Pretty Name VERSION (type_element-VERSION)" # Build release name: "Pretty Name VERSION (type_element-VERSION)"
# Strip existing type prefix to prevent duplication
EXT_ELEMENT=$(echo "$EXT_ELEMENT" | sed -E 's/^(pkg_|com_|mod_|plg_[a-z]+_|tpl_|lib_)//')
TYPE_PREFIX="" TYPE_PREFIX=""
case "${EXT_TYPE}" in case "${EXT_TYPE}" in
plugin) TYPE_PREFIX="plg_${EXT_FOLDER}_" ;; plugin) TYPE_PREFIX="plg_${EXT_FOLDER}_" ;;
@@ -407,6 +419,13 @@ jobs:
# ZIP name: type_folder_element-VERSION (e.g. plg_system_mokojgdpc-01.01.00.zip) # ZIP name: type_folder_element-VERSION (e.g. plg_system_mokojgdpc-01.01.00.zip)
EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1) EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
EXT_FOLDER=$(sed -n 's/.*<extension[^>]*group="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1) EXT_FOLDER=$(sed -n 's/.*<extension[^>]*group="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
# For packages, prefer <packagename> over filename-derived element
if [ "$EXT_TYPE" = "package" ]; then
PKG_NAME=$(sed -n 's/.*<packagename>\([^<]*\)<\/packagename>.*/\1/p' "$MANIFEST" 2>/dev/null | head -1)
[ -n "$PKG_NAME" ] && EXT_ELEMENT="$PKG_NAME"
fi
# Strip existing type prefix to prevent duplication (e.g. pkg_mokowaas → mokowaas)
EXT_ELEMENT=$(echo "$EXT_ELEMENT" | sed -E 's/^(pkg_|com_|mod_|plg_[a-z]+_|tpl_|lib_)//')
TYPE_PREFIX="" TYPE_PREFIX=""
case "${EXT_TYPE}" in case "${EXT_TYPE}" in
plugin) TYPE_PREFIX="plg_${EXT_FOLDER}_" ;; plugin) TYPE_PREFIX="plg_${EXT_FOLDER}_" ;;
@@ -442,110 +461,35 @@ jobs:
SHA256_ZIP=$(sha256sum "/tmp/${ZIP_NAME}" | cut -d' ' -f1) SHA256_ZIP=$(sha256sum "/tmp/${ZIP_NAME}" | cut -d' ' -f1)
SHA256_TAR=$(sha256sum "/tmp/${TAR_NAME}" | cut -d' ' -f1) SHA256_TAR=$(sha256sum "/tmp/${TAR_NAME}" | cut -d' ' -f1)
# -- Delete existing assets with same name before uploading ------ # -- Get existing assets for cleanup --------------------------------
ASSETS=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \ ASSETS=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
"${API_BASE}/releases/${RELEASE_ID}/assets" 2>/dev/null || echo "[]") "${API_BASE}/releases/${RELEASE_ID}/assets" 2>/dev/null || echo "[]")
for ASSET_NAME in "$ZIP_NAME" "$TAR_NAME"; do
# -- Create per-file .sha256 checksum files -------------------------
echo "${SHA256_ZIP} ${ZIP_NAME}" > "/tmp/${ZIP_NAME}.sha256"
echo "${SHA256_TAR} ${TAR_NAME}" > "/tmp/${TAR_NAME}.sha256"
# -- Upload packages + checksums to release tag --------------------
for ASSET in "${ZIP_NAME}" "${TAR_NAME}" "${ZIP_NAME}.sha256" "${TAR_NAME}.sha256"; do
[ ! -f "/tmp/${ASSET}" ] && continue
# Delete existing asset with same name
ASSET_ID=$(echo "$ASSETS" | python3 -c " ASSET_ID=$(echo "$ASSETS" | python3 -c "
import sys,json import sys,json
assets = json.load(sys.stdin) assets = json.load(sys.stdin)
for a in assets: for a in assets:
if a['name'] == '${ASSET_NAME}': if a['name'] == '${ASSET}':
print(a['id']); break print(a['id']); break
" 2>/dev/null || true) " 2>/dev/null || true)
if [ -n "$ASSET_ID" ]; then [ -n "$ASSET_ID" ] && curl -sf -X DELETE -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
curl -sf -X DELETE -H "Authorization: token ${{ secrets.GA_TOKEN }}" \ "${API_BASE}/releases/${RELEASE_ID}/assets/${ASSET_ID}" 2>/dev/null || true
"${API_BASE}/releases/${RELEASE_ID}/assets/${ASSET_ID}" 2>/dev/null || true # Upload
fi curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
-H "Content-Type: application/octet-stream" \
--data-binary @"/tmp/${ASSET}" \
"${API_BASE}/releases/${RELEASE_ID}/assets?name=${ASSET}" > /dev/null 2>&1 || true
done done
# -- Upload both to release tag ---------------------------------- # updates.xml already handled by Step 5 (updates_xml_build.php with preserve logic)
curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
-H "Content-Type: application/octet-stream" \
--data-binary @"/tmp/${ZIP_NAME}" \
"${API_BASE}/releases/${RELEASE_ID}/assets?name=${ZIP_NAME}" > /dev/null 2>&1 || true
curl -sf -X POST -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
-H "Content-Type: application/octet-stream" \
--data-binary @"/tmp/${TAR_NAME}" \
"${API_BASE}/releases/${RELEASE_ID}/assets?name=${TAR_NAME}" > /dev/null 2>&1 || true
# -- Update updates.xml with both download formats ---------------
if [ -f "updates.xml" ]; then
ZIP_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${ZIP_NAME}"
TAR_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${TAR_NAME}"
# Use Python to update only the stable entry's downloads + sha256
export PY_ZIP_URL="$ZIP_URL" PY_TAR_URL="$TAR_URL" PY_SHA="$SHA256_ZIP"
python3 << 'PYEOF'
import re, os
with open("updates.xml") as f:
content = f.read()
zip_url = os.environ["PY_ZIP_URL"]
tar_url = os.environ["PY_TAR_URL"]
sha = os.environ["PY_SHA"]
# Find the stable update block and replace its downloads + sha256
def replace_stable(m):
block = m.group(0)
# Replace downloads block
new_downloads = (
" <downloads>\n"
f" <downloadurl type=\"full\" format=\"zip\">{zip_url}</downloadurl>\n"
" </downloads>"
)
block = re.sub(r' <downloads>.*?</downloads>', new_downloads, block, flags=re.DOTALL)
# Add or replace sha256
if '<sha256>' in block:
block = re.sub(r' <sha256>.*?</sha256>', f' <sha256>{sha}</sha256>', block)
else:
block = block.replace('</downloads>', f'</downloads>\n <sha256>{sha}</sha256>')
return block
content = re.sub(
r' <update>.*?<tag>stable</tag>.*?</update>',
replace_stable,
content,
flags=re.DOTALL
)
with open("updates.xml", "w") as f:
f.write(content)
PYEOF
CURRENT_BRANCH="${{ github.ref_name }}"
git add updates.xml
git commit -m "chore(release): ZIP + tar.gz for ${VERSION} [skip ci]" \
--author="gitea-actions[bot] <gitea-actions[bot]@mokoconsulting.tech>" || true
git push || true
# Sync updates.xml to main via direct API (always runs — may be on version/XX branch)
GA_TOKEN="${{ secrets.GA_TOKEN }}"
API="${GITEA_URL:-https://git.mokoconsulting.tech}/api/v1/repos/${{ github.repository }}"
FILE_SHA=$(curl -sf -H "Authorization: token ${GA_TOKEN}" \
"${API}/contents/updates.xml?ref=main" | jq -r '.sha // empty')
if [ -n "$FILE_SHA" ]; then
CONTENT=$(base64 -w0 updates.xml)
curl -sf -X PUT -H "Authorization: token ${GA_TOKEN}" \
-H "Content-Type: application/json" \
"${API}/contents/updates.xml" \
-d "$(jq -n \
--arg content "$CONTENT" \
--arg sha "$FILE_SHA" \
--arg msg "chore: sync updates.xml ${VERSION} [skip ci]" \
--arg branch "main" \
'{content: $content, sha: $sha, message: $msg, branch: $branch}'
)" > /dev/null 2>&1 \
&& echo "updates.xml synced to main via API" \
|| echo "WARNING: failed to sync updates.xml to main"
else
echo "WARNING: could not get updates.xml SHA from main"
fi
fi
echo "### Packages" >> $GITHUB_STEP_SUMMARY echo "### Packages" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY
@@ -556,72 +500,33 @@ jobs:
echo "| Release | \`${RELEASE_TAG}\` | |" >> $GITHUB_STEP_SUMMARY echo "| Release | \`${RELEASE_TAG}\` | |" >> $GITHUB_STEP_SUMMARY
echo "| Download | [${ZIP_NAME}](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${ZIP_NAME}) |" >> $GITHUB_STEP_SUMMARY echo "| Download | [${ZIP_NAME}](${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${RELEASE_TAG}/${ZIP_NAME}) |" >> $GITHUB_STEP_SUMMARY
# -- STEP 8b: Update release description with changelog + SHA ---------------- # -- STEP 8b: Update release description with changelog ----------------------
- name: "Step 8b: Update release body with changelog and SHA" - name: "Step 8b: Update release body"
if: steps.version.outputs.skip != 'true' if: steps.version.outputs.skip != 'true'
run: | run: |
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 }}"
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" MOKO_CLI="/tmp/moko-platform-api/cli"
EXT_ELEMENT="${{ steps.updates.outputs.ext_element }}"
EXT_TYPE="${{ steps.updates.outputs.ext_type }}"
EXT_FOLDER="${{ steps.updates.outputs.ext_folder }}"
# Build TYPE_PREFIX to match Step 8's ZIP naming php ${MOKO_CLI}/release_body_update.php \
TYPE_PREFIX="" --path . --version "${VERSION}" --tag "${RELEASE_TAG}" \
case "${EXT_TYPE}" in --token "${{ secrets.GA_TOKEN }}" \
plugin) TYPE_PREFIX="plg_${EXT_FOLDER}_" ;; --gitea-url "${GITEA_URL}" --org "${GITEA_ORG}" --repo "${GITEA_REPO}" \
module) TYPE_PREFIX="mod_" ;; 2>/dev/null || {
component) TYPE_PREFIX="com_" ;; # Fallback: simple body update if CLI not available
template) TYPE_PREFIX="tpl_" ;; API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
library) TYPE_PREFIX="lib_" ;; RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
package) TYPE_PREFIX="pkg_" ;; "${API_BASE}/releases/tags/${RELEASE_TAG}" 2>/dev/null | \
esac python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
ZIP_NAME="${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION}.zip" if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "None" ]; then
TAR_NAME="${TYPE_PREFIX}${EXT_ELEMENT}-${VERSION}.tar.gz" BODY="## ${VERSION} ($(date +%Y-%m-%d))\n\nChecksum files attached as \`*.sha256\` assets."
curl -sf -X PATCH -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
# Get SHA from the built files -H "Content-Type: application/json" \
SHA256_ZIP="" "${API_BASE}/releases/${RELEASE_ID}" \
[ -f "/tmp/${ZIP_NAME}" ] && SHA256_ZIP=$(sha256sum "/tmp/${ZIP_NAME}" | cut -d' ' -f1) -d "{\"body\":\"${BODY}\"}" > /dev/null 2>&1
SHA256_TAR="" fi
[ -f "/tmp/${TAR_NAME}" ] && SHA256_TAR=$(sha256sum "/tmp/${TAR_NAME}" | cut -d' ' -f1) }
echo "Release body updated" >> $GITHUB_STEP_SUMMARY
# Extract latest changelog entry (strip the ## header to avoid duplicate)
CHANGELOG=""
if [ -f "CHANGELOG.md" ]; then
CHANGELOG=$(sed -n "/^## \[*${VERSION}/,/^## \[*[0-9]/p" CHANGELOG.md | sed '$d' | sed '1d')
[ -z "$CHANGELOG" ] && CHANGELOG=$(sed -n '/^## /,/^## /p' CHANGELOG.md | sed '$d' | sed '1d' | head -30)
fi
# Build release body (single header, no duplicate from changelog)
BODY="## ${VERSION} ($(date +%Y-%m-%d))\n\n"
if [ -n "$CHANGELOG" ]; then
BODY="${BODY}${CHANGELOG}\n\n"
fi
BODY="${BODY}---\n\n### Checksums\n\n"
BODY="${BODY}| File | SHA-256 |\n|------|--------|\n"
[ -n "$SHA256_ZIP" ] && BODY="${BODY}| \`${ZIP_NAME}\` | \`${SHA256_ZIP}\` |\n"
[ -n "$SHA256_TAR" ] && BODY="${BODY}| \`${TAR_NAME}\` | \`${SHA256_TAR}\` |\n"
# Get release ID and update body
RELEASE_ID=$(curl -sf -H "Authorization: token ${{ secrets.GA_TOKEN }}" \
"${API_BASE}/releases/tags/${RELEASE_TAG}" 2>/dev/null | \
python3 -c "import sys,json; print(json.load(sys.stdin).get('id',''))" 2>/dev/null || true)
if [ -n "$RELEASE_ID" ] && [ "$RELEASE_ID" != "None" ]; then
python3 -c "
import json, urllib.request
body = '''$(printf '%b' "$BODY")'''
data = json.dumps({'body': body}).encode()
req = urllib.request.Request(
'${API_BASE}/releases/${RELEASE_ID}',
data=data,
headers={'Authorization': 'token ${{ secrets.GA_TOKEN }}', 'Content-Type': 'application/json'},
method='PATCH'
)
urllib.request.urlopen(req)
" 2>/dev/null && echo "Release body updated with changelog + SHA" >> $GITHUB_STEP_SUMMARY
fi
# -- STEP 9: Mirror to GitHub (stable only) -------------------------------- # -- STEP 9: Mirror to GitHub (stable only) --------------------------------
- name: "Step 9: Mirror release to GitHub" - name: "Step 9: Mirror release to GitHub"
+13 -10
View File
@@ -124,16 +124,16 @@ jobs:
echo "### PHPCS" >> $GITHUB_STEP_SUMMARY echo "### PHPCS" >> $GITHUB_STEP_SUMMARY
echo "PSR-12 compliance: passed" >> $GITHUB_STEP_SUMMARY echo "PSR-12 compliance: passed" >> $GITHUB_STEP_SUMMARY
- name: "PHPStan (Level 2)" - name: "PHPStan (Level 6)"
continue-on-error: true
run: | run: |
vendor/bin/phpstan analyse -c phpstan.neon --no-progress --error-format=github 2>&1 || { vendor/bin/phpstan analyse -c phpstan.neon --no-progress --memory-limit=512M --error-format=github 2>&1 || {
echo "::warning::PHPStan found type errors (advisory)" echo "::error::PHPStan found type errors"
echo "### PHPStan" >> $GITHUB_STEP_SUMMARY echo "### PHPStan" >> $GITHUB_STEP_SUMMARY
echo "Static analysis errors detected. Run \`composer phpstan\` locally." >> $GITHUB_STEP_SUMMARY echo "Static analysis errors detected. Run \`composer phpstan\` locally." >> $GITHUB_STEP_SUMMARY
exit 1
} }
echo "### PHPStan" >> $GITHUB_STEP_SUMMARY echo "### PHPStan" >> $GITHUB_STEP_SUMMARY
echo "Static analysis: advisory (level 0)" >> $GITHUB_STEP_SUMMARY echo "Static analysis (level 6): passed" >> $GITHUB_STEP_SUMMARY
- name: "Psalm" - name: "Psalm"
continue-on-error: true continue-on-error: true
@@ -177,11 +177,14 @@ jobs:
- name: "PHPUnit (PHP ${{ matrix.php }})" - name: "PHPUnit (PHP ${{ matrix.php }})"
run: | run: |
vendor/bin/phpunit --testdox 2>&1 vendor/bin/phpunit --testdox 2>&1 || {
{ echo "::error::PHPUnit tests failed"
echo "### PHPUnit (PHP ${{ matrix.php }})" echo "### PHPUnit (PHP ${{ matrix.php }})" >> $GITHUB_STEP_SUMMARY
echo "All tests passed." echo "Tests failed. Run \`vendor/bin/phpunit --testdox\` locally." >> $GITHUB_STEP_SUMMARY
} >> $GITHUB_STEP_SUMMARY exit 1
}
echo "### PHPUnit (PHP ${{ matrix.php }})" >> $GITHUB_STEP_SUMMARY
echo "All tests passed." >> $GITHUB_STEP_SUMMARY
# ═══════════════════════════════════════════════════════════════════════ # ═══════════════════════════════════════════════════════════════════════
# Gate 3 — Self-Health (Dogfood) # Gate 3 — Self-Health (Dogfood)
+375 -375
View File
@@ -1,375 +1,375 @@
# 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: Gitea.Workflow
# INGROUP: moko-platform.Release # INGROUP: moko-platform.Release
# REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform # REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
# PATH: /templates/workflows/universal/pre-release.yml.template # PATH: /templates/workflows/universal/pre-release.yml.template
# VERSION: 05.01.00 # VERSION: 05.01.00
# BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch # BRIEF: Manual pre-release -- builds dev/alpha/beta/rc packages from any branch
name: "Universal: Pre-Release" name: "Universal: Pre-Release"
on: on:
workflow_dispatch: workflow_dispatch:
inputs: inputs:
stability: stability:
description: 'Pre-release channel' description: 'Pre-release channel'
required: true required: true
type: choice type: choice
options: options:
- development - development
- alpha - alpha
- beta - beta
- release-candidate - release-candidate
permissions: permissions:
contents: write contents: write
env: env:
GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }} GITEA_URL: ${{ vars.GITEA_URL || 'https://git.mokoconsulting.tech' }}
GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }} GITEA_ORG: ${{ vars.GITEA_ORG || github.repository_owner }}
GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }} GITEA_REPO: ${{ vars.GITEA_REPO || github.event.repository.name }}
jobs: jobs:
build: build:
name: "Build Pre-Release (${{ inputs.stability }})" name: "Build Pre-Release (${{ inputs.stability }})"
runs-on: release runs-on: release
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
with: with:
fetch-depth: 0 fetch-depth: 0
token: ${{ secrets.GA_TOKEN }} token: ${{ secrets.GA_TOKEN }}
- name: Setup tools - name: Setup tools
run: | run: |
# Update moko-platform CLI tools if available; install PHP if missing # Update moko-platform CLI tools if available; install PHP if missing
if command -v moko-platform-update &> /dev/null; then if command -v moko-platform-update &> /dev/null; then
moko-platform-update moko-platform-update
elif [ -d "/opt/moko-platform" ]; then elif [ -d "/opt/moko-platform" ]; then
cd /opt/moko-platform && git pull origin main --quiet 2>/dev/null || true cd /opt/moko-platform && git pull origin main --quiet 2>/dev/null || true
else else
if ! command -v php &> /dev/null; then if ! command -v php &> /dev/null; then
sudo apt-get update -qq sudo apt-get update -qq
sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl >/dev/null 2>&1 sudo apt-get install -y -qq php-cli php-mbstring php-xml php-zip php-curl >/dev/null 2>&1
fi fi
git clone --depth 1 --branch main --quiet \ git clone --depth 1 --branch main --quiet \
"https://x-access-token:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/moko-platform.git" \ "https://x-access-token:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/MokoConsulting/moko-platform.git" \
/tmp/moko-platform-api /tmp/moko-platform-api
fi fi
# Set MOKO_CLI to whichever path exists # Set MOKO_CLI to whichever path exists
if [ -d "/opt/moko-platform/cli" ]; then if [ -d "/opt/moko-platform/cli" ]; then
echo "MOKO_CLI=/opt/moko-platform/cli" >> "$GITHUB_ENV" echo "MOKO_CLI=/opt/moko-platform/cli" >> "$GITHUB_ENV"
else else
echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV" echo "MOKO_CLI=/tmp/moko-platform-api/cli" >> "$GITHUB_ENV"
fi fi
- name: Detect platform - name: Detect platform
id: platform id: platform
run: | run: |
PLATFORM=$(sed -n 's/.*<platform>\([^<]*\)<\/platform>.*/\1/p' .mokogitea/manifest.xml 2>/dev/null | head -1 | tr -d '[:space:]') PLATFORM=$(sed -n 's/.*<platform>\([^<]*\)<\/platform>.*/\1/p' .mokogitea/manifest.xml 2>/dev/null | head -1 | tr -d '[:space:]')
[ -z "$PLATFORM" ] && PLATFORM="generic" [ -z "$PLATFORM" ] && PLATFORM="generic"
echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT" echo "platform=$PLATFORM" >> "$GITHUB_OUTPUT"
MANIFEST=$(find ./src -maxdepth 1 -name "pkg_*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1) MANIFEST=$(find ./src -maxdepth 1 -name "pkg_*.xml" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
[ -z "$MANIFEST" ] && MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" ! -path "*/packages/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1) [ -z "$MANIFEST" ] && MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" ! -path "*/packages/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
[ -z "$MANIFEST" ] && MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1) [ -z "$MANIFEST" ] && MANIFEST=$(find . -maxdepth 3 -name "*.xml" ! -path "./.git/*" -exec grep -l '<extension' {} \; 2>/dev/null | head -1)
MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1) MOD_FILE=$(find . -maxdepth 4 -name "mod*.class.php" ! -path "./.git/*" -exec grep -l 'extends DolibarrModules' {} \; 2>/dev/null | head -1)
echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT" echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT"
echo "mod_file=${MOD_FILE}" >> "$GITHUB_OUTPUT" echo "mod_file=${MOD_FILE}" >> "$GITHUB_OUTPUT"
- name: Resolve metadata and bump version - name: Resolve metadata and bump version
id: meta id: meta
run: | run: |
STABILITY="${{ inputs.stability }}" STABILITY="${{ inputs.stability }}"
case "$STABILITY" in case "$STABILITY" in
development) SUFFIX="-dev"; TAG="development" ;; development) SUFFIX="-dev"; TAG="development" ;;
alpha) SUFFIX="-alpha"; TAG="alpha" ;; alpha) SUFFIX="-alpha"; TAG="alpha" ;;
beta) SUFFIX="-beta"; TAG="beta" ;; beta) SUFFIX="-beta"; TAG="beta" ;;
release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;; release-candidate) SUFFIX="-rc"; TAG="release-candidate" ;;
esac esac
# Patch bump via CLI tool # Patch bump via CLI tool
php ${MOKO_CLI}/version_bump.php --path . php ${MOKO_CLI}/version_bump.php --path .
VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null) VERSION=$(php ${MOKO_CLI}/version_read.php --path . 2>/dev/null)
[ -z "$VERSION" ] && VERSION="00.00.01" [ -z "$VERSION" ] && VERSION="00.00.01"
TODAY=$(date +%Y-%m-%d) TODAY=$(date +%Y-%m-%d)
# Update platform-specific manifest # Update platform-specific manifest
PLATFORM="${{ steps.platform.outputs.platform }}" PLATFORM="${{ steps.platform.outputs.platform }}"
MANIFEST="${{ steps.platform.outputs.manifest }}" MANIFEST="${{ steps.platform.outputs.manifest }}"
MOD_FILE="${{ steps.platform.outputs.mod_file }}" MOD_FILE="${{ steps.platform.outputs.mod_file }}"
php ${MOKO_CLI}/version_set_platform.php \ php ${MOKO_CLI}/version_set_platform.php \
--path . --version "$VERSION" --branch "${{ github.ref_name }}" 2>/dev/null || true --path . --version "$VERSION" --branch "${{ github.ref_name }}" 2>/dev/null || true
# Commit version bump # Commit version bump
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]" git config --local user.name "gitea-actions[bot]"
git remote set-url origin "https://jmiller:${{ secrets.GA_TOKEN }}@git.mokoconsulting.tech/${{ github.repository }}.git" git remote set-url origin "https://jmiller:${{ secrets.GA_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 git push origin HEAD 2>&1
} }
# Auto-detect element (platform-aware) # Auto-detect element (platform-aware)
EXT_ELEMENT="" EXT_ELEMENT=""
case "$PLATFORM" in case "$PLATFORM" in
joomla) joomla)
if [ -n "$MANIFEST" ]; then if [ -n "$MANIFEST" ]; then
EXT_ELEMENT=$(sed -n 's/.*<element>\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" 2>/dev/null | head -1) EXT_ELEMENT=$(sed -n 's/.*<element>\([^<]*\)<\/element>.*/\1/p' "$MANIFEST" 2>/dev/null | head -1)
if [ -z "$EXT_ELEMENT" ]; then if [ -z "$EXT_ELEMENT" ]; then
EXT_ELEMENT=$(basename "$MANIFEST" .xml | tr '[:upper:]' '[:lower:]') EXT_ELEMENT=$(basename "$MANIFEST" .xml | tr '[:upper:]' '[:lower:]')
case "$EXT_ELEMENT" in case "$EXT_ELEMENT" in
templatedetails|manifest) EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') ;; templatedetails|manifest) EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') ;;
esac esac
fi fi
else else
EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
fi fi
;; ;;
dolibarr) dolibarr)
if [ -n "$MOD_FILE" ]; then if [ -n "$MOD_FILE" ]; then
MOD_BASENAME=$(basename "$MOD_FILE" .class.php) MOD_BASENAME=$(basename "$MOD_FILE" .class.php)
EXT_ELEMENT=$(echo "$MOD_BASENAME" | sed 's/^mod//' | tr '[:upper:]' '[:lower:]') EXT_ELEMENT=$(echo "$MOD_BASENAME" | sed 's/^mod//' | tr '[:upper:]' '[:lower:]')
else else
EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
fi fi
;; ;;
*) *)
EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -') EXT_ELEMENT=$(echo "${GITEA_REPO}" | tr '[:upper:]' '[:lower:]' | tr -d ' -')
;; ;;
esac esac
ZIP_NAME="${EXT_ELEMENT}-${VERSION}${SUFFIX}.zip" ZIP_NAME="${EXT_ELEMENT}-${VERSION}${SUFFIX}.zip"
echo "version=${VERSION}" >> "$GITHUB_OUTPUT" echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT" echo "stability=${STABILITY}" >> "$GITHUB_OUTPUT"
echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT" echo "suffix=${SUFFIX}" >> "$GITHUB_OUTPUT"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT" echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT" echo "zip_name=${ZIP_NAME}" >> "$GITHUB_OUTPUT"
echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT" echo "ext_element=${EXT_ELEMENT}" >> "$GITHUB_OUTPUT"
echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT" echo "manifest=${MANIFEST}" >> "$GITHUB_OUTPUT"
echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ===" echo "=== Pre-Release: ${EXT_ELEMENT} ${VERSION}${SUFFIX} ==="
- name: Build package - name: Build package
run: | run: |
SOURCE_DIR="src" SOURCE_DIR="src"
[ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs" [ ! -d "$SOURCE_DIR" ] && SOURCE_DIR="htdocs"
if [ ! -d "$SOURCE_DIR" ]; then if [ ! -d "$SOURCE_DIR" ]; then
echo "::error::No src/ or htdocs/ directory" echo "::error::No src/ or htdocs/ directory"
exit 1 exit 1
fi fi
MANIFEST="${{ steps.meta.outputs.manifest }}" MANIFEST="${{ steps.meta.outputs.manifest }}"
EXT_TYPE="" EXT_TYPE=""
if [ -n "$MANIFEST" ]; then if [ -n "$MANIFEST" ]; then
EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1) EXT_TYPE=$(sed -n 's/.*<extension[^>]*type="\([^"]*\)".*/\1/p' "$MANIFEST" | head -1)
fi fi
EXCLUDES="sftp-config* .ftpignore *.ppk *.pem *.key .env* *.local .build-trigger" EXCLUDES="sftp-config* .ftpignore *.ppk *.pem *.key .env* *.local .build-trigger"
mkdir -p build/package mkdir -p build/package
if [ "$EXT_TYPE" = "package" ] && [ -d "${SOURCE_DIR}/packages" ]; then if [ "$EXT_TYPE" = "package" ] && [ -d "${SOURCE_DIR}/packages" ]; then
echo "=== Building Joomla PACKAGE (multi-extension) ===" echo "=== Building Joomla PACKAGE (multi-extension) ==="
for ext_dir in "${SOURCE_DIR}"/packages/*/; do for ext_dir in "${SOURCE_DIR}"/packages/*/; do
[ ! -d "$ext_dir" ] && continue [ ! -d "$ext_dir" ] && continue
EXT_NAME=$(basename "$ext_dir") EXT_NAME=$(basename "$ext_dir")
echo " Packaging sub-extension: ${EXT_NAME}" echo " Packaging sub-extension: ${EXT_NAME}"
cd "$ext_dir" cd "$ext_dir"
zip -r "../../build/package/${EXT_NAME}.zip" . -x $EXCLUDES zip -r "../../build/package/${EXT_NAME}.zip" . -x $EXCLUDES
cd "$OLDPWD" cd "$OLDPWD"
done done
for f in "${SOURCE_DIR}"/*.xml "${SOURCE_DIR}"/*.php; do for f in "${SOURCE_DIR}"/*.xml "${SOURCE_DIR}"/*.php; do
[ -f "$f" ] && cp "$f" build/package/ [ -f "$f" ] && cp "$f" build/package/
done done
else else
echo "=== Building standard extension ===" echo "=== Building standard extension ==="
rsync -a \ rsync -a \
--exclude='sftp-config*' \ --exclude='sftp-config*' \
--exclude='.ftpignore' \ --exclude='.ftpignore' \
--exclude='*.ppk' \ --exclude='*.ppk' \
--exclude='*.pem' \ --exclude='*.pem' \
--exclude='*.key' \ --exclude='*.key' \
--exclude='.env*' \ --exclude='.env*' \
--exclude='*.local' \ --exclude='*.local' \
--exclude='.build-trigger' \ --exclude='.build-trigger' \
"${SOURCE_DIR}/" build/package/ "${SOURCE_DIR}/" build/package/
fi fi
- name: Create ZIP - name: Create ZIP
id: zip id: zip
run: | run: |
ZIP_NAME="${{ steps.meta.outputs.zip_name }}" ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
cd build/package cd build/package
zip -r "../${ZIP_NAME}" . zip -r "../${ZIP_NAME}" .
cd .. cd ..
SHA256=$(sha256sum "${ZIP_NAME}" | cut -d' ' -f1) SHA256=$(sha256sum "${ZIP_NAME}" | cut -d' ' -f1)
echo "sha256=${SHA256}" >> "$GITHUB_OUTPUT" echo "sha256=${SHA256}" >> "$GITHUB_OUTPUT"
echo "ZIP: ${ZIP_NAME} (SHA: ${SHA256:0:16}...)" echo "ZIP: ${ZIP_NAME} (SHA: ${SHA256:0:16}...)"
- name: Create or replace Gitea release - name: Create or replace Gitea release
id: release id: release
run: | run: |
TAG="${{ steps.meta.outputs.tag }}" TAG="${{ steps.meta.outputs.tag }}"
VERSION="${{ steps.meta.outputs.version }}" VERSION="${{ steps.meta.outputs.version }}"
STABILITY="${{ steps.meta.outputs.stability }}" STABILITY="${{ steps.meta.outputs.stability }}"
SHA256="${{ steps.zip.outputs.sha256 }}" SHA256="${{ steps.zip.outputs.sha256 }}"
ZIP_NAME="${{ steps.meta.outputs.zip_name }}" ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
EXT_ELEMENT="${{ steps.meta.outputs.ext_element }}" EXT_ELEMENT="${{ steps.meta.outputs.ext_element }}"
TOKEN="${{ secrets.GA_TOKEN }}" TOKEN="${{ secrets.GA_TOKEN }}"
API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
BRANCH=$(git branch --show-current) BRANCH=$(git branch --show-current)
BODY="## ${VERSION} ($(date +%Y-%m-%d)) BODY="## ${VERSION} ($(date +%Y-%m-%d))
**Channel:** ${STABILITY} **Channel:** ${STABILITY}
**SHA-256:** \`${SHA256}\`" **SHA-256:** \`${SHA256}\`"
# Delete existing release # Delete existing release
EXISTING_ID=$(curl -sS -H "Authorization: token ${TOKEN}" \ EXISTING_ID=$(curl -sS -H "Authorization: token ${TOKEN}" \
"${API}/releases/tags/${TAG}" | jq -r '.id // empty' 2>/dev/null) "${API}/releases/tags/${TAG}" | jq -r '.id // empty' 2>/dev/null)
if [ -n "$EXISTING_ID" ]; then if [ -n "$EXISTING_ID" ]; then
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \ curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \
"${API}/releases/${EXISTING_ID}" 2>/dev/null || true "${API}/releases/${EXISTING_ID}" 2>/dev/null || true
curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \ curl -sS -X DELETE -H "Authorization: token ${TOKEN}" \
"${API}/tags/${TAG}" 2>/dev/null || true "${API}/tags/${TAG}" 2>/dev/null || true
fi fi
# Create release # Create release
RELEASE_ID=$(curl -sS -X POST -H "Authorization: token ${TOKEN}" \ RELEASE_ID=$(curl -sS -X POST -H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"${API}/releases" \ "${API}/releases" \
-d "$(jq -n \ -d "$(jq -n \
--arg tag "$TAG" \ --arg tag "$TAG" \
--arg target "$BRANCH" \ --arg target "$BRANCH" \
--arg name "${EXT_ELEMENT} ${VERSION} (${STABILITY})" \ --arg name "${EXT_ELEMENT} ${VERSION} (${STABILITY})" \
--arg body "$BODY" \ --arg body "$BODY" \
'{tag_name: $tag, target_commitish: $target, name: $name, body: $body, prerelease: true}' '{tag_name: $tag, target_commitish: $target, name: $name, body: $body, prerelease: true}'
)" | jq -r '.id') )" | jq -r '.id')
echo "release_id=${RELEASE_ID}" >> "$GITHUB_OUTPUT" echo "release_id=${RELEASE_ID}" >> "$GITHUB_OUTPUT"
# Upload ZIP # Upload ZIP
curl -sS -X POST -H "Authorization: token ${TOKEN}" \ curl -sS -X POST -H "Authorization: token ${TOKEN}" \
-H "Content-Type: application/octet-stream" \ -H "Content-Type: application/octet-stream" \
"${API}/releases/${RELEASE_ID}/assets?name=${ZIP_NAME}" \ "${API}/releases/${RELEASE_ID}/assets?name=${ZIP_NAME}" \
--data-binary "@build/${ZIP_NAME}" --data-binary "@build/${ZIP_NAME}"
echo "Released: ${EXT_ELEMENT} ${VERSION} (${STABILITY})" echo "Released: ${EXT_ELEMENT} ${VERSION} (${STABILITY})"
- name: Update updates.xml - name: Update updates.xml
if: steps.platform.outputs.platform == 'joomla' if: steps.platform.outputs.platform == 'joomla'
run: | run: |
STABILITY="${{ steps.meta.outputs.stability }}" STABILITY="${{ steps.meta.outputs.stability }}"
VERSION="${{ steps.meta.outputs.version }}" VERSION="${{ steps.meta.outputs.version }}"
SHA256="${{ steps.zip.outputs.sha256 }}" SHA256="${{ steps.zip.outputs.sha256 }}"
ZIP_NAME="${{ steps.meta.outputs.zip_name }}" ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
TAG="${{ steps.meta.outputs.tag }}" TAG="${{ steps.meta.outputs.tag }}"
if [ ! -f "updates.xml" ]; then if [ ! -f "updates.xml" ]; then
echo "No updates.xml -- skipping" echo "No updates.xml -- skipping"
exit 0 exit 0
fi fi
# Map stability to XML tag name # Map stability to XML tag name
case "$STABILITY" in case "$STABILITY" in
development) XML_TAG="development" ;; development) XML_TAG="development" ;;
alpha) XML_TAG="alpha" ;; alpha) XML_TAG="alpha" ;;
beta) XML_TAG="beta" ;; beta) XML_TAG="beta" ;;
release-candidate) XML_TAG="rc" ;; release-candidate) XML_TAG="rc" ;;
*) XML_TAG="$STABILITY" ;; *) XML_TAG="$STABILITY" ;;
esac esac
DOWNLOAD_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${TAG}/${ZIP_NAME}" DOWNLOAD_URL="${GITEA_URL}/${GITEA_ORG}/${GITEA_REPO}/releases/download/${TAG}/${ZIP_NAME}"
# Use PHP to update the channel in updates.xml # Use PHP to update the channel in updates.xml
php -r ' php -r '
$xml_tag = $argv[1]; $xml_tag = $argv[1];
$version = $argv[2]; $version = $argv[2];
$sha256 = $argv[3]; $sha256 = $argv[3];
$url = $argv[4]; $url = $argv[4];
$date = date("Y-m-d"); $date = date("Y-m-d");
$content = file_get_contents("updates.xml"); $content = file_get_contents("updates.xml");
$pattern = "/(<update>(?:(?!<\/update>).)*?<tag>" . preg_quote($xml_tag) . "<\/tag>.*?<\/update>)/s"; $pattern = "/(<update>(?:(?!<\/update>).)*?<tag>" . preg_quote($xml_tag) . "<\/tag>.*?<\/update>)/s";
$content = preg_replace_callback($pattern, function($m) use ($version, $sha256, $url, $date) { $content = preg_replace_callback($pattern, function($m) use ($version, $sha256, $url, $date) {
$block = $m[0]; $block = $m[0];
$block = preg_replace("/<version>[^<]*<\/version>/", "<version>{$version}</version>", $block); $block = preg_replace("/<version>[^<]*<\/version>/", "<version>{$version}</version>", $block);
if (strpos($block, "<sha256>") !== false) { if (strpos($block, "<sha256>") !== false) {
$block = preg_replace("/<sha256>[^<]*<\/sha256>/", "<sha256>{$sha256}</sha256>", $block); $block = preg_replace("/<sha256>[^<]*<\/sha256>/", "<sha256>{$sha256}</sha256>", $block);
} else { } else {
$block = str_replace("</downloads>", "</downloads>\n <sha256>{$sha256}</sha256>", $block); $block = str_replace("</downloads>", "</downloads>\n <sha256>{$sha256}</sha256>", $block);
} }
$block = preg_replace("/(<downloadurl[^>]*>)[^<]*(<\/downloadurl>)/", "\${1}{$url}\${2}", $block); $block = preg_replace("/(<downloadurl[^>]*>)[^<]*(<\/downloadurl>)/", "\${1}{$url}\${2}", $block);
return $block; return $block;
}, $content); }, $content);
file_put_contents("updates.xml", $content); file_put_contents("updates.xml", $content);
echo "Updated {$xml_tag} channel: version={$version}\n"; echo "Updated {$xml_tag} channel: version={$version}\n";
' "$XML_TAG" "$VERSION" "$SHA256" "$DOWNLOAD_URL" ' "$XML_TAG" "$VERSION" "$SHA256" "$DOWNLOAD_URL"
# Commit and push # Commit and push
if ! git diff --quiet updates.xml 2>/dev/null; then if ! git diff --quiet updates.xml 2>/dev/null; then
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]" git config --local user.name "gitea-actions[bot]"
git add updates.xml git add updates.xml
git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]" git commit -m "chore: update ${STABILITY} channel ${VERSION} [skip ci]"
git push origin HEAD 2>&1 || echo "WARNING: push failed" git push origin HEAD 2>&1 || echo "WARNING: push failed"
fi fi
- name: "Sync updates.xml to all branches" - name: "Sync updates.xml to all branches"
if: steps.platform.outputs.platform == 'joomla' if: steps.platform.outputs.platform == 'joomla'
run: | run: |
CURRENT_BRANCH="${{ github.ref_name }}" CURRENT_BRANCH="${{ github.ref_name }}"
git config --local user.email "gitea-actions[bot]@mokoconsulting.tech" git config --local user.email "gitea-actions[bot]@mokoconsulting.tech"
git config --local user.name "gitea-actions[bot]" git config --local user.name "gitea-actions[bot]"
for BRANCH in main dev; do for BRANCH in main dev; do
[ "$BRANCH" = "$CURRENT_BRANCH" ] && continue [ "$BRANCH" = "$CURRENT_BRANCH" ] && continue
echo "Syncing updates.xml -> ${BRANCH}" echo "Syncing updates.xml -> ${BRANCH}"
git fetch origin "${BRANCH}" 2>/dev/null || continue git fetch origin "${BRANCH}" 2>/dev/null || continue
git checkout "origin/${BRANCH}" -- . 2>/dev/null || continue git checkout "origin/${BRANCH}" -- . 2>/dev/null || continue
git checkout "${CURRENT_BRANCH}" -- updates.xml git checkout "${CURRENT_BRANCH}" -- updates.xml
if ! git diff --quiet updates.xml 2>/dev/null; then if ! git diff --quiet updates.xml 2>/dev/null; then
git add updates.xml git add updates.xml
git commit -m "chore: sync updates.xml from ${CURRENT_BRANCH} [skip ci]" git commit -m "chore: sync updates.xml from ${CURRENT_BRANCH} [skip ci]"
git push origin HEAD:refs/heads/${BRANCH} 2>&1 || echo "WARNING: push to ${BRANCH} failed" git push origin HEAD:refs/heads/${BRANCH} 2>&1 || echo "WARNING: push to ${BRANCH} failed"
fi fi
git checkout "${CURRENT_BRANCH}" 2>/dev/null git checkout "${CURRENT_BRANCH}" 2>/dev/null
done done
- name: "Delete lesser pre-release channels (cascade)" - name: "Delete lesser pre-release channels (cascade)"
continue-on-error: true continue-on-error: true
run: | run: |
API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}" API_BASE="${GITEA_URL}/api/v1/repos/${GITEA_ORG}/${GITEA_REPO}"
TOKEN="${{ secrets.GA_TOKEN }}" TOKEN="${{ secrets.GA_TOKEN }}"
php ${MOKO_CLI}/release_cascade.php \ php ${MOKO_CLI}/release_cascade.php \
--stability "${{ steps.meta.outputs.stability }}" \ --stability "${{ steps.meta.outputs.stability }}" \
--token "${TOKEN}" \ --token "${TOKEN}" \
--api-base "${API_BASE}" --api-base "${API_BASE}"
- name: Summary - name: Summary
if: always() if: always()
run: | run: |
VERSION="${{ steps.meta.outputs.version }}" VERSION="${{ steps.meta.outputs.version }}"
STABILITY="${{ steps.meta.outputs.stability }}" STABILITY="${{ steps.meta.outputs.stability }}"
ZIP_NAME="${{ steps.meta.outputs.zip_name }}" ZIP_NAME="${{ steps.meta.outputs.zip_name }}"
SHA256="${{ steps.zip.outputs.sha256 }}" SHA256="${{ steps.zip.outputs.sha256 }}"
echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY echo "## Pre-Release Complete" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY echo "| Version | \`${VERSION}\` |" >> $GITHUB_STEP_SUMMARY
echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY echo "| Channel | ${STABILITY} |" >> $GITHUB_STEP_SUMMARY
echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY echo "| Package | \`${ZIP_NAME}\` |" >> $GITHUB_STEP_SUMMARY
echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY echo "| SHA-256 | \`${SHA256:-n/a}\` |" >> $GITHUB_STEP_SUMMARY
+26
View File
@@ -18,6 +18,32 @@ Version format: `XX.YY.ZZ` (zero-padded semver).
## [Unreleased] ## [Unreleased]
## [09.00.00] - 2026-05-26
### Added
- PHPDoc on Priority 1 Enterprise classes (CliFramework, adapters, ApiClient)
- Wiki: Coding-Standards page with PHPDoc standard, PHPCS exclusions, file patterns
- CI: PHPStan enforced at level 6 (was advisory), PHPUnit blocks on failure
### Fixed
- `updates_xml_build.php`: cascade entries down to lower channels — stable now writes all 5 entries instead of wiping them
- `updates_xml_build.php`: separate Joomla stability tags (`dev`, `rc`) from Gitea release tags (`development`, `release-candidate`) — download URLs now point to correct release assets
- `updates_xml_build.php`: only emit `<client>site</client>` for templates and modules, not packages or components
- `updates_xml_build.php`: preservation logic matches Joomla tag names when deciding which existing entries to keep
## [08.00.00] - 2026-05-26
### Changed
- PHPStan: level 5 → 6 (401 baselined, 0 new errors)
- Branch protection: 5 required checks enabled on main
- Workflows synced to all governed repos (72+ repos across 3 orgs)
- Flushed 44 stale runners from Gitea admin (3 active remain)
### Fixed
- PHPStan level 3→4: removed 13 dead properties, 41 defensive patterns baselined
- PHPStan level 4→5: fixed metrics `increment()` bug (labels passed as value param)
- PHPStan level 5→6: 360 missing array generic types baselined
## [07.00.00] - 2026-05-25 ## [07.00.00] - 2026-05-25
### Added ### Added
-2
View File
@@ -53,7 +53,6 @@ class BulkJoomlaTemplate extends CliFramework
public const VERSION = '04.06.10'; public const VERSION = '04.06.10';
private GitPlatformAdapter $adapter; private GitPlatformAdapter $adapter;
private AuditLogger $logger;
private Config $config; private Config $config;
protected function configure(): void protected function configure(): void
@@ -85,7 +84,6 @@ class BulkJoomlaTemplate extends CliFramework
return 1; return 1;
} }
$this->logger = new AuditLogger('joomla_template');
$org = $this->getArgument('--org', self::DEFAULT_ORG); $org = $this->getArgument('--org', self::DEFAULT_ORG);
$platform = $this->adapter->getPlatformName(); $platform = $this->adapter->getPlatformName();
$this->log("Platform: {$platform} | Organization: {$org}", 'INFO'); $this->log("Platform: {$platform} | Organization: {$org}", 'INFO');
+1 -7
View File
@@ -66,9 +66,6 @@ class BulkSync extends CliFramework
private AuditLogger $logger; private AuditLogger $logger;
private CheckpointManager $checkpoints; private CheckpointManager $checkpoints;
private MetricsCollector $metrics; private MetricsCollector $metrics;
private SecurityValidator $security;
private PluginFactory $pluginFactory;
private ProjectTypeDetector $typeDetector;
private Config $config; private Config $config;
/** Set to true by signal handler or rate-limit detection to abort the sync loop gracefully. */ /** Set to true by signal handler or rate-limit detection to abort the sync loop gracefully. */
@@ -204,7 +201,6 @@ class BulkSync extends CliFramework
$this->logger = new AuditLogger('bulk_sync'); $this->logger = new AuditLogger('bulk_sync');
$this->metrics = new MetricsCollector(); $this->metrics = new MetricsCollector();
$this->checkpoints = new CheckpointManager('.checkpoints'); $this->checkpoints = new CheckpointManager('.checkpoints');
$this->security = new SecurityValidator();
$this->synchronizer = new RepositorySynchronizer( $this->synchronizer = new RepositorySynchronizer(
$this->api, $this->api,
$this->logger, $this->logger,
@@ -215,8 +211,6 @@ class BulkSync extends CliFramework
); );
// Initialize plugin system // Initialize plugin system
$this->pluginFactory = new PluginFactory($this->logger, $this->metrics);
$this->typeDetector = new ProjectTypeDetector($this->logger);
$this->log("✓ Enterprise components initialized for platform: {$platform}", 'INFO'); $this->log("✓ Enterprise components initialized for platform: {$platform}", 'INFO');
return true; return true;
@@ -288,7 +282,7 @@ class BulkSync extends CliFramework
} }
} }
return array_values(array_merge($priority, $rest)); return array_merge($priority, $rest);
} }
/** /**
-4
View File
@@ -58,8 +58,6 @@ class RepoCleanup extends CliFramework
private ApiClient $api; private ApiClient $api;
private GitPlatformAdapter $adapter; private GitPlatformAdapter $adapter;
private AuditLogger $logger;
private MetricsCollector $metrics;
protected bool $dryRun = false; protected bool $dryRun = false;
private float $startTime; private float $startTime;
@@ -99,8 +97,6 @@ class RepoCleanup extends CliFramework
return 1; return 1;
} }
$this->logger = new AuditLogger('repo_cleanup');
$this->metrics = new MetricsCollector('repo_cleanup');
$this->logMsg("🧹 MokoStandards Repository Cleanup v" . self::VERSION); $this->logMsg("🧹 MokoStandards Repository Cleanup v" . self::VERSION);
$this->logMsg("Organization: {$org}"); $this->logMsg("Organization: {$org}");
-2
View File
@@ -48,7 +48,6 @@ class JoomlaRelease extends CliFramework
]; ];
private ApiClient $api; private ApiClient $api;
private AuditLogger $logger;
private \MokoEnterprise\GitPlatformAdapter $adapter; private \MokoEnterprise\GitPlatformAdapter $adapter;
protected function configure(): void protected function configure(): void
@@ -76,7 +75,6 @@ class JoomlaRelease extends CliFramework
$config = Config::load(); $config = Config::load();
$this->adapter = PlatformAdapterFactory::create($config); $this->adapter = PlatformAdapterFactory::create($config);
$this->api = $this->adapter->getApiClient(); $this->api = $this->adapter->getApiClient();
$this->logger = new AuditLogger('joomla_release');
if ($repo !== '') { if ($repo !== '') {
$path = $this->cloneRepo($repo); $path = $this->cloneRepo($repo);
+47 -38
View File
@@ -194,29 +194,35 @@ $stabilitySuffixMap = [
'development' => '-dev', 'development' => '-dev',
]; ];
// Joomla <tags><tag> values — maps to Joomla's stabilityTagToInteger()
$stabilityTagMap = [ $stabilityTagMap = [
'stable' => 'stable', 'stable' => 'stable',
'rc' => 'rc', 'rc' => 'rc',
'beta' => 'beta', 'beta' => 'beta',
'alpha' => 'alpha', 'alpha' => 'alpha',
'development' => 'dev',
];
// Gitea release tag names (used in download/info URLs)
$releaseTagMap = [
'stable' => 'stable',
'rc' => 'release-candidate',
'beta' => 'beta',
'alpha' => 'alpha',
'development' => 'development', 'development' => 'development',
]; ];
// -- Build update entries ----------------------------------------------------- // -- Build update entries -----------------------------------------------------
$releaseTag = $stabilityTagMap[$stability] ?? $stability;
// For the primary entry: apply suffix if not stable // For the primary entry: apply suffix if not stable
$primarySuffix = $stabilitySuffixMap[$stability] ?? ''; $primarySuffix = $stabilitySuffixMap[$stability] ?? '';
$primaryVersion = $version . $primarySuffix; $primaryVersion = $version . $primarySuffix;
$downloadUrl = "{$giteaUrl}/{$org}/{$repo}/releases/download/{$releaseTag}/{$typePrefix}{$extElement}-{$primaryVersion}.zip"; // Build client tag — only needed for templates and modules (site vs admin).
$infoUrl = "{$giteaUrl}/{$org}/{$repo}/releases/tag/{$releaseTag}"; // Packages and components don't use client; plugins use folder instead.
// Build client tag
$clientTag = ''; $clientTag = '';
if (!empty($extClient)) { if (!empty($extClient)) {
$clientTag = " <client>{$extClient}</client>"; $clientTag = " <client>{$extClient}</client>";
} elseif ($extType === 'module' || $extType === 'plugin') { } elseif (in_array($extType, ['template', 'module'])) {
$clientTag = ' <client>site</client>'; $clientTag = ' <client>site</client>';
} }
@@ -282,41 +288,44 @@ function buildEntry(
} }
// -- Determine which channels to write ---------------------------------------- // -- Determine which channels to write ----------------------------------------
// Stable cascades to all channels; pre-releases only write their level and below // Stable cascades to all channels; pre-releases cascade down to lower channels.
// Each channel gets its own suffixed version: // Each channel entry represents "latest release available at this stability or higher".
// development -> 04.01.00-dev // When stable releases, ALL channels point to stable (it's the newest for everyone).
// alpha -> 04.01.00-alpha // When RC releases, rc/beta/alpha/dev point to RC; stable is preserved.
// beta -> 04.01.00-beta // When dev releases, only dev is updated; everything else is preserved.
// rc -> 04.01.00-rc
// stable -> 04.01.00
$allChannels = ['development', 'alpha', 'beta', 'rc', 'stable']; $allChannels = ['development', 'alpha', 'beta', 'rc', 'stable'];
$stabilityIndex = array_search($stability === 'development' ? 'development' : $stability, $allChannels); $stabilityIndex = array_search($stability === 'development' ? 'development' : $stability, $allChannels);
if ($stabilityIndex === false) $stabilityIndex = 4; // default to stable if ($stabilityIndex === false) $stabilityIndex = 4; // default to stable
// Write only the current channel entry (not cascade) // Write entries for the current channel AND all lower channels (cascade down)
// Each channel release only creates its own entry; preserved entries handle other channels // All cascaded entries point to the CURRENT release (the highest stability being built)
$entries = []; $entries = [];
$channelName = $allChannels[$stabilityIndex]; $giteaTag = $releaseTagMap[$stability] ?? $stability;
$channelSuffix = $stabilitySuffixMap[$channelName] ?? ''; $channelVersion = $version . ($stabilitySuffixMap[$stability] ?? '');
$channelVersion = $version . $channelSuffix; $channelDownloadUrl = "{$giteaUrl}/{$org}/{$repo}/releases/download/{$giteaTag}/{$typePrefix}{$extElement}-{$channelVersion}.zip";
$channelTag = $stabilityTagMap[$channelName] ?? $channelName; $channelInfoUrl = "{$giteaUrl}/{$org}/{$repo}/releases/tag/{$giteaTag}";
$channelDownloadUrl = "{$giteaUrl}/{$org}/{$repo}/releases/download/{$channelTag}/{$typePrefix}{$extElement}-{$channelVersion}.zip";
$channelInfoUrl = "{$giteaUrl}/{$org}/{$repo}/releases/tag/{$channelTag}";
$entries[] = buildEntry( for ($i = 0; $i <= $stabilityIndex; $i++) {
$channelName, $channelName = $allChannels[$i];
$channelVersion, $joomlaTag = $stabilityTagMap[$channelName] ?? $channelName;
$channelDownloadUrl, // Only attach SHA to the primary channel entry
$extName, $entrySha = ($i === $stabilityIndex) ? $shaTag : '';
$extElement,
$extType, $entries[] = buildEntry(
$clientTag, $joomlaTag,
$folderTag, $channelVersion,
$channelInfoUrl, $channelDownloadUrl,
$targetPlatform, $extName,
$phpTag, $extElement,
$shaTag $extType,
); $clientTag,
$folderTag,
$channelInfoUrl,
$targetPlatform,
$phpTag,
$entrySha
);
}
// -- Preserve existing entries for channels not being updated ----------------- // -- Preserve existing entries for channels not being updated -----------------
$dest = $outputFile ?? "{$root}/updates.xml"; $dest = $outputFile ?? "{$root}/updates.xml";
@@ -325,10 +334,10 @@ $preservedEntries = [];
if (file_exists($dest)) { if (file_exists($dest)) {
$existingXml = @simplexml_load_file($dest); $existingXml = @simplexml_load_file($dest);
if ($existingXml) { if ($existingXml) {
// Channels we're writing — don't preserve these // Joomla tags we're writing — don't preserve these
$writtenChannels = []; $writtenChannels = [];
for ($i = 0; $i <= $stabilityIndex; $i++) { for ($i = 0; $i <= $stabilityIndex; $i++) {
$writtenChannels[] = $allChannels[$i]; $writtenChannels[] = $stabilityTagMap[$allChannels[$i]] ?? $allChannels[$i];
} }
foreach ($existingXml->update as $existingUpdate) { foreach ($existingXml->update as $existingUpdate) {
+138
View File
@@ -0,0 +1,138 @@
#!/usr/bin/env php
<?php
/* Copyright (C) 2026 Moko Consulting <hello@mokoconsulting.tech>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
* FILE INFORMATION
* DEFGROUP: moko-platform.CLI
* INGROUP: moko-platform
* REPO: https://git.mokoconsulting.tech/MokoConsulting/moko-platform
* PATH: /cli/version_check.php
* VERSION: 05.00.00
* BRIEF: Validate version consistency across README, manifests, and sub-packages
*
* Usage:
* php version_check.php --path /repo
* php version_check.php --path /repo --strict # exit 1 on mismatch
* php version_check.php --path /repo --fix # fix mismatches to highest version
*/
declare(strict_types=1);
$path = '.';
$strict = false;
$fix = false;
foreach ($argv as $i => $arg) {
if ($arg === '--path' && isset($argv[$i + 1])) $path = $argv[$i + 1];
if ($arg === '--strict') $strict = true;
if ($arg === '--fix') $fix = true;
}
$root = realpath($path) ?: $path;
$errors = 0;
$versions = [];
// ── Read README.md version ───────────────────────────────────────────────────
$readme = "{$root}/README.md";
if (file_exists($readme)) {
$content = file_get_contents($readme);
if (preg_match('/VERSION:\s*(\d{2}\.\d{2}\.\d{2})/m', $content, $m)) {
$versions['README.md'] = $m[1];
}
}
// ── Read manifest XML versions ───────────────────────────────────────────────
$xmlGlobs = [
"{$root}/src/pkg_*.xml",
"{$root}/src/*.xml",
"{$root}/src/packages/*/*.xml",
"{$root}/*.xml",
];
foreach ($xmlGlobs as $glob) {
foreach (glob($glob) ?: [] as $file) {
// Skip updates.xml
if (basename($file) === 'updates.xml') continue;
$xmlContent = file_get_contents($file);
if (strpos($xmlContent, '<extension') === false) continue;
if (preg_match('|<version>(\d{2}\.\d{2}\.\d{2})(?:-[a-z]+)?</version>|', $xmlContent, $xm)) {
$relPath = str_replace($root . '/', '', $file);
$relPath = str_replace($root . '\\', '', $relPath);
$versions[$relPath] = $xm[1];
}
}
}
if (empty($versions)) {
fwrite(STDERR, "No version sources found\n");
exit(1);
}
// ── Compare versions ─────────────────────────────────────────────────────────
$uniqueVersions = array_unique(array_values($versions));
$highestVersion = '00.00.00';
foreach ($versions as $v) {
if (version_compare($v, $highestVersion, '>')) {
$highestVersion = $v;
}
}
echo "=== Version Consistency Check ===\n";
foreach ($versions as $source => $ver) {
$status = ($ver === $highestVersion) ? 'OK' : 'MISMATCH';
if ($status === 'MISMATCH') $errors++;
echo sprintf(" %-50s %s %s\n", $source, $ver, $status === 'OK' ? '' : "** MISMATCH (expected {$highestVersion})");
}
if (count($uniqueVersions) === 1) {
echo "\nAll {$ver} — consistent.\n";
} else {
echo "\n** {$errors} mismatch(es) found. Highest version: {$highestVersion}\n";
if ($fix) {
echo "\n=== Fixing mismatches to {$highestVersion} ===\n";
// Fix README.md
if (isset($versions['README.md']) && $versions['README.md'] !== $highestVersion) {
$content = file_get_contents($readme);
$content = preg_replace(
'/(VERSION:\s*)\d{2}\.\d{2}\.\d{2}/m',
'${1}' . $highestVersion,
$content,
1
);
file_put_contents($readme, $content);
echo " Fixed: README.md -> {$highestVersion}\n";
}
// Fix XML manifests
foreach ($versions as $source => $ver) {
if ($source === 'README.md') continue;
if ($ver === $highestVersion) continue;
$file = "{$root}/{$source}";
if (!file_exists($file)) continue;
$content = file_get_contents($file);
$content = preg_replace(
'|<version>[^<]*</version>|',
"<version>{$highestVersion}</version>",
$content
);
file_put_contents($file, $content);
echo " Fixed: {$source} -> {$highestVersion}\n";
}
echo "Done.\n";
}
}
if ($strict && $errors > 0) {
exit(1);
}
exit(0);
+1 -1
View File
@@ -2,7 +2,7 @@
"name": "mokoconsulting-tech/enterprise", "name": "mokoconsulting-tech/enterprise",
"description": "MokoStandards Enterprise API \u2014 PHP implementation", "description": "MokoStandards Enterprise API \u2014 PHP implementation",
"type": "library", "type": "library",
"version": "07.00.00", "version": "09.00.00",
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"authors": [ "authors": [
{ {
+2 -2
View File
@@ -92,6 +92,8 @@ class CircuitBreakerOpen extends RuntimeException
* ); * );
* $response = $client->get('/repos/owner/repo'); * $response = $client->get('/repos/owner/repo');
* ``` * ```
*
* @since 04.00.00
*/ */
class ApiClient class ApiClient
{ {
@@ -124,7 +126,6 @@ class ApiClient
private ?DateTime $circuitLastFailure = null; private ?DateTime $circuitLastFailure = null;
/** @var LoggerInterface|null Optional logger instance */ /** @var LoggerInterface|null Optional logger instance */
private ?LoggerInterface $logger = null;
/** @var array<string, mixed> Request metrics */ /** @var array<string, mixed> Request metrics */
private array $metrics = [ private array $metrics = [
@@ -179,7 +180,6 @@ class ApiClient
$this->circuitBreakerTimeout = $circuitBreakerTimeout; $this->circuitBreakerTimeout = $circuitBreakerTimeout;
$this->enableCaching = $enableCaching; $this->enableCaching = $enableCaching;
$this->userAgent = $userAgent; $this->userAgent = $userAgent;
$this->logger = $logger;
$this->authScheme = $authScheme; $this->authScheme = $authScheme;
// Initialize HTTP client // Initialize HTTP client
+13
View File
@@ -716,6 +716,9 @@ class ValidationCLI extends CLIApp
* Lifecycle: configure() -> parseArguments() -> printBanner() -> initialize() -> run() * Lifecycle: configure() -> parseArguments() -> printBanner() -> initialize() -> run()
* *
* All new scripts must extend CliFramework and implement configure() + run(). * All new scripts must extend CliFramework and implement configure() + run().
*
* @since 04.00.15
* @see CLIApp Legacy base class (deprecated)
*/ */
abstract class CliFramework abstract class CliFramework
{ {
@@ -932,6 +935,11 @@ abstract class CliFramework
// Argument parsing (internal) // Argument parsing (internal)
// ========================================================================= // =========================================================================
/**
* Parse CLI arguments from $_SERVER['argv'] into registered argument definitions.
*
* @since 04.00.15
*/
private function parseArguments(): void private function parseArguments(): void
{ {
$argv = array_slice($_SERVER['argv'] ?? [], 1); $argv = array_slice($_SERVER['argv'] ?? [], 1);
@@ -970,6 +978,11 @@ abstract class CliFramework
// Help screen // Help screen
// ========================================================================= // =========================================================================
/**
* Print auto-generated help screen from registered arguments.
*
* @since 04.00.15
*/
protected function printHelp(): void protected function printHelp(): void
{ {
$w = $this->termWidth(); $w = $this->termWidth();
+7 -2
View File
@@ -32,12 +32,17 @@ use RuntimeException;
* - Workflow dir: .github/workflows * - Workflow dir: .github/workflows
* *
* @package MokoStandards\Enterprise * @package MokoStandards\Enterprise
* @version 04.06.10 * @since 04.06.10
* @see GitPlatformAdapter
*/ */
class GitHubAdapter implements GitPlatformAdapter class GitHubAdapter implements GitPlatformAdapter
{ {
/** @var ApiClient HTTP client for GitHub API calls. */
private ApiClient $apiClient; private ApiClient $apiClient;
/**
* @param ApiClient $apiClient Configured API client for api.github.com
*/
public function __construct(ApiClient $apiClient) public function __construct(ApiClient $apiClient)
{ {
$this->apiClient = $apiClient; $this->apiClient = $apiClient;
@@ -405,7 +410,7 @@ class GitHubAdapter implements GitPlatformAdapter
$page++; $page++;
} }
return $all; return array_values($all);
} }
// ────────────────────────────────────────────── // ──────────────────────────────────────────────
+7 -7
View File
@@ -175,7 +175,7 @@ interface GitPlatformAdapter
/** /**
* List all branches in a repository. * List all branches in a repository.
* *
* @return array<int, array<string, mixed>> * @return array<mixed>
*/ */
public function listBranches(string $org, string $repo): array; public function listBranches(string $org, string $repo): array;
@@ -202,7 +202,7 @@ interface GitPlatformAdapter
* @param string $repo Repository name * @param string $repo Repository name
* @param string $path File path within the repository * @param string $path File path within the repository
* @param string|null $ref Branch/tag/SHA reference (null = default branch) * @param string|null $ref Branch/tag/SHA reference (null = default branch)
* @return array{content: string, sha: string, size: int, encoding: string} File data (content is base64-encoded) * @return array<string, mixed> File data (content is base64-encoded)
*/ */
public function getFileContents(string $org, string $repo, string $path, ?string $ref = null): array; public function getFileContents(string $org, string $repo, string $path, ?string $ref = null): array;
@@ -258,7 +258,7 @@ interface GitPlatformAdapter
* @param string $org Organization name * @param string $org Organization name
* @param string $repo Repository name * @param string $repo Repository name
* @param array<string, mixed> $filters Filters (state, head, base, sort, direction) * @param array<string, mixed> $filters Filters (state, head, base, sort, direction)
* @return array<int, array<string, mixed>> Pull request list * @return array<mixed> Pull request list
*/ */
public function listPullRequests(string $org, string $repo, array $filters = []): array; public function listPullRequests(string $org, string $repo, array $filters = []): array;
@@ -305,7 +305,7 @@ interface GitPlatformAdapter
* @param string $org Organization name * @param string $org Organization name
* @param string $repo Repository name * @param string $repo Repository name
* @param array<string, mixed> $filters Filters (state, labels, assignee, etc.) * @param array<string, mixed> $filters Filters (state, labels, assignee, etc.)
* @return array<int, array<string, mixed>> Issue list * @return array<mixed> Issue list
*/ */
public function listIssues(string $org, string $repo, array $filters = []): array; public function listIssues(string $org, string $repo, array $filters = []): array;
@@ -357,7 +357,7 @@ interface GitPlatformAdapter
* *
* @param string $org Organization name * @param string $org Organization name
* @param string $repo Repository name * @param string $repo Repository name
* @return array<int, array{name: string, color: string, description: string}> Label list * @return array<mixed> Label list
*/ */
public function listLabels(string $org, string $repo): array; public function listLabels(string $org, string $repo): array;
@@ -406,7 +406,7 @@ interface GitPlatformAdapter
* *
* @param string $org Organization name * @param string $org Organization name
* @param string $repo Repository name * @param string $repo Repository name
* @return array<int, array<string, mixed>> Protection rules * @return array<mixed> Protection rules
*/ */
public function listBranchProtections(string $org, string $repo): array; public function listBranchProtections(string $org, string $repo): array;
@@ -445,7 +445,7 @@ interface GitPlatformAdapter
* @param string $endpoint API endpoint path * @param string $endpoint API endpoint path
* @param array<string, mixed> $params Query parameters * @param array<string, mixed> $params Query parameters
* @param int $perPage Items per page (platform default if 0) * @param int $perPage Items per page (platform default if 0)
* @return array<int, array<string, mixed>> All items across all pages * @return array<mixed> All items across all pages
*/ */
public function paginateAll(string $endpoint, array $params = [], int $perPage = 100): array; public function paginateAll(string $endpoint, array $params = [], int $perPage = 100): array;
+10 -2
View File
@@ -34,13 +34,21 @@ use RuntimeException;
* - Workflow dir: .mokogitea/workflows * - Workflow dir: .mokogitea/workflows
* *
* @package MokoStandards\Enterprise * @package MokoStandards\Enterprise
* @version 04.06.10 * @since 04.06.10
* @see GitPlatformAdapter
*/ */
class MokoGiteaAdapter implements GitPlatformAdapter class MokoGiteaAdapter implements GitPlatformAdapter
{ {
/** @var ApiClient HTTP client for Gitea API calls. */
private ApiClient $apiClient; private ApiClient $apiClient;
/** @var string Base URL for Gitea API (e.g. https://git.mokoconsulting.tech/api/v1). */
private string $baseUrl; private string $baseUrl;
/**
* @param ApiClient $apiClient Configured API client
* @param string $baseUrl Gitea API base URL
*/
public function __construct(ApiClient $apiClient, string $baseUrl = 'https://git.mokoconsulting.tech/api/v1') public function __construct(ApiClient $apiClient, string $baseUrl = 'https://git.mokoconsulting.tech/api/v1')
{ {
$this->apiClient = $apiClient; $this->apiClient = $apiClient;
@@ -468,7 +476,7 @@ class MokoGiteaAdapter implements GitPlatformAdapter
$page++; $page++;
} }
return $all; return array_values($all);
} }
// ────────────────────────────────────────────── // ──────────────────────────────────────────────
@@ -29,7 +29,6 @@ class RepositoryHealthChecker
{ {
private AuditLogger $logger; private AuditLogger $logger;
private MetricsCollector $metrics; private MetricsCollector $metrics;
private UnifiedValidator $validator;
private array $results = [ private array $results = [
'categories' => [], 'categories' => [],
@@ -50,7 +49,6 @@ class RepositoryHealthChecker
) { ) {
$this->logger = $logger ?? new AuditLogger('repo_health_checker'); $this->logger = $logger ?? new AuditLogger('repo_health_checker');
$this->metrics = $metrics ?? new MetricsCollector(); $this->metrics = $metrics ?? new MetricsCollector();
$this->validator = $validator ?? new UnifiedValidator();
} }
/** /**
+3 -5
View File
@@ -39,7 +39,6 @@ class RepositorySynchronizer
private const VERSION_BRANCH = 'version/' . self::STANDARDS_MAJOR; private const VERSION_BRANCH = 'version/' . self::STANDARDS_MAJOR;
private const SYNC_BRANCH = 'chore/sync-mokostandards-v' . self::STANDARDS_MINOR; private const SYNC_BRANCH = 'chore/sync-mokostandards-v' . self::STANDARDS_MINOR;
private ApiClient $apiClient;
private GitPlatformAdapter $adapter; private GitPlatformAdapter $adapter;
private AuditLogger $logger; private AuditLogger $logger;
private MetricsCollector $metrics; private MetricsCollector $metrics;
@@ -65,7 +64,6 @@ class RepositorySynchronizer
?DefinitionParser $definitionParser = null, ?DefinitionParser $definitionParser = null,
?GitPlatformAdapter $adapter = null ?GitPlatformAdapter $adapter = null
) { ) {
$this->apiClient = $apiClient;
$this->adapter = $adapter ?? new MokoGiteaAdapter($apiClient); $this->adapter = $adapter ?? new MokoGiteaAdapter($apiClient);
$this->logger = $logger; $this->logger = $logger;
$this->metrics = $metrics; $this->metrics = $metrics;
@@ -1510,16 +1508,16 @@ HCL;
if ($updated) { if ($updated) {
$results['success']++; $results['success']++;
$this->metrics->increment('repos_updated_total', ['status' => 'success']); $this->metrics->increment('repos_updated_total', 1, ['status' => 'success']);
$results['repositories'][$repoName] = 'updated'; $results['repositories'][$repoName] = 'updated';
} else { } else {
$results['skipped']++; $results['skipped']++;
$this->metrics->increment('repos_updated_total', ['status' => 'skipped']); $this->metrics->increment('repos_updated_total', 1, ['status' => 'skipped']);
$results['repositories'][$repoName] = 'skipped'; $results['repositories'][$repoName] = 'skipped';
} }
} catch (Exception $e) { } catch (Exception $e) {
$results['failed']++; $results['failed']++;
$this->metrics->increment('repos_updated_total', ['status' => 'failed']); $this->metrics->increment('repos_updated_total', 1, ['status' => 'failed']);
$results['repositories'][$repoName] = 'failed: ' . $e->getMessage(); $results['repositories'][$repoName] = 'failed: ' . $e->getMessage();
} }
-2
View File
@@ -96,8 +96,6 @@ class TransactionStep
*/ */
class Transaction class Transaction
{ {
private const VERSION = '04.06.00';
private string $name; private string $name;
/** @var array<int, TransactionStep> */ /** @var array<int, TransactionStep> */
private array $steps = []; private array $steps = [];
File diff suppressed because it is too large Load Diff
+4 -1
View File
@@ -6,7 +6,7 @@
# PHPStan configuration for moko-platform projects # PHPStan configuration for moko-platform projects
parameters: parameters:
level: 2 level: 6
paths: paths:
- lib - lib
- validate - validate
@@ -21,3 +21,6 @@ parameters:
checkFunctionNameCase: true checkFunctionNameCase: true
checkInternalClassCaseSensitivity: true checkInternalClassCaseSensitivity: true
includes:
- phpstan-baseline.neon
+1 -1
View File
@@ -25,7 +25,7 @@ final class CheckFileIntegrity
private bool $verbose = false; private bool $verbose = false;
private bool $jsonOutput = false; private bool $jsonOutput = false;
/** @var array{host: string, port: int, user: string, identity: string} */ /** @var array<string, mixed> */
private array $sftpConfig = []; private array $sftpConfig = [];
public function run(): int public function run(): int
-3
View File
@@ -35,10 +35,8 @@ use MokoEnterprise\{AuditLogger, CliFramework, MetricsCollector, PluginFactory};
class RepoHealthChecker extends CliFramework class RepoHealthChecker extends CliFramework
{ {
private const DEFAULT_THRESHOLD = 70.0;
private AuditLogger $logger; private AuditLogger $logger;
private MetricsCollector $metrics; private MetricsCollector $metrics;
private PluginFactory $pluginFactory;
private string $apiBaseUrl = 'https://git.mokoconsulting.tech/api/v1'; private string $apiBaseUrl = 'https://git.mokoconsulting.tech/api/v1';
private array $results = [ private array $results = [
@@ -61,7 +59,6 @@ class RepoHealthChecker extends CliFramework
parent::initialize(); parent::initialize();
$this->logger = new AuditLogger('repo_health_checker'); $this->logger = new AuditLogger('repo_health_checker');
$this->metrics = new MetricsCollector(); $this->metrics = new MetricsCollector();
$this->pluginFactory = new PluginFactory($this->logger, $this->metrics);
$config = \MokoEnterprise\Config::load(); $config = \MokoEnterprise\Config::load();
$this->apiBaseUrl = rtrim($config->getString('gitea.url', 'https://git.mokoconsulting.tech'), '/') . '/api/v1'; $this->apiBaseUrl = rtrim($config->getString('gitea.url', 'https://git.mokoconsulting.tech'), '/') . '/api/v1';
} }
-2
View File
@@ -38,7 +38,6 @@ class DriftScanner extends CliFramework
private const DEFAULT_ORG = 'mokoconsulting-tech'; private const DEFAULT_ORG = 'mokoconsulting-tech';
private ApiClient $apiClient; private ApiClient $apiClient;
private AuditLogger $logger;
private MetricsCollector $metrics; private MetricsCollector $metrics;
private \MokoEnterprise\GitPlatformAdapter $adapter; private \MokoEnterprise\GitPlatformAdapter $adapter;
@@ -60,7 +59,6 @@ class DriftScanner extends CliFramework
{ {
parent::initialize(); parent::initialize();
$this->logger = new AuditLogger('drift_scanner');
$this->metrics = new MetricsCollector(); $this->metrics = new MetricsCollector();
// Initialize API client via platform adapter // Initialize API client via platform adapter